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 6990037..da05ce9 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 @@ -86,14 +86,19 @@ public class LaunchCommandBuilder { classpath = filterClasspathAgainstModulePath(classpath, manifest.getJvmArguments()); } command.add("-cp"); - command.add(writeClasspathFile(classpath)); + String cpFile = writeClasspathFile(classpath); + command.add(cpFile); // Add JVM arguments from version.json manifest // Includes -DignoreList, -p (module path), --add-modules, --add-opens, // and -DlibraryDirectory — all required for BootstrapLauncher to // resolve cpw.mods.securejarhandler as a module. + // Forge manifest JVM args also contain "-cp ${classpath}" — we resolve + // it here so the manifest's -cp takes precedence (avoids duplicate -cp + // issues and ensures the manifest controls the final classpath). if (manifest != null) { Map vars = buildVariableMap(options); + vars.put("classpath", cpFile); for (String arg : manifest.getJvmArguments()) { command.add(resolveVariable(arg, vars)); } @@ -500,8 +505,8 @@ public class LaunchCommandBuilder { // through Forge's own module system (securejarhandler + module path). // Having them on the classpath creates automatic modules that conflict // with Forge's own module layer (split-package ResolutionException). - if (isForgeLike && (lib.name.startsWith("net.minecraft:client") || lib.name.contains("minecraft"))) { - System.out.println(ZAnsi.cyan(" Skipping Minecraft lib: " + lib.name)); + if (isForgeLike && lib.name.startsWith("net.minecraft:")) { + System.out.println(ZAnsi.cyan(" Skipping Minecraft lib (loaded via module system): " + lib.name)); continue; } Path libPath = lib.artifactPath != null ? librariesDir.resolve(lib.artifactPath) : null; @@ -545,12 +550,22 @@ public class LaunchCommandBuilder { Path versionJar = findVersionJar(); if (versionJar != null) { - if (isValidJar(versionJar)) { - paths.add(0, versionJar.toAbsolutePath().toString()); - System.out.println(ZAnsi.green(" Added version jar: " + versionJar.getFileName())); + // For Forge/NeoForge: do NOT add version jar to classpath. + // Forge loads Minecraft classes through its own module system + // (securejarhandler + BootstrapLauncher module layer). Having the + // version jar on the classpath creates an automatic module (e.g. + // _1._20._1) that conflicts with Forge's "minecraft" module, + // causing ResolutionException split-package errors. + if (isForgeLike) { + System.out.println(ZAnsi.cyan(" Skipping version jar for Forge/NeoForge (loaded via module system): " + versionJar.getFileName())); } else { - System.out.println(ZAnsi.yellow(" Corrupt version jar, deleting: " + versionJar.getFileName())); - Files.delete(versionJar); + if (isValidJar(versionJar)) { + paths.add(0, versionJar.toAbsolutePath().toString()); + System.out.println(ZAnsi.green(" Added version jar: " + versionJar.getFileName())); + } else { + System.out.println(ZAnsi.yellow(" Corrupt version jar, deleting: " + versionJar.getFileName())); + Files.delete(versionJar); + } } }