diff --git a/converter/build.gradle b/converter/build.gradle deleted file mode 100644 index a3661b7..0000000 --- a/converter/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ - -plugins { - id "java" - id "eclipse" -} - -version = "${rootProject.fw_version}${-> getVersionSuffix()}" -group = "io.github.zekerzhayard" -archivesBaseName = rootProject.name + "Converter" - -sourceCompatibility = targetCompatibility = 1.8 -compileJava { - sourceCompatibility = targetCompatibility = 1.8 -} - -configurations { - provided { - implementation.extendsFrom provided - } -} - -repositories { - mavenCentral() -} - -dependencies { - compileOnly "com.google.code.gson:gson:2.8.7" - provided rootProject -} - -jar { - manifest.attributes rootProject.jar.manifest.attributes - manifest.attributes([ - "Main-Class": "io.github.zekerzhayard.forgewrapper.converter.Main" - ]) - - from configurations.provided.files.collect { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - zipTree(it) - } -} - -processResources { - inputs.property "version", project.version - from(sourceSets.main.resources.srcDirs) { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - include "patches/net.minecraftforge.json" - expand "version": project.version - } - from(sourceSets.main.resources.srcDirs) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - exclude "patches/net.minecraftforge.json" - } -} diff --git a/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Converter.java b/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Converter.java deleted file mode 100644 index e5771bc..0000000 --- a/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Converter.java +++ /dev/null @@ -1,174 +0,0 @@ -package io.github.zekerzhayard.forgewrapper.converter; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -public class Converter { - public static void convert(Path installerPath, Path targetDir, Path multimcDir) throws Exception { - JsonObject installer = getJsonFromZip(installerPath, "version.json"); - JsonObject installProfile = getJsonFromZip(installerPath, "install_profile.json"); - List arguments = getAdditionalArgs(installer); - String mcVersion = arguments.get(arguments.indexOf("--fml.mcVersion") + 1); - String forgeGroup = arguments.contains("--fml.forgeGroup") ? arguments.get(arguments.indexOf("--fml.forgeGroup") + 1) : "net.neoforged"; - String forgeVersion = arguments.get(arguments.indexOf("--fml.forgeVersion") + 1); - String forgeFullVersion = "forge-" + mcVersion + "-" + forgeVersion; - StringBuilder wrapperVersion = new StringBuilder(); - - JsonObject pack = convertPackJson(mcVersion, forgeGroup); - JsonObject patches = convertPatchesJson(installer, installProfile, mcVersion, forgeGroup, forgeVersion, wrapperVersion); - - Files.createDirectories(targetDir); - - // Copy mmc-pack.json and instance.cfg to folder. - Path instancePath = targetDir.resolve(forgeFullVersion); - Files.createDirectories(instancePath); - Files.copy(new ByteArrayInputStream(pack.toString().getBytes(StandardCharsets.UTF_8)), instancePath.resolve("mmc-pack.json"), StandardCopyOption.REPLACE_EXISTING); - Files.copy(new ByteArrayInputStream(("InstanceType=OneSix\nname=" + forgeFullVersion).getBytes(StandardCharsets.UTF_8)), instancePath.resolve("instance.cfg"), StandardCopyOption.REPLACE_EXISTING); - - // Copy ForgeWrapper to /libraries folder. - Path librariesPath = instancePath.resolve("libraries"); - Files.createDirectories(librariesPath); - Files.copy(Paths.get(Converter.class.getProtectionDomain().getCodeSource().getLocation().toURI()), librariesPath.resolve(wrapperVersion.toString()), StandardCopyOption.REPLACE_EXISTING); - - // Copy .json to /patches folder. - Path patchesPath = instancePath.resolve("patches"); - Files.createDirectories(patchesPath); - Files.copy(new ByteArrayInputStream(patches.toString().getBytes(StandardCharsets.UTF_8)), patchesPath.resolve(forgeGroup + ".json"), StandardCopyOption.REPLACE_EXISTING); - - // Copy forge installer to MultiMC/libraries//forge/- folder. - if (multimcDir != null) { - Path targetInstallerPath = multimcDir.resolve("libraries"); - for (String dir : forgeGroup.split("\\.")) - targetInstallerPath = targetInstallerPath.resolve(dir); - targetInstallerPath = targetInstallerPath.resolve("forge").resolve(mcVersion + "-" + forgeVersion); - Files.createDirectories(targetInstallerPath); - Files.copy(installerPath, targetInstallerPath.resolve(forgeFullVersion + "-installer.jar"), StandardCopyOption.REPLACE_EXISTING); - } - } - - public static List getAdditionalArgs(JsonObject installer) { - List args = new ArrayList<>(); - getElement(installer.getAsJsonObject("arguments"), "game").getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString())); - return args; - } - - public static JsonObject getJsonFromZip(Path path, String json) { - try { - ZipFile zf = new ZipFile(path.toFile()); - ZipEntry versionFile = zf.getEntry(json); - if (versionFile == null) { - throw new RuntimeException("The zip file is invalid!"); - } - InputStreamReader isr = new InputStreamReader(zf.getInputStream(versionFile), StandardCharsets.UTF_8); - return JsonParser.parseReader(isr).getAsJsonObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - // Convert mmc-pack.json: - // - Replace Minecraft version - // - Replace Forge package group - private static JsonObject convertPackJson(String mcVersion, String forgeGroup) { - JsonObject pack = JsonParser.parseReader(new InputStreamReader(Converter.class.getResourceAsStream("/mmc-pack.json"))).getAsJsonObject(); - - for (JsonElement component : getElement(pack, "components").getAsJsonArray()) { - JsonObject componentObject = component.getAsJsonObject(); - JsonElement version = getElement(componentObject, "version"); - if (!version.isJsonNull() && getElement(componentObject, "uid").getAsString().equals("net.minecraft")) { - componentObject.addProperty("version", mcVersion); - } else if (getElement(componentObject, "uid").getAsString().equals("{FORGE_GROUP}")) { - componentObject.addProperty("uid", forgeGroup); - } - } - return pack; - } - - // Convert patches/.json: - // - Add libraries - // - Add forge-launcher url - // - Replace Minecraft & Forge versions - // - Replace Forge package group - private static JsonObject convertPatchesJson(JsonObject installer, JsonObject installProfile, String mcVersion, String forgeGroup, String forgeVersion, StringBuilder wrapperVersion) { - JsonObject patches = JsonParser.parseReader(new InputStreamReader(Converter.class.getResourceAsStream("/patches/template.json"))).getAsJsonObject(); - JsonArray mavenFiles = getElement(patches, "mavenFiles").getAsJsonArray(); - JsonArray libraries = getElement(patches, "libraries").getAsJsonArray(); - - String minecraftArguments = String.join(" ", getElement(patches, "minecraftArguments").getAsString(), "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", String.join(" ", getAdditionalArgs(installer))).trim(); - patches.addProperty("minecraftArguments", minecraftArguments); - - String[] forgeGroupParts = forgeGroup.split("\\."); - String[] forgeURLParts = new String[forgeGroupParts.length]; - for (int i = 0; i < forgeURLParts.length; i++) - forgeURLParts[i] = forgeGroupParts[forgeGroupParts.length - i - 1]; - String mavenURL = "https://maven." + String.join(".", forgeURLParts); - - for (JsonElement mavenFile : mavenFiles) { - JsonObject mavenJsonObj = mavenFile.getAsJsonObject(); - String name = getElement(mavenJsonObj, "name").getAsString(); - mavenJsonObj.addProperty("name", name.replace("{FORGE_GROUP}", forgeGroup).replace("{VERSION}", mcVersion).replace("{FORGE_VERSION}", forgeVersion)); - mavenJsonObj.addProperty("url", mavenURL); - } - for (JsonElement lib : libraries) { - String name = getElement(lib.getAsJsonObject(), "name").getAsString(); - if (name.startsWith("io.github.zekerzhayard:ForgeWrapper:")) { - wrapperVersion.append(getElement(lib.getAsJsonObject(), "MMC-filename").getAsString()); - } - } - Map additionalUrls = new HashMap<>(); - String path = String.format("%s/forge/%s-%s/forge-%s-%s", String.join("/", forgeGroupParts), mcVersion, forgeVersion, mcVersion, forgeVersion); - additionalUrls.put(path + "-universal.jar", mavenURL + "/" + path + "-universal.jar"); - transformLibraries(getElement(installProfile, "libraries").getAsJsonArray(), mavenFiles, additionalUrls); - additionalUrls.clear(); - additionalUrls.put(path + ".jar", mavenURL + "/" + path + "-launcher.jar"); - transformLibraries(getElement(installer, "libraries").getAsJsonArray(), libraries, additionalUrls); - - patches.addProperty("uid", forgeGroup); - patches.addProperty("version", forgeVersion); - for (JsonElement require : getElement(patches, "requires").getAsJsonArray()) { - JsonObject requireObject = require.getAsJsonObject(); - if (getElement(requireObject, "uid").getAsString().equals("net.minecraft")) { - requireObject.addProperty("equals", mcVersion); - } - } - return patches; - } - - private static JsonElement getElement(JsonObject object, String property) { - Optional> first = object.entrySet().stream().filter(e -> e.getKey().equals(property)).findFirst(); - if (first.isPresent()) { - return first.get().getValue(); - } - return JsonNull.INSTANCE; - } - - private static void transformLibraries(JsonArray source, JsonArray target, Map additionalUrls) { - for (JsonElement lib : source) { - JsonObject artifact = getElement(getElement(lib.getAsJsonObject(), "downloads").getAsJsonObject(), "artifact").getAsJsonObject(); - String path = getElement(artifact, "path").getAsString(); - if (additionalUrls.containsKey(path)) { - artifact.getAsJsonObject().addProperty("url", additionalUrls.get(path)); - } - target.add(lib); - } - } -} diff --git a/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Main.java b/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Main.java deleted file mode 100644 index 83ecdf9..0000000 --- a/converter/src/main/java/io/github/zekerzhayard/forgewrapper/converter/Main.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.zekerzhayard.forgewrapper.converter; - -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; - -public class Main { - public static void main(String[] args) { - Path installer = null, instance = Paths.get("."), multimc = null; - try { - HashMap argsMap = parseArgs(args); - installer = Paths.get(argsMap.get("--installer")); - if (argsMap.containsKey("--instance")) { - instance = Paths.get(argsMap.get("--instance")); - multimc = instance.getParent(); - } - } catch (Exception e) { - System.out.println("Invalid arguments! Use: java -jar --installer= [--instance=]"); - throw new RuntimeException(e); - } - - try { - URLClassLoader ucl = URLClassLoader.newInstance(new URL[] { - Converter.class.getProtectionDomain().getCodeSource().getLocation(), - installer.toUri().toURL() - }, null); - ucl.loadClass("io.github.zekerzhayard.forgewrapper.converter.Converter").getMethod("convert", Path.class, Path.class, Path.class).invoke(null, installer, instance, multimc); - System.out.println("Successfully install Forge for MultiMC!"); - } catch (Exception e) { - System.out.println("Failed to install Forge!"); - throw new RuntimeException(e); - } - } - - /** - * @return installer -- The path of forge installer.
- * instance -- The instance folder of MultiMC.
- * cursepack -- The version of cursepacklocator.
- */ - private static HashMap parseArgs(String[] args) { - HashMap map = new HashMap<>(); - for (String arg : args) { - String[] params = arg.split("=", 2); - map.put(params[0], params[1]); - } - if (!map.containsKey("--installer")) { - throw new IllegalArgumentException(); - } - return map; - } -} diff --git a/converter/src/main/resources/mmc-pack.json b/converter/src/main/resources/mmc-pack.json deleted file mode 100644 index 51ba0bf..0000000 --- a/converter/src/main/resources/mmc-pack.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "formatVersion": 1, - "components": [ - { - "important": true, - "uid": "net.minecraft", - "version": "{VERSION}" - }, - { - "uid": "{FORGE_GROUP}" - } - ] -} diff --git a/converter/src/main/resources/patches/template.json b/converter/src/main/resources/patches/template.json deleted file mode 100644 index 5a46916..0000000 --- a/converter/src/main/resources/patches/template.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "formatVersion": 1, - "mainClass": "io.github.zekerzhayard.forgewrapper.installer.Main", - "minecraftArguments": "", - "name": "Forge", - "requires": [ - { - "equals": "{VERSION}", - "uid": "net.minecraft" - } - ], - "type": "release", - "uid": "{FORGE_GROUP}", - "version": "{FORGE_VERSION}", - "mavenFiles": [ - { - "name": "{FORGE_GROUP}:forge:{VERSION}-{FORGE_VERSION}:installer", - "url": "{MAVEN_URL}" - } - ], - "libraries": [ - { - "name": "io.github.zekerzhayard:ForgeWrapper:${version}", - "MMC-hint": "local", - "MMC-filename": "ForgeWrapper-${version}.jar" - } - ] -} diff --git a/gradle.properties b/gradle.properties index f1db528..4fb1212 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.daemon = false -fw_version = 1.5.7 +fw_version = 1.5.8 diff --git a/jigsaw/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java b/jigsaw/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java index 9a49d7f..e15d39c 100644 --- a/jigsaw/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java +++ b/jigsaw/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java @@ -9,6 +9,7 @@ import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; import java.lang.reflect.Field; +import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; @@ -159,6 +160,15 @@ private static class ParserData { } } + public static void setupClassPath(Path libraryDir, List paths) throws Throwable { + Class urlClassPathClass = Class.forName("jdk.internal.loader.URLClassPath"); + Object ucp = IMPL_LOOKUP.findGetter(Class.forName("jdk.internal.loader.BuiltinClassLoader"), "ucp", urlClassPathClass).invokeWithArguments(ClassLoader.getSystemClassLoader()); + MethodHandle addURLMH = IMPL_LOOKUP.findVirtual(urlClassPathClass, "addURL", MethodType.methodType(void.class, URL.class)); + for (String path : paths) { + addURLMH.invokeWithArguments(ucp, libraryDir.resolve(path).toUri().toURL()); + } + } + // ForgeWrapper need some extra settings to invoke BootstrapLauncher. public static Class setupBootstrapLauncher(Class mainClass) throws Throwable { if (!mainClass.getModule().isOpen(mainClass.getPackageName(), ModuleUtil.class.getModule())) { diff --git a/settings.gradle b/settings.gradle index 7bda9a2..34c563d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ rootProject.name = 'ForgeWrapper' include 'common' -include 'converter' include 'legacy' include 'jigsaw' diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java index 2740b76..3604371 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java @@ -15,6 +15,15 @@ public static void bootstrap(List jvmArgs, String minecraftJar, String l } jvmArgs = replacedJvmArgs; + // Remove NewLaunch.jar from property to prevent Forge from adding it to the module path + StringBuilder newCP = new StringBuilder(); + for (String path : System.getProperty("java.class.path").split(File.pathSeparator)) { + if (!path.endsWith("NewLaunch.jar")) { + newCP.append(path).append(File.pathSeparator); + } + } + System.setProperty("java.class.path", newCP.substring(0, newCP.length() - 1)); + String modulePath = null; List addExports = new ArrayList<>(); List addOpens = new ArrayList<>(); diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java index 5ecd971..37876a6 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java @@ -62,6 +62,7 @@ public static void main(String[] args) throws Throwable { } } + ModuleUtil.setupClassPath(detector.getLibraryDir(), detector.getExtraLibraries(forgeGroup, forgeArtifact, forgeFullVersion)); Class mainClass = ModuleUtil.setupBootstrapLauncher(Class.forName(detector.getMainClass(forgeGroup, forgeArtifact, forgeFullVersion))); mainClass.getMethod("main", String[].class).invoke(null, new Object[] { args }); } diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/detector/IFileDetector.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/detector/IFileDetector.java index b69e9b8..67d3e1d 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/detector/IFileDetector.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/detector/IFileDetector.java @@ -94,7 +94,7 @@ default Path getMinecraftJar(String mcVersion) { */ default List getJvmArgs(String forgeGroup, String forgeArtifact, String forgeFullVersion) { return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> { - JsonElement element = getElement(e.getAsJsonObject().getAsJsonObject("arguments"), "jvm"); + JsonElement element = e.getAsJsonObject().get("arguments").getAsJsonObject().get("jvm"); List args = new ArrayList<>(); if (!element.equals(JsonNull.INSTANCE)) { element.getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString())); @@ -103,6 +103,19 @@ default List getJvmArgs(String forgeGroup, String forgeArtifact, String }); } + default List getExtraLibraries(String forgeGroup, String forgeArtifact, String forgeFullVersion) { + return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> { + List paths = new ArrayList<>(); + e.getAsJsonObject().getAsJsonArray("libraries").iterator().forEachRemaining(je -> { + JsonObject artifact = je.getAsJsonObject().get("downloads").getAsJsonObject().get("artifact").getAsJsonObject(); + if (artifact.get("url").getAsString().isEmpty()) { + paths.add(artifact.get("path").getAsString()); + } + }); + return paths; + }); + } + /** * @param forgeGroup Forge package group (e.g. net.minecraftforge). * @param forgeArtifact Forge package artifact (e.g. forge). @@ -162,7 +175,7 @@ default boolean checkExtraFiles(String forgeGroup, String forgeArtifact, String // Get all "data//client" elements. Pattern artifactPattern = Pattern.compile("^\\[(?[^:]*):(?[^:]*):(?[^:@]*)(:(?[^@]*))?(@(?[^]]*))?]$"); for (Map.Entry entry : jo.entrySet()) { - String clientStr = getElement(entry.getValue().getAsJsonObject(), "client").getAsString(); + String clientStr = entry.getValue().getAsJsonObject().get("client").getAsString(); if (entry.getKey().endsWith("_SHA")) { Pattern p = Pattern.compile("^'(?[A-Za-z0-9]{40})'$"); Matcher m = p.matcher(clientStr); @@ -181,7 +194,7 @@ default boolean checkExtraFiles(String forgeGroup, String forgeArtifact, String .resolve(groupId.replace('.', File.separatorChar)) .resolve(artifactId) .resolve(version) - .resolve(artifactId + "-" + version + (classifier.equals("") ? "" : "-") + classifier + "." + type).toAbsolutePath()); + .resolve(artifactId + "-" + version + (classifier.isEmpty() ? "" : "-") + classifier + "." + type).toAbsolutePath()); } } } @@ -208,21 +221,13 @@ default boolean checkExtraFiles(String forgeGroup, String forgeArtifact, String * @return True represents the file is ready. */ static boolean checkExtraFile(Path path, String sha1) { - return sha1 == null || sha1.equals("") || (isFile(path) && sha1.toLowerCase(Locale.ENGLISH).equals(getFileSHA1(path))); + return sha1 == null || sha1.isEmpty() || (isFile(path) && sha1.toLowerCase(Locale.ENGLISH).equals(getFileSHA1(path))); } static boolean isFile(Path path) { return path != null && Files.isRegularFile(path); } - static JsonElement getElement(JsonObject object, String property) { - Optional> first = object.entrySet().stream().filter(e -> e.getKey().equals(property)).findFirst(); - if (first.isPresent()) { - return first.get().getValue(); - } - return JsonNull.INSTANCE; - } - static String getFileSHA1(Path path) { try { StringBuilder sha1 = new StringBuilder(new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(path))).toString(16)); diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java index c89d714..9eee574 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java @@ -1,5 +1,10 @@ package io.github.zekerzhayard.forgewrapper.installer.util; +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; import java.util.List; public class ModuleUtil { @@ -15,6 +20,14 @@ public static void addOpens(List opens) { // nothing to do with Java 8 } + public static void setupClassPath(Path libraryDir, List paths) throws Throwable { + Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addURLMethod.setAccessible(true); + for (String path : paths) { + addURLMethod.invoke(ClassLoader.getSystemClassLoader(), libraryDir.resolve(path).toUri().toURL()); + } + } + public static Class setupBootstrapLauncher(Class mainClass) { // nothing to do with Java 8 return mainClass;