v1.0.16.0 — fix Zern-OBT asset index (1.20.1->5), JavaFX 23->23.0.1 (WebKit WebSocket fix), drop embedded JavaFX from jar (scope=provided), deliver JavaFX 23.0.1 via incremental update, JFXBridge for safe CLI fallback
This commit is contained in:
@@ -394,9 +394,49 @@ public class Bootstrap {
|
|||||||
String summary = "Updated files: " + downloaded + ", skipped: " + skipped + ", failed: " + failed;
|
String summary = "Updated files: " + downloaded + ", skipped: " + skipped + ", failed: " + failed;
|
||||||
if (staged > 0) summary += ", staged self-update: " + staged;
|
if (staged > 0) summary += ", staged self-update: " + staged;
|
||||||
log(summary);
|
log(summary);
|
||||||
|
|
||||||
|
cleanStaleJavaFxJars(serverFiles);
|
||||||
|
|
||||||
log("Updated to v" + newVersion);
|
log("Updated to v" + newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes old lib/javafx/*-win.jar files that are no longer part of the
|
||||||
|
* server meta (e.g. the previous JavaFX 23 jars after an upgrade to
|
||||||
|
* 23.0.1). Keeping both would put two JARs exporting the same module on
|
||||||
|
* the --module-path, which breaks module resolution.
|
||||||
|
*/
|
||||||
|
private static void cleanStaleJavaFxJars(Map<String, FileMeta> serverFiles) {
|
||||||
|
Path javafxDir = baseDir.resolve("lib").resolve("javafx");
|
||||||
|
if (!Files.isDirectory(javafxDir)) return;
|
||||||
|
|
||||||
|
java.util.Set<String> keep = new java.util.HashSet<>();
|
||||||
|
for (String filePath : serverFiles.keySet()) {
|
||||||
|
String normalized = filePath.toLowerCase().replace("\\", "/");
|
||||||
|
if (normalized.startsWith("lib/javafx/") && normalized.endsWith("-win.jar")) {
|
||||||
|
keep.add(new File(filePath).getName().toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try (java.util.stream.Stream<Path> stream = Files.list(javafxDir)) {
|
||||||
|
stream.filter(Files::isRegularFile)
|
||||||
|
.filter(p -> {
|
||||||
|
String name = p.getFileName().toString().toLowerCase();
|
||||||
|
return name.endsWith("-win.jar") && !keep.contains(name);
|
||||||
|
})
|
||||||
|
.forEach(p -> {
|
||||||
|
try {
|
||||||
|
Files.delete(p);
|
||||||
|
log("Removed stale JavaFX jar: " + p.getFileName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("Warning: could not remove stale JavaFX jar " + p.getFileName() + " - " + e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("Warning: failed to scan JavaFX dir: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isSelfExe(String filePath) {
|
private static boolean isSelfExe(String filePath) {
|
||||||
String lower = filePath.toLowerCase().replace("\\", "/");
|
String lower = filePath.toLowerCase().replace("\\", "/");
|
||||||
return lower.equals("zernmc.exe") || lower.equals("zernmc-cli.exe")
|
return lower.equals("zernmc.exe") || lower.equals("zernmc-cli.exe")
|
||||||
|
|||||||
+15
-10
@@ -61,32 +61,37 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-controls</artifactId>
|
<artifactId>javafx-controls</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-web</artifactId>
|
<artifactId>javafx-web</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-graphics</artifactId>
|
<artifactId>javafx-graphics</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-base</artifactId>
|
<artifactId>javafx-base</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-media</artifactId>
|
<artifactId>javafx-media</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
@@ -235,27 +240,27 @@
|
|||||||
<!-- Копируем JavaFX модули в lib/javafx -->
|
<!-- Копируем JavaFX модули в lib/javafx -->
|
||||||
<mkdir dir="../../server/builds/lib/javafx"/>
|
<mkdir dir="../../server/builds/lib/javafx"/>
|
||||||
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
||||||
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-controls/23">
|
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-controls/23.0.1">
|
||||||
<include name="*win.jar"/>
|
<include name="*win.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
||||||
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-graphics/23">
|
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-graphics/23.0.1">
|
||||||
<include name="*win.jar"/>
|
<include name="*win.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
||||||
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-base/23">
|
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-base/23.0.1">
|
||||||
<include name="*win.jar"/>
|
<include name="*win.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
||||||
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-web/23">
|
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-web/23.0.1">
|
||||||
<include name="*win.jar"/>
|
<include name="*win.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
<copy todir="../../server/builds/lib/javafx" overwrite="true">
|
||||||
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-media/23">
|
<fileset dir="${user.home}/.m2/repository/org/openjfx/javafx-media/23.0.1">
|
||||||
<include name="*win.jar"/>
|
<include name="*win.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class Main {
|
|||||||
|
|
||||||
private static void launchJFX() {
|
private static void launchJFX() {
|
||||||
try {
|
try {
|
||||||
System.setProperty("javafx.runtime.version", "21");
|
System.setProperty("javafx.runtime.version", "23.0.1");
|
||||||
|
|
||||||
JFXLauncher.main(new String[]{});
|
JFXLauncher.main(new String[]{});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
+5
-5
@@ -6,7 +6,7 @@ import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
|||||||
import me.sashegdev.zernmc.launcher.minecraft.InstanceManager;
|
import me.sashegdev.zernmc.launcher.minecraft.InstanceManager;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||||
import me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher;
|
import me.sashegdev.zernmc.launcher.ui.jfx.JFXBridge;
|
||||||
import me.sashegdev.zernmc.launcher.utils.Config;
|
import me.sashegdev.zernmc.launcher.utils.Config;
|
||||||
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
|
|
||||||
@@ -102,14 +102,14 @@ public class LaunchService {
|
|||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
String timestamped = "[" + java.time.LocalTime.now().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss")) + "] " + line;
|
String timestamped = "[" + java.time.LocalTime.now().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss")) + "] " + line;
|
||||||
JFXLauncher.appendGameLog(line);
|
JFXBridge.appendGameLog(line);
|
||||||
try {
|
try {
|
||||||
logFileOut.write((timestamped + "\n").getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
logFileOut.write((timestamped + "\n").getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
logFileOut.flush();
|
logFileOut.flush();
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JFXLauncher.appendGameLog("[Error reading logs: " + e.getMessage() + "]");
|
JFXBridge.appendGameLog("[Error reading logs: " + e.getMessage() + "]");
|
||||||
} finally {
|
} finally {
|
||||||
try { logFileOut.close(); } catch (Exception ignored) {}
|
try { logFileOut.close(); } catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
@@ -127,11 +127,11 @@ public class LaunchService {
|
|||||||
try {
|
try {
|
||||||
int exitCode = process.waitFor();
|
int exitCode = process.waitFor();
|
||||||
runningProcesses.remove(pid);
|
runningProcesses.remove(pid);
|
||||||
JFXLauncher.appendGameLog("[Minecraft exited with code: " + exitCode + "]");
|
JFXBridge.appendGameLog("[Minecraft exited with code: " + exitCode + "]");
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JFXLauncher.appendGameLog("[Error watching process: " + e.getMessage() + "]");
|
JFXBridge.appendGameLog("[Error watching process: " + e.getMessage() + "]");
|
||||||
}
|
}
|
||||||
}, "ProcessWatcher-" + instanceName);
|
}, "ProcessWatcher-" + instanceName);
|
||||||
processWatcher.setDaemon(true);
|
processWatcher.setDaemon(true);
|
||||||
|
|||||||
+5
-5
@@ -5,7 +5,7 @@ import me.sashegdev.zernmc.launcher.minecraft.installer.ModLoaderInstaller;
|
|||||||
import me.sashegdev.zernmc.launcher.minecraft.installer.VersionInstaller;
|
import me.sashegdev.zernmc.launcher.minecraft.installer.VersionInstaller;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||||
import me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher;
|
import me.sashegdev.zernmc.launcher.ui.jfx.JFXBridge;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ConsoleUtils;
|
import me.sashegdev.zernmc.launcher.utils.ConsoleUtils;
|
||||||
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||||
@@ -141,10 +141,10 @@ public class MinecraftLib {
|
|||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
JFXLauncher.appendGameLog(line);
|
JFXBridge.appendGameLog(line);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JFXLauncher.appendGameLog("[Error reading output: " + e.getMessage() + "]");
|
JFXBridge.appendGameLog("[Error reading output: " + e.getMessage() + "]");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
outThread.setDaemon(true);
|
outThread.setDaemon(true);
|
||||||
@@ -155,10 +155,10 @@ public class MinecraftLib {
|
|||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
JFXLauncher.appendGameLog("[ERR] " + line);
|
JFXBridge.appendGameLog("[ERR] " + line);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JFXLauncher.appendGameLog("[Error reading stderr: " + e.getMessage() + "]");
|
JFXBridge.appendGameLog("[Error reading stderr: " + e.getMessage() + "]");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
errThread.setDaemon(true);
|
errThread.setDaemon(true);
|
||||||
|
|||||||
+52
-2
@@ -250,7 +250,7 @@ public class PackDownloader {
|
|||||||
instance.setMinecraftVersion(manifest.getMinecraftVersion());
|
instance.setMinecraftVersion(manifest.getMinecraftVersion());
|
||||||
instance.setLoaderType(manifest.getLoaderType());
|
instance.setLoaderType(manifest.getLoaderType());
|
||||||
instance.setLoaderVersion(manifest.getLoaderVersion());
|
instance.setLoaderVersion(manifest.getLoaderVersion());
|
||||||
instance.setAssetIndex(manifest.getAssetIndex());
|
instance.setAssetIndex(resolveAssetIndex(manifest));
|
||||||
|
|
||||||
reportProgress("Pack installed successfully!", 100, "Installing pack files", 4, 5);
|
reportProgress("Pack installed successfully!", 100, "Installing pack files", 4, 5);
|
||||||
System.out.println(ZAnsi.brightGreen("Pack installed successfully!"));
|
System.out.println(ZAnsi.brightGreen("Pack installed successfully!"));
|
||||||
@@ -300,7 +300,7 @@ public class PackDownloader {
|
|||||||
instance.setMinecraftVersion(manifest.getMinecraftVersion());
|
instance.setMinecraftVersion(manifest.getMinecraftVersion());
|
||||||
instance.setLoaderType(manifest.getLoaderType());
|
instance.setLoaderType(manifest.getLoaderType());
|
||||||
instance.setLoaderVersion(manifest.getLoaderVersion());
|
instance.setLoaderVersion(manifest.getLoaderVersion());
|
||||||
instance.setAssetIndex(manifest.getAssetIndex());
|
instance.setAssetIndex(resolveAssetIndex(manifest));
|
||||||
|
|
||||||
System.out.println(ZAnsi.brightGreen("Pack installed successfully!"));
|
System.out.println(ZAnsi.brightGreen("Pack installed successfully!"));
|
||||||
LauncherLogger.info("installOrUpdatePack: SUCCESS");
|
LauncherLogger.info("installOrUpdatePack: SUCCESS");
|
||||||
@@ -771,6 +771,56 @@ public class PackDownloader {
|
|||||||
public boolean isEmpty() { return files == null || files.isEmpty(); }
|
public boolean isEmpty() { return files == null || files.isEmpty(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the correct asset index for the installed Minecraft version.
|
||||||
|
* The manifest/instance.json asset_index value may be wrong (e.g. Zern-OBT
|
||||||
|
* shipped "1.20.1" while the real index for MC 1.20.1 is "5"). The source of
|
||||||
|
* truth is the local Mojang version JSON downloaded during install
|
||||||
|
* (versions/<mc>/<mc>.json -> assetIndex.id). We only trust it when the
|
||||||
|
* corresponding index file actually exists on disk; otherwise fall back to
|
||||||
|
* the manifest value.
|
||||||
|
*/
|
||||||
|
private String resolveAssetIndex(PackManifest manifest) {
|
||||||
|
String manifestIndex = manifest.getAssetIndex();
|
||||||
|
if (manifest == null || manifest.getMinecraftVersion() == null) {
|
||||||
|
return manifestIndex;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Path versionJson = instance.getPath().resolve("versions")
|
||||||
|
.resolve(manifest.getMinecraftVersion())
|
||||||
|
.resolve(manifest.getMinecraftVersion() + ".json");
|
||||||
|
if (Files.exists(versionJson)) {
|
||||||
|
JsonObject versionData = JsonParser.parseString(Files.readString(versionJson)).getAsJsonObject();
|
||||||
|
String realIndex = null;
|
||||||
|
if (versionData.has("assetIndex") && versionData.get("assetIndex").isJsonObject()
|
||||||
|
&& versionData.getAsJsonObject("assetIndex").has("id")) {
|
||||||
|
realIndex = versionData.getAsJsonObject("assetIndex").get("id").getAsString();
|
||||||
|
} else if (versionData.has("assets") && !versionData.get("assets").isJsonNull()) {
|
||||||
|
realIndex = versionData.get("assets").getAsString();
|
||||||
|
}
|
||||||
|
if (realIndex != null && !realIndex.isEmpty()) {
|
||||||
|
Path indexPath = instance.getPath().resolve("assets").resolve("indexes")
|
||||||
|
.resolve(realIndex + ".json");
|
||||||
|
if (Files.exists(indexPath)) {
|
||||||
|
if (!realIndex.equals(manifestIndex)) {
|
||||||
|
LauncherLogger.warn("resolveAssetIndex: manifest asset_index=" + manifestIndex
|
||||||
|
+ " but real index=" + realIndex + " exists on disk, using real index");
|
||||||
|
}
|
||||||
|
return realIndex;
|
||||||
|
} else {
|
||||||
|
LauncherLogger.warn("resolveAssetIndex: real index=" + realIndex
|
||||||
|
+ " from version JSON, but " + indexPath + " does not exist; falling back to manifest=" + manifestIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LauncherLogger.warn("resolveAssetIndex: local version JSON not found: " + versionJson);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LauncherLogger.warn("resolveAssetIndex: failed to resolve: " + e.getClass().getSimpleName() + " - " + e.getMessage());
|
||||||
|
}
|
||||||
|
return manifestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
public static class DiffResponse {
|
public static class DiffResponse {
|
||||||
private int version;
|
private int version;
|
||||||
private List<FileInfo> to_download;
|
private List<FileInfo> to_download;
|
||||||
|
|||||||
+23
@@ -498,6 +498,29 @@ public class LaunchCommandBuilder {
|
|||||||
assetIndex = instance.getMinecraftVersion();
|
assetIndex = instance.getMinecraftVersion();
|
||||||
System.out.println(ZAnsi.yellow("Asset index not found, using version: " + assetIndex));
|
System.out.println(ZAnsi.yellow("Asset index not found, using version: " + assetIndex));
|
||||||
} else {
|
} else {
|
||||||
|
// Validate that the index file exists. A wrong/stale asset index
|
||||||
|
// (e.g. the Zern-OBT "1.20.1" bug, real index is "5") makes the game
|
||||||
|
// fail to load the base resource pack (grey panorama, no sounds).
|
||||||
|
Path indexPath = instance.getPath().resolve("assets").resolve("indexes").resolve(assetIndex + ".json");
|
||||||
|
if (!Files.exists(indexPath)) {
|
||||||
|
System.out.println(ZAnsi.yellow("Asset index file missing: " + assetIndex + ".json, searching for existing index..."));
|
||||||
|
try (java.util.stream.Stream<Path> stream = Files.list(instance.getPath().resolve("assets").resolve("indexes"))) {
|
||||||
|
java.util.List<Path> existing = stream
|
||||||
|
.filter(p -> p.getFileName().toString().endsWith(".json"))
|
||||||
|
.sorted()
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
if (!existing.isEmpty()) {
|
||||||
|
String found = existing.get(existing.size() - 1).getFileName().toString()
|
||||||
|
.replace(".json", "");
|
||||||
|
System.out.println(ZAnsi.green("Using existing asset index: " + found + " (was " + assetIndex + ")"));
|
||||||
|
assetIndex = found;
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.yellow("No asset index files found in assets/indexes/"));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.yellow("Failed to search asset indexes: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
System.out.println(ZAnsi.green("Using asset index: " + assetIndex));
|
System.out.println(ZAnsi.green("Using asset index: " + assetIndex));
|
||||||
}
|
}
|
||||||
args.add(assetIndex);
|
args.add(assetIndex);
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package me.sashegdev.zernmc.launcher.ui.jfx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe reflection bridge to JFXLauncher for use from non-JFX code (CLI, menus,
|
||||||
|
* installers).
|
||||||
|
*
|
||||||
|
* IMPORTANT: JFXLauncher extends javafx.application.Application. In CLI mode
|
||||||
|
* JavaFX is NOT on the module path, so loading JFXLauncher (or any class that
|
||||||
|
* references JavaFX) throws NoClassDefFoundError (an Error, not an Exception).
|
||||||
|
* Therefore the bridge must catch Throwable and never let JFX-launcher errors
|
||||||
|
* leak into CLI/game-launch code paths.
|
||||||
|
*
|
||||||
|
* Rule: any call into JFX UI state from non-JFX context MUST go through this
|
||||||
|
* bridge. Direct JFXLauncher.x() calls are only allowed inside JFXLauncher.java.
|
||||||
|
*/
|
||||||
|
public final class JFXBridge {
|
||||||
|
|
||||||
|
private JFXBridge() {}
|
||||||
|
|
||||||
|
private static final String JFX_CLASS = "me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher";
|
||||||
|
|
||||||
|
private static volatile java.lang.reflect.Method appendGameLogMethod;
|
||||||
|
private static volatile java.lang.reflect.Method setInstallProgressMethod;
|
||||||
|
private static volatile java.lang.reflect.Method setInstallStageMethod;
|
||||||
|
|
||||||
|
private static java.lang.reflect.Method method(String name, Class<?>... params) {
|
||||||
|
java.lang.reflect.Method m = null;
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(JFX_CLASS);
|
||||||
|
m = clazz.getMethod(name, params);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// JavaFX not available (CLI mode) or JFXLauncher not initialized.
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forward a game log line to the JFX UI. Safe no-op in CLI mode. */
|
||||||
|
public static void appendGameLog(String msg) {
|
||||||
|
if (appendGameLogMethod == null) {
|
||||||
|
appendGameLogMethod = method("appendGameLog", String.class);
|
||||||
|
}
|
||||||
|
if (appendGameLogMethod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
appendGameLogMethod.invoke(null, msg);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
appendGameLogMethod = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forward install progress to the JFX UI. Safe no-op in CLI mode. */
|
||||||
|
public static void setInstallProgress(String label, int current, int total) {
|
||||||
|
if (setInstallProgressMethod == null) {
|
||||||
|
setInstallProgressMethod = method("setInstallProgress", String.class, int.class, int.class);
|
||||||
|
}
|
||||||
|
if (setInstallProgressMethod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setInstallProgressMethod.invoke(null, label, current, total);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
setInstallProgressMethod = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forward install stage to the JFX UI. Safe no-op in CLI mode. */
|
||||||
|
public static void setInstallStage(String stageName, int stageIndex, int stageCount) {
|
||||||
|
if (setInstallStageMethod == null) {
|
||||||
|
setInstallStageMethod = method("setInstallStage", String.class, int.class, int.class);
|
||||||
|
}
|
||||||
|
if (setInstallStageMethod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setInstallStageMethod.invoke(null, stageName, stageIndex, stageCount);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
setInstallStageMethod = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package me.sashegdev.zernmc.launcher.utils;
|
package me.sashegdev.zernmc.launcher.utils;
|
||||||
|
|
||||||
|
import me.sashegdev.zernmc.launcher.ui.jfx.JFXBridge;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
public class ProgressBar {
|
public class ProgressBar {
|
||||||
@@ -14,11 +16,7 @@ public class ProgressBar {
|
|||||||
currentLabel = label;
|
currentLabel = label;
|
||||||
currentTotal = total;
|
currentTotal = total;
|
||||||
|
|
||||||
try {
|
JFXBridge.setInstallProgress(label, (int) current, (int) total);
|
||||||
Class<?> jfxClass = Class.forName("me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher");
|
|
||||||
java.lang.reflect.Method setProgress = jfxClass.getMethod("setInstallProgress", String.class, int.class, int.class);
|
|
||||||
setProgress.invoke(null, label, (int) current, (int) total);
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
if (total <= 0) {
|
if (total <= 0) {
|
||||||
System.out.print("\r" + ZAnsi.cyan(label) + " ...");
|
System.out.print("\r" + ZAnsi.cyan(label) + " ...");
|
||||||
@@ -40,11 +38,7 @@ public class ProgressBar {
|
|||||||
currentLabel = label;
|
currentLabel = label;
|
||||||
currentTotal = totalBytes;
|
currentTotal = totalBytes;
|
||||||
|
|
||||||
try {
|
JFXBridge.setInstallProgress(label + " " + formatBytes(downloaded) + "/" + formatBytes(totalBytes), (int) downloaded, (int) totalBytes);
|
||||||
Class<?> jfxClass = Class.forName("me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher");
|
|
||||||
java.lang.reflect.Method setProgress = jfxClass.getMethod("setInstallProgress", String.class, int.class, int.class);
|
|
||||||
setProgress.invoke(null, label + " " + formatBytes(downloaded) + "/" + formatBytes(totalBytes), (int) downloaded, (int) totalBytes);
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
if (totalBytes <= 0) {
|
if (totalBytes <= 0) {
|
||||||
System.out.print("\r" + ZAnsi.cyan(label) + " ...");
|
System.out.print("\r" + ZAnsi.cyan(label) + " ...");
|
||||||
@@ -71,11 +65,7 @@ public class ProgressBar {
|
|||||||
currentLabel = label;
|
currentLabel = label;
|
||||||
currentTotal = total;
|
currentTotal = total;
|
||||||
|
|
||||||
try {
|
JFXBridge.setInstallProgress(label, (int) current, (int) (total > 0 ? total : 100));
|
||||||
Class<?> jfxClass = Class.forName("me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher");
|
|
||||||
java.lang.reflect.Method setProgress = jfxClass.getMethod("setInstallProgress", String.class, int.class, int.class);
|
|
||||||
setProgress.invoke(null, label, (int) current, (int) (total > 0 ? total : 100));
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
if (total <= 0) {
|
if (total <= 0) {
|
||||||
char[] spinner = {'|', '/', '-', '\\'};
|
char[] spinner = {'|', '/', '-', '\\'};
|
||||||
@@ -92,11 +82,7 @@ public class ProgressBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setStage(String stageName, int stageIndex, int stageCount) {
|
public static void setStage(String stageName, int stageIndex, int stageCount) {
|
||||||
try {
|
JFXBridge.setInstallStage(stageName, stageIndex, stageCount);
|
||||||
Class<?> jfxClass = Class.forName("me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher");
|
|
||||||
java.lang.reflect.Method setStage = jfxClass.getMethod("setInstallStage", String.class, int.class, int.class);
|
|
||||||
setStage.invoke(null, stageName, stageIndex, stageCount);
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearLine() {
|
public static void clearLine() {
|
||||||
|
|||||||
+7
-7
@@ -18,8 +18,8 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.0.15</revision>
|
<revision>1.0.16</revision>
|
||||||
<hotfix>3</hotfix>
|
<hotfix>0</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>
|
||||||
@@ -74,31 +74,31 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-controls</artifactId>
|
<artifactId>javafx-controls</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-web</artifactId>
|
<artifactId>javafx-web</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-graphics</artifactId>
|
<artifactId>javafx-graphics</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-base</artifactId>
|
<artifactId>javafx-base</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-media</artifactId>
|
<artifactId>javafx-media</artifactId>
|
||||||
<version>23</version>
|
<version>23.0.1</version>
|
||||||
<classifier>win</classifier>
|
<classifier>win</classifier>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
+7
-2
@@ -1332,8 +1332,13 @@ def generate_launcher_builds_meta():
|
|||||||
rel_path = str(file_path.relative_to(BUILDS_DIR))
|
rel_path = str(file_path.relative_to(BUILDS_DIR))
|
||||||
|
|
||||||
# Only include runtime files needed for launcher updates
|
# Only include runtime files needed for launcher updates
|
||||||
# Skip: JRE, JavaFX, old ZIPs, extracted versions, docs, root JARs
|
# Skip: JRE, old ZIPs, extracted versions, docs, root JARs.
|
||||||
if rel_path.startswith(("jre21/", "lib/", "lib-javafx/", "versions/", "libs/")):
|
# lib/javafx/*-win.jar IS included so existing clients get the
|
||||||
|
# JavaFX update (e.g. 23 -> 23.0.1 WebKit WebSocket fix) via the
|
||||||
|
# incremental updater, not only through a full ZIP reinstall.
|
||||||
|
if rel_path.startswith(("jre21/", "lib-javafx/", "versions/", "libs/", "logs/")):
|
||||||
|
continue
|
||||||
|
if rel_path.startswith("lib/") and not rel_path.startswith("lib/javafx/"):
|
||||||
continue
|
continue
|
||||||
if rel_path.endswith(".zip") or rel_path in ("README.txt", "build.version"):
|
if rel_path.endswith(".zip") or rel_path in ("README.txt", "build.version"):
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user