From 1900db3caf4d9d1fb0fa404e184c74aa7893bebb Mon Sep 17 00:00:00 2001 From: SashegDev Date: Tue, 1 Sep 2026 13:51:30 +0000 Subject: [PATCH] fix: 1.1.1.3 online installer 5.8M + mods toggle + ZGC vs G1GC conflict - server/main.py BUILDS_DIR absolute + /launcher/download/online,offine-setup - site index/js online->/download/online 5.8M - LaunchCommandBuilder skip G1GC when systemBasedJvm (ZGC conflict -> Bootstrap crash) - JFXLauncher toggle robust rename + FileAlreadyExists fix --- .../launch/LaunchCommandBuilder.java | 17 ++++---- .../zernmc/launcher/ui/jfx/JFXLauncher.java | 18 ++++++++- launcher/pom.xml | 2 +- server/main.py | 39 ++++++++++++++++++- server/site/index.html | 4 +- server/site/js/main.js | 8 +--- 6 files changed, 69 insertions(+), 19 deletions(-) diff --git a/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/minecraft/launch/LaunchCommandBuilder.java b/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/minecraft/launch/LaunchCommandBuilder.java index 9477e12..e821625 100644 --- a/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/minecraft/launch/LaunchCommandBuilder.java +++ b/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/minecraft/launch/LaunchCommandBuilder.java @@ -120,16 +120,19 @@ public class LaunchCommandBuilder { command.add("-Djava.library.path=" + nativesDir.toAbsolutePath()); } - // Append memory/GC args (always after version.json args, like AstralRinth) + // Append memory/GC args (always after version.json args, like AstralRinth) — skip if systemBasedJvm adds its own GC (ZGC/G1) + boolean useSystemGc = me.sashegdev.zernmc.launcher.utils.Config.isSystemBasedJvm(); int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 4096; command.add("-Xmx" + ramMB + "M"); command.add("-Xms" + Math.max(512, ramMB / 2) + "M"); - command.add("-XX:+UseG1GC"); - command.add("-XX:+UnlockExperimentalVMOptions"); - command.add("-XX:G1NewSizePercent=20"); - command.add("-XX:G1ReservePercent=20"); - command.add("-XX:MaxGCPauseMillis=50"); - command.add("-XX:G1HeapRegionSize=32M"); + if (!useSystemGc) { + command.add("-XX:+UseG1GC"); + command.add("-XX:+UnlockExperimentalVMOptions"); + command.add("-XX:G1NewSizePercent=20"); + command.add("-XX:G1ReservePercent=20"); + command.add("-XX:MaxGCPauseMillis=50"); + command.add("-XX:G1HeapRegionSize=32M"); + } // Append custom user JVM args if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) { diff --git a/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/ui/jfx/JFXLauncher.java b/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/ui/jfx/JFXLauncher.java index da937c9..a7cfe15 100644 --- a/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/ui/jfx/JFXLauncher.java +++ b/launcher/launcher/src/main/java/sashegdev/zernmc/launcher/ui/jfx/JFXLauncher.java @@ -1856,16 +1856,30 @@ public class JFXLauncher extends Application { if (inst.isServerPack()) { sendJson(exchange, Map.of("success", false, "error", "Cannot toggle mods in server pack (only whitelist)")); return; } Path modsDir = inst.getPath().resolve("mods"); Path src = modsDir.resolve(file); - if (!Files.exists(src)) { sendJson(exchange, Map.of("success", false, "error", "File not found")); return; } + if (!Files.exists(src)) { + // Try alternative name (enable: caller may send base name without .disabled) + String alt = enable ? file + ".disabled" : file.replace(".disabled", ""); + Path altSrc = modsDir.resolve(alt); + if (Files.exists(altSrc)) src = altSrc; + else if (enable && file.endsWith(".jar")) { + Path disabledSrc = modsDir.resolve(file + ".disabled"); + if (Files.exists(disabledSrc)) src = disabledSrc; + else { sendJson(exchange, Map.of("success", false, "error", "File not found: " + file)); return; } + file = file + ".disabled"; + } else { sendJson(exchange, Map.of("success", false, "error", "File not found: " + file)); return; } + } Path dst; if (enable) { if (file.endsWith(".disabled")) dst = modsDir.resolve(file.substring(0, file.length()-9)); + else if (src.getFileName().toString().endsWith(".disabled")) dst = modsDir.resolve(src.getFileName().toString().replace(".disabled", "")); else { sendJson(exchange, Map.of("success", true)); return; } } else { if (file.endsWith(".jar")) dst = modsDir.resolve(file + ".disabled"); + else if (src.getFileName().toString().endsWith(".jar")) dst = modsDir.resolve(src.getFileName().toString() + ".disabled"); else { sendJson(exchange, Map.of("success", true)); return; } } - Files.move(src, dst); + if (Files.exists(dst)) Files.delete(dst); + Files.move(src, dst, java.nio.file.StandardCopyOption.REPLACE_EXISTING); sendJson(exchange, Map.of("success", true)); } catch (Exception e) { sendJson(exchange, Map.of("success", false, "error", e.getMessage())); } } diff --git a/launcher/pom.xml b/launcher/pom.xml index f03fba2..05eeb9c 100644 --- a/launcher/pom.xml +++ b/launcher/pom.xml @@ -20,7 +20,7 @@ 1.1.1 - 2 + 3 21 21 UTF-8 diff --git a/server/main.py b/server/main.py index e198e9d..5da3ea4 100644 --- a/server/main.py +++ b/server/main.py @@ -43,7 +43,7 @@ logger = structlog.get_logger(__name__) # Cache for manifests - expires after 5 minutes manifest_cache = TTLCache(maxsize=100, ttl=300) -BUILDS_DIR = Path("builds") +BUILDS_DIR = Path(__file__).parent / "builds" VERSIONS_DIR = BUILDS_DIR / "versions" WHITELIST_DIR = Path(__file__).parent / "whitelist" @@ -1813,6 +1813,43 @@ async def download_jre(request: Request): return await send_file_async(pp, request, content_type="application/zip", cache=True) raise HTTPException(404, "JRE zip not found on server") +def _find_latest_setup(pattern: str) -> Path | None: + """Find latest Go setup exe by pattern (Online/Offline)""" + cands = list(BUILDS_DIR.glob(pattern)) + if not cands: + return None + # sort by version key from filename + def ver_key(p: Path): + # ZernMC-Online-Setup-1.1.1.2.exe -> 1.1.1.2 + name = p.stem + ver = name.split("-")[-1] + try: + return tuple(int(x) for x in ver.split(".")) + except: + return (0,) + cands.sort(key=ver_key, reverse=True) + return cands[0] + +@app.get("/launcher/download/online") +async def download_online_setup(request: Request): + """Download online Go installer (5.8M, STANDALONE EXE without bundled JRE)""" + p = _find_latest_setup("ZernMC-Online-Setup-*.exe") + if p and p.exists(): + return await send_file_async(p, request, content_type="application/vnd.microsoft.portable-executable", cache=True) + raise HTTPException(404, "Online installer not found") + +@app.get("/launcher/download/offline-setup") +async def download_offline_setup(request: Request): + """Download offline Go installer (STANDALONE EXE with embedded build, ~100M)""" + p = _find_latest_setup("ZernMC-Offline-Setup-*.exe") + if p and p.exists(): + return await send_file_async(p, request, content_type="application/vnd.microsoft.portable-executable", cache=True) + # fallback: try legacy offline zip embed name without -Setup + p2 = _find_latest_setup("ZernMC-Offline-*.exe") + if p2 and p2.exists(): + return await send_file_async(p2, request, content_type="application/vnd.microsoft.portable-executable", cache=True) + raise HTTPException(404, "Offline installer not found") + @app.get("/launcher/download/zip/{filename}") async def download_launcher_zip(filename: str, request: Request = None): """Download specific launcher ZIP archive""" diff --git a/server/site/index.html b/server/site/index.html index d1aacce..6cc7b5b 100644 --- a/server/site/index.html +++ b/server/site/index.html @@ -383,11 +383,11 @@
- + Online Setup (5.8М) - + Offline ZIP (98М) diff --git a/server/site/js/main.js b/server/site/js/main.js index 335001b..c601ecb 100644 --- a/server/site/js/main.js +++ b/server/site/js/main.js @@ -345,16 +345,12 @@ // online setup is Go exe (~5.8M) — try to discover via /launcher/version or static name try { const v = version && version !== '—' ? version : '1.1.1.2'; - dlOnline.href = API + '/launcher/download/jre'; // JRE endpoint validated, exe name is ZernMC-Online-Setup-.exe + dlOnline.href = API + '/launcher/download/online'; dlOnline.setAttribute('data-version', v); - // probe if online exe exists - fetch(API + '/launcher/file/' + v + '/ZernMC-Online-Setup-' + v + '.exe', {method:'HEAD'}).then(r=>{ - if (r.ok) dlOnline.href = API + '/launcher/file/' + v + '/ZernMC-Online-Setup-' + v + '.exe'; - }).catch(()=>{}); } catch(e){} } + if (dlOffline) dlOffline.href = API + '/launcher/download/offline-setup'; if (dlOffline && data.new_format && data.new_format.download_url) { - dlOffline.href = API + data.new_format.download_url; } } catch (e) { console.warn('[site] failed to load launcher info', e);