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
This commit is contained in:
SashegDev
2026-05-08 11:04:45 +00:00
parent e5948b5337
commit ec551ab2e3
3 changed files with 62 additions and 14 deletions
@@ -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);
@@ -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";
}
+2 -1
View File
@@ -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