patcher update, no more hardcoded urls, launch fixes

This commit is contained in:
HorizonCode 2024-04-14 16:36:32 +02:00
parent 1c4a40c495
commit 6f2764a047
11 changed files with 551 additions and 864 deletions

View File

@ -1,4 +1,4 @@
const appName = "EZPPLauncher"; const appName = "EZPPLauncher";
const appVersion = "2.1.2"; const appVersion = "2.1.3";
module.exports = { appName, appVersion }; module.exports = { appName, appVersion };

View File

@ -44,26 +44,7 @@ const osuEntities = [
"scores.db", "scores.db",
]; ];
const patcherFiles = [ const ezppLauncherUpdateList = "https://ez-pp.farm/ezpplauncher"
{
name: "patcher.exe",
url_download: "https://ez-pp.farm/assets/patcher.exe",
url_hash: "https://ez-pp.farm/assets/patcher.md5",
},
{
name: "patch.hook.dll",
url_download: "https://ez-pp.farm/assets/patch.hook.dll",
url_hash: "https://ez-pp.farm/assets/patch.hook.md5",
},
];
const uiFiles = [
{
name: "ezpp!ui.dll",
url_download: "https://ez-pp.farm/assets/ezpp!ui.dll",
url_hash: "https://ez-pp.farm/assets/ezpp!ui.md5",
},
];
async function isValidOsuFolder(path) { async function isValidOsuFolder(path) {
const allFiles = await fs.promises.readdir(path); const allFiles = await fs.promises.readdir(path);
@ -265,115 +246,41 @@ function runOsuWithDevServer(osuPath, serverDomain, onExit) {
runFile(osuPath, osuExecuteable, ["-devserver", serverDomain], onExit); runFile(osuPath, osuExecuteable, ["-devserver", serverDomain], onExit);
} }
async function getPatcherUpdates(osuPath) { async function getEZPPLauncherUpdateFiles(osuPath) {
const filesToDownload = []; const filesToDownload = [];
const updateFilesRequest = await fetch(ezppLauncherUpdateList, { method: "PATCH" });
const patcherDir = path.join(osuPath, "EZPPLauncher"); const updateFiles = await updateFilesRequest.json();
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir); for (const updateFile of updateFiles) {
const filePath = path.join(osuPath, ...updateFile.folder.split("/"), updateFile.name);
for (const patcherFile of patcherFiles) { if (fs.existsSync(filePath)) {
if (fs.existsSync(path.join(patcherDir, patcherFile.name))) { const fileHash = updateFile.md5.toLowerCase();
const latestPatchFileHash = await (await fetch(patcherFile.url_hash)) const localFileHash = crypto.createHash("md5").update(
.text(); fs.readFileSync(filePath),
const localPatchFileHash = crypto.createHash("md5").update( ).digest("hex").toLowerCase();
fs.readFileSync(path.join(patcherDir, patcherFile.name)), if (fileHash !== localFileHash) {
).digest("hex"); filesToDownload.push(updateFile);
if (
latestPatchFileHash.trim().toLowerCase() !=
localPatchFileHash.trim().toLowerCase()
) filesToDownload.push(patcherFile);
} else filesToDownload.push(patcherFile);
}
return filesToDownload;
}
function downloadPatcherUpdates(osuPath, patcherUpdates) {
const eventEmitter = new EventEmitter();
const startDownload = async () => {
const patcherDir = path.join(osuPath, "EZPPLauncher");
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir);
for (const patcherFile of patcherUpdates) {
const fileName = patcherFile.name;
const fileURL = patcherFile.url_download;
const axiosDownloadWithProgress = await axios.get(fileURL, {
responseType: "stream",
onDownloadProgress: (progressEvent) => {
const { loaded, total } = progressEvent;
eventEmitter.emit("data", {
fileName,
loaded,
total,
progress: Math.floor((loaded / total) * 100),
});
},
});
try {
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) {
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), {
force: true,
});
}
await fs.promises.writeFile(
path.join(osuPath, "EZPPLauncher", fileName),
axiosDownloadWithProgress.data,
);
} catch (err) {
console.log(err);
eventEmitter.emit("error", {
fileName,
});
} }
} else {
filesToDownload.push(updateFile);
} }
};
return {
eventEmitter,
startDownload,
};
}
async function getUIFiles(osuPath) {
const filesToDownload = [];
const ezppLauncherDir = path.join(osuPath, "EZPPLauncher");
if (!fs.existsSync(ezppLauncherDir)) fs.mkdirSync(ezppLauncherDir);
for (const uiFile of uiFiles) {
if (fs.existsSync(path.join(ezppLauncherDir, uiFile.name))) {
const latestPatchFileHash = await (await fetch(uiFile.url_hash)).text();
const localPatchFileHash = crypto.createHash("md5").update(
fs.readFileSync(path.join(ezppLauncherDir, uiFile.name)),
).digest("hex");
if (
latestPatchFileHash.trim().toLowerCase() !=
localPatchFileHash.trim().toLowerCase()
) filesToDownload.push(uiFile);
} else filesToDownload.push(uiFile);
} }
return filesToDownload; return filesToDownload;
} }
function downloadUIFiles(osuPath, uiFiles) { async function downloadEZPPLauncherUpdateFiles(osuPath, updateFiles) {
const eventEmitter = new EventEmitter(); const eventEmitter = new EventEmitter();
const startDownload = async () => { const startDownload = async () => {
const ezpplauncherDir = path.join(osuPath, "EZPPLauncher"); for (const updateFile of updateFiles) {
if (!fs.existsSync(ezpplauncherDir)) fs.mkdirSync(ezpplauncherDir); const filePath = path.join(osuPath, ...updateFile.folder.split("/"), updateFile.name);
const folder = path.dirname(filePath);
for (const uiFile of uiFiles) { if (!fs.existsSync(folder)) await fs.promises.mkdir(folder, { recursive: true });
const fileName = uiFile.name; const axiosDownloadWithProgress = await axios.get(updateFile.url, {
const fileURL = uiFile.url_download;
const axiosDownloadWithProgress = await axios.get(fileURL, {
responseType: "stream", responseType: "stream",
onDownloadProgress: (progressEvent) => { onDownloadProgress: (progressEvent) => {
const { loaded, total } = progressEvent; const { loaded, total } = progressEvent;
eventEmitter.emit("data", { eventEmitter.emit("data", {
fileName, fileName: path.basename(filePath),
loaded, loaded,
total, total,
progress: Math.floor((loaded / total) * 100), progress: Math.floor((loaded / total) * 100),
@ -381,24 +288,23 @@ function downloadUIFiles(osuPath, uiFiles) {
}, },
}); });
try { try {
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) { if (fs.existsSync(filePath)) {
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), { await fs.promises.rm(filePath, {
force: true, force: true,
}); });
} }
await fs.promises.writeFile( await fs.promises.writeFile(
path.join(osuPath, "EZPPLauncher", fileName), filePath,
axiosDownloadWithProgress.data, axiosDownloadWithProgress.data,
); );
} catch (err) { } catch (err) {
console.log(err); console.log(err);
eventEmitter.emit("error", { eventEmitter.emit("error", {
fileName, fileName: path.basename(filePath),
}); });
} }
} }
}; }
return { return {
eventEmitter, eventEmitter,
@ -406,23 +312,41 @@ function downloadUIFiles(osuPath, uiFiles) {
}; };
} }
async function replaceUIFile(osuPath, revert) { async function replaceUIFiles(osuPath, revert) {
if (!revert) { if (!revert) {
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll"); const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll"); const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
const ezppGameplayFile = path.join(osuPath, "EZPPLauncher", "ezpp!gameplay.dll");
const oldOsuGameplayFile = path.join(osuPath, "osu!gameplay.dll");
await fs.promises.rename( await fs.promises.rename(
oldOsuUIFile, oldOsuUIFile,
path.join(osuPath, "osu!ui.dll.bak"), path.join(osuPath, "osu!ui.dll.bak"),
); );
await fs.promises.rename(ezppUIFile, oldOsuUIFile); await fs.promises.rename(ezppUIFile, oldOsuUIFile);
await fs.promises.rename(
oldOsuGameplayFile,
path.join(osuPath, "osu!gameplay.dll.bak"),
);
await fs.promises.rename(ezppGameplayFile, oldOsuGameplayFile);
} else { } else {
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll"); const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll"); const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
const oldOsuGameplayFile = path.join(osuPath, "osu!gameplay.dll");
const ezppGameplayFile = path.join(osuPath, "EZPPLauncher", "ezpp!gameplay.dll");
await fs.promises.rename(oldOsuUIFile, ezppUIFile); await fs.promises.rename(oldOsuUIFile, ezppUIFile);
await fs.promises.rename( await fs.promises.rename(
path.join(osuPath, "osu!ui.dll.bak"), path.join(osuPath, "osu!ui.dll.bak"),
oldOsuUIFile, oldOsuUIFile,
); );
await fs.promises.rename(oldOsuGameplayFile, ezppGameplayFile);
await fs.promises.rename(
path.join(osuPath, "osu!gameplay.dll.bak"),
oldOsuGameplayFile,
);
} }
} }
@ -483,13 +407,11 @@ module.exports = {
getFilesThatNeedUpdate, getFilesThatNeedUpdate,
downloadUpdateFiles, downloadUpdateFiles,
runOsuWithDevServer, runOsuWithDevServer,
getPatcherUpdates, replaceUIFiles,
downloadPatcherUpdates,
downloadUIFiles,
getUIFiles,
replaceUIFile,
findOsuInstallation, findOsuInstallation,
updateOsuConfigHashes, updateOsuConfigHashes,
runOsuUpdater, runOsuUpdater,
getEZPPLauncherUpdateFiles,
downloadEZPPLauncherUpdateFiles,
gamemodes gamemodes
}; };

