diff --git a/app/assets/js/index.js b/app/assets/js/index.js index a247827..fed06ff 100644 --- a/app/assets/js/index.js +++ b/app/assets/js/index.js @@ -2,7 +2,7 @@ const navBar = document.querySelector("nav") const uiButtons = document.querySelector("footer>section.left") const footer = document.querySelector("footer") const leftSection = document.querySelector("section.left") -const audioPourcentageLabel = document.querySelector("label[for='audioVolume']") +const audioPourcentageLabel = document.querySelector("label[for='audioVolume']") const audio = new Audio() function startAudio() { @@ -79,18 +79,46 @@ function logout() { document.location.href = './login.html' } -window.onload = () => { - system.call("server::ping") - system.call("player::profile") - startAudio() -} - -system.result("server::ping", (data) => { - playersStatus.innerText = `${data.players.online}/${data.players.max}` +system.result("server::ping", (pong) => { + playersStatus.innerText = `${pong.players.online}/${pong.players.max}` }) -system.result("player::profile", (data) => { +system.result("player::profile", (playerProfile) => { if (!localStorage.getItem("user")) { - localStorage.setItem("user", JSON.stringify(data)) + localStorage.setItem("user", JSON.stringify(playerProfile)) } -}) \ No newline at end of file +}) + +function handleOptionsChanges(key, value) { + system.call("game::optionSet", { key, value }) +} + +function handleSettingsChanges(key, value) { + system.call("settings::set", { key, value }) +} + +system.result("game::parseOptions", (options) => { + gamma.checked = options.gamma == 1 ? true : false + renderClouds.checked = options.renderrenderClouds == false ? false : true + guiScale.value = options.guiScale + graphicsMode.checked = options.graphicsMode == 0 ? true : false + renderDistance.value = options.renderDistance +}) + +system.result("settings::read", (settings) => { + ram.value = settings.ram.max +}) + +system.result("hardware::ramInformation", ($ram) => { + ram.setAttribute("max", $ram.avaibleRam) + maxRam.innerText = `${Math.floor($ram.avaibleRam / 1024)} G` +}) + +window.onload = () => { + system.call("hardware::ramInformation") + system.call("game::parseOptions") + system.call("server::ping") + system.call("player::profile") + system.call("settings::read") + startAudio() +} \ No newline at end of file diff --git a/app/logged.html b/app/logged.html index 90a47bc..e7fe7c6 100644 --- a/app/logged.html +++ b/app/logged.html @@ -49,8 +49,8 @@ 0.5GB - - + + MAX @@ -62,20 +62,20 @@ 4 - + 32
1 - + 4 @@ -83,29 +83,29 @@
- -
- -
- -
-
+
diff --git a/main.js b/main.js index 3d7d1ae..31478d9 100644 --- a/main.js +++ b/main.js @@ -4,82 +4,90 @@ const os = require("os") const hwid = require("./modules/hwid") const path = require("node:path") const serverPing = require("./modules/serverPing") +const gameOptions = require("./modules/gameOptions") +const launcherSettings = require("./modules/launcherSettings") const config = require("./config.json") const { Authenticator } = require("minecraft-launcher-core") -let launcherWindow, auth, skinPath +let launcherWindow, auth async function createLauncherWindow() { + gameOptions.initOptions(path.join(app.getPath("appData"), ".catboat", "options.txt")) + launcherSettings.initSettings(path.join(app.getPath("appData"), ".catboat")) if (net.isOnline()) { let win_width = 1550 let win_height = parseInt(win_width / (16/9)) const isLauncherNotBanned = await checkIfIAmBanned() - launcherWindow = new BrowserWindow({ - frame: false, - width: win_width, - height: win_height, - minWidth: win_width, - minHeight: win_height, - titleBarStyle: "hidden", - autoHideMenuBar: true, - roundedCorners: false, - resizable: false, - webPreferences: { - nodeIntegration: false, - contextIsolation: true, - preload: path.join(__dirname, "modules", "preload.js"), - webviewTag: true - } - }) - if (os.platform() == "darwin") { - app.dock.setIcon(nativeImage.createFromPath(path.join(__dirname, "app", "assets", "img", "icon.png"))) - } - launcherWindow.setIcon(path.join(__dirname, "app", "assets", "img", "icon.png")) - if (isLauncherNotBanned.success) { - launcherWindow.loadFile(path.join(__dirname, "app", "login.html")) - session.defaultSession.webRequest.onBeforeRequest({ - urls: [ - "https://embed.twitch.tv/*channel=*" - ] - }, (details, callback) => { - const url = details.url - const urlParams = new URLSearchParams(url.replace("https://embed.twitch.tv/", "")) - if (urlParams.get("parent")) { - callback({}) - return + try { + launcherWindow = new BrowserWindow({ + frame: false, + width: win_width, + height: win_height, + minWidth: win_width, + minHeight: win_height, + titleBarStyle: "hidden", + autoHideMenuBar: true, + roundedCorners: false, + resizable: false, + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + preload: path.join(__dirname, "modules", "preload.js"), + webviewTag: true } - - urlParams.set("parent", "localhost") - urlParams.set("referrer", "https://localhost/") - - const redirectUrl = `https://embed.twitch.tv/?${urlParams.toString()}` - callback({ - cancel: false, - redirectURL: redirectUrl - }) }) - - session.defaultSession.webRequest.onHeadersReceived({ - urls: [ - "https://www.twitch.tv/*", - "https://player.twitch.tv/*", - "https://embed.twitch.tv/*" - ] - }, (details, callback) => { - const responseHeaders = details.responseHeaders - delete responseHeaders["Content-Security-Policy"] - callback({ - cancel: false, - responseHeaders + if (os.platform() == "darwin") { + app.dock.setIcon(nativeImage.createFromPath(path.join(__dirname, "app", "assets", "img", "icon.png"))) + } + launcherWindow.setIcon(path.join(__dirname, "app", "assets", "img", "icon.png")) + if (isLauncherNotBanned.success) { + launcherWindow.loadFile(path.join(__dirname, "app", "login.html")) + session.defaultSession.webRequest.onBeforeRequest({ + urls: [ + "https://embed.twitch.tv/*channel=*" + ] + }, (details, callback) => { + const url = details.url + const urlParams = new URLSearchParams(url.replace("https://embed.twitch.tv/", "")) + if (urlParams.get("parent")) { + callback({}) + return + } + + urlParams.set("parent", "localhost") + urlParams.set("referrer", "https://localhost/") + + const redirectUrl = `https://embed.twitch.tv/?${urlParams.toString()}` + callback({ + cancel: false, + redirectURL: redirectUrl + }) }) - }) - launcherWindow.webContents.openDevTools() - } else { - launcherWindow.loadFile(path.join(__dirname, "app", "banned.html")) - launcherWindow.webContents.executeJavaScript(` - setBannedBy("${isLauncherNotBanned.banned_by}") - setBannedAt("${isLauncherNotBanned.banned_at}") - setBannedBecause("${isLauncherNotBanned.reason}") - `) + + session.defaultSession.webRequest.onHeadersReceived({ + urls: [ + "https://www.twitch.tv/*", + "https://player.twitch.tv/*", + "https://embed.twitch.tv/*" + ] + }, (details, callback) => { + const responseHeaders = details.responseHeaders + delete responseHeaders["Content-Security-Policy"] + callback({ + cancel: false, + responseHeaders + }) + }) + launcherWindow.webContents.openDevTools() + } else { + launcherWindow.loadFile(path.join(__dirname, "app", "banned.html")) + launcherWindow.webContents.executeJavaScript(` + setBannedBy("${isLauncherNotBanned.banned_by}") + setBannedAt("${isLauncherNotBanned.banned_at}") + setBannedBecause("${isLauncherNotBanned.reason}") + `) + } + } catch (error) { + dialog.showErrorBox("Erreur", error) } } else { dialog.showErrorBox("Connexion internet", "Le launcher requiert une connexion internet.") @@ -87,26 +95,23 @@ async function createLauncherWindow() { } async function checkIfIAmBanned() { - // if (net.isOnline()) { - // try { - // const reponse = await fetch(`${config.api.base}${config.api.endpoints.checkBanStatus}`, { - // method: "post", - // body: JSON.stringify({ - // hwid: hwid.getHWID() - // }) - // }) - // const json = await reponse.json() - // return json - // } catch (error) { - // dialog.showErrorBox("Connexion à l'API", "Impossible de contacter l'API, fermeture du launcher.") - // console.error(error) - // app.exit() - // } - // } else { - // dialog.showErrorBox("Connexion internet", "Le launcher requiert une connexion internet.") - // } - return { - success: true + if (net.isOnline()) { + try { + const reponse = await fetch(`${config.api.base}${config.api.endpoints.checkBanStatus}`, { + method: "post", + body: JSON.stringify({ + hwid: hwid.getHWID() + }) + }) + const json = await reponse.json() + return json + } catch (error) { + dialog.showErrorBox("Connexion à l'API", "Impossible de contacter l'API, fermeture du launcher.") + console.error(error) + app.exit() + } + } else { + dialog.showErrorBox("Connexion internet", "Le launcher requiert une connexion internet.") } } @@ -124,7 +129,7 @@ app.on("window-all-closed", () => { ipcMain.on("call", async (event, data) => { switch (data.method) { case "hardware::ramInformation": - launcherWindow.webContents.send("hardware::ramInformation", { + launcherWindow.webContents.send("Response", { totalRam: Math.round(os.totalmem() / 1024 / 1024 * 100) / 100, avaibleRam: Math.round(os.freemem() / 1024 / 1024 * 100) / 100, }) @@ -219,5 +224,32 @@ ipcMain.on("call", async (event, data) => { case "player::profile": await launcherWindow.webContents.send("Response", auth) break + case "game::parseOptions": + const $gameOptions = gameOptions.parseOptions() + const allowedKeys = ["renderDistance", "renderClouds", "graphicsMode", "gamma", "graphicsMode", "guiScale"] + const filteredOptions = Object.fromEntries( + Object.entries($gameOptions).filter(([key]) => allowedKeys.includes(key)) + ) + launcherWindow.webContents.send("Response", filteredOptions) + break + case "game::optionSet": + const options = gameOptions.parseOptions() + switch (data.args.key) { + case "graphicsMode": + options.graphicsMode = data.args.key == true ? 0 : 1 + break + case "gamma": + options.gamma = data.args.key == false ? 0.50 : 1.0 + break + default: + options[data.args.key] = data.args.value + break + } + gameOptions.saveOptions(gameOptions.stringfyOptions(options)) + break + case "settings::read": + const $launcherSettings = launcherSettings.readSettings() + launcherWindow.webContents.send("Response", $launcherSettings) + break } }) \ No newline at end of file diff --git a/modules/gameOptions.js b/modules/gameOptions.js new file mode 100644 index 0000000..2aa13a5 --- /dev/null +++ b/modules/gameOptions.js @@ -0,0 +1,68 @@ +const fs = require("node:fs") +const path = require("node:path") +let optionsFile + +function initOptions($optionFile) { + optionsFile = $optionFile + const gameDir = path.parse($optionFile).dir + if (!fs.existsSync(gameDir)) { + try { + fs.mkdirSync(gameDir) + } catch (error) { + throw error + } + } + if (!fs.existsSync($optionFile)) { + try { + fs.copyFileSync(path.join(__dirname, "..", "options.txt"), $optionFile) + } catch (error) { + throw error + } + } +} + +function parseOptions() { + const options = fs.readFileSync(optionsFile, "utf-8").toString().split("\n").filter(line => line.trim() !== "") + const parsedOptions = {} + for (let i = 0; i < options.length; i++) { + const line = options[i].trim() + const [key, value] = line.split(":") + if (key && value) { + const trimmedValue = value.trim().toLowerCase() + if (trimmedValue === "true" || trimmedValue === "false") { + parsedOptions[key.trim()] = trimmedValue === "true" + } else { + parsedOptions[key.trim()] = isNaN(value) ? value.trim() : parseInt(value, 10) + } + } + } + return parsedOptions +} + +function stringfyOptions(optionsObject) { + const optionsArray = [] + if (typeof optionsObject !== "object" || optionsObject === null) { + throw new Error("Invalid options object") + } + for (const settingKey in optionsObject) { + if (Object.prototype.hasOwnProperty.call(optionsObject, settingKey)) { + const settingValue = optionsObject[settingKey] + if (typeof settingValue === "object" || typeof settingValue === "function" || typeof settingValue === "symbol") { + throw new Error(`Invalid value for setting "${settingKey}": ${settingValue}`) + } + optionsArray.push(`${settingKey}:${settingValue}`) + } + } + return optionsArray.join("\n") +} + +function saveOptions(options) { + fs.writeFileSync(optionsFile, options, "utf8") +} + +module.exports = { + initOptions, + saveOptions, + parseOptions, + stringfyOptions +} \ No newline at end of file diff --git a/modules/gameSettings.js b/modules/gameSettings.js deleted file mode 100644 index 8ed46f5..0000000 --- a/modules/gameSettings.js +++ /dev/null @@ -1,43 +0,0 @@ -const fs = require("node:fs") -const path = require("node:path") - -function parseSettings(settingsFile) { - const settings = fs.readFileSync(settingsFile, "utf-8").toString().split("\n").filter(line => line.trim() !== "") - const parseSettings = {} - for (let i = 0; i < settings.length; i++) { - const line = settings[i].trim() - const [key, value] = line.split(":") - if (key && value) { - const trimmedValue = value.trim().toLowerCase() - if (trimmedValue === "true" || trimmedValue === "false") { - parseSettings[key.trim()] = trimmedValue === "true" - } else { - parseSettings[key.trim()] = isNaN(value) ? value.trim() : parseInt(value, 10) - } - } - } - return parseSettings -} - -function stringfySettings(settingsFile) { - const settingsObject = JSON.parse(fs.readFileSync(settingsFile, "utf-8").toString()) - const settingsArray = [] - if (typeof settingsObject !== "object" || settingsObject === null) { - throw new Error("Invalid settings object") - } - for (const settingKey in settingsObject) { - if (Object.prototype.hasOwnProperty.call(settingsObject, settingKey)) { - const settingValue = settingsObject[settingKey] - if (typeof settingValue === "object" || typeof settingValue === "function" || typeof settingValue === "symbol") { - throw new Error(`Invalid value for setting "${settingKey}": ${settingValue}`) - } - settingsArray.push(`${settingKey}:${settingValue}`) - } - } - return settingsArray.join("\n") -} - -module.exports = { - parseSettings, - stringfySettings -} \ No newline at end of file diff --git a/modules/launcherSettings.js b/modules/launcherSettings.js new file mode 100644 index 0000000..9d73dc5 --- /dev/null +++ b/modules/launcherSettings.js @@ -0,0 +1,116 @@ +const fs = require("node:fs") +const path = require("node:path") + +let launcherDataPath = path.join(__dirname, ".catboat") + +const baseSettings = { + ram: { + max: 1024 + } +} + +function initSettings($launcherDataPath) { + launcherDataPath = $launcherDataPath + const gameDir = path.parse($launcherDataPath).dir + if (!fs.existsSync(gameDir)) { + try { + fs.mkdirSync(gameDir) + } catch (error) { + throw error + } + } + if (!fs.existsSync($launcherDataPath)) { + try { + fs.writeFileSync($launcherDataPath, JSON.stringify(baseSettings, null, 4)) + } catch (error) { + throw error + } + } +} + +function getLauncherDataPath() { + return launcherDataPath +} + +function writeSettings(settings) { + try { + fs.writeFileSync(path.join(launcherDataPath, "launcher_settings.json"), JSON.stringify(settings, null, 4)) + return + } catch (error) { + throw error + } +} + +function get(key) { + try { + const settings = JSON.parse(fs.readFileSync(path.join(launcherDataPath, "launcher_settings.json"))) + + const keys = key.split(".") + let value = settings + + for (const k of keys) { + if (value[k] !== undefined) { + value = value[k] + } else { + throw new Error(`La clé '${key}' n'existe pas.`) + } + } + + return value + } catch (error) { + console.error("Erreur lors de l'accès aux paramètres :", error.message) + throw error + } +} + +function set(key, newValue) { + try { + const filePath = path.join(launcherDataPath, "launcher_settings.json") + const settings = JSON.parse(fs.readFileSync(filePath)) + + const keys = key.split(".") + let obj = settings + + for (let i = 0; i < keys.length - 1; i++) { + const k = keys[i] + if (!obj[k]) { + obj[k] = {} + } + obj = obj[k] + } + + obj[keys[keys.length - 1]] = newValue + + fs.writeFileSync(filePath, JSON.stringify(settings, null, 2)) + } catch (error) { + console.error("Erreur lors de la mise à jour des paramètres :", error.message) + throw error + } +} + +function clean() { + try { + fs.writeFileSync(path.join(launcherDataPath, "launcher_settings.json"), JSON.stringify(baseSettings, null, 4)) + return + } catch (error) { + throw error + } +} + +function readSettings() { + try { + return JSON.parse(fs.readFileSync(path.join(launcherDataPath, "launcher_settings.json"))) + } catch (error) { + throw error + } +} + +module.exports = { + getLauncherDataPath, + writeSettings, + readSettings, + initSettings, + clean, + get, + set, +} \ No newline at end of file