const path = require("node:path"); const fs = require("node:fs"); const https = require("node:https"); const process = require("node:process"); const os = require("node:os"); const child_process = require("node:child_process"); const electron = require("electron"); const update = require(path.join(__dirname, "update.js")); let configFilePath; let plutoniumInstallDirectory; switch (os.platform()) { case "win32": configFilePath = path.join(process.env["LOCALAPPDATA"], "Plutonium", "open-plutonium-launcher.json"); plutoniumInstallDirectory = path.join(process.env["LOCALAPPDATA"], "Plutonium"); break; case "linux": configFilePath = path.join(os.userInfo().homedir, ".config", "open-plutonium-launcher.json"); plutoniumInstallDirectory = path.join(os.userInfo().homedir, ".local", "share", "Plutonium"); break; case "darwin": configFilePath = path.join(os.userInfo().homedir, "Library", "Application Support", "open-plutonium-launcher.json"); plutoniumInstallDirectory = path.join(os.userInfo().homedir, "Library", "Application Support", "Plutonium"); break; default: electron.dialog.showErrorBox("Incompatible system", "Sorry, your operating system doesn't seem to be supported..."); process.exit(1); } let mainWindow; let plutoniumManifest = { "product": "plutonium-core-prod", "revision": -1, "baseUrl": "https://cdn.plutonium.pw/updater/prod/files/", "files": [] }; let userInfo = { "token": "V3ryS3cr3t4uth3nt1c4t10nT0k3n", "userId": -1, "username": "AnonGuest", "email": "guest@neverla.nd", "emailVerified": false, "avatar": "https://forum.plutonium.pw/assets/uploads/system/avatar-default.png" }; function createMainWindow() { mainWindow = new electron.BrowserWindow({ "minWidth": 640, "minHeight": 480, "width": 800, "height": 600, "fullscreenable": false, "show": false, "webPreferences": { "preload": path.join(__dirname, "preload.js"), "contextIsolation": true } }); mainWindow.loadFile(path.join(__dirname, "views", "login.html")); mainWindow.once("ready-to-show", function () { mainWindow.show(); }); } function fetchPlutoniumManifest() { return new Promise(function (resolve, reject) { let request = https.request("https://cdn.plutonium.pw/updater/prod/info.json", { "method": "GET" }, function (response) { if (response.statusCode !== 200) { return reject(new Error("Failed to fetch the Plutonium launcher manifest.")); } response.setEncoding("utf-8"); let body = ""; response.on("data", function (chunk) { body += chunk; }); response.on("end", function () { try { let data = JSON.parse(body); resolve(data); } catch (error) { reject(error); } }); }); request.on("error", function (error) { reject(error); }); request.end(); }); } function getPlutoniumSession(game, token) { return new Promise(function (resolve, reject) { let payload = JSON.stringify({ "game": game }); let request = https.request("https://nix.plutonium.pw/api/auth/session", { "method": "POST", "headers": { "Content-Type": "application/json", "Content-Length": payload.length, "Authorization": "UserToken " + token, "X-Plutonium-Revision": String(plutoniumManifest.revision), "User-Agent": "Nix/3.0" } }, function (response) { if (response.statusCode !== 200) { return resolve({ "status": "unauthenticated", "successful": false }); } response.setEncoding("utf-8"); let body = ""; response.on("data", function (chunk) { body += chunk; }); response.on("end", function () { try { let data = JSON.parse(body); if (!("token" in data)) { reject(new Error("Authentication seems to be successful but no token was returned.")); } resolve({ "status": "authenticated", "successful": true, ...data }); userInfo = data; mainWindow.loadFile(path.join(__dirname, "src", "views", "games.html")); } catch (error) { reject(error); } }); }); request.on("error", function (error) { reject(error); }); request.write(payload); request.end(); }); } function launch(plutoniumInstallDirectory, game, gameInstallDirectory, online) { return new Promise(function (resolve, reject) { getPlutoniumSession(game, userInfo.token).then(function (data) { if (!data.successful) { return reject(new Error("Authentication has failed.")); } let bootstrapperBinary = path.join(plutoniumInstallDirectory, "bin", "plutonium-bootstrapper-win32.exe"); let bootstrapperArguments = [game, gameInstallDirectory]; if (online) { bootstrapperArguments.push("-token"); bootstrapperArguments.push(data.token); } else { bootstrapperArguments.push("+name"); bootstrapperArguments.push(userInfo.username); bootstrapperArguments.push("-lan"); } let gameProcess = child_process.spawn(bootstrapperBinary, bootstrapperArguments, { "detached": true, "stdio": "ignore" }); gameProcess.on("spawn", function () { resolve(true); }); gameProcess.on("error", function (error) { reject(error); }); }); }); } electron.app.once("ready", function () { createMainWindow(); fetchPlutoniumManifest().then(function (manifest) { plutoniumManifest = manifest; }).catch(function (error) { electron.dialog.showErrorBox("Error", "The Plutonium launcher manifest could not be fetched, auto-updating might not be possible.\n" + error.message); }); }); electron.app.on("window-all-closed", function () { electron.app.quit(); }); electron.ipcMain.handle("login", function (event, username, password) { return new Promise(function (resolve, reject) { let payload = JSON.stringify({ "username": username, "password": password }); let request = https.request("https://nix.plutonium.pw/api/auth/login", { "method": "POST", "headers": { "Content-Type": "application/json", "Content-Length": payload.length, "Authorization": "UserToken", "X-Plutonium-Revision": String(plutoniumManifest.revision), "User-Agent": "Nix/3.0" } }, function (response) { if (response.statusCode !== 200) { return resolve({ "status": "unauthenticated", "successful": false }); } response.setEncoding("utf-8"); let body = ""; response.on("data", function (chunk) { body += chunk; }); response.on("end", function () { try { let data = JSON.parse(body); if (!("token" in data)) { reject(new Error("Login seems to be successful but no token was returned.")); } resolve({ "status": "authenticated", "successful": true, ...data }); userInfo = data; mainWindow.loadFile(path.join(__dirname, "src", "views", "games.html")); } catch (error) { reject(error); } }); }); request.on("error", function (error) { reject(error); }); request.write(payload); request.end(); }); }); electron.ipcMain.handle("launch", function (event, game, online = true) { return new Promise(function (resolve, reject) { if (!(game in config.gameDirectories)) { return resolve(false); } let gameInstallDirectory = config.gameDirectories[game]; update.checkFiles(plutoniumManifest, plutoniumInstallDirectory).then(function (fileList) { let filesToUpdate = fileList.filter(function (file) { return !file.ok; }); update.downloadFiles(plutoniumInstallDirectory, plutoniumManifest.baseUrl, filesToUpdate).then(function () { launch(plutoniumInstallDirectory, game, gameInstallDirectory, online).then(function (result) { resolve(result); }); }).catch(function (error) { reject(error); }); }).catch(function (error) { reject(error); }); }); });