Слияние ui -> main #1
+55
-24
@@ -63,13 +63,19 @@ public class LaunchCommandBuilder {
|
|||||||
// always use ModLauncher as main class (version.json might have
|
// always use ModLauncher as main class (version.json might have
|
||||||
// BootstrapLauncher which breaks with Java module system)
|
// BootstrapLauncher which breaks with Java module system)
|
||||||
if ("forge".equals(loaderType) || "neoforge".equals(loaderType)) {
|
if ("forge".equals(loaderType) || "neoforge".equals(loaderType)) {
|
||||||
// Forge: vanilla version jar on -cp creates automatic module _1._20._1
|
// Ensure securejarhandler can create the "minecraft" module:
|
||||||
// which conflicts with Forge's "minecraft" module → split-package error.
|
// It looks for versions/<versionId>/<versionId>.jar.
|
||||||
// Fix: use patched Forge client jar instead of vanilla version jar.
|
// The Forge installer only creates the JSON there, not the jar.
|
||||||
// The patched jar (forge-1.20.1-47.4.20-client.jar) is created by the
|
// We copy the vanilla version jar (e.g. versions/1.20.1/1.20.1.jar)
|
||||||
// Forge installer in libraries/ and contains all MC classes including
|
// to that location so securejarhandler finds it and creates the
|
||||||
// PreparableReloadListener. It is NOT listed in version.json manifest,
|
// "minecraft" module (identified by Automatic-Module-Name: minecraft
|
||||||
// so we find and add it separately.
|
// in the jar's MANIFEST.MF).
|
||||||
|
ensureVersionJarForForge();
|
||||||
|
|
||||||
|
// Build classpath from manifest libraries only (no version jar on -cp).
|
||||||
|
// Putting the vanilla jar on -cp would create automatic module _1._20._1
|
||||||
|
// (from filename 1.20.1.jar) which conflicts with the "minecraft" module
|
||||||
|
// created by securejarhandler → split-package ResolutionException.
|
||||||
String classpath;
|
String classpath;
|
||||||
if (manifest != null) {
|
if (manifest != null) {
|
||||||
classpath = buildClasspathFromManifest(manifest, false);
|
classpath = buildClasspathFromManifest(manifest, false);
|
||||||
@@ -80,21 +86,6 @@ public class LaunchCommandBuilder {
|
|||||||
classpath = buildVanillaClasspath();
|
classpath = buildVanillaClasspath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and add the patched Forge client jar
|
|
||||||
Path patchedJar = findPatchedForgeJar();
|
|
||||||
if (patchedJar != null) {
|
|
||||||
String sep = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
|
||||||
classpath = patchedJar.toAbsolutePath().toString() + sep + classpath;
|
|
||||||
System.out.println(ZAnsi.green(" Added patched Forge jar: " + patchedJar.getFileName()));
|
|
||||||
} else {
|
|
||||||
System.out.println(ZAnsi.yellow(" No patched Forge client jar found, falling back to vanilla version jar"));
|
|
||||||
Path versionJar = findVersionJar();
|
|
||||||
if (versionJar != null && isValidJar(versionJar)) {
|
|
||||||
String sep = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
|
||||||
classpath = versionJar.toAbsolutePath().toString() + sep + classpath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
command.add("-cp");
|
command.add("-cp");
|
||||||
String cpFile = writeClasspathFile(classpath);
|
String cpFile = writeClasspathFile(classpath);
|
||||||
command.add(cpFile);
|
command.add(cpFile);
|
||||||
@@ -102,8 +93,7 @@ public class LaunchCommandBuilder {
|
|||||||
// Add JVM arguments from version.json manifest as-is.
|
// Add JVM arguments from version.json manifest as-is.
|
||||||
// Forge's -p is a HARDCODED list of 8 bootstrap JARs (bootstraplauncher,
|
// Forge's -p is a HARDCODED list of 8 bootstrap JARs (bootstraplauncher,
|
||||||
// securejarhandler, asm-*, JarJarFileSystems) — NOT ${classpath}.
|
// securejarhandler, asm-*, JarJarFileSystems) — NOT ${classpath}.
|
||||||
// Do NOT intercept it. Version jar stays on -cp only (not -p),
|
// Do NOT intercept it.
|
||||||
// preventing _1._20._1 vs minecraft split-package conflicts.
|
|
||||||
if (manifest != null) {
|
if (manifest != null) {
|
||||||
Map<String, String> vars = buildVariableMap(options);
|
Map<String, String> vars = buildVariableMap(options);
|
||||||
vars.put("classpath", cpFile);
|
vars.put("classpath", cpFile);
|
||||||
@@ -657,6 +647,47 @@ public class LaunchCommandBuilder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the version jar exists at versions/<versionId>/<versionId>.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.
|
||||||
|
*
|
||||||
|
* 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/<versionId>/
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
private void ensureVersionJarForForge() throws IOException {
|
||||||
|
String versionId = getVersionId();
|
||||||
|
String mcVersion = instance.getMinecraftVersion();
|
||||||
|
Path versionsDir = instance.getPath().resolve("versions");
|
||||||
|
|
||||||
|
Path targetDir = versionsDir.resolve(versionId);
|
||||||
|
Path targetJar = targetDir.resolve(versionId + ".jar");
|
||||||
|
|
||||||
|
if (Files.exists(targetJar) && isValidJar(targetJar)) {
|
||||||
|
System.out.println(ZAnsi.green(" Version jar already exists: " + targetJar));
|
||||||
|
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));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.createDirectories(targetDir);
|
||||||
|
Files.copy(sourceJar, targetJar);
|
||||||
|
System.out.println(ZAnsi.green(" Copied version jar for securejarhandler: " + sourceJar.getFileName() + " → " + targetJar));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the patched Forge client jar in libraries/.
|
* Find the patched Forge client jar in libraries/.
|
||||||
* The Forge installer creates it via binarypatcher processor:
|
* The Forge installer creates it via binarypatcher processor:
|
||||||
|
|||||||
Reference in New Issue
Block a user