Compare commits
9 Commits
0.0.1-alph
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 72354c053f | |||
| 5f8ba166cb | |||
| eb8f11ac89 | |||
| 86bb37944c | |||
| cd627b03ae | |||
| 1106ced049 | |||
| 0c01e826d9 | |||
| 48c5fd4ead | |||
| d6fa5b69ce |
32
.github/workflows/build.yml
vendored
Normal file
32
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Release app
|
||||
on:
|
||||
workflow_dispatch: null
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
- macos-latest
|
||||
- macos-latest
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@4.2.2
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@4.4.0
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Config git user
|
||||
run: |
|
||||
git config --global user.email "gilleslazure04@gmail.com"
|
||||
git config --global user.name "Gilles Lazure <azures04>"
|
||||
- name: Install app dependencies
|
||||
run: |
|
||||
npm i
|
||||
- name: Build app
|
||||
run: |
|
||||
node build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
@ -11,6 +11,14 @@ body {
|
||||
background-color: #262626;
|
||||
}
|
||||
|
||||
main {
|
||||
app-region: drag;
|
||||
}
|
||||
|
||||
main > * {
|
||||
app-region: no-drag;
|
||||
}
|
||||
|
||||
main > article > section > img {
|
||||
width: 150px;
|
||||
}
|
||||
@ -33,6 +41,14 @@ main > article > section.informations {
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
main > article > section.logo > h2 {
|
||||
color: #ffffff;
|
||||
padding: 13px 13px 13px 13px;
|
||||
font-weight: bolder;
|
||||
text-align: center;
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
main > article > section.informations > h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
@ -16,6 +18,11 @@ body {
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
img {
|
||||
app-region: drag;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
main {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
@ -24,12 +31,19 @@ main {
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
app-region: drag;
|
||||
}
|
||||
|
||||
main > * {
|
||||
app-region: no-drag;
|
||||
}
|
||||
|
||||
main > nav {
|
||||
margin-left: 1rem;
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: #3e3e3ee6;
|
||||
}
|
||||
|
||||
@ -372,25 +386,63 @@ div.checkboxes > input[type="checkbox"] {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
button.load {
|
||||
content: "";
|
||||
transition: background-color 0.3s;
|
||||
div.loader {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
div.loader > div.full {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
background-color: #3e3e3ee6;
|
||||
}
|
||||
|
||||
div.loader > div.full > div.progress,
|
||||
div.loader > div.full > div.loading {
|
||||
height: 10px;
|
||||
width: 50%;
|
||||
transition: 1s width;
|
||||
background-color: #39aa6d;
|
||||
}
|
||||
|
||||
div.loader > div.full > div.loading {
|
||||
animation: animateLoadingEffect 1s linear infinite;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
@keyframes backgroundAnimation {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
/* Chrome-specific scrollbar styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1); /* Slightly visible track for better contrast */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #2E8B57;
|
||||
border-radius: 4px; /* Rounded edges for a modern look */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #39aa6d;
|
||||
}
|
||||
|
||||
@keyframes animateLoadingEffect {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(200%);
|
||||
}
|
||||
}
|
||||
@ -58,18 +58,32 @@ function toggleAudio(element) {
|
||||
|
||||
function toggleMusic(element) {
|
||||
if (element.getAttribute("state") == 0) {
|
||||
system.call("audio::mute")
|
||||
audio.pause()
|
||||
element.setAttribute("state", 1)
|
||||
element.children[0].classList.replace("fa-pause", "fa-play")
|
||||
element.children[1].innerText = "Reprendre"
|
||||
} else {
|
||||
system.call("audio::unmute")
|
||||
audio.play()
|
||||
element.setAttribute("state", 0)
|
||||
element.children[0].classList.replace("fa-play", "fa-pause")
|
||||
element.children[1].innerText = "Pause"
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMusicVolume(element) {
|
||||
if (element.getAttribute("state") == 0) {
|
||||
system.call("audio::mute")
|
||||
element.setAttribute("state", 1)
|
||||
element.children[0].classList.replace("fa-volume", "fa-volume-slash")
|
||||
element.children[1].innerText = "Activer le son"
|
||||
} else {
|
||||
system.call("audio::unmute")
|
||||
element.setAttribute("state", 0)
|
||||
element.children[0].classList.replace("fa-volume-slash", "fa-volume")
|
||||
element.children[1].innerText = "Couper le son"
|
||||
}
|
||||
}
|
||||
|
||||
function updateVolume(value) {
|
||||
audio.volume = value / 100
|
||||
audioPourcentageLabel.innerText = `${value}%`
|
||||
@ -86,7 +100,7 @@ system.result("server::ping", pong => {
|
||||
|
||||
function handleOptionsChanges(key, value) {
|
||||
system.call("game::optionSet", { key, value })
|
||||
const span = document.querySelector(`span#${key}`)
|
||||
const span = document.querySelector(`span#current${key.replace(/./, c => c.toUpperCase())}`)
|
||||
if (span) {
|
||||
span.innerText = Math.floor(value)
|
||||
}
|
||||
@ -100,16 +114,66 @@ function handleSettingsChanges(key, value) {
|
||||
}
|
||||
}
|
||||
|
||||
function setLoadingType(type) {
|
||||
const loader = document.querySelector(".loader")
|
||||
switch (type) {
|
||||
case "continuous":
|
||||
loader.children[0].children[0].classList.remove("progress")
|
||||
loader.children[0].children[0].classList.add("loading")
|
||||
break
|
||||
case "progress":
|
||||
loader.children[0].children[0].classList.remove("loading")
|
||||
loader.children[0].children[0].classList.add("progress")
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function setLoadingProgress(pourcentage) {
|
||||
const loader = document.querySelector(".loader")
|
||||
const progress = loader.querySelector(".progress")
|
||||
if (progress) {
|
||||
progress.setAttribute("style", `width: ${pourcentage}%`)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLoadingBar() {
|
||||
const loader = document.querySelector(".loader")
|
||||
if (loader.hasAttribute("hidden")) {
|
||||
loader.removeAttribute("hidden")
|
||||
} else {
|
||||
loader.setAttribute("hidden", "")
|
||||
}
|
||||
}
|
||||
|
||||
function showLoadingBar() {
|
||||
const loader = document.querySelector(".loader")
|
||||
if (loader.hasAttribute("hidden")) {
|
||||
loader.removeAttribute("hidden")
|
||||
}
|
||||
}
|
||||
|
||||
function hideLoadingBar() {
|
||||
const loader = document.querySelector(".loader")
|
||||
if (!loader.hasAttribute("hidden")) {
|
||||
loader.setAttribute("hidden", "")
|
||||
}
|
||||
}
|
||||
|
||||
system.result("game::parseOptions", options => {
|
||||
gamma.checked = options.gamma == 1 ? true : false
|
||||
renderClouds.checked = options.renderrenderClouds == false ? false : true
|
||||
guiScale.value = options.guiScale
|
||||
currentGuiScale.innerText = options.guiScale
|
||||
graphicsMode.checked = options.graphicsMode == 0 ? true : false
|
||||
renderDistance.value = options.renderDistance
|
||||
currentRenderDistance.innerText = options.renderDistance
|
||||
})
|
||||
|
||||
system.result("settings::read", settings => {
|
||||
ram.value = settings.ram.max
|
||||
currentRam.innerText = `${Math.floor(settings.ram.max / 1024)} G`
|
||||
})
|
||||
|
||||
system.result("hardware::ramInformation", $ram => {
|
||||
@ -120,11 +184,17 @@ system.result("hardware::ramInformation", $ram => {
|
||||
system.result("game::launch", info => {
|
||||
if (info.disablePlayButton) {
|
||||
playButton.removeAttribute("hidden")
|
||||
hideLoadingBar()
|
||||
} else {
|
||||
playButton.setAttribute("hidden", "")
|
||||
}
|
||||
})
|
||||
|
||||
system.result("game::launched", info => {
|
||||
muteAudio()
|
||||
hideLoadingBar()
|
||||
})
|
||||
|
||||
system.result("player::profile", playerProfile => {
|
||||
console.log(playerProfile)
|
||||
})
|
||||
@ -133,6 +203,26 @@ system.result("oculus::getdefaultshaderset", boolean => {
|
||||
sildurs_shader.checked = boolean
|
||||
})
|
||||
|
||||
system.result("progress::update", info => {
|
||||
showLoadingBar()
|
||||
setLoadingType(info.type)
|
||||
if (info.type == "progress" && typeof info.pourcentage == "number") {
|
||||
setLoadingProgress(info.pourcentage)
|
||||
}
|
||||
})
|
||||
|
||||
system.result("progress::hide", () => {
|
||||
hideLoadingBar()
|
||||
})
|
||||
|
||||
system.result("izitoast::error", info => {
|
||||
iziToast.error(info)
|
||||
})
|
||||
|
||||
system.result("progress::info", info => {
|
||||
iziToast.error(info)
|
||||
})
|
||||
|
||||
window.onload = () => {
|
||||
system.call("hardware::ramInformation")
|
||||
system.call("game::parseOptions")
|
||||
@ -140,5 +230,6 @@ window.onload = () => {
|
||||
system.call("player::profile")
|
||||
system.call("settings::read")
|
||||
system.call("oculus::getdefaultshaderset")
|
||||
hideLoadingBar()
|
||||
startAudio()
|
||||
}
|
||||
28
app/loading.html
Normal file
28
app/loading.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="./assets/css/banned.css">
|
||||
<title>Launcher banni</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<main>
|
||||
<button class="close" onclick="system.call('window::close')">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<article>
|
||||
<section class="logo">
|
||||
<img src="./assets/img/icon.png" alt="">
|
||||
<h2>
|
||||
Veuillez patienter
|
||||
</h2>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<script src="./assets/js/banned.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/izitoast/1.4.0/css/iziToast.css">
|
||||
<link rel="stylesheet" href="./assets/css/index.css">
|
||||
<title>NyanLauncher</title>
|
||||
</head>
|
||||
@ -11,6 +12,9 @@
|
||||
<button class="close" onclick="system.call('window::close')">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<button class="close" onclick="system.call('window::reduce')">
|
||||
<i class="fas fa-minus"></i>
|
||||
</button>
|
||||
<nav hidden>
|
||||
<button class="close" onclick="hideNavBar()">
|
||||
<i class="fas fa-times"></i>
|
||||
@ -59,7 +63,7 @@
|
||||
Distance de rendu
|
||||
</label>
|
||||
<div class="ranges">
|
||||
<span id="renderDistance">
|
||||
<span id="currentRenderDistance">
|
||||
4
|
||||
</span>
|
||||
<input type="range" name="renderDistance" min="4" max="32" id="renderDistance" onchange="handleOptionsChanges(this.name, this.value)">
|
||||
@ -72,7 +76,7 @@
|
||||
Taille de l'interface
|
||||
</label>
|
||||
<div class="ranges">
|
||||
<span id="guiScale">
|
||||
<span id="currentGuiScale">
|
||||
1
|
||||
</span>
|
||||
<input type="range" name="guiScale" min="1" max="4" id="guiScale" onchange="handleOptionsChanges(this.name, this.value)">
|
||||
@ -125,6 +129,12 @@
|
||||
100%
|
||||
</label>
|
||||
</div>
|
||||
<button class="classic" onclick="toggleMusicVolume(this)" state="0">
|
||||
<i class="fas fa-volume"></i>
|
||||
<span>
|
||||
Couper le son
|
||||
</span>
|
||||
</button>
|
||||
<button class="classic" onclick="toggleMusic(this)" state="0">
|
||||
<i class="fas fa-pause"></i>
|
||||
<span>
|
||||
@ -213,8 +223,15 @@
|
||||
<img class="mascot" src="./assets/img/sulli.png" alt="">
|
||||
</section>
|
||||
</footer>
|
||||
<div class="loader">
|
||||
<div class="full">
|
||||
<div class="loading">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/izitoast/1.4.0/js/iziToast.min.js"></script>
|
||||
<script src="./assets/js/index.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
@ -12,6 +12,9 @@
|
||||
<button class="close" onclick="system.call('window::close')">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<button class="close" onclick="system.call('window::reduce')">
|
||||
<i class="fas fa-minus"></i>
|
||||
</button>
|
||||
<article class="loginchoice">
|
||||
<div frame="select">
|
||||
<h2>
|
||||
|
||||
2
build.js
2
build.js
@ -1,4 +1,4 @@
|
||||
const fs = require("node:fs")
|
||||
require("v8-compile-cache")
|
||||
const path = require("node:path")
|
||||
const builder = require("electron-builder")
|
||||
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
"checkBanStatus": "/api/v1/ban/iam",
|
||||
"telemetry": "/api/v1/telemetry",
|
||||
"gameFiles": "/api/v1/file/game",
|
||||
"downloadFile": "/api/v1/file/game/download"
|
||||
"downloadFile": "/api/v1/file/game/download",
|
||||
"countdown": "/api/v1/countdown"
|
||||
},
|
||||
"websockets": {
|
||||
"base": {
|
||||
|
||||
145
main.js
145
main.js
@ -15,12 +15,13 @@ const launcher = new Client()
|
||||
const { io } = require("socket.io-client")
|
||||
const download = require("download")
|
||||
const rpc = require("./modules/rpc")
|
||||
const java = require("./modules/java")
|
||||
const socket = io({
|
||||
host: config.api.websockets.base.host,
|
||||
port: config.api.websockets.base.port
|
||||
})
|
||||
|
||||
let launcherWindow, auth, gamePlayable = false, launchProcess
|
||||
let launcherWindow, defaultWindow, auth, gamePlayable = false, launchProcess
|
||||
|
||||
rpc.startRichPresence()
|
||||
|
||||
@ -108,6 +109,63 @@ async function createLauncherWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
async function createDefaultWindow() {
|
||||
gameOptions.initOptions(path.join(app.getPath("appData"), ".catboat", "options.txt"))
|
||||
launcherSettings.initSettings(path.join(app.getPath("appData"), ".catboat"))
|
||||
if (net.isOnline()) {
|
||||
const isLauncherNotBanned = await checkIfIAmBanned()
|
||||
try {
|
||||
defaultWindow = new BrowserWindow({
|
||||
frame: false,
|
||||
width: 300,
|
||||
height: 400,
|
||||
minWidth: 300,
|
||||
minHeight: 400,
|
||||
titleBarStyle: "hidden",
|
||||
autoHideMenuBar: true,
|
||||
roundedCorners: false,
|
||||
resizable: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, "modules", "preload.js"),
|
||||
webviewTag: true,
|
||||
devTools: false
|
||||
}
|
||||
})
|
||||
if (os.platform() == "darwin") {
|
||||
app.dock.setIcon(nativeImage.createFromPath(path.join(__dirname, "app", "assets", "img", "icon.png")))
|
||||
}
|
||||
defaultWindow.setIcon(path.join(__dirname, "app", "assets", "img", "icon.png"))
|
||||
if (isLauncherNotBanned.success) {
|
||||
defaultWindow.loadFile(path.join(__dirname, "app", "loading.html"))
|
||||
defaultWindow.webContents.openDevTools()
|
||||
try {
|
||||
await java.main(path.join(app.getPath("appData"), ".catboat", "jre"))
|
||||
await launchGame(false)
|
||||
defaultWindow.hide()
|
||||
createLauncherWindow()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
defaultWindow.hide()
|
||||
createLauncherWindow()
|
||||
}
|
||||
} else {
|
||||
launcherWindow.loadFile(path.join(__dirname, "app", "banned.html"))
|
||||
defaultWindow.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 {
|
||||
@ -133,10 +191,31 @@ async function checkIfIAmBanned() {
|
||||
}
|
||||
}
|
||||
|
||||
async function checkCoutdown(uuid) {
|
||||
try {
|
||||
const response = await fetch(`${config.api.base}${config.api.endpoints.countdown}/${uuid}`)
|
||||
const json = await response.json()
|
||||
console.log(json)
|
||||
return json.success
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function writeOculusConfigFile(filePath) {
|
||||
const oculusProperties = properties.parse(fs.readFileSync(filePath).toString())
|
||||
if (data.args.boolean) {
|
||||
properties.setProperty(oculusProperties, "shaderPack", "Sildurs_Vibrant_Shaders_v1.50_Medium.zip")
|
||||
} else {
|
||||
properties.setProperty(oculusProperties, "shaderPack", "")
|
||||
}
|
||||
fs.writeFileSync(filePath, properties.stringify(oculusProperties))
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createLauncherWindow()
|
||||
createDefaultWindow()
|
||||
app.on("activate", async () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) await createLauncherWindow()
|
||||
if (BrowserWindow.getAllWindows().length === 0) await createDefaultWindow()
|
||||
})
|
||||
})
|
||||
|
||||
@ -160,6 +239,9 @@ ipcMain.on("call", async (event, data) => {
|
||||
launcherWindow.close()
|
||||
app.quit()
|
||||
break
|
||||
case "window::reduce":
|
||||
launcherWindow.minimize()
|
||||
break
|
||||
case "skin::set":
|
||||
const file = await dialog.showOpenDialog(launcherWindow, {
|
||||
filters: [
|
||||
@ -186,8 +268,18 @@ ipcMain.on("call", async (event, data) => {
|
||||
if (data.args.trim() != "") {
|
||||
auth = Authenticator.getAuth(data.args.username, data.args.password)
|
||||
launcherSettings.set("auth", { token: (await auth).access_token, type: "mojang", clientToken: (await auth).client_token })
|
||||
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${(await auth).uuid}`)
|
||||
if (await checkCoutdown((await auth).uuid)) {
|
||||
await launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
||||
await fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${(await auth).uuid}`)
|
||||
} else {
|
||||
await launcherWindow.loadURL("https://nyancraft.catboat.fr")
|
||||
await launcherWindow.webContents.insertCSS("a.download-button { display: none; } #return-button { app-region: no-drag } ")
|
||||
await launcherWindow.webContents.executeJavaScript(`
|
||||
const returnButton = document.querySelector(\"#return-button\")
|
||||
returnButton.innerText = "Fermer le launcher"
|
||||
returnButton.onclick = () => system.call("window::close")
|
||||
`)
|
||||
}
|
||||
} else {
|
||||
dialog.showErrorBox("Erreur", "Le mot de passe n'est pas défini. Les comptes crackés ne sont pas supporté par le launcher.")
|
||||
}
|
||||
@ -200,8 +292,18 @@ ipcMain.on("call", async (event, data) => {
|
||||
auth = token.mclc()
|
||||
console.log(auth.meta)
|
||||
launcherSettings.set("auth", { token: auth.meta, type: "msa", clientToken: auth.client_token })
|
||||
if (await checkCoutdown(auth.uuid)) {
|
||||
launcherWindow.loadFile(path.join(__dirname, "app", "logged.html"))
|
||||
fetch(`${config.api.base}${config.api.endpoints.telemetry}/${hwid.getHWID()}/${auth.uuid}`)
|
||||
} else {
|
||||
await launcherWindow.loadURL("https://nyancraft.catboat.fr")
|
||||
await launcherWindow.webContents.insertCSS("a.download-button { display: none; } #return-button { app-region: no-drag } ")
|
||||
await launcherWindow.webContents.executeJavaScript(`
|
||||
const returnButton = document.querySelector(\"#return-button\")
|
||||
returnButton.innerText = "Fermer le launcher"
|
||||
returnButton.onclick = () => system.call("window::close")
|
||||
`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
if (error == "error.gui.closed") {
|
||||
@ -261,23 +363,32 @@ ipcMain.on("call", async (event, data) => {
|
||||
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")
|
||||
const filePath = path.join(app.getPath("appData"), ".catboat", "config", "oculus.properties")
|
||||
if (fs.existsSync(filePath)) {
|
||||
writeOculusConfigFile(filePath)
|
||||
} else {
|
||||
properties.setProperty(oculusProperties, "shaderPack", "")
|
||||
fs.copyFileSync(path.join(__dirname, "oculus.properties"), filePath)
|
||||
writeOculusConfigFile(filePath)
|
||||
}
|
||||
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<oculus::getdefaultshaderset>", properties.getProperty($oculusProperties, "shaderPack") == "Sildur%27s+Vibrant+Shaders+v1.50+Medium.zip" ? true : false)
|
||||
const $filePath = path.join(app.getPath("appData"), ".catboat", "config", "oculus.properties")
|
||||
if (fs.existsSync($filePath)) {
|
||||
const $oculusProperties = properties.parse(fs.readFileSync($filePath).toString())
|
||||
launcherWindow.webContents.send("Response<oculus::getdefaultshaderset>", properties.getProperty($oculusProperties, "shaderPack") == "Sildurs_Vibrant_Shaders_v1.50_Medium.zip" ? true : false) } else {
|
||||
const $oculusProperties = properties.parse(fs.readFileSync($filePath).toString())
|
||||
launcherWindow.webContents.send("Response<oculus::getdefaultshaderset>", properties.getProperty($oculusProperties, "shaderPack") == "Sildurs_Vibrant_Shaders_v1.50_Medium.zip" ? true : false)
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
async function launchGame(restartGame) {
|
||||
let sendToRenderer = false
|
||||
if (launcherWindow instanceof BrowserWindow) {
|
||||
sendToRenderer = true
|
||||
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 })
|
||||
@ -313,11 +424,13 @@ async function launchGame(restartGame) {
|
||||
try {
|
||||
await download(url, path.join(app.getPath("appData"), ".catboat", path.dirname(item)))
|
||||
if (launcherWindow instanceof BrowserWindow) {
|
||||
launcherWindow.webContents.send("Response<progress::update>", { type: "landing" })
|
||||
launcherWindow.webContents.send("Response<izitoast::info>", { title: "Lancement du jeu", message: "Ce processus peut être plus ou moins long selon votre configuration." })
|
||||
launcherWindow.webContents.send("Response<progress::update>", { type: "continuous" })
|
||||
}
|
||||
} catch (error) {
|
||||
if (launcherWindow instanceof BrowserWindow) {
|
||||
launcherWindow.webContents.send("Response<progress::error>", { type: "landing" })
|
||||
launcherWindow.webContents.send("Response<progress::hide>")
|
||||
launcherWindow.webContents.send("Response<izitoast::error>", { title: "Erreur", message: new String(error) })
|
||||
}
|
||||
dialog.showErrorBox("Erreur lors du téléchargement des fichiers", error.toString())
|
||||
continue
|
||||
@ -332,6 +445,10 @@ async function launchGame(restartGame) {
|
||||
console.log(log)
|
||||
})
|
||||
launcher.on("data", (log) => {
|
||||
if (sendToRenderer) {
|
||||
launcherWindow.webContents.send("Response<game::launched>")
|
||||
sendToRenderer = false
|
||||
}
|
||||
console.log(log)
|
||||
})
|
||||
launcher.on("data", (log) => {
|
||||
@ -346,7 +463,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",
|
||||
javaPath: await java.getPath(path.join(app.getPath("appData"), ".catboat", "jre")),
|
||||
memory: {
|
||||
min: 512,
|
||||
max: launcherSettings.get("ram").max
|
||||
|
||||
247
modules/java.js
247
modules/java.js
@ -1,182 +1,95 @@
|
||||
const os = require("os")
|
||||
const { execSync } = require("child_process")
|
||||
const fs = require("fs")
|
||||
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 os = require("os")
|
||||
const download = require("download")
|
||||
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 = /(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)\_(?<update>\d+)/
|
||||
} else {
|
||||
matchRegEx = /(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\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
|
||||
})
|
||||
}
|
||||
})
|
||||
const JAVA_DOWNLOAD_URL = {
|
||||
win32: "https://download.oracle.com/java/17/archive/jdk-17.0.12_windows-x64_bin.zip",
|
||||
darwin: "https://download.oracle.com/java/17/archive/jdk-17.0.12_macos-x64_bin.tar.gz",
|
||||
linux: "https://download.oracle.com/java/17/archive/jdk-17.0.12_linux-aarch64_bin.tar.gz",
|
||||
}
|
||||
|
||||
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
|
||||
function checkJavaVersion(requiredVersion = "17") {
|
||||
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"))
|
||||
const javaVersionOutput = execSync("java -version", {
|
||||
encoding: "utf8",
|
||||
stdio: "pipe"
|
||||
})
|
||||
const versionMatch = javaVersionOutput.match(/"(\d+\.\d+)(\.\d+)?_\d+"/)
|
||||
if (versionMatch) {
|
||||
const installedVersion = versionMatch[1]
|
||||
console.log(`Java is installed. Version: ${installedVersion}`)
|
||||
if (parseFloat(installedVersion) >= parseFloat(requiredVersion)) {
|
||||
console.log("Required Java version is already installed.")
|
||||
return true
|
||||
} 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"))
|
||||
console.log(`Installed Java version (${installedVersion}) is less than required version (${requiredVersion}).`)
|
||||
return false
|
||||
}
|
||||
} 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()
|
||||
console.log("Java is installed but version could not be determined.")
|
||||
return false
|
||||
}
|
||||
} 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",
|
||||
console.log("Java is not installed.")
|
||||
return false
|
||||
}
|
||||
]
|
||||
})
|
||||
if (javaBinPathSelect.canceled) {
|
||||
showJavaErrorBox()
|
||||
} else {
|
||||
resolve(javaBinPathSelect.filePaths[0])
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.java = main
|
||||
async function downloadAndInstallJava(extractPath = null) {
|
||||
const platform = os.platform()
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "java-installer-"))
|
||||
|
||||
const downloadUrl = JAVA_DOWNLOAD_URL[platform]
|
||||
if (!downloadUrl) {
|
||||
console.error("Unsupported platform for Java installation.")
|
||||
return
|
||||
}
|
||||
|
||||
const zipPath = path.join(tempDir, "java-package.zip")
|
||||
await download(downloadUrl, tempDir, {
|
||||
filename: "java-package.zip"
|
||||
})
|
||||
|
||||
const extractionDir = extractPath || tempDir
|
||||
const zip = new AdmZip(zipPath)
|
||||
zip.extractAllTo(extractionDir, true)
|
||||
|
||||
fs.unlinkSync(zipPath)
|
||||
}
|
||||
|
||||
async function main(customExtractPath) {
|
||||
const requiredVersion = "17"
|
||||
const isJavaInstalled = checkJavaVersion(requiredVersion)
|
||||
|
||||
if (!isJavaInstalled) {
|
||||
|
||||
if (!fs.existsSync(path.join(customExtractPath, "jdk-17.0.12", "bin", os.platform() == "win32" ? "java.exe" : "java"))) {
|
||||
await downloadAndInstallJava(customExtractPath || path.join(__dirname, "..", "..", "java"))
|
||||
}
|
||||
} else {
|
||||
console.log("No further action is required.")
|
||||
}
|
||||
}
|
||||
|
||||
async function getPath(customExtractPath) {
|
||||
const requiredVersion = "17"
|
||||
const isJavaInstalled = checkJavaVersion(requiredVersion)
|
||||
|
||||
if (!isJavaInstalled) {
|
||||
if (!fs.existsSync(path.join(customExtractPath, "jdk-17.0.12", "bin", os.platform() == "win32" ? "java.exe" : "java"))) {
|
||||
await main(customExtractPath)
|
||||
} else {
|
||||
return path.join(customExtractPath, "jdk-17.0.12", "bin", os.platform() == "win32" ? "java.exe" : "java")
|
||||
}
|
||||
} else {
|
||||
console.log("No further action is required.")
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
main,
|
||||
getPath
|
||||
}
|
||||
@ -3,19 +3,22 @@ const RPC = require("discord-rpc")
|
||||
class DiscordRPC {
|
||||
constructor() {
|
||||
readonly: this.activity = {
|
||||
details: "Actif dans le launcher",
|
||||
details: "Officiel | Solva x Alcaz",
|
||||
timestamps : { start: Date.now() },
|
||||
assets: {
|
||||
large_image: "logo",
|
||||
large_text: "CatBoat Minecraft Launcher"
|
||||
large_image: "rpc_catboat_large",
|
||||
large_text: "CatBoat Launcher",
|
||||
small_image : "alflamme_comm_legoshi",
|
||||
small_text: "by TheAlfiTeam",
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
label: "Discord",
|
||||
url: "https://discord.com/invite/catboat"
|
||||
"label": "Discord",
|
||||
"url": "https://discord.gg/catboat"
|
||||
},
|
||||
{
|
||||
label: "CatBoat",
|
||||
url: "https://catboat.fr/"
|
||||
"label": "Download Launcher",
|
||||
"url": "https://catboat.thealfigame.fr"
|
||||
}
|
||||
],
|
||||
instance: true
|
||||
@ -30,12 +33,12 @@ class DiscordRPC {
|
||||
this.client.request("SET_ACTIVITY", { pid: process.pid, activity: this.activity })
|
||||
console.log("The Discord Rich Presence has been set successfully.")
|
||||
})
|
||||
this.client.login({ clientId: "1365563093157154868" }).catch(e => {
|
||||
console.log(e)
|
||||
this.client.login({ clientId: "1259291027148115988" }).catch(e => {
|
||||
console.log("Silent client detected: the activity status has been disabled.")
|
||||
console.log(e)
|
||||
this.isEnabled = false
|
||||
}).then(r => this.isEnabled = true)
|
||||
.catch(err => console.log)
|
||||
.catch(err => console.log(err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
oculus.properties
Normal file
8
oculus.properties
Normal file
@ -0,0 +1,8 @@
|
||||
#This file stores configuration options for Oculus, such as the currently active shaderpack
|
||||
#Sun May 11 07:39:36 CEST 2025
|
||||
colorSpace=SRGB
|
||||
disableUpdateMessage=false
|
||||
enableDebugOptions=false
|
||||
maxShadowRenderDistance=32
|
||||
shaderPack=Sildurs_Vibrant_Shaders_v1.50_Medium.zip
|
||||
enableShaders=true
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "catboat-launcher",
|
||||
"version": "0.0.1-alpha",
|
||||
"version": "0.0.9-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "catboat-launcher",
|
||||
"version": "0.0.1-alpha",
|
||||
"version": "0.0.9-alpha",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"discord-rpc": "^4.0.1",
|
||||
@ -14,7 +14,8 @@
|
||||
"js-java-properties": "^1.0.3",
|
||||
"minecraft-launcher-core": "^3.18.2",
|
||||
"msmc": "^5.0.5",
|
||||
"socket.io-client": "^4.8.1"
|
||||
"socket.io-client": "^4.8.1",
|
||||
"v8-compile-cache": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^35.2.1",
|
||||
@ -6596,6 +6597,12 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
|
||||
"integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "catboat-launcher",
|
||||
"version": "0.0.1-alpha",
|
||||
"version": "0.1.0-alpha",
|
||||
"description": "a simple minecraft launcher for catboat",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@ -22,12 +22,14 @@
|
||||
"electron-builder": "^26.0.12",
|
||||
"electronmon": "^2.0.3"
|
||||
},
|
||||
"homepage": "https://nyancraft.catboat.fr",
|
||||
"dependencies": {
|
||||
"discord-rpc": "^4.0.1",
|
||||
"download": "^8.0.0",
|
||||
"js-java-properties": "^1.0.3",
|
||||
"minecraft-launcher-core": "^3.18.2",
|
||||
"msmc": "^5.0.5",
|
||||
"socket.io-client": "^4.8.1"
|
||||
"socket.io-client": "^4.8.1",
|
||||
"v8-compile-cache": "^2.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user