p2manager.js hinzugefügt

This commit is contained in:
2025-10-09 11:26:06 +02:00
parent 0aa7cd8427
commit 6e20572ebc

289
p2manager.js Normal file
View File

@@ -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();