From ec551ab2e367c4c5f1a29c2109bc8659de0f44a9 Mon Sep 17 00:00:00 2001 From: SashegDev Date: Fri, 8 May 2026 11:04:45 +0000 Subject: [PATCH] Fix: Fabric loader launch and Bootstrap paths - Add Fabric support in LaunchCommandBuilder.findVersionJson() - Fix Bootstrap to properly use bin/ directory for launcher JAR - Fix server.py to accept both ZernMC-win-*.zip and ZernMCLauncher-*.zip - Add debug output for version.json resolution --- .../sashegdev/zernmc/launcher/Bootstrap.java | 33 +++++++++------ .../launch/LaunchCommandBuilder.java | 40 +++++++++++++++++++ server/main.py | 3 +- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/launcher/bootstrap/src/main/java/me/sashegdev/zernmc/launcher/Bootstrap.java b/launcher/bootstrap/src/main/java/me/sashegdev/zernmc/launcher/Bootstrap.java index 6f451a7..d9d8be6 100644 --- a/launcher/bootstrap/src/main/java/me/sashegdev/zernmc/launcher/Bootstrap.java +++ b/launcher/bootstrap/src/main/java/me/sashegdev/zernmc/launcher/Bootstrap.java @@ -16,12 +16,19 @@ public class Bootstrap { private static final String JAR_NAME = "zernmclauncher.jar"; private static final String BASE_URL = "http://87.120.187.36:1582"; private static final int BUFFER_SIZE = 8192; - + private static Path baseDir; + private static Path binDir; private static Path logDir; + + private static Path getLauncherJar() { + return binDir.resolve(JAR_NAME); + } public static void main(String[] args) throws Exception { baseDir = Paths.get("").toAbsolutePath(); + binDir = baseDir.resolve("bin"); + Files.createDirectories(binDir); logDir = baseDir.resolve("logs"); Files.createDirectories(logDir); @@ -65,7 +72,7 @@ public class Bootstrap { private static String readCurrentVersion() { // Читаем версию из манифеста zernmclauncher.jar в папке bin/ - Path launcherJar = baseDir.resolve("bin").resolve("zernmclauncher.jar"); + Path launcherJar = getLauncherJar(); try { if (Files.exists(launcherJar)) { try (FileInputStream fis = new FileInputStream(launcherJar.toFile())) { @@ -77,13 +84,13 @@ public class Bootstrap { } } } catch (Exception ignored) {} - + // Fallback: из build.version Path f = baseDir.resolve(VERSION_FILE); try { if (Files.exists(f)) return Files.readString(f).trim(); } catch (Exception ignored) {} - + return "0.0.0"; } @@ -469,11 +476,11 @@ public class Bootstrap { URL url = new URL(BASE_URL + "/launcher/download/jar"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); - + if (conn.getResponseCode() == 200) { - Path jarFile = baseDir.resolve(JAR_NAME); + Path jarFile = getLauncherJar(); Path tmp = jarFile.resolveSibling("zernmc-launcher-new.jar"); - + try (InputStream in = conn.getInputStream(); OutputStream out = new FileOutputStream(tmp.toFile())) { byte[] buf = new byte[BUFFER_SIZE]; @@ -486,12 +493,12 @@ public class Bootstrap { } } log("JAR скачан"); - + Path backup = jarFile.resolveSibling(JAR_NAME + ".old"); if (Files.exists(jarFile)) Files.move(jarFile, backup, StandardCopyOption.REPLACE_EXISTING); Files.move(tmp, jarFile, StandardCopyOption.REPLACE_EXISTING); if (Files.exists(backup)) Files.delete(backup); - + Files.writeString(baseDir.resolve(VERSION_FILE), newVersion); log("Обновлено до v" + newVersion + " (JAR метод)"); } else { @@ -501,8 +508,8 @@ public class Bootstrap { private static void launchJFX() throws Exception { Path javaBin = findJava(); - Path jarPath = baseDir.resolve(JAR_NAME); - + Path jarPath = getLauncherJar(); + log("Запуск JFX режима..."); log("Java: " + javaBin); log("JAR: " + jarPath); @@ -544,8 +551,8 @@ public class Bootstrap { private static void launchCLI() throws Exception { Path javaBin = findJava(); - Path jarPath = baseDir.resolve(JAR_NAME); - + Path jarPath = getLauncherJar(); + log("Запуск CLI режима..."); log("Java: " + javaBin); log("JAR: " + jarPath); 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 0be55be..0fe511f 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 @@ -63,6 +63,10 @@ public class LaunchCommandBuilder { JSONObject json = new JSONObject(content); System.out.println(ZAnsi.green("Найден version.json: " + versionJson.getFileName())); return new VersionManifest(json); + } else { + System.out.println(ZAnsi.yellow("version.json не найден для " + instance.getName())); + System.out.println(ZAnsi.yellow(" loaderType=" + instance.getLoaderType() + " mcVersion=" + instance.getMinecraftVersion() + " loaderVersion=" + instance.getLoaderVersion())); + System.out.println(ZAnsi.yellow(" path=" + instance.getPath())); } } catch (Exception e) { System.out.println(ZAnsi.yellow("Не удалось загрузить version.json: " + e.getMessage())); @@ -76,6 +80,38 @@ public class LaunchCommandBuilder { String mcVersion = instance.getMinecraftVersion(); String loaderVersion = instance.getLoaderVersion(); + if ("fabric".equals(loaderType)) { + String versionId = getVersionId(); + // Try fabric version ID first + Path jsonPath = versionsDir.resolve(versionId).resolve(versionId + ".json"); + if (Files.exists(jsonPath)) { + return jsonPath; + } + // Try instance's fabricVersionId if available + String fabricId = instance.getFabricVersionId(); + if (fabricId != null && !fabricId.isEmpty()) { + Path fabricPath = versionsDir.resolve(fabricId).resolve(fabricId + ".json"); + if (Files.exists(fabricPath)) { + return fabricPath; + } + } + // Try generic fabric pattern + try { + if (Files.exists(versionsDir)) { + try (var stream = Files.list(versionsDir)) { + return stream + .filter(Files::isDirectory) + .filter(dir -> dir.getFileName().toString().contains("fabric")) + .filter(dir -> dir.getFileName().toString().contains(mcVersion)) + .findFirst() + .map(dir -> dir.resolve(dir.getFileName().toString() + ".json")) + .filter(Files::exists) + .orElse(null); + } + } + } catch (Exception ignored) {} + } + if ("forge".equals(loaderType) || "neoforge".equals(loaderType)) { String[] candidates = { getVersionId(), @@ -152,6 +188,10 @@ public class LaunchCommandBuilder { String loaderType = instance.getLoaderType().toLowerCase(); if ("fabric".equals(loaderType)) { return "net.fabricmc.loader.impl.launch.knot.KnotClient"; + } else if ("forge".equals(loaderType)) { + return "net.minecraftforge.client.main.ForgeClient"; + } else if ("neoforge".equals(loaderType)) { + return "cpw.mods.bootstraplauncher.BootstrapLauncher"; } return "net.minecraft.client.main.Main"; } diff --git a/server/main.py b/server/main.py index 83faa59..92b2009 100644 --- a/server/main.py +++ b/server/main.py @@ -1080,7 +1080,8 @@ async def download_launcher_exe(): @app.get("/launcher/download/zip/{filename}") async def download_launcher_zip(filename: str): """Download specific launcher ZIP archive""" - if ".." in filename or not filename.startswith("ZernMCLauncher-") or not filename.endswith(".zip"): + valid_patterns = ["ZernMCLauncher-", "ZernMC-win-"] + if ".." in filename or not any(filename.startswith(p) for p in valid_patterns) or not filename.endswith(".zip"): raise HTTPException(400, "Invalid filename") file_path = BUILDS_DIR / filename