Compare commits
	
		
			3 Commits
		
	
	
		
			2.1.4
			...
			f06e63f7f8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f06e63f7f8 | |||
| 638f1e852e | |||
| 8b30b7c1fa | 
| @@ -1,20 +1,20 @@ | ||||
| const childProcess = require("child_process"); | ||||
|  | ||||
| const runFile = (folder, file, args, onExit) => { | ||||
|     childProcess.execFile(file, args, { | ||||
|         cwd: folder | ||||
|     }, (_err, _stdout, _stdin) => { | ||||
|         if (onExit) onExit(); | ||||
|     }) | ||||
| } | ||||
|   childProcess.execFile(file, args, { | ||||
|     cwd: folder, | ||||
|   }, (_err, _stdout, _stdin) => { | ||||
|     if (onExit) onExit(); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const runFileDetached = (folder, file, args) => { | ||||
|     const subProcess = childProcess.spawn(file + (args ? " " + args : ''), { | ||||
|         cwd: folder, | ||||
|         detached: true, | ||||
|         stdio: 'ignore' | ||||
|     }); | ||||
|     subProcess.unref(); | ||||
| } | ||||
|   const subProcess = childProcess.spawn(file + (args ? " " + args : ""), { | ||||
|     cwd: folder, | ||||
|     detached: true, | ||||
|     stdio: "ignore", | ||||
|   }); | ||||
|   subProcess.unref(); | ||||
| }; | ||||
|  | ||||
| module.exports = { runFile, runFileDetached }; | ||||
| module.exports = { runFile, runFileDetached }; | ||||
|   | ||||
							
								
								
									
										15
									
								
								electron/fileUtil.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								electron/fileUtil.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| const fs = require("fs"); | ||||
|  | ||||
| function isWritable(filePath) { | ||||
|   let fileAccess = false; | ||||
|   try { | ||||
|     fs.closeSync(fs.openSync(filePath, "r+")); | ||||
|     fileAccess = true; | ||||
|   } catch { | ||||
|   } | ||||
|   return fileAccess; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   isWritable, | ||||
| }; | ||||
| @@ -1,13 +1,13 @@ | ||||
| function formatBytes(bytes, decimals = 2) { | ||||
|     if (!+bytes) return '0 Bytes' | ||||
|   if (!+bytes) return "0 B"; | ||||
|  | ||||
|     const k = 1024 | ||||
|     const dm = decimals < 0 ? 0 : decimals | ||||
|     const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] | ||||
|   const k = 1024; | ||||
|   const dm = decimals < 0 ? 0 : decimals; | ||||
|   const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; | ||||
|  | ||||
|     const i = Math.floor(Math.log(bytes) / Math.log(k)) | ||||
|   const i = Math.floor(Math.log(bytes) / Math.log(k)); | ||||
|  | ||||
|     return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}` | ||||
|   return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`; | ||||
| } | ||||
|  | ||||
| module.exports = { formatBytes }; | ||||
| module.exports = { formatBytes }; | ||||
|   | ||||
							
								
								
									
										44
									
								
								electron/logging.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								electron/logging.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| const fs = require("fs"); | ||||
