From 6e20572ebce52044a424c3691aee7526d9094ba8 Mon Sep 17 00:00:00 2001 From: optimidev Date: Thu, 9 Oct 2025 11:26:06 +0200 Subject: [PATCH] =?UTF-8?q?p2manager.js=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- p2manager.js | 289 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 p2manager.js diff --git a/p2manager.js b/p2manager.js new file mode 100644 index 0000000..0ef49ea --- /dev/null +++ b/p2manager.js @@ -0,0 +1,289 @@ +#!/usr/bin/env node +/** + * p2manager - Player2 Manager (Node.js Edition) + * (C) 2025 Alex Mueller / OptimiDev + */ + +import fs from "fs"; +import os from "os"; +import path from "path"; +import { execSync } from "child_process"; +import inquirer from "inquirer"; +import chalk from "chalk"; +import fetch from "node-fetch"; +import { Command } from "commander"; + +// ───────────────────────────────────────────── +// Platform Detection +// ───────────────────────────────────────────── +const isMac = process.platform === "darwin"; +const isLinux = process.platform === "linux"; + +// ───────────────────────────────────────────── +// Paths and URLs +// ───────────────────────────────────────────── +const APP_DIR = isMac ? "/Applications/Player2.app" : "/opt/player2"; +const EXEC_PATH = isMac ? "/Applications/Player2.app/Contents/MacOS/Player2" : path.join(APP_DIR, "Player2.AppImage"); +const SYMLINK = isMac ? "/usr/local/bin/player2" : "/usr/bin/player2"; +const DESKTOP_FILE = isMac + ? path.join(os.homedir(), "Desktop/player2.desktop") + : "/usr/share/applications/player2.desktop"; + +const LATEST_URL = isMac + ? "https://player2.game/mac-intel" + : "https://player2.game/linux"; + +// ───────────────────────────────────────────── +// Warning +// ───────────────────────────────────────────── +console.log(chalk.bgYellow("!!WARNING!!")) +console.log(chalk.bgYellow("This code is made to run Player2 on unsupported devices like Macs with Intel and Linux")) +console.log(chalk.bgYellow("If you get any problems"), chalk.bgRed("DO NOT REPORT TO PLAYER2")) +console.log(chalk.bgYellow("Report the issues on: https://git.optimihost.com/NaChlorid/p2manager/issues")) + +// ───────────────────────────────────────────── +// Utils +// ───────────────────────────────────────────── +const log = (...args) => console.log(chalk.cyan("[P2Manager]"), ...args); +const warn = (...args) => console.log(chalk.bgYellow("[warn]"), ...args); +const error = (...args) => console.error(chalk.bgRed("[error]"), ...args); + +function requireRoot() { + if (isLinux && process.getuid() !== 0) { + error("This command requires root privileges. Run with sudo."); + process.exit(1); + } +} + +function ensureDir(dir) { + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); +} + +function safeExec(cmd) { + try { + execSync(cmd, { stdio: "ignore" }); + } catch { + warn(`Command failed (ignored): ${cmd}`); + } +} + +async function downloadFile(url, dest) { + log(`Downloading: ${url}`); + const res = await fetch(url); + if (!res.ok) throw new Error(`Download failed: ${res.statusText}`); + const fileStream = fs.createWriteStream(dest); + await new Promise((resolve, reject) => { + res.body.pipe(fileStream); + res.body.on("error", reject); + fileStream.on("finish", resolve); + }); +} + +// ───────────────────────────────────────────── +// Core Functions +// ───────────────────────────────────────────── +async function installPlayer2({ patches = true, monitor = false, noUI = false }) { + requireRoot(); + + log(`Installing latest Player2 for ${isMac ? "macOS" : "Linux"}...`); + + if (isMac) { + const dmgPath = "/tmp/Player2.dmg"; + await downloadFile(LATEST_URL, dmgPath); + log("Mounting DMG..."); + safeExec(`hdiutil attach "${dmgPath}" -nobrowse -quiet`); + safeExec(`cp -R "/Volumes/Player2/Player2.app" /Applications/`); + safeExec(`hdiutil detach "/Volumes/Player2" -quiet`); + fs.unlinkSync(dmgPath); + } else { + ensureDir(APP_DIR); + await downloadFile(LATEST_URL, EXEC_PATH); + fs.chmodSync(EXEC_PATH, 0o755); + } + + try { + fs.symlinkSync(EXEC_PATH, SYMLINK); + } catch { + warn("Symlink already exists or cannot be created. Skipping."); + } + + createDesktopFile(); + + if (patches && isLinux) applyPatches(); + if (monitor && isLinux) setupMonitor(); + + log(chalk.green("✅ Player2 installed successfully!")); +} + +function createDesktopFile() { + const execCmd = isMac ? "/Applications/Player2.app/Contents/MacOS/Player2" : SYMLINK; + const desktopEntry = `[Desktop Entry] +Type=Application +Name=Player2 +Exec=${execCmd} +Icon=player2 +Comment=Player2 Game Client +Categories=Game;`; + ensureDir(path.dirname(DESKTOP_FILE)); + fs.writeFileSync(DESKTOP_FILE, desktopEntry); + log("Desktop entry created."); +} + +function applyPatches() { + log("Applying WebKit patch..."); + ensureDir(APP_DIR); + fs.writeFileSync(path.join(APP_DIR, "patch.env"), "WEBKIT_DISABLE_DMABUF_RENDERER=1\n"); + log("Patch file written."); +} + +function setupMonitor() { + log("Setting up P2Monitor..."); + ensureDir("/etc/p2monitor"); + + fs.writeFileSync( + "/etc/p2monitor/monitor.py", + `#!/usr/bin/env python3 +import time +while True: + time.sleep(5)` + ); + fs.chmodSync("/etc/p2monitor/monitor.py", 0o755); + + fs.writeFileSync( + "/etc/systemd/system/p2monitor.service", + `[Unit] +Description=Player2 Log Monitor +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/python3 /etc/p2monitor/monitor.py +Restart=always +User=root + +[Install] +WantedBy=multi-user.target` + ); + + safeExec("systemctl daemon-reload"); + safeExec("systemctl enable p2monitor"); + safeExec("systemctl start p2monitor"); + log("P2Monitor service installed and started (Linux only)."); +} + +function uninstallAll() { + requireRoot(); + log("Uninstalling Player2 components..."); + const removeTargets = [ + APP_DIR, + SYMLINK, + DESKTOP_FILE, + "/etc/p2monitor", + "/etc/systemd/system/p2monitor.service" + ]; + removeTargets.forEach((p) => { + if (fs.existsSync(p)) { + fs.rmSync(p, { recursive: true, force: true }); + log(`Removed: ${p}`); + } + }); + safeExec("systemctl daemon-reload"); + log(chalk.green("✅ Uninstallation complete.")); +} + +async function updatePlayer2() { + requireRoot(); + if (!fs.existsSync(EXEC_PATH)) { + error("Player2 is not installed. Run 'p2manager install' first."); + process.exit(1); + } + log("Updating Player2..."); + await downloadFile(LATEST_URL, EXEC_PATH); + fs.chmodSync(EXEC_PATH, 0o755); + log(chalk.green("✅ Player2 updated successfully!")); +} + +function showStatus() { + console.log(chalk.bold("\nPlayer2 Manager Status")); + console.log("──────────────────────────────"); + console.log("Platform:", isMac ? "macOS" : "Linux"); + console.log("Installed:", fs.existsSync(EXEC_PATH) ? "✅ Yes" : "❌ No"); + console.log("Executable:", EXEC_PATH); + console.log("Symlink:", fs.existsSync(SYMLINK) ? "✅ Present" : "❌ Missing"); + console.log("Desktop Entry:", fs.existsSync(DESKTOP_FILE) ? "✅ Present" : "❌ Missing"); + if (isLinux) + console.log("Monitor Service:", fs.existsSync("/etc/p2monitor") ? "✅ Installed" : "❌ Not installed"); + console.log(""); +} + +// ───────────────────────────────────────────── +// Interactive UI +// ───────────────────────────────────────────── +async function interactiveInstall() { + const answers = await inquirer.prompt([ + { + type: "checkbox", + name: "components", + message: "Select components to install:", + choices: [ + { name: "Player2 Core", value: "core", checked: true }, + ...(isLinux ? [{ name: "WebKit Patch", value: "patch", checked: true }] : []), + ...(isLinux ? [{ name: "P2Monitor (Linux only)", value: "monitor" }] : []) + ] + } + ]); + + await installPlayer2({ + patches: answers.components.includes("patch"), + monitor: answers.components.includes("monitor") + }); +} + +async function interactiveUninstall() { + await inquirer.prompt([ + { + type: "checkbox", + name: "remove", + message: "Select components to uninstall:", + choices: [ + { name: "Player2 Core", value: "core", checked: true }, + ...(isLinux ? [{ name: "Patches", value: "patch", checked: true }] : []), + ...(isLinux ? [{ name: "P2Monitor", value: "monitor" }] : []) + ] + } + ]); + uninstallAll(); +} + +// ───────────────────────────────────────────── +// CLI Entrypoint +// ───────────────────────────────────────────── +const program = new Command(); + +program + .name("p2manager") + .description("Manage Player2 installation, patches, and updates") + .version("1.1"); + +program + .command("install") + .option("--no-ui", "Run without interactive UI") + .action(async (opts) => { + if (opts.noUi) { + await installPlayer2({ patches: true, monitor: false, noUI: true }); + } else { + await interactiveInstall(); + } + }); + +program + .command("uninstall") + .option("--no-ui", "Run without interactive UI") + .action(async (opts) => { + if (opts.noUi) uninstallAll(); + else await interactiveUninstall(); + }); + +program.command("update").action(updatePlayer2); +program.command("status").action(showStatus); +program.parse();