mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-21 18:13:55 +00:00
Merge pull request #1947 from hydralauncher/feat/LBX-452
feat: implement dynamic port discovery for Python RPC service
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"file-type": "^20.5.0",
|
||||
"framer-motion": "^12.15.0",
|
||||
"get-port": "^7.1.0",
|
||||
"hls.js": "^1.5.12",
|
||||
"i18next": "^23.11.2",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from "axios";
|
||||
import http from "node:http";
|
||||
import getPort, { portNumbers } from "get-port";
|
||||
|
||||
import cp from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
@@ -27,11 +28,17 @@ const binaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
|
||||
win32: "hydra-python-rpc.exe",
|
||||
};
|
||||
|
||||
const RPC_PORT_RANGE_START = 8080;
|
||||
const RPC_PORT_RANGE_END = 9000;
|
||||
const DEFAULT_RPC_PORT = 8084;
|
||||
const HEALTH_CHECK_INTERVAL_MS = 100;
|
||||
const HEALTH_CHECK_TIMEOUT_MS = 10000;
|
||||
|
||||
export class PythonRPC {
|
||||
public static readonly BITTORRENT_PORT = "5881";
|
||||
public static readonly RPC_PORT = "8084";
|
||||
|
||||
public static readonly rpc = axios.create({
|
||||
baseURL: `http://localhost:${this.RPC_PORT}`,
|
||||
baseURL: `http://localhost:${DEFAULT_RPC_PORT}`,
|
||||
httpAgent: new http.Agent({
|
||||
family: 4, // Force IPv4
|
||||
}),
|
||||
@@ -62,15 +69,46 @@ export class PythonRPC {
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
private static async waitForHealthCheck(): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (Date.now() - startTime < HEALTH_CHECK_TIMEOUT_MS) {
|
||||
try {
|
||||
const response = await this.rpc.get("/healthcheck", { timeout: 1000 });
|
||||
if (response.status === 200) {
|
||||
pythonRpcLogger.log("RPC health check passed");
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// Server not ready yet, continue polling
|
||||
}
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, HEALTH_CHECK_INTERVAL_MS)
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("RPC health check timed out");
|
||||
}
|
||||
|
||||
public static async spawn(
|
||||
initialDownload?: GamePayload,
|
||||
initialSeeding?: GamePayload[]
|
||||
) {
|
||||
const rpcPassword = await this.getRPCPassword();
|
||||
|
||||
const port = await getPort({
|
||||
port: [
|
||||
DEFAULT_RPC_PORT,
|
||||
...portNumbers(RPC_PORT_RANGE_START, RPC_PORT_RANGE_END),
|
||||
],
|
||||
});
|
||||
|
||||
this.rpc.defaults.baseURL = `http://localhost:${port}`;
|
||||
pythonRpcLogger.log(`Using RPC port: ${port}`);
|
||||
|
||||
const commonArgs = [
|
||||
this.BITTORRENT_PORT,
|
||||
this.RPC_PORT,
|
||||
String(port),
|
||||
rpcPassword,
|
||||
initialDownload ? JSON.stringify(initialDownload) : "",
|
||||
initialSeeding ? JSON.stringify(initialSeeding) : "",
|
||||
@@ -91,6 +129,7 @@ export class PythonRPC {
|
||||
);
|
||||
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
const childProcess = cp.spawn(binaryPath, commonArgs, {
|
||||
@@ -99,7 +138,6 @@ export class PythonRPC {
|
||||
});
|
||||
|
||||
this.logStderr(childProcess.stderr);
|
||||
|
||||
this.pythonProcess = childProcess;
|
||||
} else {
|
||||
const scriptPath = path.join(
|
||||
@@ -115,11 +153,23 @@ export class PythonRPC {
|
||||
});
|
||||
|
||||
this.logStderr(childProcess.stderr);
|
||||
|
||||
this.pythonProcess = childProcess;
|
||||
}
|
||||
|
||||
this.rpc.defaults.headers.common["x-hydra-rpc-password"] = rpcPassword;
|
||||
|
||||
try {
|
||||
await this.waitForHealthCheck();
|
||||
pythonRpcLogger.log(`Python RPC started successfully on port ${port}`);
|
||||
} catch (err) {
|
||||
pythonRpcLogger.log(`Failed to start Python RPC: ${err}`);
|
||||
dialog.showErrorBox(
|
||||
"RPC Error",
|
||||
`Failed to start download service.\n\nThe service did not respond in time. Please try restarting Hydra.`
|
||||
);
|
||||
this.kill();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public static kill() {
|
||||
|
||||
@@ -5587,6 +5587,11 @@ get-nonce@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||
|
||||
get-port@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec"
|
||||
integrity sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==
|
||||
|
||||
get-proto@^1.0.0, get-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
|
||||
Reference in New Issue
Block a user