Hydra Launcher
repository·main·Indexed 12 days ago
https://github.com/hydralauncher/hydraAn open-source gaming platform for managing game libraries, featuring cloud saves, achievements, and game discovery. Built with Electron, TypeScript, Python, and Rust, it includes a Python-based RPC for torrent management and a native Rust component for system process listing and image processing.
What's inside Hydra Launcher
- Hydra Launcher is an open-source gaming platform designed to manage your gaming library. It allows users to add owned games, maintain profiles, save progress via Hydra Cloud, unlock achievements, and discover new games through a suggestion algorithm. The project is built using Node.js (Electron, React, TypeScript), Python, and Rust.
Set up local development for Hydra Launcher
mainTo build Hydra Launcher from source, you must satisfy the following environment requirements:
- Node.js & Yarn: For the core application logic and frontend.
- Python 3.9+: Required for the RPC layer. Install dependencies using
pip install -r requirements.txt. - Rust toolchain: Required for the
hydra-nativecomponent.
Automatic Builds
- The
postinstallscript automatically builds the Rust native addon (hydra-native/hydra-native.node). - Packaging scripts automatically trigger the Python RPC build.
# Install Python dependencies pip install -r requirements.txt # Build for specific platforms yarn build:win yarn build:mac yarn build:linux yarn build:unpackHow the Virtual Keyboard handles input and focus
mainThe virtual keyboard operates by monitoring focus events on the document. When an
isEditableTarget(defined as a non-disabled, non-readonly text input or a content-editable element) gains focus, the provider sets that element as thetarget.Key Behaviors:
- Input Injection: When a key is pressed, the keyboard uses
replaceInputSelection,backspaceInput, orinsertContentEditableTextto manipulate the target's value or content, ensuring compatibility with native input events. - Keyboard Avoidance: The keyboard supports two layout modes:
floatingandavoiding. If the target element is obscured by the keyboard area, the provider switches toavoidingmode and attempts to scroll the target container so the element is visible above the keyboard. - Navigation & Shortcuts: The keyboard integrates with gamepad inputs. For example, holding
BUTTON_Xperforms a backspace, andLEFT_BUMPER/RIGHT_BUMPERmove the cursor. It also maps specific gamepad buttons to keyboard actions likeShift(L3),Toggle Layer(R3), andEnter(RT).
- Input Injection: When a key is pressed, the keyboard uses
BigPictureToastFallbackVisual types
mainWhen an
imageUrlis not provided for aBigPictureToastCard, thefallbackVisualprop determines which icon is rendered in the media section:'settings': Renders aGearIcon.'downloads': Renders aDownloadSimpleIcon.- Default (or any other value): Renders the Hydra brand icon.
Disc sorting logic in DiscSelectionModal
mainWhen providing a list of
discsto theDiscSelectionModal, the component automatically sorts them using the following priority:- Region Order: Discs are grouped by their SKU region (e.g., US, EU, JP, KR, ASIA). The order of regions is determined by their appearance in the provided
discsarray. - Label Numbering: Within the same region, discs are sorted numerically based on the first integer found in their
labelstring (e.g., "Disc 1" comes before "Disc 2").
If a disc has no SKU/region, it is placed at the end of the list.
- Region Order: Discs are grouped by their SKU region (e.g., US, EU, JP, KR, ASIA). The order of regions is determined by their appearance in the provided
Validate Magnet URIs and Trackers
mainWhen interacting with the RPC, ensure your data adheres to these validation rules:
- Magnet URIs: Must start with
magnet:, be under 8192 characters, and contain a validxtparameter with aurn:btih:prefix followed by a 40-character hex or 32-character Base32 hash. - Trackers: Must be a list of strings. Each string must be a valid URL with a scheme from the following set:
http,https,udp,ws,wss.
- Magnet URIs: Must start with
Launch the Python RPC via CLI
mainThe Python RPC entrypoint accepts arguments via command line to configure the torrent port, RPC password, and initial download/seeding payloads. It supports three distinct argument formats:
- Legacy format (6 arguments):
[script, torrent_port, http_port, rpc_password, initial_download, initial_seeding] - Stdio format with RPC password (5 arguments):
[script, torrent_port, rpc_password, initial_download, initial_seeding] - Backward-compatible stdio format (4 arguments, no password):
[script, torrent_port, initial_download, initial_seeding]
Communication occurs via
stdin(receiving JSON requests) andstdout(sending JSON responses).# Example using the Stdio format with RPC password python python_rpc/main.py 6881 my_secret_password '{"game_id": 1}' '[]'- Legacy format (6 arguments):
Use the Tabs component
mainThe
Tabscomponent is a highly configurable UI component for managing tabbed navigation. It supports different visual variants, gamepad navigation, and automatic scrolling for overflow content. It can be used as a controlled or uncontrolled component.Variants
default: Standard tab layout with an indicator.segmented: A segmented control style where the indicator spans the background of the active tab.settings: A specialized variant for settings menus, typically used with a different focus model.
Key Features
- Gamepad Support: Automatically handles bumper presses (
LEFT_BUMPER,RIGHT_BUMPER) to switch tabs when configured. - Auto-scrolling: Automatically scrolls the tab list to ensure the active tab is visible in the viewport.
- Focus Management: Can be integrated into a focus region using
HorizontalFocusGroupvia themanageFocusRegionprop.
import { Tabs, type TabsItem } from './path-to-tabs'; const myItems: TabsItem[] = [ { value: 'tab1', label: 'Tab 1' }, { value: 'tab2', label: 'Tab 2', disabled: true }, { value: 'tab3', label: 'Tab 3' }, ]; function MyComponent() { const [val, setVal] = useState('tab1'); return ( <Tabs items={myItems} value={val} onValueChange={setVal} variant="default" selectOnFocus={true} /> ); }Configure Main process aliases and plugins
mainThe
mainconfiguration block defines settings for the Electron main process. It includes source mapping for debugging and path aliases to simplify imports.Path Aliases:
@main:src/main@locales:src/locales@resources:resources@shared:src/shared
Plugins:
externalizeDepsPlugin(): Used to externalize dependencies.swcPlugin(): Used for fast compilation via SWC.
main: { build: { sourcemap: true, }, resolve: { alias: { "@main": resolve("src/main"), "@locales": resolve("src/locales"), "@resources": resolve("resources"), "@shared": resolve("src/shared"), }, }, plugins: [externalizeDepsPlugin(), swcPlugin()], }Configure Big Picture build settings
mainThe
bigPictureconfiguration defines a specialized build target. It uses a specific root directory and output directory, and includes React and SVGR support.Key Settings:
root:src/big-picturebuild.outDir:out/big-picturebuild.rollupOptions.input:src/big-picture/index.htmlcss.postcss.plugins: UsesscopeBigPictureCss()to scope styles.
Path Aliases:
@renderer:src/renderer/src@locales:src/locales@shared:src/shared
bigPicture: { root: "src/big-picture", build: { outDir: "out/big-picture", rollupOptions: { input: resolve("src/big-picture/index.html"), }, }, css: { postcss: { plugins: [scopeBigPictureCss()], }, }, resolve: { alias: { "@renderer": resolve("src/renderer/src"), "@locales": resolve("src/locales"), "@shared": resolve("src/shared"), }, }, plugins: [svgr(), react()], }Configure Preload script plugins
mainThe
preloadconfiguration block manages the Electron preload scripts. Currently, it utilizes theexternalizeDepsPlugin()to ensure dependencies are handled correctly in the preload context.preload: { plugins: [externalizeDepsPlugin()], }Configure the Tabs component props
mainThe
Tabscomponent accepts the following configuration options:Prop Type Description itemsArray<TabsItem<TValue>>Required. The list of tab items to render. valueTValueThe currently selected tab value (for controlled mode). defaultValueTValueThe initial selected tab value (for uncontrolled mode). onValueChange(value: TValue) => voidCallback triggered when the selected tab changes. itemsFocusablebooleanIf false, tabs will not participate in the navigation system. Defaults totrue.manageFocusRegionbooleanIf true, wraps the tab list in aHorizontalFocusGroupfor better focus management. Defaults totrue.selectOnFocusbooleanIf true, moving focus to a tab automatically selects it. Defaults totrue.ignoreInitialFocusSelectionbooleanIf true, prevents the first tab from being automatically selected when the component mounts.animateSegmentedIndicatorbooleanEnables Framer Motion animations for the indicator in segmentedvariant.variant'default' | 'segmented' | 'settings'The visual style of the tabs. beforeTabsReactNodeContent rendered before the tab list. afterTabsReactNodeContent rendered after the tab list. trailingActionReactNodeContent rendered at the end of the tabs container. regionIdstringThe ID of the focus region if manageFocusRegionis enabled.navigationOverridesFocusOverridesCustom navigation behavior for the entire component. ariaLabelstringAccessibility label for the tab list. Defaults to 'Tabs'.classNamestringAdditional CSS classes for the container.