356 lines
15 KiB
JavaScript
356 lines
15 KiB
JavaScript
const { BrowserWindow, app, net, dialog, ipcMain, nativeImage, session, shell } = require("electron")
|
|
const msmc = require("msmc")
|
|
const os = require("node:os")
|
|
const fs = require("node:fs")
|
|
const path = require("node:path")
|
|
const hwid = require("./modules/hwid")
|
|
const serverPing = require("./modules/serverPing")
|
|
const gameOptions = require("./modules/gameOptions")
|
|
const launcherSettings = require("./modules/launcherSettings")
|
|
const config = require("./config.json")
|
|
const { Authenticator, Client } = require("minecraft-launcher-core")
|
|
const fileManager = require("./modules/fileManager")
|
|
const launcher = new Client()
|
|
const { io } = require("socket.io-client")
|
|
const download = require("download")
|
|
const socket = io({
|
|
host: config.api.websockets.base.host,
|
|
port: config.api.websockets.base.port
|
|
})
|
|
|
|
let launcherWindow, auth, gamePlayable = false
|
|
|
|
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()
|
|
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
|
|
}
|
|
})
|
|
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
|
|
})
|
|
})
|
|
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.toString())
|
|
}
|
|
} else {
|
|
dialog.showErrorBox("Connexion internet", "Le launcher requiert une connexion internet.")
|
|
}
|
|
}
|
|
|
|
async function checkIfIAmBanned() {
|
|
if (net.isOnline()) {
|
|
try {
|
|
const reponse = await fetch(`${config.api.base}${config.api.endpoints.checkBanStatus}`, {
|
|
method: "post",
|
|
headers: {
|
|
accept: "application/json",
|
|
"Content-Type": "application/json"
|
|
},
|
|
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.")
|
|
}
|
|
}
|
|
|
|
app.whenReady().then(() => {
|
|
createLauncherWindow()
|
|
app.on("activate", async () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) await createLauncherWindow()
|
|
})
|
|
})
|
|
|
|
app.on("window-all-closed", () => {
|
|
app.quit()
|
|
})
|
|
|
|
ipcMain.on("call", async (event, data) => {
|
|
switch (data.method) {
|
|
case "hardware::ramInformation":
|
|
launcherWindow.webContents.send("Response<hardware::ramInformation>", {
|
|
totalRam: Math.round(os.totalmem() / 1024 / 1024 * 100) / 100,
|
|
avaibleRam: Math.round(os.freemem() / 1024 / 1024 * 100) / 100,
|
|
})
|
|
break
|
|
case "server::ping":
|
|
const status = await serverPing.fetchServerStatus()
|
|
launcherWindow.webContents.send("Response<server::ping>", status)
|
|
break
|
|
case "window::close":
|
|
launcherWindow.close()
|
|
app.quit()
|
|
break
|
|
case "skin::set":
|
|
const file = await dialog.showOpenDialog(launcherWindow, {
|
|
filters: [
|
|
{ name: "Images", extensions: ["png", "jpg", "jpeg"] }
|
|
],
|
|
properties: ["openFile", "dontAddToRecent", "showHiddenFiles"],
|
|
title: "Sélectionner l'image de votre skin",
|
|
securityScopedBookmarks: true
|
|
})
|
|
if (!file.canceled) {
|
|
const confirmDialog = await dialog.showMessageBox(launcherWindow, {
|
|
type: "question",
|
|
message: "Êtes-vous sûr de vouloir changer votre skin ?",
|
|
buttons: [
|
|
"Oui",
|
|
"Annuler"
|
|
],
|
|
title: "Confirmer le changement de skin"
|
|
})
|
|
confirmDialog.response == 0 ? skinPath = file.filePaths[0] : skinPath = null
|
|
}
|
|
break
|
|
case "auth::mojang":
|
|
if (data.args.trim() != "") {
|
|
auth = Authenticator.getAuth(data.args.username, data.args.password)
|
|
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${(await auth).uuid}`)
|
|
await launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
|
} else {
|
|
dialog.showErrorBox("Erreur", "Le mot de passe n'est pas défini. Les comptes crackés ne sont pas supporté par le launcher.")
|
|
}
|
|
break
|
|
case "auth::microsoft":
|
|
const authManager = new msmc.Auth("select_account")
|
|
try {
|
|
const xboxManager = await authManager.launch("raw")
|
|
const token = await xboxManager.getMinecraft()
|
|
auth = token.mclc()
|
|
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${auth.uuid}`)
|
|
await launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
|
} catch (error) {
|
|
console.error(error)
|
|
if (error == "error.gui.closed") {
|
|
launcherWindow.webContents.send("Response<auth::microsoft>")
|
|
}
|
|
}
|
|
break
|
|
case "auth::refresh":
|
|
const user = data.args.user
|
|
if (user.meta?.type == "msa") {
|
|
try {
|
|
const authManager = new msmc.Auth("none")
|
|
const xboxManager = await authManager.refresh(user.meta.refresh)
|
|
const minecraft = await xboxManager.getMinecraft()
|
|
auth = minecraft.mclc()
|
|
await launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
|
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${auth.uuid}`)
|
|
} catch (error) {
|
|
dialog.showErrorBox("Erreur lors de la connexion via token", error)
|
|
console.error(error)
|
|
launcherWindow.webContents.send("Response<auth::refresh>")
|
|
}
|
|
} else {
|
|
try {
|
|
auth = Authenticator.refreshAuth(user.access_token, user.client_token)
|
|
await launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
|
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${(await auth).uuid}`)
|
|
} catch (error) {
|
|
dialog.showErrorBox("Erreur lors de la connexion via token", error)
|
|
console.error(error)
|
|
launcherWindow.webContents.send("Response<auth::refresh>")
|
|
}
|
|
}
|
|
break
|
|
case "shell::openExternal":
|
|
shell.openExternal(data.args.url)
|
|
break
|
|
case "audio::mute":
|
|
launcherWindow.webContents.setAudioMuted(true)
|
|
break
|
|
case "audio::unmute":
|
|
launcherWindow.webContents.setAudioMuted(false)
|
|
break
|
|
case "player::profile":
|
|
await launcherWindow.webContents.send("Response<player::profile>", 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<game::parseOptions>", 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<settings::read>", $launcherSettings)
|
|
break
|
|
case "game::launch":
|
|
launcherWindow.webContents.send("Response<game::launch>", { disablePlayButton: false })
|
|
const downloadQueue = []
|
|
const remoteFiles = await fileManager.getRemoteFiles()
|
|
const localFiles = fs.readdirSync(path.join(app.getPath("appData"), ".catboat"), { recursive: true })
|
|
launcherWindow.setProgressBar(10, {
|
|
mode: "indeterminate"
|
|
})
|
|
for (const remoteFile of remoteFiles) {
|
|
try {
|
|
const localFile = localFiles.find(file => file === remoteFile)
|
|
|
|
if (!localFile) {
|
|
downloadQueue.push(remoteFile)
|
|
continue
|
|
}
|
|
|
|
const localHash = await fileManager.getFileHash(path.join(app.getPath("appData"), ".catboat", localFile))
|
|
const remoteHash = await fileManager.getRemoteFileHash(localFile)
|
|
if (localHash != remoteHash) {
|
|
downloadQueue.push(localFile)
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
break
|
|
}
|
|
}
|
|
for (const localFile of localFiles) {
|
|
if (!remoteFiles.find(remoteFile => remoteFile.path == localFile) && localFile.startsWith("/mods")) {
|
|
fs.unlinkSync(path.join(app.getPath("appData"), ".catboat", localFile))
|
|
}
|
|
}
|
|
for (const item of downloadQueue) {
|
|
const url = `${config.api.base}${config.api.endpoints.downloadFile}/${new String(item).replace(/\\/g, "/")}`
|
|
try {
|
|
await download(url, path.join(app.getPath("appData"), ".catboat", path.dirname(item)))
|
|
launcherWindow.setProgressBar(((downloadQueue.indexOf(item) + 1) / downloadQueue.length) * 100, {
|
|
mode: "normal"
|
|
})
|
|
} catch (error) {
|
|
launcherWindow.setProgressBar(((downloadQueue.indexOf(item) + 1) / downloadQueue.length) * 100, {
|
|
mode: "error"
|
|
})
|
|
dialog.showErrorBox("Erreur lors du téléchargement des fichiers", error.toString())
|
|
continue
|
|
}
|
|
}
|
|
launcherWindow.setProgressBar(0, {
|
|
mode: "none"
|
|
})
|
|
dialog.showMessageBox(launcherWindow, {
|
|
title: "Téléchargement des fichiers",
|
|
message: "Téléchargement fini."
|
|
})
|
|
gamePlayable = true
|
|
launcher.on("close", () => {
|
|
launcherSettings.webContents.send("Response<game::launch>", { disablePlayButton: true })
|
|
gamePlayable = true
|
|
})
|
|
launcher.on("debug", (log) => {
|
|
console.log(log)
|
|
})
|
|
launcher.on("data", (log) => {
|
|
console.log(log)
|
|
})
|
|
if (gamePlayable) {
|
|
launcher.launch({
|
|
root: path.join(app.getPath("appData"), ".catboat"),
|
|
authorization: auth,
|
|
version: {
|
|
number: "1.16.5",
|
|
type: "release"
|
|
},
|
|
forge: path.join(app.getPath("appData"), ".catboat", "forge-1.16.5.jar"),
|
|
memory: {
|
|
min: 512,
|
|
max: launcherSettings.get("ram").max
|
|
}
|
|
})
|
|
|
|
}
|
|
break
|
|
case "app::devtools":
|
|
launcherWindow.webContents.openDevTools()
|
|
break
|
|
}
|
|
}) |