| const path = require("path"); | ||||
|  | ||||
| class Logger { | ||||
|   constructor(directory) { | ||||
|     this.directory = directory; | ||||
|     this.enabled = false; | ||||
|   } | ||||
|  | ||||
|   async init() { | ||||
|     const filename = `${new Date().toISOString().replace(/:/g, "-")}.log`; | ||||
|     this.logPath = path.join(this.directory, filename); | ||||
|   } | ||||
|  | ||||
|   async log(message) { | ||||
|     if (this.logPath === undefined || this.enabled == false) { | ||||
|       return; | ||||
|     } | ||||
|     if (!fs.existsSync(this.logPath)) { | ||||
|       await fs.promises.mkdir(this.directory, { recursive: true }); | ||||
|       await fs.promises.writeFile(this.logPath, ""); | ||||
|     } | ||||
|     const logMessage = `[${new Date().toISOString()}] LOG: ${message}`; | ||||
|     await fs.promises.appendFile(this.logPath, `${logMessage}\n`); | ||||
|     console.log(logMessage); | ||||
|   } | ||||
|  | ||||
|   async error(message, error) { | ||||
|     if (this.logPath === undefined || this.enabled == false) { | ||||
|       return; | ||||
|     } | ||||
|     if (!fs.existsSync(this.logPath)) { | ||||
|       await fs.promises.mkdir(this.directory, { recursive: true }); | ||||
|       await fs.promises.writeFile(this.logPath, ""); | ||||
|     } | ||||
|     const errorMessage = `[${ | ||||
|       new Date().toISOString() | ||||
|     }] ERROR: ${message}\n${error.stack}`; | ||||
|     await fs.promises.appendFile(this.logPath, `${errorMessage}\n`); | ||||
|     console.error(errorMessage); | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Logger; | ||||
| @@ -1,8 +1,8 @@ | ||||
| const { exec } = require("child_process"); | ||||
|  | ||||
| async function isNet8Installed() { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         exec("dotnet --version", (error, stdout, stderr) => { | ||||
|     return new Promise((resolve) => { | ||||
|         exec("dotnet --list-runtimes", (error, stdout, stderr) => { | ||||
|             if (error) { | ||||
|                 resolve(false); | ||||
|                 return; | ||||
| @@ -12,7 +12,13 @@ async function isNet8Installed() { | ||||
|                 return; | ||||
|             } | ||||
|             const version = stdout.trim(); | ||||
|             resolve(version.startsWith("8.")); | ||||
|             for (const line of version.split('\n')) { | ||||
|                 if (line.startsWith("Microsoft.WindowsDesktop.App 8.")) { | ||||
|                     resolve(true); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             resolve(false); | ||||
|         }) | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -18,8 +18,8 @@ const gamemodes = { | ||||
|   4: "osu!(rx)", | ||||
|   5: "taiko(rx)", | ||||
|   6: "catch(rx)", | ||||
|   8: "osu!(ap)" | ||||
| } | ||||
|   8: "osu!(ap)", | ||||
| }; | ||||
| const osuEntities = [ | ||||
|   "avcodec-51.dll", | ||||
|   "avformat-52.dll", | ||||
| @@ -44,7 +44,7 @@ const osuEntities = [ | ||||
|   "scores.db", | ||||
| ]; | ||||
|  | ||||
| const ezppLauncherUpdateList = "https://ez-pp.farm/ezpplauncher" | ||||
| const ezppLauncherUpdateList = "https://ez-pp.farm/ezpplauncher"; | ||||
|  | ||||
| async function isValidOsuFolder(path) { | ||||
|   const allFiles = await fs.promises.readdir(path); | ||||
| @@ -184,31 +184,24 @@ function downloadUpdateFiles(osuPath, updateFiles) { | ||||
|  | ||||
|   const startDownload = async () => { | ||||
|     for (const updatePatch of updateFiles) { | ||||
|       const fileName = updatePatch.filename; | ||||
|       const fileSize = updatePatch.filesize; | ||||
|       const fileURL = updatePatch.url_full; | ||||
|  | ||||
|       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), | ||||
|           }); | ||||
|         }, | ||||
|       }); | ||||
|       axiosDownloadWithProgress.data.on("end", () => { | ||||
|         eventEmitter.emit("data", { | ||||
|           fileName, | ||||
|           loaded: fileSize, | ||||
|           total: fileSize, | ||||
|           progress: 100, | ||||
|         }); | ||||
|       }); | ||||
|       try { | ||||
|         const fileName = updatePatch.filename; | ||||
|         const fileSize = updatePatch.filesize; | ||||
|         const fileURL = updatePatch.url_full; | ||||
|  | ||||
|         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), | ||||
|             }); | ||||
|           }, | ||||
|         }); | ||||
|  | ||||
|         if (fs.existsSync(path.join(osuPath, fileName))) { | ||||
|           await fs.promises.rm(path.join(osuPath, fileName), { | ||||
|             force: true, | ||||
| @@ -222,6 +215,7 @@ function downloadUpdateFiles(osuPath, updateFiles) { | ||||
|         console.log(err); | ||||
|         eventEmitter.emit("error", { | ||||
|           fileName, | ||||
|           error: err, | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
| @@ -248,10 +242,16 @@ function runOsuWithDevServer(osuPath, serverDomain, onExit) { | ||||
|  | ||||
| async function getEZPPLauncherUpdateFiles(osuPath) { | ||||
|   const filesToDownload = []; | ||||
|   const updateFilesRequest = await fetch(ezppLauncherUpdateList, { method: "PATCH" }); | ||||
|   const updateFilesRequest = await fetch(ezppLauncherUpdateList, { | ||||
|     method: "PATCH", | ||||
|   }); | ||||
|   const updateFiles = await updateFilesRequest.json(); | ||||
|   for (const updateFile of updateFiles) { | ||||
|     const filePath = path.join(osuPath, ...updateFile.folder.split("/"), updateFile.name); | ||||
|     const filePath = path.join( | ||||
|       osuPath, | ||||
|       ...updateFile.folder.split("/"), | ||||
|       updateFile.name, | ||||
|     ); | ||||
|     if (fs.existsSync(filePath)) { | ||||
|       const fileHash = updateFile.md5.toLowerCase(); | ||||
|       const localFileHash = crypto.createHash("md5").update( | ||||
| @@ -272,22 +272,30 @@ async function downloadEZPPLauncherUpdateFiles(osuPath, updateFiles) { | ||||
|  | ||||
|   const startDownload = async () => { | ||||
|     for (const updateFile of updateFiles) { | ||||
|       const filePath = path.join(osuPath, ...updateFile.folder.split("/"), updateFile.name); | ||||
|       const folder = path.dirname(filePath); | ||||
|       if (!fs.existsSync(folder)) await fs.promises.mkdir(folder, { recursive: true }); | ||||
|       const axiosDownloadWithProgress = await axios.get(updateFile.url, { | ||||
|         responseType: "stream", | ||||
|         onDownloadProgress: (progressEvent) => { | ||||
|           const { loaded, total } = progressEvent; | ||||
|           eventEmitter.emit("data", { | ||||
|             fileName: path.basename(filePath), | ||||
|             loaded, | ||||
|             total, | ||||
|             progress: Math.floor((loaded / total) * 100), | ||||
|           }); | ||||
|         }, | ||||
|       }); | ||||
|       try { | ||||
|         const filePath = path.join( | ||||
|           osuPath, | ||||
|           ...updateFile.folder.split("/"), | ||||
|           updateFile.name, | ||||
|         ); | ||||
|         const folder = path.dirname(filePath); | ||||
|         if (!fs.existsSync(folder)) { | ||||
|           await fs.promises.mkdir(folder, { recursive: true }); | ||||
|         } | ||||
|         const axiosDownloadWithProgress = await axios.get(updateFile.url, { | ||||
|           responseType: "stream", | ||||
|           onDownloadProgress: (progressEvent) => { | ||||
|             const fileSize = updateFile.size; | ||||
|             const { loaded } = progressEvent; | ||||
|             eventEmitter.emit("data", { | ||||
|               fileName: path.basename(filePath), | ||||
|               loaded, | ||||
|               total: fileSize, | ||||
|               progress: Math.floor((loaded / fileSize) * 100), | ||||
|             }); | ||||
|           }, | ||||
|         }); | ||||
|  | ||||
|         if (fs.existsSync(filePath)) { | ||||
|           await fs.promises.rm(filePath, { | ||||
|             force: true, | ||||
| @@ -301,10 +309,11 @@ async function downloadEZPPLauncherUpdateFiles(osuPath, updateFiles) { | ||||
|         console.log(err); | ||||
|         eventEmitter.emit("error", { | ||||
|           fileName: path.basename(filePath), | ||||
|           error: err, | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   }; | ||||
|  | ||||
|   return { | ||||
|     eventEmitter, | ||||
| @@ -316,7 +325,11 @@ async function replaceUIFiles(osuPath, revert) { | ||||
|   if (!revert) { | ||||
|     const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll"); | ||||
|     const oldOsuUIFile = path.join(osuPath, "osu!ui.dll"); | ||||
|     const ezppGameplayFile = path.join(osuPath, "EZPPLauncher", "ezpp!gameplay.dll"); | ||||
|     const ezppGameplayFile = path.join( | ||||
|       osuPath, | ||||
|       "EZPPLauncher", | ||||
|       "ezpp!gameplay.dll", | ||||
|     ); | ||||
|     const oldOsuGameplayFile = path.join(osuPath, "osu!gameplay.dll"); | ||||
|  | ||||
|     await fs.promises.rename( | ||||
| @@ -334,7 +347,11 @@ async function replaceUIFiles(osuPath, revert) { | ||||
|     const oldOsuUIFile = path.join(osuPath, "osu!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"); | ||||
|     const ezppGameplayFile = path.join( | ||||
|       osuPath, | ||||
|       "EZPPLauncher", | ||||
|       "ezpp!gameplay.dll", | ||||
|     ); | ||||
|  | ||||
|     await fs.promises.rename(oldOsuUIFile, ezppUIFile); | ||||
|     await fs.promises.rename( | ||||
| @@ -413,5 +430,5 @@ module.exports = { | ||||
|   runOsuUpdater, | ||||
|   getEZPPLauncherUpdateFiles, | ||||
|   downloadEZPPLauncherUpdateFiles, | ||||
|   gamemodes | ||||
|   gamemodes, | ||||
| }; | ||||
|   | ||||
| @@ -35,7 +35,9 @@ module.exports = { | ||||
|       richPresence = new DiscordRPC.AutoClient({ transport: "ipc" }); | ||||
|       richPresence.endlessLogin({ clientId }); | ||||
|       richPresence.once("ready", () => { | ||||
|         console.log("connected presence with user " + richPresence.user.username); | ||||
|         console.log( | ||||
|           "connected presence with user " + richPresence.user.username, | ||||
|         ); | ||||
|         richPresence.setActivity(currentStatus); | ||||
|         intervalId = setInterval(() => { | ||||
|           richPresence.setActivity(currentStatus); | ||||
|   | ||||
							
								
								
									
										504
									
								
								main.js
									
									
									
									
									
								
							
							
						
						
									
										504
									
								
								main.js
									
									
									
									
									
								
							| @@ -20,7 +20,6 @@ const { | ||||
|   runOsuWithDevServer, | ||||
|   replaceUIFiles, | ||||
|   findOsuInstallation, | ||||
|   updateOsuConfigHashes, | ||||
|   runOsuUpdater, | ||||
|   gamemodes, | ||||
|   getEZPPLauncherUpdateFiles, | ||||
| @@ -38,6 +37,8 @@ const { updateAvailable, releasesUrl } = require("./electron/updateCheck"); | ||||
| const fkill = require("fkill"); | ||||
| const { checkImageExists } = require("./electron/imageUtil"); | ||||
| const { isNet8Installed } = require("./electron/netUtils"); | ||||
| const Logger = require("./electron/logging"); | ||||
| const { isWritable } = require("./electron/fileUtil"); | ||||
|  | ||||
| // 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. | ||||
| @@ -46,6 +47,13 @@ let osuCheckInterval; | ||||
| let userOsuPath; | ||||
| let osuLoaded = false; | ||||
| let patch = false; | ||||
| let logger = new Logger(path.join( | ||||
|   process.platform == "win32" | ||||
|     ? process.env["LOCALAPPDATA"] | ||||
|     : process.env["HOME"], | ||||
|   "EZPPLauncher", | ||||
|   "logs", | ||||
| )); | ||||
|  | ||||
| let currentUser = undefined; | ||||
|  | ||||
| @@ -66,10 +74,18 @@ function startOsuStatus() { | ||||
|         osuLoaded = true; | ||||
|  | ||||
|         try { | ||||
|           const currentUserInfo = await fetch(`https://api.ez-pp.farm/get_player_info?name=${currentUser.username}&scope=info`); | ||||
|           const currentUserInfo = await fetch( | ||||
|             `https://api.ez-pp.farm/get_player_info?name=${currentUser.username}&scope=info`, | ||||
|           ); | ||||
|           const currentUserInfoJson = await currentUserInfo.json(); | ||||
|           if ("player" in currentUserInfoJson && currentUserInfoJson.player != null) { | ||||
|             if ("info" in currentUserInfoJson.player && currentUserInfoJson.player.info != null) { | ||||
|           if ( | ||||
|             "player" in currentUserInfoJson && | ||||
|             currentUserInfoJson.player != null | ||||
|           ) { | ||||
|             if ( | ||||
|               "info" in currentUserInfoJson.player && | ||||
|               currentUserInfoJson.player.info != null | ||||
|             ) { | ||||
|               const id = currentUserInfoJson.player.info.id; | ||||
|               const username = currentUserInfoJson.player.info.name; | ||||
|               richPresence.updateUser({ | ||||
| @@ -80,7 +96,6 @@ function startOsuStatus() { | ||||
|             } | ||||
|           } | ||||
|         } catch { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         setTimeout(() => { | ||||
| @@ -112,7 +127,8 @@ function startOsuStatus() { | ||||
|       const currentModeString = gamemodes[currentMode]; | ||||
|  | ||||
|       const currentInfoRequest = await fetch( | ||||
|         "https://api.ez-pp.farm/get_player_info?name=" + currentUser.username + "&scope=all", | ||||
|         "https://api.ez-pp.farm/get_player_info?name=" + currentUser.username + | ||||
|           "&scope=all", | ||||
|       ); | ||||
|       const currentInfo = await currentInfoRequest.json(); | ||||
|       let currentUsername = currentInfo.player.info.name; | ||||
| @@ -192,7 +208,7 @@ function startOsuStatus() { | ||||
|       richPresence.updateUser({ | ||||
|         username: currentUsername, | ||||
|         id: currentId, | ||||
|       }) | ||||
|       }); | ||||
|  | ||||
|       richPresence.updateStatus({ | ||||
|         details, | ||||
| @@ -211,9 +227,19 @@ function stopOsuStatus() { | ||||
|  | ||||
| function registerIPCPipes() { | ||||
|   ipcMain.handle("ezpplauncher:login", async (e, args) => { | ||||
|     const hwid = getHwId(); | ||||
|     let hwid = ""; | ||||
|     try { | ||||
|       hwid = getHwId(); | ||||
|     } catch (err) { | ||||
|       logger.error(`Failed to get HWID.`, err); | ||||
|       return { | ||||
|         code: 500, | ||||
|         message: "Failed to get HWID.", | ||||
|       }; | ||||
|     } | ||||
|     const timeout = new AbortController(); | ||||
|     const timeoutId = setTimeout(() => timeout.abort(), 8000); | ||||
|     logger.log(`Logging in with user ${args.username}...`); | ||||
|     try { | ||||
|       const fetchResult = await fetch("https://ez-pp.farm/login/check", { | ||||
|         signal: timeout.signal, | ||||
| @@ -238,14 +264,20 @@ function registerIPCPipes() { | ||||
|           } | ||||
|           currentUser = args; | ||||
|           config.remove("guest"); | ||||
|         } | ||||
|           logger.log(`Logged in as user ${args.username}!`); | ||||
|         } else logger.log(`Login failed for user ${args.username}.`); | ||||
|         return result; | ||||
|       } | ||||
|       logger.log( | ||||
|         `Login failed for user ${username}.\nResponse:\n${await fetchResult | ||||
|           .text()}`, | ||||
|       ); | ||||
|       return { | ||||
|         code: 500, | ||||
|         message: "Something went wrong while logging you in.", | ||||
|       }; | ||||
|     } catch (err) { | ||||
|       logger.error("Error while logging in:", err); | ||||
|       return { | ||||
|         code: 500, | ||||
|         message: "Something went wrong while logging you in.", | ||||
| @@ -275,6 +307,7 @@ function registerIPCPipes() { | ||||
|     } | ||||
|     const timeout = new AbortController(); | ||||
|     const timeoutId = setTimeout(() => timeout.abort(), 8000); | ||||
|     logger.log(`Logging in with user ${username}...`); | ||||
|     try { | ||||
|       const fetchResult = await fetch("https://ez-pp.farm/login/check", { | ||||
|         signal: timeout.signal, | ||||
| @@ -297,16 +330,23 @@ function registerIPCPipes() { | ||||
|             username: username, | ||||
|             password: password, | ||||
|           }; | ||||
|         } | ||||
|           logger.log(`Logged in as user ${username}!`); | ||||
|         } else logger.log(`Login failed for user ${username}.`); | ||||
|  | ||||
|         return result; | ||||
|       } else { | ||||
|         config.remove("password"); | ||||
|       } | ||||
|       logger.log( | ||||
|         `Login failed for user ${username}.\nResponse:\n${await fetchResult | ||||
|           .text()}`, | ||||
|       ); | ||||
|       return { | ||||
|         code: 500, | ||||
|         message: "Something went wrong while logging you in.", | ||||
|       }; | ||||
|     } catch (err) { | ||||
|       logger.error("Error while logging in:", err); | ||||
|       return { | ||||
|         code: 500, | ||||
|         message: "Something went wrong while logging you in.", | ||||
| @@ -319,6 +359,7 @@ function registerIPCPipes() { | ||||
|     config.remove("password"); | ||||
|     config.set("guest", "1"); | ||||
|     currentUser = undefined; | ||||
|     logger.log("Logged in as guest user."); | ||||
|   }); | ||||
|  | ||||
|   ipcMain.handle("ezpplauncher:logout", (e) => { | ||||
| @@ -326,6 +367,7 @@ function registerIPCPipes() { | ||||
|     config.remove("password"); | ||||
|     config.remove("guest"); | ||||
|     currentUser = undefined; | ||||
|     logger.log("Loging out."); | ||||
|     return true; | ||||
|   }); | ||||
|  | ||||
| @@ -342,6 +384,10 @@ function registerIPCPipes() { | ||||
|         else richPresence.connect(); | ||||
|       } | ||||
|  | ||||
|       if (key == "logging") { | ||||
|         logger.enabled = logging; | ||||
|       } | ||||
|  | ||||
|       if (typeof value == "boolean") { | ||||
|         config.set(key, value ? "true" : "false"); | ||||
|       } else { | ||||
| @@ -391,100 +437,78 @@ function registerIPCPipes() { | ||||
|   }); | ||||
|  | ||||
|   ipcMain.handle("ezpplauncher:launch", async (e) => { | ||||
|     const configPatch = config.get("patch"); | ||||
|     patch = configPatch != undefined ? configPatch == "true" : true; | ||||
|     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!", | ||||
|       }); | ||||
|       mainWindow.webContents.send("ezpplauncher:open-settings"); | ||||
|       return; | ||||
|     } | ||||
|     if (!(await isValidOsuFolder(osuPath))) { | ||||
|       mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|       mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|         type: "error", | ||||
|         message: "invalid osu! path!", | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|     if (patch) { | ||||
|       if (!(await isNet8Installed())) { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|     try { | ||||
|       const osuWindowTitle = windowName.getWindowText("osu!.exe"); | ||||
|       if (osuWindowTitle.length > 0) { | ||||
|         mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|           type: "error", | ||||
|           message: ".NET 8 is not installed.", | ||||
|           message: "osu! is running, please exit.", | ||||
|         }); | ||||
|         //open .net 8 download in browser | ||||
|         shell.openExternal('https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.4-windows-x64-installer'); | ||||
|       } | ||||
|     } | ||||
|     mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|       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 updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles); | ||||
|     if (updateFiles.length > 0) { | ||||
|       const updateDownloader = downloadUpdateFiles(osuPath, updateFiles); | ||||
|       let errored = false; | ||||
|       updateDownloader.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.`, | ||||
|         }); | ||||
|       }); | ||||
|       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, | ||||
|       }); | ||||
|       if (errored) { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|         return; | ||||
|       } | ||||
|       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) { | ||||
|       logger.log("Preparing launch..."); | ||||
|       const configPatch = config.get("patch"); | ||||
|       patch = configPatch != undefined ? configPatch == "true" : true; | ||||
|       mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|         status: "Looking for patcher updates...", | ||||
|         status: "Checking osu! directory...", | ||||
|       }); | ||||
|       await new Promise((res) => setTimeout(res, 1000)); | ||||
|       const patchFiles = await getEZPPLauncherUpdateFiles(osuPath); | ||||
|       if (patchFiles.length > 0) { | ||||
|         const patcherDownloader = await downloadEZPPLauncherUpdateFiles(osuPath, patchFiles); | ||||
|       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!", | ||||
|         }); | ||||
|         mainWindow.webContents.send("ezpplauncher:open-settings"); | ||||
|         logger.log("osu! path is not set."); | ||||
|         return; | ||||
|       } | ||||
|       if (!(await isValidOsuFolder(osuPath))) { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|         mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|           type: "error", | ||||
|           message: "invalid osu! path!", | ||||
|         }); | ||||
|         logger.log("osu! path is invalid."); | ||||
|         return; | ||||
|       } | ||||
|       if (patch) { | ||||
|         if (!(await isNet8Installed())) { | ||||
|           mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|           mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|             type: "error", | ||||
|             message: ".NET 8 is not installed.", | ||||
|           }); | ||||
|           //open .net 8 download in browser | ||||
|           shell.openExternal( | ||||
|             "https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.4-windows-x64-installer", | ||||
|           ); | ||||
|           logger.log(".NET 8 is not installed."); | ||||
|         } | ||||
|       } | ||||
|       mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|         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 updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles); | ||||
|       if (updateFiles.length > 0) { | ||||
|         logger.log("osu! updates found."); | ||||
|         const updateDownloader = downloadUpdateFiles(osuPath, updateFiles); | ||||
|         let errored = false; | ||||
|         patcherDownloader.eventEmitter.on("error", (data) => { | ||||
|         updateDownloader.eventEmitter.on("error", (data) => { | ||||
|           const filename = data.fileName; | ||||
|           logger.error( | ||||
|             `Failed to download/replace ${filename}!`, | ||||
|             data.error, | ||||
|           ); | ||||
|           errored = true; | ||||
|           mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|             type: "error", | ||||
| @@ -492,16 +516,20 @@ function registerIPCPipes() { | ||||
|               `Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`, | ||||
|           }); | ||||
|         }); | ||||
|         patcherDownloader.eventEmitter.on("data", (data) => { | ||||
|         updateDownloader.eventEmitter.on("data", (data) => { | ||||
|           if (data.progress >= 100) { | ||||
|             logger.log(`Downloaded ${data.fileName} successfully.`); | ||||
|           } | ||||
|           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) | ||||
|               })...`, | ||||
|             status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${ | ||||
|               formatBytes(data.total) | ||||
|             })...`, | ||||
|           }); | ||||
|         }); | ||||
|         await patcherDownloader.startDownload(); | ||||
|         await updateDownloader.startDownload(); | ||||
|         mainWindow.webContents.send("ezpplauncher:launchprogress", { | ||||
|           progress: -1, | ||||
|         }); | ||||
| @@ -510,119 +538,206 @@ function registerIPCPipes() { | ||||
|           return; | ||||
|         } | ||||
|         mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|           status: "Patcher is now up to date!", | ||||
|           status: "osu! is now up to date!", | ||||
|         }); | ||||
|         await new Promise((res) => setTimeout(res, 1000)); | ||||
|       } else { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|           status: "Patcher is up to date!", | ||||
|           status: "osu! is up to date!", | ||||
|         }); | ||||
|         await new Promise((res) => setTimeout(res, 1000)); | ||||
|       } | ||||
|       await new Promise((res) => setTimeout(res, 1000)); | ||||
|     } | ||||
|     if (updateFiles.length > 0) { | ||||
|       mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|         status: "Launching osu! updater to verify updates...", | ||||
|       }); | ||||
|       await new Promise((res) => setTimeout(res, 1000)); | ||||
|  | ||||
|       await new Promise((res) => { | ||||
|         runOsuUpdater(osuPath, async () => { | ||||
|           await new Promise((res) => setTimeout(res, 500)); | ||||
|           const terminationThread = setInterval(async () => { | ||||
|             const osuWindowTitle = windowName.getWindowText("osu!.exe"); | ||||
|             if (osuWindowTitle.length < 0) { | ||||
|               return; | ||||
|             } | ||||
|             const firstInstance = osuWindowTitle[0]; | ||||
|             if (firstInstance) { | ||||
|               const processId = firstInstance.processId; | ||||
|               await fkill(processId, { force: true, silent: true }); | ||||
|               clearInterval(terminationThread); | ||||
|               res(); | ||||
|             } | ||||
|           }, 500); | ||||
|       if (patch) { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|           status: "Looking for patcher updates...", | ||||
|         }); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     await new Promise((res) => setTimeout(res, 1000)); | ||||
|  | ||||
|     mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|       status: "Preparing launch...", | ||||
|     }); | ||||
|  | ||||
|     await updateOsuConfigHashes(osuPath); | ||||
|     await replaceUIFiles(osuPath, false); | ||||
|  | ||||
|     const forceUpdateFiles = [ | ||||
|       ".require_update", | ||||
|       "help.txt", | ||||
|       "_pending", | ||||
|     ]; | ||||
|  | ||||
|     try { | ||||
|       for (const updateFileName of forceUpdateFiles) { | ||||
|         const updateFile = path.join(osuPath, updateFileName); | ||||
|         if (fs.existsSync(updateFile)) { | ||||
|           await fs.promises.rm(updateFile, { | ||||
|             force: true, | ||||
|             recursive: (await fs.promises.lstat(updateFile)).isDirectory, | ||||
|         await new Promise((res) => setTimeout(res, 1000)); | ||||
|         const patchFiles = await getEZPPLauncherUpdateFiles(osuPath); | ||||
|         if (patchFiles.length > 0) { | ||||
|           logger.log("EZPPLauncher updates found."); | ||||
|           const patcherDownloader = await downloadEZPPLauncherUpdateFiles( | ||||
|             osuPath, | ||||
|             patchFiles, | ||||
|           ); | ||||
|           let errored = false; | ||||
|           patcherDownloader.eventEmitter.on("error", (data) => { | ||||
|             const filename = data.fileName; | ||||
|             logger.error(`Failed to download/replace ${filename}!`, data.error); | ||||
|             errored = true; | ||||
|             mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|               type: "error", | ||||
|               message: | ||||
|                 `Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`, | ||||
|             }); | ||||
|           }); | ||||
|           patcherDownloader.eventEmitter.on("data", (data) => { | ||||
|             if (data.progress >= 100) { | ||||
|               logger.log(`Downloaded ${data.fileName} successfully.`); | ||||
|             } | ||||
|             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, | ||||
|           }); | ||||
|           if (errored) { | ||||
|             mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|             return; | ||||
|           } | ||||
|           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)); | ||||
|       } | ||||
|     } catch { } | ||||
|       if (updateFiles.length > 0) { | ||||
|         mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|           status: "Launching osu! updater to verify updates...", | ||||
|         }); | ||||
|         await new Promise((res) => setTimeout(res, 1000)); | ||||
|  | ||||
|     const userConfig = getUserConfig(osuPath); | ||||
|     if (richPresence.hasPresence) { | ||||
|       await userConfig.set("DiscordRichPresence", "0"); | ||||
|     } | ||||
|     await userConfig.set("ShowInterfaceDuringRelax", "1"); | ||||
|     if (currentUser) { | ||||
|       await userConfig.set("CredentialEndpoint", "ez-pp.farm"); | ||||
|       await userConfig.set("SavePassword", "1"); | ||||
|       await userConfig.set("SaveUsername", "1"); | ||||
|       await userConfig.set("Username", currentUser.username); | ||||
|       await userConfig.set("Password", currentUser.password); | ||||
|     } | ||||
|         await new Promise((res) => { | ||||
|           runOsuUpdater(osuPath, async () => { | ||||
|             await new Promise((res) => setTimeout(res, 500)); | ||||
|             const terminationThread = setInterval(async () => { | ||||
|               const osuWindowTitle = windowName.getWindowText("osu!.exe"); | ||||
|               if (osuWindowTitle.length < 0) { | ||||
|                 return; | ||||
|               } | ||||
|               const firstInstance = osuWindowTitle[0]; | ||||
|               if (firstInstance) { | ||||
|                 const processId = firstInstance.processId; | ||||
|                 await fkill(processId, { force: true, silent: true }); | ||||
|                 clearInterval(terminationThread); | ||||
|                 res(); | ||||
|               } | ||||
|             }, 500); | ||||
|           }); | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|     mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|       status: "Launching osu!...", | ||||
|     }); | ||||
|       await new Promise((res) => setTimeout(res, 1000)); | ||||
|  | ||||
|     const onExitHook = () => { | ||||
|       mainWindow.show(); | ||||
|       mainWindow.focus(); | ||||
|       stopOsuStatus(); | ||||
|       richPresence.updateUser({ | ||||
|         username: "  ", | ||||
|         id: undefined | ||||
|       }); | ||||
|       richPresence.updateStatus({ | ||||
|         state: "Idle in Launcher...", | ||||
|         details: undefined, | ||||
|       }); | ||||
|       richPresence.update(); | ||||
|       mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|         status: "Waiting for cleanup...", | ||||
|         status: "Preparing launch...", | ||||
|       }); | ||||
|  | ||||
|       setTimeout(async () => { | ||||
|         await replaceUIFiles(osuPath, true); | ||||
|       /* await updateOsuConfigHashes(osuPath); */ | ||||
|       logger.log("Replacing UI files..."); | ||||
|       try { | ||||
|         await replaceUIFiles(osuPath, false); | ||||
|         logger.log("UI files replaced successfully."); | ||||
|       } catch (err) { | ||||
|         logger.error("Failed to replace UI files:", err); | ||||
|         mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|           type: "error", | ||||
|           message: "Failed to replace UI files. try restarting EZPPLauncher.", | ||||
|         }); | ||||
|         mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|         osuLoaded = false; | ||||
|       }, 5000); | ||||
|     }; | ||||
|     runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook); | ||||
|     mainWindow.hide(); | ||||
|     startOsuStatus(); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|     /* mainWindow.webContents.send("ezpplauncher:launchprogress", { | ||||
|       progress: 0, | ||||
|     }); | ||||
|     mainWindow.webContents.send("ezpplauncher:launchprogress", { | ||||
|       progress: 100, | ||||
|     }); */ | ||||
|     return true; | ||||
|       const forceUpdateFiles = [ | ||||
|         ".require_update", | ||||
|         "help.txt", | ||||
|         "_pending", | ||||
|       ]; | ||||
|  | ||||
|       try { | ||||
|         for (const updateFileName of forceUpdateFiles) { | ||||
|           const updateFile = path.join(osuPath, updateFileName); | ||||
|           if (fs.existsSync(updateFile)) { | ||||
|             await fs.promises.rm(updateFile, { | ||||
|               force: true, | ||||
|               recursive: (await fs.promises.lstat(updateFile)).isDirectory, | ||||
|             }); | ||||
|           } | ||||
|         } | ||||
|       } catch (err) { | ||||
|         logger.error("Failed to remove force update files:", err); | ||||
|       } | ||||
|  | ||||
|       const userConfig = getUserConfig(osuPath); | ||||
|       if (richPresence.hasPresence) { | ||||
|         await userConfig.set("DiscordRichPresence", "0"); | ||||
|       } | ||||
|       await userConfig.set("ShowInterfaceDuringRelax", "1"); | ||||
|       if (currentUser) { | ||||
|         await userConfig.set("CredentialEndpoint", "ez-pp.farm"); | ||||
|         await userConfig.set("SavePassword", "1"); | ||||
|         await userConfig.set("SaveUsername", "1"); | ||||
|         await userConfig.set("Username", currentUser.username); | ||||
|         await userConfig.set("Password", currentUser.password); | ||||
|       } | ||||
|  | ||||
|       mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|         status: "Launching osu!...", | ||||
|       }); | ||||
|  | ||||
|       await new Promise((res) => setTimeout(res, 1000)); | ||||
|  | ||||
|       logger.log("Launching osu!..."); | ||||
|  | ||||
|       const onExitHook = () => { | ||||
|         logger.log("osu! has exited."); | ||||
|         mainWindow.show(); | ||||
|         mainWindow.focus(); | ||||
|         stopOsuStatus(); | ||||
|         richPresence.updateUser({ | ||||
|           username: "  ", | ||||
|           id: undefined, | ||||
|         }); | ||||
|         richPresence.updateStatus({ | ||||
|           state: "Idle in Launcher...", | ||||
|           details: undefined, | ||||
|         }); | ||||
|         richPresence.update(); | ||||
|         mainWindow.webContents.send("ezpplauncher:launchstatus", { | ||||
|           status: "Waiting for cleanup...", | ||||
|         }); | ||||
|         const timeStart = performance.now(); | ||||
|         logger.log("Waiting for cleanup..."); | ||||
|  | ||||
|         const cleanup = setInterval(async () => { | ||||
|           const osuUIFile = path.join(osuPath, "osu!ui.dll"); | ||||
|           const osuGameplayFile = path.join(osuPath, "osu!gameplay.dll"); | ||||
|           if (isWritable(osuUIFile) && isWritable(osuGameplayFile)) { | ||||
|             logger.log( | ||||
|               `Cleanup complete, took ${ | ||||
|                 ((performance.now() - timeStart) / 1000).toFixed(3) | ||||
|               } seconds.`, | ||||
|             ); | ||||
|             clearInterval(cleanup); | ||||
|             await replaceUIFiles(osuPath, true); | ||||
|             mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|             osuLoaded = false; | ||||
|           } | ||||
|         }, 1000); | ||||
|       }; | ||||
|       runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook); | ||||
|       mainWindow.hide(); | ||||
|       startOsuStatus(); | ||||
|       return true; | ||||
|     } catch (err) { | ||||
|       logger.error("Failed to launch", err); | ||||
|       mainWindow.webContents.send("ezpplauncher:alert", { | ||||
|         type: "error", | ||||
|         message: "Failed to launch osu!. Please try again.", | ||||
|       }); | ||||
|       mainWindow.webContents.send("ezpplauncher:launchabort"); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| @@ -678,6 +793,13 @@ function createWindow() { | ||||
|       richPresence.connect(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   logger.init(); | ||||
|  | ||||
|   const loggingEnabled = config.get("logging"); | ||||
|   if (loggingEnabled && loggingEnabled == "true") { | ||||
|     logger.enabled = true; | ||||
|   } | ||||
|   // Uncomment the following line of code when app is ready to be packaged. | ||||
|   // loadURL(mainWindow); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <script lang="ts"> | ||||
|   import { Button, ButtonGroup, Input, Toggle } from "flowbite-svelte"; | ||||
|   import { FileSearchSolid, FolderSolid } from "flowbite-svelte-icons"; | ||||
|   import { patch, presence } from "./../storage/localStore"; | ||||
|   import { patch, presence, logging } from "./../storage/localStore"; | ||||
|  | ||||
|   let folderPath: string = ""; | ||||
|  | ||||
| @@ -12,8 +12,10 @@ | ||||
|     const settingPresence = settings.find( | ||||
|       (setting) => setting.key == "presence" | ||||
|     ); | ||||
|     const settingLogging = settings.find((setting) => setting.key == "logging"); | ||||
|     patch.set(settingPatch ? settingPatch.val == "true" : true); | ||||
|     presence.set(settingPresence ? settingPresence.val == "true" : true); | ||||
|     logging.set(settingLogging ? settingLogging.val == "true" : false); | ||||
|     folderPath = osuPath ? osuPath.val : ""; | ||||
|   }); | ||||
|   window.dispatchEvent(new CustomEvent("settings-get")); | ||||
| @@ -39,19 +41,18 @@ | ||||
|       new CustomEvent("setting-update", { detail: { presence: $presence } }) | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   const toggleLogging = () => { | ||||
|     logging.set(!$logging); | ||||
|     window.dispatchEvent( | ||||
|       new CustomEvent("setting-update", { detail: { logging: $logging } }) | ||||
|     ); | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <main | ||||
|   class="h-[265px] flex flex-col justify-start p-3 animate-fadeIn opacity-0" | ||||
| > | ||||
|   <div class="flex flex-col gap-2 p-3"> | ||||
|     <Toggle class="w-fit" bind:checked={$presence} on:click={togglePresence} | ||||
|       >Discord Presence</Toggle | ||||
|     > | ||||
|     <Toggle class="w-fit" bind:checked={$patch} on:click={togglePatching} | ||||
|       >Patching</Toggle | ||||
|     > | ||||
|   </div> | ||||
|   <div | ||||
|     class="container flex flex-col items-center justify-center gap-5 rounded-lg p-3" | ||||
|   > | ||||
| @@ -77,4 +78,15 @@ | ||||
|       </Button> | ||||
|     </ButtonGroup> | ||||
|   </div> | ||||
|   <div class="flex flex-col gap-2 p-3"> | ||||
|     <Toggle class="w-fit" bind:checked={$presence} on:click={togglePresence} | ||||
|       >Discord Presence</Toggle | ||||
|     > | ||||
|     <Toggle class="w-fit" bind:checked={$patch} on:click={togglePatching} | ||||
|       >Patching</Toggle | ||||
|     > | ||||
|     <Toggle class="w-fit" bind:checked={$logging} on:click={toggleLogging} | ||||
|       >Debug Logging</Toggle | ||||
|     > | ||||
|   </div> | ||||
| </main> | ||||
|   | ||||
| @@ -7,8 +7,11 @@ export const updateAvailable = writable(false); | ||||
| export const launching = writable(false); | ||||
| export const launchStatus = writable("Waiting..."); | ||||
| export const launchPercentage = writable(-1); | ||||
|  | ||||
| export const currentUser: Writable<undefined | User> = writable(undefined); | ||||
| export const currentPage = writable(Page.Login); | ||||
|  | ||||
| export const osuPath: Writable<undefined | string> = writable(undefined); | ||||
| export const patch = writable(true); | ||||
| export const presence = writable(true); | ||||
| export const currentUser: Writable<undefined | User> = writable(undefined); | ||||
| export const currentPage = writable(Page.Login); | ||||
| export const logging = writable(false); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user