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
This commit is contained in:
+4
-1
@@ -120,16 +120,19 @@ public class LaunchCommandBuilder {
|
|||||||
command.add("-Djava.library.path=" + nativesDir.toAbsolutePath());
|
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;
|
int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 4096;
|
||||||
command.add("-Xmx" + ramMB + "M");
|
command.add("-Xmx" + ramMB + "M");
|
||||||
command.add("-Xms" + Math.max(512, ramMB / 2) + "M");
|
command.add("-Xms" + Math.max(512, ramMB / 2) + "M");
|
||||||
|
if (!useSystemGc) {
|
||||||
command.add("-XX:+UseG1GC");
|
command.add("-XX:+UseG1GC");
|
||||||
command.add("-XX:+UnlockExperimentalVMOptions");
|
command.add("-XX:+UnlockExperimentalVMOptions");
|
||||||
command.add("-XX:G1NewSizePercent=20");
|
command.add("-XX:G1NewSizePercent=20");
|
||||||
command.add("-XX:G1ReservePercent=20");
|
command.add("-XX:G1ReservePercent=20");
|
||||||
command.add("-XX:MaxGCPauseMillis=50");
|
command.add("-XX:MaxGCPauseMillis=50");
|
||||||
command.add("-XX:G1HeapRegionSize=32M");
|
command.add("-XX:G1HeapRegionSize=32M");
|
||||||
|
}
|
||||||
|
|
||||||
// Append custom user JVM args
|
// Append custom user JVM args
|
||||||
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
||||||
|
|||||||
@@ -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; }
|
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 modsDir = inst.getPath().resolve("mods");
|
||||||
Path src = modsDir.resolve(file);
|
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;
|
Path dst;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (file.endsWith(".disabled")) dst = modsDir.resolve(file.substring(0, file.length()-9));
|
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 { sendJson(exchange, Map.of("success", true)); return; }
|
||||||
} else {
|
} else {
|
||||||
if (file.endsWith(".jar")) dst = modsDir.resolve(file + ".disabled");
|
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; }
|
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));
|
sendJson(exchange, Map.of("success", true));
|
||||||
} catch (Exception e) { sendJson(exchange, Map.of("success", false, "error", e.getMessage())); }
|
} catch (Exception e) { sendJson(exchange, Map.of("success", false, "error", e.getMessage())); }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.1.1</revision>
|
<revision>1.1.1</revision>
|
||||||
<hotfix>2</hotfix>
|
<hotfix>3</hotfix>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
+38
-1
@@ -43,7 +43,7 @@ logger = structlog.get_logger(__name__)
|
|||||||
# Cache for manifests - expires after 5 minutes
|
# Cache for manifests - expires after 5 minutes
|
||||||
manifest_cache = TTLCache(maxsize=100, ttl=300)
|
manifest_cache = TTLCache(maxsize=100, ttl=300)
|
||||||
|
|
||||||
BUILDS_DIR = Path("builds")
|
BUILDS_DIR = Path(__file__).parent / "builds"
|
||||||
VERSIONS_DIR = BUILDS_DIR / "versions"
|
VERSIONS_DIR = BUILDS_DIR / "versions"
|
||||||
WHITELIST_DIR = Path(__file__).parent / "whitelist"
|
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)
|
return await send_file_async(pp, request, content_type="application/zip", cache=True)
|
||||||
raise HTTPException(404, "JRE zip not found on server")
|
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}")
|
@app.get("/launcher/download/zip/{filename}")
|
||||||
async def download_launcher_zip(filename: str, request: Request = None):
|
async def download_launcher_zip(filename: str, request: Request = None):
|
||||||
"""Download specific launcher ZIP archive"""
|
"""Download specific launcher ZIP archive"""
|
||||||
|
|||||||
@@ -383,11 +383,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
||||||
<a class="btn btn-primary btn-block" id="download-online-btn" href="/launcher/download/jre" download>
|
<a class="btn btn-primary btn-block" id="download-online-btn" href="/launcher/download/online" download>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M4 21h16"/></svg>
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M4 21h16"/></svg>
|
||||||
<span>Online Setup (5.8М)</span>
|
<span>Online Setup (5.8М)</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-ghost btn-block" id="download-offline-btn" href="/launcher/download/latest">
|
<a class="btn btn-ghost btn-block" id="download-offline-btn" href="/launcher/download/offline-setup">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||||
<span>Offline ZIP (98М)</span>
|
<span>Offline ZIP (98М)</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -345,16 +345,12 @@
|
|||||||
// online setup is Go exe (~5.8M) — try to discover via /launcher/version or static name
|
// online setup is Go exe (~5.8M) — try to discover via /launcher/version or static name
|
||||||
try {
|
try {
|
||||||
const v = version && version !== '—' ? version : '1.1.1.2';
|
const v = version && version !== '—' ? version : '1.1.1.2';
|
||||||
dlOnline.href = API + '/launcher/download/jre'; // JRE endpoint validated, exe name is ZernMC-Online-Setup-<ver>.exe
|
dlOnline.href = API + '/launcher/download/online';
|
||||||
dlOnline.setAttribute('data-version', v);
|
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){}
|
} catch(e){}
|
||||||
}
|
}
|
||||||
|
if (dlOffline) dlOffline.href = API + '/launcher/download/offline-setup';
|
||||||
if (dlOffline && data.new_format && data.new_format.download_url) {
|
if (dlOffline && data.new_format && data.new_format.download_url) {
|
||||||
dlOffline.href = API + data.new_format.download_url;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[site] failed to load launcher info', e);
|
console.warn('[site] failed to load launcher info', e);
|
||||||
|
|||||||
Reference in New Issue
Block a user