From 03a1a1533ed78c0b539a726cca618b37203b69be Mon Sep 17 00:00:00 2001 From: SashegDev Date: Tue, 14 Jul 2026 07:27:48 +0000 Subject: [PATCH] fix: use SRG client jar for securejarhandler minecraft module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensureVersionJarForForge() was copying the vanilla Mojang jar (obfuscated class names) to versions//.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. --- .../launch/LaunchCommandBuilder.java | 68 ++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) 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 f386580..6dd063c 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 @@ -125,7 +125,6 @@ public class LaunchCommandBuilder { } } - command.add("-DlibraryDirectory=" + instance.getPath().resolve("libraries").toAbsolutePath()); command.add(getVanillaMainClass()); command.addAll(getVanillaGameArguments(options)); command.addAll(getModloaderLaunchArgs()); @@ -674,17 +673,15 @@ public class LaunchCommandBuilder { * Ensure the version jar exists at versions//.jar * so that securejarhandler can create the "minecraft" module from it. * - * The Forge installer only creates the JSON at that path, not the jar. - * We copy the vanilla version jar (e.g. versions/1.20.1/1.20.1.jar) there. + * The version jar MUST contain SRG-mapped classes (not obfuscated Mojang + * names) and have Automatic-Module-Name: minecraft in its MANIFEST.MF. + * Without this, securejarhandler creates an automatic module from the + * filename, claims the net.minecraft.server.packs.resources package, but + * doesn't have SRG-named classes like PreparableReloadListener → crash. * - * securejarhandler reads Automatic-Module-Name: minecraft from the jar's - * MANIFEST.MF and creates a named "minecraft" module. This is the module - * that Forge's code expects classes like PreparableReloadListener to be in. - * - * Critical: the jar is NOT put on -cp. Only the copy at versions// - * is used by securejarhandler. The original 1.20.1.jar must stay off the - * classpath to prevent automatic module _1._20._1 from being created, - * which would conflict with the "minecraft" module (split-package error). + * Priority: + * 1. SRG-mapped client jar from Forge libraries (client-*-srg.jar) + * 2. Vanilla version jar (fallback, may cause module issues) */ private void ensureVersionJarForForge() throws IOException { String versionId = getVersionId(); @@ -699,16 +696,53 @@ public class LaunchCommandBuilder { return; } - // Source: the vanilla version jar - Path sourceJar = versionsDir.resolve(mcVersion).resolve(mcVersion + ".jar"); - if (!Files.exists(sourceJar) || !isValidJar(sourceJar)) { - System.out.println(ZAnsi.yellow(" Vanilla version jar not found at " + sourceJar)); + Files.createDirectories(targetDir); + + // Priority 1: SRG-mapped client jar from Forge libraries + // This jar has Automatic-Module-Name: minecraft and correct SRG class names + Path srgJar = findSrgClientJar(); + if (srgJar != null && isValidJar(srgJar)) { + Files.copy(srgJar, targetJar); + System.out.println(ZAnsi.green(" Copied SRG client jar for securejarhandler: " + srgJar.getFileName() + " → " + targetJar)); + return; + } + + // Fallback: vanilla version jar (obfuscated names, may cause module issues) + Path sourceJar = versionsDir.resolve(mcVersion).resolve(mcVersion + ".jar"); + if (!Files.exists(sourceJar) || !isValidJar(sourceJar)) { + System.out.println(ZAnsi.yellow(" No suitable version jar found (SRG or vanilla)")); return; } - Files.createDirectories(targetDir); Files.copy(sourceJar, targetJar); - System.out.println(ZAnsi.green(" Copied version jar for securejarhandler: " + sourceJar.getFileName() + " → " + targetJar)); + System.out.println(ZAnsi.yellow(" Copied vanilla jar (fallback) for securejarhandler: " + sourceJar.getFileName() + " → " + targetJar)); + } + + /** + * Find the SRG-mapped client jar in Forge libraries. + * Path: libraries/net/minecraftforge/client/-/client---srg.jar + * This jar contains SRG-mapped classes and has Automatic-Module-Name: minecraft. + */ + private Path findSrgClientJar() { + Path librariesDir = instance.getPath().resolve("libraries"); + Path mcDir = librariesDir.resolve("net/minecraftforge/client"); + if (!Files.exists(mcDir)) return null; + + String mcVersion = instance.getMinecraftVersion(); + try (var stream = Files.list(mcDir)) { + return stream + .filter(Files::isDirectory) + .filter(dir -> dir.getFileName().toString().startsWith(mcVersion + "-")) + .flatMap(dir -> { + try { return Files.list(dir); } catch (Exception e) { return java.util.stream.Stream.empty(); } + }) + .filter(p -> p.getFileName().toString().endsWith("-srg.jar")) + .filter(this::isValidJar) + .findFirst() + .orElse(null); + } catch (Exception e) { + return null; + } } /**