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; + } } /**