UNPKG

@ov2/lightrail

Version:

Node.js CLI tool for launching and managing ARK game servers with restart scheduling.

134 lines (123 loc) 3.91 kB
import { spawn, execSync } from "child_process"; import path from "path"; import fs from "fs"; let currentServerProcess = null; let childProcesses = []; function getChildPids(parentPid) { if (process.platform !== "win32") return []; try { const stdout = execSync( `wmic process where (ParentProcessId=${parentPid}) get ProcessId`, { encoding: "utf8" } ); return stdout .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => /^\d+$/.test(line)) .map(Number); } catch { return []; } } function getAllChildPids(pid) { const directChildren = getChildPids(pid); let all = [...directChildren]; for (const child of directChildren) { all = all.concat(getAllChildPids(child)); } return all; } export function startServer(profile, serverInstance) { const dir = path.join(profile.dir, serverInstance.worldName); const clusterDir = profile.clusterDir; const mods = profile.mods; const symlinkPath = path.join( dir, "ShooterGame", "Binaries", "Win64", "PlayersJoinNoCheckList.txt" ); const clusterSymlinkSrc = path.join(clusterDir, "PlayersJoinNoCheckList.txt"); // Delete symlink if exists try { if (fs.existsSync(symlinkPath)) { fs.unlinkSync(symlinkPath); console.log("Deleted symlink:", symlinkPath); } } catch (err) { console.warn("Could not delete symlink:", err.message); } // Create symlink try { fs.symlinkSync(clusterSymlinkSrc, symlinkPath, "file"); console.log("Created symlink:", symlinkPath, "->", clusterSymlinkSrc); } catch (err) { console.warn("Could not create symlink:", err.message); } // Build command line const commandLine = `${serverInstance.worldName}?listen?Port=${serverInstance.Port}?RCONEnabled=True?RCONPort=${serverInstance.RCONPort}?ServerAdminPassword=${profile.adminPassword}`; const serverExe = path.join( dir, "ShooterGame", "Binaries", "Win64", "ArkAscendedServer.exe" ); const args = [ commandLine, `-clusterID=${profile.clusterID}`, `-ClusterDirOverride=\"${profile.clusterOverride || ""}\"`, "-NoTransferFromFiltering", "-PreventHibernation", "-ForceRespawnDinos", `-mods=\"${mods}\"`, ]; // Start server console.log("Starting Ark server..."); const proc = spawn(serverExe, args, { stdio: "inherit", shell: true }); currentServerProcess = proc; serverInstance._process = proc; serverInstance._pid = proc.pid; // Fetch and save all child PIDs serverInstance._childPids = getAllChildPids(proc.pid); console.log("childPids", serverInstance._childPids); childProcesses.push(proc); return proc; } function killProcessTree(pid) { if (process.platform === "win32") { // Use taskkill to kill process tree try { execSync(`taskkill /PID ${pid} /T /F`); console.log(`Killed process tree (PID: ${pid})`); } catch (err) { console.error(`Failed to kill process tree: ${err}`); } } else { // On Unix, kill process group try { process.kill(-pid); console.log(`Killed process group (PID: ${pid})`); } catch (err) { console.error(`Failed to kill process group: ${err}`); } } } export function killServer(serverInstance) { // Kill all child processes of the server process if (serverInstance && Array.isArray(serverInstance._childPids)) { for (const pid of serverInstance._childPids) { killProcessTree(pid); } } // Kill main server process if (serverInstance && serverInstance._process && serverInstance._pid) { killProcessTree(serverInstance._pid); serverInstance._process = null; serverInstance._pid = null; serverInstance._childPids = []; } childProcesses = []; currentServerProcess = null; }