62
main.js
View File

@ -3,15 +3,6 @@ const { app, BrowserWindow, Menu, ipcMain, dialog, shell } = require(
"electron", "electron",
); );
/* const unhandled = require("electron-unhandled");
unhandled({
logger: console.error,
showDialog: true,
reportButton: () => {
shell.openExternal("https://ez-pp.farm/discord");
},
}); */
const path = require("path"); const path = require("path");
const serve = require("electron-serve"); const serve = require("electron-serve");
const loadURL = serve({ directory: "public" }); const loadURL = serve({ directory: "public" });
@ -27,15 +18,13 @@ const {
downloadUpdateFiles, downloadUpdateFiles,
getUserConfig, getUserConfig,
runOsuWithDevServer, runOsuWithDevServer,
getPatcherUpdates, replaceUIFiles,
downloadPatcherUpdates,
getUIFiles,
downloadUIFiles,
replaceUIFile,
findOsuInstallation, findOsuInstallation,
updateOsuConfigHashes, updateOsuConfigHashes,
runOsuUpdater, runOsuUpdater,
gamemodes, gamemodes,
getEZPPLauncherUpdateFiles,
downloadEZPPLauncherUpdateFiles,
} = require("./electron/osuUtil"); } = require("./electron/osuUtil");
const { formatBytes } = require("./electron/formattingUtil"); const { formatBytes } = require("./electron/formattingUtil");
const windowName = require("get-window-by-name"); const windowName = require("get-window-by-name");
@ -98,7 +87,8 @@ function startOsuStatus() {
const patcherExecuteable = path.join( const patcherExecuteable = path.join(
userOsuPath, userOsuPath,
"EZPPLauncher", "EZPPLauncher",
"patcher.exe", "patcher",
"osu!.patcher.exe",
); );
if (fs.existsSync(patcherExecuteable)) { if (fs.existsSync(patcherExecuteable)) {
runFileDetached(userOsuPath, patcherExecuteable); runFileDetached(userOsuPath, patcherExecuteable);
@ -414,6 +404,7 @@ function registerIPCPipes() {
type: "error", type: "error",
message: "osu! path not set!", message: "osu! path not set!",
}); });
mainWindow.webContents.send("ezpplauncher:open-settings");
return; return;
} }
if (!(await isValidOsuFolder(osuPath))) { if (!(await isValidOsuFolder(osuPath))) {
@ -430,38 +421,7 @@ function registerIPCPipes() {
await new Promise((res) => setTimeout(res, 1000)); await new Promise((res) => setTimeout(res, 1000));
const releaseStream = await getGlobalConfig(osuPath).get("_ReleaseStream"); const releaseStream = await getGlobalConfig(osuPath).get("_ReleaseStream");
const latestFiles = await getUpdateFiles(releaseStream); const latestFiles = await getUpdateFiles(releaseStream);
const uiFiles = await getUIFiles(osuPath);
const updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles); const updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles);
if (uiFiles.length > 0) {
const uiDownloader = downloadUIFiles(osuPath, uiFiles);
let errored = false;
uiDownloader.eventEmitter.on("error", (data) => {
const filename = data.fileName;
errored = true;
mainWindow.webContents.send("ezpplauncher:alert", {
type: "error",
message:
`Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
});
});
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 (errored) {
mainWindow.webContents.send("ezpplauncher:launchabort");
return;
}
}
if (updateFiles.length > 0) { if (updateFiles.length > 0) {
const updateDownloader = downloadUpdateFiles(osuPath, updateFiles); const updateDownloader = downloadUpdateFiles(osuPath, updateFiles);
let errored = false; let errored = false;
@ -507,9 +467,9 @@ function registerIPCPipes() {
status: "Looking for patcher updates...", status: "Looking for patcher updates...",
}); });
await new Promise((res) => setTimeout(res, 1000)); await new Promise((res) => setTimeout(res, 1000));
const patchFiles = await getPatcherUpdates(osuPath); const patchFiles = await getEZPPLauncherUpdateFiles(osuPath);
if (patchFiles.length > 0) { if (patchFiles.length > 0) {
const patcherDownloader = downloadPatcherUpdates(osuPath, patchFiles); const patcherDownloader = await downloadEZPPLauncherUpdateFiles(osuPath, patchFiles);
let errored = false; let errored = false;
patcherDownloader.eventEmitter.on("error", (data) => { patcherDownloader.eventEmitter.on("error", (data) => {
const filename = data.fileName; const filename = data.fileName;
@ -580,14 +540,14 @@ function registerIPCPipes() {
}); });
await updateOsuConfigHashes(osuPath); await updateOsuConfigHashes(osuPath);
await replaceUIFile(osuPath, false); await replaceUIFiles(osuPath, false);
const forceUpdateFiles = [ const forceUpdateFiles = [
".require_update", ".require_update",
"help.txt", "help.txt",
"_pending", "_pending",
]; ];
//TODO: needs testing
try { try {
for (const updateFileName of forceUpdateFiles) { for (const updateFileName of forceUpdateFiles) {
const updateFile = path.join(osuPath, updateFileName); const updateFile = path.join(osuPath, updateFileName);
@ -635,7 +595,7 @@ function registerIPCPipes() {
}); });
setTimeout(async () => { setTimeout(async () => {
await replaceUIFile(osuPath, true); await replaceUIFiles(osuPath, true);
mainWindow.webContents.send("ezpplauncher:launchabort"); mainWindow.webContents.send("ezpplauncher:launchabort");
osuLoaded = false; osuLoaded = false;
}, 5000); }, 5000);

1113
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "ezpplauncher-next", "name": "ezpplauncher-next",
"version": "2.1.2", "version": "2.1.3",
"description": "EZPPLauncher rewritten with Svelte.", "description": "EZPPLauncher rewritten with Svelte.",
"private": false, "private": false,
"license": "MIT", "license": "MIT",
@ -61,7 +61,6 @@
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.5", "@rollup/plugin-typescript": "^11.1.5",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@tsconfig/svelte": "^5.0.2", "@tsconfig/svelte": "^5.0.2",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",

View File

@ -115,3 +115,9 @@ ipcRenderer.addListener("ezpplauncher:update", (e, args) => {
new CustomEvent("update", { detail: args }), new CustomEvent("update", { detail: args }),
); );
}); });
ipcRenderer.addListener("ezpplauncher:open-settings", (e, args) => {
window.dispatchEvent(
new CustomEvent("open-settings"),
);
});

View File

@ -66,6 +66,10 @@
window.dispatchEvent(new CustomEvent("updateExit")); window.dispatchEvent(new CustomEvent("updateExit"));
}); });
window.addEventListener("open-settings", (e) => {
currentPage.set(Page.Settings);
});
window.addEventListener("launchStatusUpdate", (e) => { window.addEventListener("launchStatusUpdate", (e) => {
const status = (e as CustomEvent).detail.status; const status = (e as CustomEvent).detail.status;
launchStatus.set(status); launchStatus.set(status);

View File

@ -26,12 +26,12 @@
green: "bg-green-600 dark:bg-green-500", green: "bg-green-600 dark:bg-green-500",
yellow: "bg-yellow-400", yellow: "bg-yellow-400",
purple: "bg-purple-600 dark:bg-purple-500", purple: "bg-purple-600 dark:bg-purple-500",
indigo: "bg-indigo-600 dark:bg-indigo-500", indigo: "bg-indigo-600 dark:bg-indigo-500"
}; };
let _progress = tweened(0, { let _progress = tweened(0, {
duration: tweenDuration, duration: tweenDuration,
easing, easing
}); });
$: { $: {
@ -50,8 +50,12 @@
> >
<span class="text-sm font-medium text-blue-700 dark:text-white" <span class="text-sm font-medium text-blue-700 dark:text-white"
>{animate >{animate
? $_progress.toFixed(precision) ? isNaN($_progress)
: progress.toFixed(precision)}%</span ? parseInt("100").toFixed(precision)
: $_progress.toFixed(precision)
: isNaN(progress)
? parseInt("100").toFixed(precision)
: progress.toFixed(precision)}%</span
> >
</div> </div>
{/if} {/if}
@ -63,7 +67,13 @@
class={twJoin(labelInsideClass, barColors[color])} class={twJoin(labelInsideClass, barColors[color])}
style="width: {animate ? $_progress : progress}%" style="width: {animate ? $_progress : progress}%"
> >
{animate ? $_progress.toFixed(precision) : progress.toFixed(precision)}% {animate
? isNaN($_progress)
? parseInt("100").toFixed(precision)
: $_progress.toFixed(precision)
: isNaN(progress)
? parseInt("100").toFixed(precision)
: progress.toFixed(precision)}%
</div> </div>
{:else} {:else}
<div <div

View File

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { Button, Checkbox } from "flowbite-svelte"; import { Button } from "flowbite-svelte";
import Progressbar from "../lib/Progressbar.svelte"; import Progressbar from "../lib/Progressbar.svelte";
import { import {
launching, launching,
patch, patch,
launchStatus, launchStatus,
launchPercentage, launchPercentage
} from "./../storage/localStore"; } from "./../storage/localStore";
let progressbarFix = true; let progressbarFix = true;

View File

@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Input, Button, Spinner, Checkbox } from "flowbite-svelte"; import { Input, Button, Spinner, Checkbox } from "flowbite-svelte";
import type { User } from "../types/user"; import { type User } from "../types/user";
import type { Error } from "../types/error";
import { currentPage, currentUser, startup } from "../storage/localStore"; import { currentPage, currentUser, startup } from "../storage/localStore";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
import { Page } from "../consts/pages"; import { Page } from "../consts/pages";
@ -43,14 +42,14 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
}); });
}, },
{ once: true } { once: true }
); );
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("login-attempt", { new CustomEvent("login-attempt", {
detail: { username, password, saveCredentials }, detail: { username, password, saveCredentials }
}) })
); );
}); });
@ -59,13 +58,13 @@
{ {
loading: "Logging in...", loading: "Logging in...",
success: "Successfully logged in!", success: "Successfully logged in!",
error: "Invalid Username or Password!", error: "Invalid Username or Password!"
}, },
{ {
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
} }
); );
}; };
@ -87,7 +86,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
}); });
return; return;
} }
@ -104,7 +103,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
}); });
loading = false; loading = false;
}, },
@ -117,13 +116,13 @@
{ {
loading: "Logging in...", loading: "Logging in...",
success: "Successfully logged in!", success: "Successfully logged in!",
error: "Failed to login.", error: "Failed to login."
}, },
{ {
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
} }
); );
}; };
@ -135,7 +134,7 @@
position: "bottom-center", position: "bottom-center",
className: className:
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100", "dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
duration: 3000, duration: 3000
}); });
}; };

View File

@ -1,7 +1,7 @@
const { vitePreprocess } = require("@sveltejs/vite-plugin-svelte"); const preprocess = require("svelte-preprocess");
const config = { const config = {
preprocess: [vitePreprocess({})], preprocess: [preprocess()],
}; };
module.exports = config; module.exports = config;