diff --git a/main.js b/main.js index d56be3a..b8d2278 100644 --- a/main.js +++ b/main.js @@ -7,16 +7,51 @@ const config = require("./src/config/config"); const { setupTitlebar, attachTitlebarToWindow } = require( "custom-electron-titlebar/main", ); -const { isValidOsuFolder } = require("./src/util/osuUtil"); +const { isValidOsuFolder, getUpdateFiles, getGlobalConfig, getFilesThatNeedUpdate, downloadUpdateFiles, getUserConfig, runOsuWithDevServer, getPatcherUpdates, downloadPatcherUpdates, getUIFiles, downloadUIFiles, replaceUIFile } = require("./src/util/osuUtil"); +const { formatBytes } = require("./src/util/formattingUtil"); +const windowName = require("get-window-by-name"); +const { existsSync } = require("fs"); +const { runFileDetached } = require("./src/util/executeUtil"); // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow; +let osuCheckInterval; +let userOsuPath; +let osuLoaded = false; + +let currentUser = undefined; function isDev() { return !app.isPackaged; } +function startOsuStatus() { + osuCheckInterval = setInterval(async () => { + const osuWindowTitle = windowName.getWindowText("osu!.exe"); + if (osuWindowTitle.length < 0) { + console.log("No osu! window found"); + return; + } + const firstInstance = osuWindowTitle[0]; + if (firstInstance) { + if (!osuLoaded) { + osuLoaded = true; + setTimeout(() => { + const patcherExecuteable = path.join(userOsuPath, "EZPPLauncher", "patcher.exe"); + if (existsSync(patcherExecuteable)) { + runFileDetached(userOsuPath, patcherExecuteable); + } + }, 3000); + } + } + }, 1000); +} + +function stopOsuStatus() { + clearInterval(osuCheckInterval); +} + function registerIPCPipes() { ipcMain.handle("ezpplauncher:login", async (e, args) => { const timeout = new AbortController(); @@ -43,6 +78,7 @@ function registerIPCPipes() { config.set("username", args.username); config.set("password", args.password); } + currentUser = args; config.remove("guest"); } return result; @@ -86,6 +122,12 @@ function registerIPCPipes() { if (fetchResult.ok) { const result = await fetchResult.json(); + if ("user" in result) { + currentUser = { + username: username, + password: password, + }; + } return result; } return { @@ -104,12 +146,14 @@ function registerIPCPipes() { config.remove("username"); config.remove("password"); config.set("guest", "1"); + currentUser = undefined; }); ipcMain.handle("ezpplauncher:logout", (e) => { config.remove("username"); config.remove("password"); config.remove("guest"); + currentUser = undefined return true; }); @@ -140,12 +184,22 @@ function registerIPCPipes() { return config.all(); }); - ipcMain.handle("ezpplauncher:launch", async (e) => { + ipcMain.handle("ezpplauncher:launch", async (e, args) => { + const patch = args.patch; mainWindow.webContents.send("ezpplauncher:launchstatus", { status: "Checking osu! directory...", }); await new Promise((res) => setTimeout(res, 1000)); const osuPath = config.get("osuPath"); + userOsuPath = osuPath; + if (osuPath == undefined) { + mainWindow.webContents.send("ezpplauncher:launchabort"); + mainWindow.webContents.send("ezpplauncher:alert", { + type: "error", + message: "osu! path not set!", + }); + return; + } if (!(await isValidOsuFolder(osuPath))) { mainWindow.webContents.send("ezpplauncher:launchabort"); mainWindow.webContents.send("ezpplauncher:alert", { @@ -158,6 +212,114 @@ function registerIPCPipes() { status: "Checking for osu! updates...", }); await new Promise((res) => setTimeout(res, 1000)); + const releaseStream = await getGlobalConfig(osuPath).get("_ReleaseStream"); + const latestFiles = await getUpdateFiles(releaseStream); + const uiFiles = await getUIFiles(osuPath); + const updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles); + if (uiFiles.length > 0) { + const uiDownloader = downloadUIFiles(osuPath, uiFiles); + uiDownloader.eventEmitter.on("data", (data) => { + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: Math.ceil(data.progress), + }); + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`, + }); + }); + await uiDownloader.startDownload(); + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: -1, + }); + } + if (updateFiles.length > 0) { + const updateDownloader = downloadUpdateFiles(osuPath, updateFiles); + updateDownloader.eventEmitter.on("data", (data) => { + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: Math.ceil(data.progress), + }); + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`, + }); + }); + await updateDownloader.startDownload(); + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: -1, + }); + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "osu! is now up to date!", + }); + await new Promise((res) => setTimeout(res, 1000)); + } else { + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "osu! is up to date!", + }); + await new Promise((res) => setTimeout(res, 1000)); + } + + if (patch) { + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Looking for patcher updates...", + }); + await new Promise((res) => setTimeout(res, 1000)); + const patchFiles = await getPatcherUpdates(osuPath); + if (patchFiles.length > 0) { + const patcherDownloader = downloadPatcherUpdates(osuPath, patchFiles); + patcherDownloader.eventEmitter.on("data", (data) => { + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: Math.ceil(data.progress), + }); + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`, + }); + }); + await patcherDownloader.startDownload(); + mainWindow.webContents.send("ezpplauncher:launchprogress", { + progress: -1, + }) + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Patcher is now up to date!", + }); + } else { + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Patcher is up to date!", + }); + } + await new Promise((res) => setTimeout(res, 1000)); + } + + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Preparing launch...", + }); + + //TODO: save credentials to osu!.%username%.cfg + if (currentUser) { + const userConfig = getUserConfig(osuPath); + await userConfig.set("Username", currentUser.username); + await userConfig.set("Password", currentUser.password); + } + + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Launching osu!...", + }); + + const onExitHook = () => { + mainWindow.show(); + stopOsuStatus(); + mainWindow.webContents.send("ezpplauncher:launchstatus", { + status: "Waiting for cleanup...", + }); + + setTimeout(async () => { + await replaceUIFile(osuPath, true); + mainWindow.webContents.send("ezpplauncher:launchabort"); + }, 5000); + } + await replaceUIFile(osuPath, false); + runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook); + mainWindow.hide(); + startOsuStatus(); + + /* mainWindow.webContents.send("ezpplauncher:launchprogress", { progress: 0, }); diff --git a/package-lock.json b/package-lock.json index ced0649..f5453a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "@types/better-sqlite3": "^7.6.8", "axios": "^1.6.5", "better-sqlite3": "^9.2.2", + "crypto": "^1.0.1", "custom-electron-titlebar": "^4.2.7", "electron-serve": "^1.1.0", + "get-window-by-name": "^2.0.0", "svelte-french-toast": "^1.2.0" }, "devDependencies": { @@ -3049,6 +3051,12 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -4291,6 +4299,78 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-window-by-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-window-by-name/-/get-window-by-name-2.0.0.tgz", + "integrity": "sha512-VXszlUFwkmWAZzxEERRJisiVvGMeB+Zjl5I9f0mwJjhfLTOkD5n5OR9Z518XBZemKLRzIs91TlfKbPmZywS84Q==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.2.1", + "cross-spawn": "^6.0.5", + "nan": "^2.0.5" + } + }, + "node_modules/get-window-by-name/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/get-window-by-name/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-window-by-name/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/get-window-by-name/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-window-by-name/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-window-by-name/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -4905,8 +4985,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/jackspeak": { "version": "2.3.6", @@ -5553,6 +5632,11 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -5585,6 +5669,11 @@ "node": ">= 0.6" } }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node_modules/node-abi": { "version": "3.54.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", diff --git a/package.json b/package.json index 79d50ec..8fdbf5b 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,10 @@ "@types/better-sqlite3": "^7.6.8", "axios": "^1.6.5", "better-sqlite3": "^9.2.2", + "crypto": "^1.0.1", "custom-electron-titlebar": "^4.2.7", "electron-serve": "^1.1.0", + "get-window-by-name": "^2.0.0", "svelte-french-toast": "^1.2.0" }, "devDependencies": { diff --git a/preload.js b/preload.js index b6e29f9..7e15fd6 100644 --- a/preload.js +++ b/preload.js @@ -37,8 +37,8 @@ window.addEventListener("guest-login", async () => { await ipcRenderer.invoke("ezpplauncher:guestlogin"); }); -window.addEventListener("launch", async () => { - await ipcRenderer.invoke("ezpplauncher:launch"); +window.addEventListener("launch", async (e) => { + await ipcRenderer.invoke("ezpplauncher:launch", e.detail); }); window.addEventListener("settings-get", async () => { diff --git a/src/App.svelte b/src/App.svelte index bc47fbd..7c60e2b 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -52,7 +52,7 @@ launchPercentage.set(progress); }); - window.addEventListener("launchabort", () => { + window.addEventListener("launch-abort", () => { launchPercentage.set(-1); launchStatus.set(""); launching.set(false); diff --git a/src/pages/Launch.svelte b/src/pages/Launch.svelte index 14da0f4..78fd6ca 100644 --- a/src/pages/Launch.svelte +++ b/src/pages/Launch.svelte @@ -15,7 +15,7 @@ const launch = () => { launching.set(true); - window.dispatchEvent(new CustomEvent("launch")); + window.dispatchEvent(new CustomEvent("launch", { detail: { patch: $patch } }));; }; diff --git a/src/pages/Settings.svelte b/src/pages/Settings.svelte index 209130a..ac34dd3 100644 --- a/src/pages/Settings.svelte +++ b/src/pages/Settings.svelte @@ -42,11 +42,10 @@ >