Hang on close (JFX freezes on exit):
- LaunchService: replace process.onExit().thenRun() — it completes on the common
ForkJoinPool whose non-daemon workers keep the JVM alive (the v1.0.14.2 regression
re-introduced in v1.0.14.3's sendBounded) — with a daemon watcher thread that blocks
on process.waitFor(). Removes ForkJoinPool pinning.
- JFXLauncher: HttpServer executor now uses daemon thread factory; installThread is daemon.
- ZHttpClient: drop the non-daemon CachedThreadPool executor and restore synchronous
client.send() with request-level timeout, undoing the v1.0.14.3 sendAsync().get() that
submitted work to a non-daemon pool.
Hang / no game launch (Forge/NeoForge):
- LaunchCommandBuilder: remove the self-invented -DlegacyClassPath injection. Modern
Forge derives the MC-BOOTSTRAP layer from the module path / the ${classpath}
substitution that AstralRinth and the 1.0.13 launcher use; a redundant -DlegacyClassPath
duplicated every library and broke the bootstrap. Now relies solely on ${classpath}
substitution from version.json (only built when referenced by the version.json).
- update LaunchCommandBuilderTest to assert the corrected contract (no -DlegacyClassPath,
${classpath} substituted into -p).
Version bump to new revision (hotfix=0): revision 1.0.14 -> 1.0.15.
Build: mvn -pl launcher package -> ZernMC-win-1.0.15.0.zip, build.version=1.0.15.0.
Signed-off-by: SashegDev <gdsasheg@gmail.com>
- ZHttpClient: single direct attempt per resource, then immediate retry via /proxy/download (getWithSmartProxy, downloadFileWithSmartProxy)
- getRetry() for server proxy endpoints incl. transient 502/5xx; isRetryableError classification
- MAX_FAILS_BEFORE_PROXY 1 -> service switches to proxy-first after first failure
- detectService: files.minecraftforge.net routed through proxy
- repairMissingLibraries/repairLibrariesFromJson: re-download missing libs (incl. maven-coordinates format with -v2 fallback)
- JFX: force Mojang service check synchronously before install if network init not finished
- Forge/NeoForge: installer + downloads via ZHttpClient (no more bare HttpClient without request timeout), NeoForge subprocess 10-min timeout
- Fabric: pre-download loader libs via proxy, repair after install
findVersionJar() falls back to versions/1.20.1/1.20.1.jar when the Forge
version jar doesnt exist. Filename 1.20.1.jar produces automatic module _1._20._1
which conflicts with module minecraft (split-package on net.minecraft.server).
Fix: ensureVersionJarForForge() copies vanilla jar to versions/<versionId>/
<versionId>.jar before findVersionJar() is called. Vanilla jar has
Automatic-Module-Name: minecraft so it becomes module minecraft, not _1._20._1.
Root cause: our launcher was fighting with version.json by manually constructing
-p, -cp, --add-modules, --add-opens, -DignoreList, and -DlibraryDirectory.
AstralRinth passes version.json JVM args through with placeholder substitution,
then appends memory/GC/custom args.
Changes:
- VersionManifest: add getAllJvmArguments() and getAllGameArguments() that merge
parent (vanilla) args with child (forge) args
- LaunchCommandBuilder.build(): rewrite Forge/NeoForge branch to parse ALL
version.json JVM args and game args with placeholder substitution
- Removed: ensureVersionJarForForge(), findSrgClientJar(),
filterClasspathAgainstModulePath(), manual -Djava.library.path for Forge
- Classpath: built from version.json libraries + client jar (first position)
- Memory/GC args appended AFTER version.json args (like AstralRinth)
- Fallback to manual args if version.json has no JVM/game args
ensureVersionJarForForge() was copying the vanilla Mojang jar
(obfuscated class names) to versions/<forgeId>/<forgeId>.jar.
securejarhandler creates the minecraft module from this jar, but
obfuscated names dont match SRG names like PreparableReloadListener.
The package is claimed by the module but class is not found → crash.
Now prioritizes the SRG-mapped client jar from Forge libraries
(client-*-srg.jar) which has Automatic-Module-Name: minecraft and
correct SRG class names. Falls back to vanilla jar only if SRG jar
is not found.
Also removed redundant -DlibraryDirectory already in manifest args.
The forge patched client jar (forge-<version>-client.jar) contains all
Minecraft classes with Automatic-Module-Name: minecraft, but it is NOT
listed in the version JSON libraries array - so buildClasspathFromManifest
does not include it on the -cp. FML discovers it by scanning the library
directory, but securejarhandler only processes classpath entries to create
named modules.
This fix explicitly finds and prepends forge-<version>-client.jar to the
classpath when launching Forge/NeoForge, so securejarhandler can create
the minecraft module from it.
Instead of copying the vanilla jar to versions/<versionId>/ and adding it to
-cp (Fix 9/10), we now simply remove forge- from the -DignoreList pattern.
This allows securejarhandler to process forge-1.20.1-47.4.20-client.jar on
the classpath, which has Automatic-Module-Name: minecraft in its MANIFEST,
and create the minecraft module from it.
The forge client jar contains all patched MC classes, so PreparableReloadListener
and all other vanilla classes are accessible in the minecraft module.
No duplicate module creation, no split-package errors.
The copied version jar (1.20.1-forge-47.4.20.jar) is now on -cp so
securejarhandler can scan it and create the minecraft module. The jar
name does not match the ignoreList pattern forge- (starts with 1),
so it is included in the module layer. This ensures PreparableReloadListener
and other vanilla classes are findable in the minecraft module.