diff --git a/app/assets/css/index.css b/app/assets/css/index.css
index 418e668..a055621 100644
--- a/app/assets/css/index.css
+++ b/app/assets/css/index.css
@@ -92,7 +92,7 @@ img.logo {
img.mascot {
width: 50%;
float: right;
- margin-top: calc(300px - (50% + 12px));
+ margin-top: calc(300px - (50% + 32px));
}
fieldset {
@@ -372,7 +372,25 @@ div.checkboxes > input[type="checkbox"] {
font-size: small;
}
+button.load {
+ content: "";
+ transition: background-color 0.3s;
+}
+
+
[hidden] {
display: none;
visibility: hidden;
-}
\ No newline at end of file
+}
+@keyframes backgroundAnimation {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+ }
+
\ No newline at end of file
diff --git a/app/assets/js/index.js b/app/assets/js/index.js
index fc84e38..e497fd2 100644
--- a/app/assets/js/index.js
+++ b/app/assets/js/index.js
@@ -86,10 +86,18 @@ system.result("server::ping", pong => {
function handleOptionsChanges(key, value) {
system.call("game::optionSet", { key, value })
+ const span = document.querySelector(`span#${key}`)
+ if (span) {
+ span.innerText = Math.floor(value)
+ }
}
function handleSettingsChanges(key, value) {
system.call("settings::set", { key, value })
+ const span = document.querySelector(`span#${key == "ram" ? "currentRam" : key}`)
+ if (span) {
+ span.innerText = key == "ram" ? Math.floor(value.max / 1024) + " G" : value
+ }
}
system.result("game::parseOptions", options => {
@@ -121,6 +129,9 @@ system.result("player::profile", playerProfile => {
console.log(playerProfile)
})
+system.result("oculus::getdefaultshaderset", boolean => {
+ sildurs_shader.checked = boolean
+})
window.onload = () => {
system.call("hardware::ramInformation")
@@ -128,5 +139,6 @@ window.onload = () => {
system.call("server::ping")
system.call("player::profile")
system.call("settings::read")
+ system.call("oculus::getdefaultshaderset")
startAudio()
}
\ No newline at end of file
diff --git a/app/logged.html b/app/logged.html
index cd72cad..e3b0e2e 100644
--- a/app/logged.html
+++ b/app/logged.html
@@ -46,10 +46,10 @@
Ram alloué
@@ -205,7 +205,7 @@
-
diff --git a/main.js b/main.js
index 98d05a0..78a40a3 100644
--- a/main.js
+++ b/main.js
@@ -1,4 +1,5 @@
const { BrowserWindow, app, net, dialog, ipcMain, nativeImage, session, shell } = require("electron")
+const properties = require("js-java-properties")
const msmc = require("msmc")
const os = require("node:os")
const fs = require("node:fs")
@@ -46,7 +47,7 @@ async function createLauncherWindow() {
contextIsolation: true,
preload: path.join(__dirname, "modules", "preload.js"),
webviewTag: true,
- devTools: true
+ devTools: false
}
})
if (os.platform() == "darwin") {
@@ -211,6 +212,9 @@ ipcMain.on("call", async (event, data) => {
case "auth::reset":
launcherSettings.set("auth", { token: "", type: "msa", clientToken: "" })
break
+ case "settings::set":
+ launcherSettings.set(data.args.key, data.args.value)
+ break
case "shell::openExternal":
shell.openExternal(data.args.url)
break
@@ -256,6 +260,19 @@ ipcMain.on("call", async (event, data) => {
case "app::devtools":
launcherWindow.webContents.openDevTools()
break
+ case "oculus::defaultshaderset":
+ const oculusProperties = properties.parse(fs.readFileSync(path.join(app.getPath("appData"), ".catboat", "config", "oculus.properties")).toString())
+ if (data.args.boolean) {
+ properties.setProperty(oculusProperties, "shaderPack", "Sildur%27s+Vibrant+Shaders+v1.50+Medium.zip")
+ } else {
+ properties.setProperty(oculusProperties, "shaderPack", "")
+ }
+ fs.writeFileSync(path.join(app.getPath("appData"), ".catboat", "config", "oculus.properties"), properties.stringify(oculusProperties))
+ break
+ case "oculus::getdefaultshaderset":
+ const $oculusProperties = properties.parse(fs.readFileSync(path.join(app.getPath("appData"), ".catboat", "config", "oculus.properties")).toString())
+ launcherWindow.webContents.send("Response", properties.getProperty($oculusProperties, "shaderPack") == "Sildur%27s+Vibrant+Shaders+v1.50+Medium.zip" ? true : false)
+ break
}
})
@@ -264,20 +281,51 @@ async function launchGame(restartGame) {
const downloadQueue = []
const remoteFiles = await fileManager.getRemoteFiles()
const localFiles = fs.readdirSync(path.join(app.getPath("appData"), ".catboat"), { recursive: true })
- launcherWindow.setProgressBar(10, {
- mode: "indeterminate"
- })
+ if (launcherWindow instanceof BrowserWindow) {
+ launcherWindow.webContents.send("Response", { type: "landing" })
+ }
+ for (const remoteFile of remoteFiles) {
+ try {
+ const localFile = localFiles.find(file => file === remoteFile)
- launcherWindow.setProgressBar(0, {
- mode: "none"
- })
- dialog.showMessageBox(launcherWindow, {
- title: "Téléchargement des fichiers",
- message: "Téléchargement fini."
- })
+ 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)))
+ if (launcherWindow instanceof BrowserWindow) {
+ launcherWindow.webContents.send("Response", { type: "landing" })
+ }
+ } catch (error) {
+ if (launcherWindow instanceof BrowserWindow) {
+ launcherWindow.webContents.send("Response", { type: "landing" })
+ }
+ dialog.showErrorBox("Erreur lors du téléchargement des fichiers", error.toString())
+ continue
+ }
+ }
gamePlayable = true
launcher.on("close", () => {
- launcherSettings.webContents.send("Response", { disablePlayButton: true })
+ launcherWindow.webContents.send("Response", { disablePlayButton: true })
gamePlayable = true
})
launcher.on("debug", (log) => {
@@ -286,6 +334,9 @@ async function launchGame(restartGame) {
launcher.on("data", (log) => {
console.log(log)
})
+ launcher.on("data", (log) => {
+ console.log(log)
+ })
if (gamePlayable || restartGame) {
const $launchProcess = await launcher.launch({
root: path.join(app.getPath("appData"), ".catboat"),
@@ -295,6 +346,7 @@ async function launchGame(restartGame) {
type: "release"
},
forge: path.join(app.getPath("appData"), ".catboat", "forge-1.16.5.jar"),
+ javaPath: "C:\\Program Files\\Java\\jdk-17\\bin\\java.exe",
memory: {
min: 512,
max: launcherSettings.get("ram").max
diff --git a/modules/java.js b/modules/java.js
new file mode 100644
index 0000000..8780d77
--- /dev/null
+++ b/modules/java.js
@@ -0,0 +1,182 @@
+const os = require("os")
+const path = require("path")
+const osmeta = require("./osmeta")
+const { exec } = require("child_process")
+const files = require("./files")
+const config = require("../config.json")
+const settings = require("./settings")
+const { dialog, BrowserWindow, shell } = require("electron")
+const AdmZip = require("adm-zip")
+
+function isJavaInstalled() {
+ return new Promise((resolve, reject) => {
+ try {
+ exec(`"${settings.get("javaPath") == "" ? "java" : settings.get("javaPath")}" -version`, (error, stdout, stderr) => {
+ if (error) {
+ reject({
+ java: false
+ })
+ }
+ const output = `${stdout}\n${stderr}`
+ const lines = output.split("\n")
+ const version = lines.filter(line => line.includes("version \""))[0]
+ try {
+ let matchRegEx
+ if (version.includes("_")) {
+ matchRegEx = /(?\d+)\.(?\d+)\.(?\d+)\_(?\d+)/
+ } else {
+ matchRegEx = /(?\d+)\.(?\d+)\.(?\d+)/
+ }
+ const versionMatch = version.match(matchRegEx)
+ const { major, minor, patch, update } = versionMatch.groups
+ resolve({
+ java: true,
+ version: {
+ major: parseInt(major),
+ minor: parseInt(minor),
+ patch: parseInt(patch),
+ update: update ? parseInt(update) : null
+ }
+ })
+ } catch (error) {
+ reject({
+ java: false,
+ error
+ })
+ }
+ })
+ } catch (error) {
+ reject({
+ java: false,
+ error
+ })
+ }
+ })
+}
+
+async function showJavaErrorBox() {
+ const javaErrorBox = await dialog.showMessageBox(BrowserWindow.getFocusedWindow(), {
+ title: "Mauvaise version de Java",
+ message: "Mauvaise version de Java",
+ detail: "La version de java installé sur votre ordinateur n'est pas compatible avec la version de minecraft utilisé",
+ icon: path.join(__dirname, "code/app/assets/img/java.png"),
+ buttons: [
+ "Fermer",
+ "Installer automatiquement java",
+ "Installer manuellement java",
+ "Utilise un chemin d'accès personnalié",
+ ],
+ cancelId: 0,
+ defaultId: 1
+ })
+ return javaErrorBox
+}
+function installJava(dest) {
+ return new Promise(async (resolve, reject) => {
+ const response = await fetch(`${config.apiServicesURL}/api/java`)
+ const java = await response.json()
+ const arch = osmeta.arch() == "unknow" ? "x64" : osmeta.arch()
+ if (java.downloads[os.platform()]) {
+ if (java.downloads[os.platform()][arch]) {
+ files.downloadFile(config.apiServicesURL + java.downloads[os.platform()][arch].url, path.join(dest, "java.zip"), (error, file) => {
+ if (error) throw error
+ try {
+ const zip = new AdmZip(path.join(dest, "java.zip"))
+ zip.extractAllTo(path.join(dest, "java"), true)
+ files.removeFile(path.join(dest, "java.zip"))
+ if (os.platform() == "darwin") {
+ exec(`chmod +x "${path.join(dest, "java", "jre-1.8.0_411.jre", "Contents", "Home", "bin", "java")}"`, (error) => {
+ if (error) reject(error)
+ resolve(path.join(dest, "java", "jre-1.8.0_411.jre", "Contents", "Home", "bin", "java"))
+ })
+ } else {
+ exec(`chmod +x "${path.join(dest, "java", "bin", "java")}"`, (error) => {
+ if (error) reject(error)
+ resolve(path.join(dest, "java", "bin", "java"))
+ })
+ }
+ } catch (err) {
+ reject(err)
+ }
+ })
+ } else {
+ reject(new Error("Java are not supported for your OS arch"))
+ }
+ } else {
+ reject(new Error("Java are not supported for your OS"))
+ }
+ })
+}
+
+async function main(requiredJavaVersionMinor, dest) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ const java = await isJavaInstalled()
+ if (java.version.minor < parseInt(requiredJavaVersionMinor)) {
+ const javaErrorBox = await showJavaErrorBox()
+ switch (javaErrorBox.response) {
+ case 0:
+ resolve()
+ break
+ case 1:
+ installJava(dest).then(javaPath => {
+ resolve(javaPath)
+ })
+ break
+ case 2:
+ shell.openExternal(`https://www.java.com/fr/download/manual.jsp`)
+ resolve()
+ break
+ case 3:
+ const javaBinPathSelect = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
+ filters: [
+ {
+ name: "java",
+ }
+ ]
+ })
+ if (javaBinPathSelect.canceled) {
+ showJavaErrorBox()
+ } else {
+ resolve(javaBinPathSelect.filePaths[0])
+ }
+ break
+ }
+ } else {
+ resolve()
+ }
+ } catch (error) {
+ const javaErrorBox = await showJavaErrorBox()
+ switch (javaErrorBox.response) {
+ case 0:
+ resolve()
+ break
+ case 1:
+ installJava(dest).then(javaPath => {
+ resolve(javaPath)
+ })
+ break
+ case 2:
+ shell.openExternal(`https://www.java.com/fr/download/manual.jsp`)
+ resolve()
+ break
+ case 3:
+ const javaBinPathSelect = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
+ filters: [
+ {
+ name: "java",
+ }
+ ]
+ })
+ if (javaBinPathSelect.canceled) {
+ showJavaErrorBox()
+ } else {
+ resolve(javaBinPathSelect.filePaths[0])
+ }
+ break
+ }
+ }
+ })
+}
+
+module.exports.java = main
\ No newline at end of file
diff --git a/modules/launcherSettings.js b/modules/launcherSettings.js
index dda2324..78069c2 100644
--- a/modules/launcherSettings.js
+++ b/modules/launcherSettings.js
@@ -5,7 +5,7 @@ let launcherDataPath = path.join(__dirname, ".catboat")
const baseSettings = {
ram: {
- max: 1024
+ max: 2048
},
auth: {
token: "",
diff --git a/modules/osmeta.js b/modules/osmeta.js
new file mode 100644
index 0000000..6a76743
--- /dev/null
+++ b/modules/osmeta.js
@@ -0,0 +1,27 @@
+const os = require("os")
+
+function arch() {
+ switch (os.arch()) {
+ case "x32":
+ case "ia32":
+ case "x86":
+ case "mips":
+ case "ppc":
+ case "s390":
+ return "x86"
+ case "x64":
+ case "arm64":
+ return "arm64"
+ case "mipsel":
+ case "ppc64":
+ case "riscv64":
+ case "s390x":
+ case "loong64":
+ return "x64"
+
+ default:
+ return "unknown"
+ }
+}
+
+module.exports.arch = arch
\ No newline at end of file