From 1d069811e361dc17eb2fd51b22ca1a8ccf0ca353 Mon Sep 17 00:00:00 2001 From: SashegDev Date: Mon, 13 Jul 2026 13:32:34 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Forge/NeoForge=20split-package=20Resolut?= =?UTF-8?q?ionException=20=E2=80=94=20skip=20version=20jar=20on=20classpat?= =?UTF-8?q?h,=20fix=20${classpath}=20variable=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../launch/LaunchCommandBuilder.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 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 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); + } } }