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.
securejarhandler creates the minecraft module from versions/<versionId>/<versionId>.jar.
The Forge installer only creates the JSON there, not the jar. Without the jar,
securejarhandler cannot create the minecraft module, causing ClassNotFoundException
for PreparableReloadListener (a vanilla MC class expected in the minecraft module).
Fix: copy the vanilla version jar (e.g. versions/1.20.1/1.20.1.jar) to the
Forge version directory (e.g. versions/1.20.1-forge-47.4.20/1.20.1-forge-47.4.20.jar)
before launch. The jar is NOT put on -cp to prevent automatic module _1._20._1
from being created, which would conflict with the minecraft module (split-package).
Also removed patched forge client jar from -cp since securejarhandler excludes it
via -DignoreList (forge-* pattern).
Vanilla 1.20.1.jar on -cp creates automatic module _1._20._1 which
conflicts with Forge minecraft module. Use forge-*-client.jar from
libraries/ instead (contains all MC classes). Falls back to vanilla.
Forge manifest -p is 8 hardcoded bootstrap JARs using ${library_directory},
not ${classpath}. Intercepting it replaced those with ALL libraries causing
duplicate module errors. Version jar stays on -cp only (not -p).
- Build two separate classpaths: modulePath (libraries only) and classpath (libraries + version jar)
- Intercept manifest -p to use modulePath (no version jar) preventing _1._20._1 vs minecraft module conflict
- Intercept manifest -cp to skip (our explicit -cp already added)
- Fix --version to use getVersionId() instead of instance.getName()
Forge version.json uses -p ${classpath} to set the module path to the same
argfile as the classpath. Previously we filtered net.minecraft:* libs from the
classpath string, which meant they were absent from the module path too, causing
ClassNotFoundException for PreparableReloadListener and other MC classes.
Now net.minecraft:* libs are included in the Forge classpath argfile so
securejarhandler can load them as modules.
- Read version from bin/.version file (reliable, no JAR locking)
- Save version to bin/.version when downloading JAR
- Use getLauncherJar() for all JAR path references
- Create binDir in main()
- Remove build.version dependency completely
- Read version from bin/.version file (reliable, no JAR locking issues)
- Save version to bin/.version when downloading JAR
- Remove complex JAR/ZIP reading code
- Use simple file-based version storage
- Read version only from JAR manifest (Implementation-Version)
- Remove all VERSION_FILE references from Bootstrap
- Remove build.version from scanLocalFiles() and update methods
- Rewrite getLauncherMeta() to properly parse server meta response
- Change downloadUpdate() fallback to JAR-only (not ZIP) to avoid JRE lock issues
- Simplify downloadUpdateLegacy() to skip ZIP (which locks JRE files)
- Add handling for AccessDeniedException when updating locked files
- Improve error logging for meta parsing failures
- 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
- Fix CLI arrow keys: remove 50ms timeout in escape sequence handling (ArrowMenu, LoginMenu)
- Add network logs polling to UI via /api/logs endpoint
- Display user role in launcher header (AuthManager, AuthService, JFXLauncher, UI)
- Capture and display game logs in launcher via /api/game-logs endpoint
- Fix demo mode bug in VersionManifest.ruleMatches() - was incorrectly adding --demo flag
- Fix modloader launch: pass proper auth info (accessToken, uuid) from AuthManager
- Add game log capture in MinecraftLib and LaunchService