diff --git a/.github/workflows/publication.yml b/.github/workflows/publication.yml index 15a9161..d141a2e 100644 --- a/.github/workflows/publication.yml +++ b/.github/workflows/publication.yml @@ -20,6 +20,8 @@ jobs: env: IS_PUBLICATION: true run: | + git clone -b maven https://github.com/ZekerZhayard/ForgeWrapper.git ./build/maven + rm -rf ./build/maven/.git/* chmod +x ./gradlew ./gradlew publish -iS - uses: actions/upload-artifact@v2 diff --git a/README.md b/README.md index d05b57d..a1e8557 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Allow [MultiMC](https://github.com/MultiMC/MultiMC5) to launch Minecraft 1.13+ w 3. Since ForgeWrapper 1.5.1, it no longer includes the json converter, so you need to build it by yourself: - [Download](https://github.com/ZekerZhayard/ForgeWrapper/archive/refs/heads/master.zip) ForgeWrapper sources. - Extract the zip and open terminal in the extracted folder. - - Run `./gradlew build` command in terminal and get the jar from `./converter/build/libs` + - Run `./gradlew converter:build` command in terminal and get the jar from `./converter/build/libs` 3. Run the below command in terminal: ``` java -jar --installer= [--instance=] diff --git a/build.gradle b/build.gradle index d975a08..b2bbbc5 100644 --- a/build.gradle +++ b/build.gradle @@ -16,10 +16,10 @@ archivesBaseName = rootProject.name configurations { provided { - compileOnly.extendsFrom provided + implementation.extendsFrom provided } multirelase { - compileOnly.extendsFrom multirelase + implementation.extendsFrom multirelase } } @@ -37,6 +37,7 @@ dependencies { compileOnly "net.minecraftforge:installer:2.1.4" compileOnly "net.sf.jopt-simple:jopt-simple:5.0.4" + provided project(":common") provided project(":legacy") multirelase project(":jigsaw") } @@ -56,7 +57,6 @@ jar { "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), "Automatic-Module-Name": "${project.group}.${project.archivesBaseName}".toString().toLowerCase(), "Multi-Release": "true", - "Main-Class": "io.github.zekerzhayard.forgewrapper.converter.Main", "GitCommit": String.valueOf(System.getenv("GITHUB_SHA")) ]) @@ -87,7 +87,6 @@ publishing { url = layout.buildDirectory.dir("maven") } } - } tasks.publish.dependsOn build diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..abd2997 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,11 @@ + +plugins { + id "java" + id "eclipse" + id "maven-publish" +} + +sourceCompatibility = targetCompatibility = 1.8 +compileJava { + sourceCompatibility = targetCompatibility = 1.8 +} diff --git a/common/src/main/java/io/github/zekerzhayard/forgewrapper/util/CheckedLambdaUtil.java b/common/src/main/java/io/github/zekerzhayard/forgewrapper/util/CheckedLambdaUtil.java new file mode 100644 index 0000000..c36b8b3 --- /dev/null +++ b/common/src/main/java/io/github/zekerzhayard/forgewrapper/util/CheckedLambdaUtil.java @@ -0,0 +1,40 @@ +package io.github.zekerzhayard.forgewrapper.util; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class CheckedLambdaUtil { + public static Consumer wrapConsumer(CheckedConsumer consumer) { + return consumer; + } + + public static BiConsumer wrapBiConsumer(CheckedBiConsumer biconsumer) { + return biconsumer; + } + + public interface CheckedConsumer extends Consumer { + void checkedAccept(T t) throws Throwable; + + @Override + default void accept(T t) { + try { + this.checkedAccept(t); + } catch (Throwable th) { + throw new RuntimeException(th); + } + } + } + + public interface CheckedBiConsumer extends BiConsumer { + void checkedAccept(T t, U u) throws Throwable; + + @Override + default void accept(T t, U u) { + try { + this.checkedAccept(t, u); + } catch (Throwable th) { + throw new RuntimeException(th); + } + } + } +} diff --git a/converter/build.gradle b/converter/build.gradle index 93be5a1..d12b3e4 100644 --- a/converter/build.gradle +++ b/converter/build.gradle @@ -8,9 +8,14 @@ version = "${rootProject.fw_version}${-> getVersionSuffix()}" group = "io.github.zekerzhayard" archivesBaseName = rootProject.name + "Converter" +sourceCompatibility = targetCompatibility = 1.8 +compileJava { + sourceCompatibility = targetCompatibility = 1.8 +} + configurations { provided { - compileOnly.extendsFrom provided + implementation.extendsFrom provided } } @@ -30,6 +35,7 @@ jar { ]) from configurations.provided.files.collect { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE zipTree(it) } } diff --git a/gradle.properties b/gradle.properties index a0a9a4a..3e2c6a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.daemon = false -fw_version = 1.5.1 +fw_version = 1.5.2 diff --git a/jigsaw/build.gradle b/jigsaw/build.gradle index a555ec3..08c394a 100644 --- a/jigsaw/build.gradle +++ b/jigsaw/build.gradle @@ -26,3 +26,7 @@ configurations { } } } + +dependencies { + compileOnly project(":common") +} 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 ce12029..3e12533 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 @@ -5,22 +5,23 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.module.Configuration; -import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; import java.lang.reflect.Field; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; +import io.github.zekerzhayard.forgewrapper.util.CheckedLambdaUtil; import sun.misc.Unsafe; public class ModuleUtil { @@ -46,39 +47,22 @@ private static MethodHandles.Lookup getImplLookup() { */ @SuppressWarnings("unchecked") public static void addModules(String modulePath) throws Throwable { - // Find all extra modules - ModuleFinder finder = ModuleFinder.of(Arrays.stream(modulePath.split(File.pathSeparator)).map(Paths::get).toArray(Path[]::new)); + // Find all extra modules, exclude all existing modules + ModuleFinder finder = ModuleFinder.of(Stream.of(modulePath.split(File.pathSeparator)).map(Paths::get).filter(p -> ModuleFinder.of(p).findAll().stream().noneMatch(mref -> ModuleLayer.boot().findModule(mref.descriptor().name()).isPresent())).toArray(Path[]::new)); MethodHandle loadModuleMH = IMPL_LOOKUP.findVirtual(Class.forName("jdk.internal.loader.BuiltinClassLoader"), "loadModule", MethodType.methodType(void.class, ModuleReference.class)); - // Resolve modules to a new config - Configuration config = Configuration.resolveAndBind(finder, List.of(ModuleLayer.boot().configuration()), finder, finder.findAll().stream().peek(mref -> { - try { - // Load all extra modules in system class loader (unnamed modules for now) - loadModuleMH.invokeWithArguments(ClassLoader.getSystemClassLoader(), mref); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - }).map(ModuleReference::descriptor).map(ModuleDescriptor::name).collect(Collectors.toList())); + // Resolve modules to a new config and load all extra modules in system class loader (unnamed modules for now) + Configuration config = Configuration.resolveAndBind(finder, List.of(ModuleLayer.boot().configuration()), finder, finder.findAll().stream().filter(mref -> !ModuleLayer.boot().findModule(mref.descriptor().name()).isPresent()).peek(CheckedLambdaUtil.wrapConsumer(mref -> loadModuleMH.invokeWithArguments(ClassLoader.getSystemClassLoader(), mref))).map(mref -> mref.descriptor().name()).collect(Collectors.toList())); // Copy the new config graph to boot module layer config MethodHandle graphGetter = IMPL_LOOKUP.findGetter(Configuration.class, "graph", Map.class); HashMap> graphMap = new HashMap<>((Map>) graphGetter.invokeWithArguments(config)); MethodHandle cfSetter = IMPL_LOOKUP.findSetter(ResolvedModule.class, "cf", Configuration.class); // Reset all extra resolved modules config to boot module layer config - graphMap.forEach((k, v) -> { - try { - cfSetter.invokeWithArguments(k, ModuleLayer.boot().configuration()); - v.forEach(m -> { - try { - cfSetter.invokeWithArguments(m, ModuleLayer.boot().configuration()); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - }); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - }); + graphMap.forEach(CheckedLambdaUtil.wrapBiConsumer((k, v) -> { + cfSetter.invokeWithArguments(k, ModuleLayer.boot().configuration()); + v.forEach(CheckedLambdaUtil.wrapConsumer(m -> cfSetter.invokeWithArguments(m, ModuleLayer.boot().configuration()))); + })); graphMap.putAll((Map>) graphGetter.invokeWithArguments(ModuleLayer.boot().configuration())); IMPL_LOOKUP.findSetter(Configuration.class, "graph", Map.class).invokeWithArguments(ModuleLayer.boot().configuration(), new HashMap<>(graphMap)); @@ -88,13 +72,13 @@ public static void addModules(String modulePath) throws Throwable { HashSet modulesSet = new HashSet<>(config.modules()); modulesSetter.invokeWithArguments(ModuleLayer.boot().configuration(), new HashSet<>(modulesSet)); - // Prepare to add all of the new config "nameToModule" to boot module layer config + // Prepare to add all the new config "nameToModule" to boot module layer config MethodHandle nameToModuleGetter = IMPL_LOOKUP.findGetter(Configuration.class, "nameToModule", Map.class); HashMap nameToModuleMap = new HashMap<>((Map) nameToModuleGetter.invokeWithArguments(ModuleLayer.boot().configuration())); nameToModuleMap.putAll((Map) nameToModuleGetter.invokeWithArguments(config)); IMPL_LOOKUP.findSetter(Configuration.class, "nameToModule", Map.class).invokeWithArguments(ModuleLayer.boot().configuration(), new HashMap<>(nameToModuleMap)); - // Define all extra modules and add all of the new config "nameToModule" to boot module layer config + // Define all extra modules and add all the new config "nameToModule" to boot module layer config ((Map) IMPL_LOOKUP.findGetter(ModuleLayer.class, "nameToModule", Map.class).invokeWithArguments(ModuleLayer.boot())).putAll((Map) IMPL_LOOKUP.findStatic(Module.class, "defineModules", MethodType.methodType(Map.class, Configuration.class, Function.class, ModuleLayer.class)).invokeWithArguments(ModuleLayer.boot().configuration(), (Function) name -> ClassLoader.getSystemClassLoader(), ModuleLayer.boot())); // Add all of resolved modules @@ -107,66 +91,56 @@ public static void addModules(String modulePath) throws Throwable { // Add reads from extra modules to jdk modules MethodHandle implAddReadsMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddReads", MethodType.methodType(void.class, Module.class)); - config.modules().forEach(rm -> ModuleLayer.boot().findModule(rm.name()).ifPresent(m -> oldBootModules.forEach(brm -> ModuleLayer.boot().findModule(brm.name()).ifPresent(bm -> { - try { - implAddReadsMH.invokeWithArguments(m, bm); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - })))); + config.modules().forEach(rm -> ModuleLayer.boot().findModule(rm.name()).ifPresent(m -> oldBootModules.forEach(brm -> ModuleLayer.boot().findModule(brm.name()).ifPresent(CheckedLambdaUtil.wrapConsumer(bm -> implAddReadsMH.invokeWithArguments(m, bm)))))); } - public static void addExports(List exports) throws Throwable { - MethodHandle implAddExportsMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddExports", MethodType.methodType(void.class, String.class, Module.class)); - MethodHandle implAddExportsToAllUnnamedMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddExportsToAllUnnamed", MethodType.methodType(void.class, String.class)); + public static void addExports(List exports) { + TypeToAdd.EXPORTS.implAdd(exports); + } - addExtra(exports, implAddExportsMH, implAddExportsToAllUnnamedMH); + public static void addOpens(List opens) { + TypeToAdd.OPENS.implAdd(opens); } - public static void addOpens(List opens) throws Throwable { - MethodHandle implAddOpensMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddOpens", MethodType.methodType(void.class, String.class, Module.class)); - MethodHandle implAddOpensToAllUnnamedMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddOpensToAllUnnamed", MethodType.methodType(void.class, String.class)); + private enum TypeToAdd { + EXPORTS("Exports"), + OPENS("Opens"); - addExtra(opens, implAddOpensMH, implAddOpensToAllUnnamedMH); - } + private final MethodHandle implAddMH; + private final MethodHandle implAddToAllUnnamedMH; - private static void addExtra(List extras, MethodHandle implAddExtraMH, MethodHandle implAddExtraToAllUnnamedMH) { - extras.forEach(extra -> { - ParserData data = parseModuleExtra(extra); - if (data != null) { - ModuleLayer.boot().findModule(data.module).ifPresent(m -> { - try { - if ("ALL-UNNAMED".equals(data.target)) { - implAddExtraToAllUnnamedMH.invokeWithArguments(m, data.packages); - } else { - ModuleLayer.boot().findModule(data.target).ifPresent(tm -> { - try { - implAddExtraMH.invokeWithArguments(m, data.packages, tm); - } catch (Throwable t) { - throw new RuntimeException(t); - } - }); - } - } catch (Throwable t) { - throw new RuntimeException(t); - } - }); + TypeToAdd(String name) { + try { + this.implAddMH = IMPL_LOOKUP.findVirtual(Module.class, "implAdd" + name, MethodType.methodType(void.class, String.class, Module.class)); + this.implAddToAllUnnamedMH = IMPL_LOOKUP.findVirtual(Module.class, "implAdd" + name + "ToAllUnnamed", MethodType.methodType(void.class, String.class)); + } catch (Throwable t) { + throw new RuntimeException(t); } - }); + } + + void implAdd(List extras) { + extras.stream().map(ModuleUtil::parseModuleExtra).filter(Optional::isPresent).map(Optional::get).forEach(CheckedLambdaUtil.wrapConsumer(data -> ModuleLayer.boot().findModule(data.module).ifPresent(CheckedLambdaUtil.wrapConsumer(m -> { + if ("ALL-UNNAMED".equals(data.target)) { + this.implAddToAllUnnamedMH.invokeWithArguments(m, data.packages); + } else { + ModuleLayer.boot().findModule(data.target).ifPresent(CheckedLambdaUtil.wrapConsumer(tm -> this.implAddMH.invokeWithArguments(m, data.packages, tm))); + } + })))); + } } // /= - private static ParserData parseModuleExtra(String extra) { + private static Optional parseModuleExtra(String extra) { String[] all = extra.split("=", 2); if (all.length < 2) { - return null; + return Optional.empty(); } String[] source = all[0].split("/", 2); if (source.length < 2) { - return null; + return Optional.empty(); } - return new ParserData(source[0], source[1], all[1]); + return Optional.of(new ParserData(source[0], source[1], all[1])); } private static class ParserData { @@ -182,14 +156,10 @@ private static class ParserData { } // ForgeWrapper need some extra settings to invoke BootstrapLauncher. - public static void setupBootstrapLauncher() throws Throwable { - MethodHandle implAddOpensMH = IMPL_LOOKUP.findVirtual(Module.class, "implAddOpens", MethodType.methodType(void.class, String.class, Module.class)); - ModuleLayer.boot().findModule("cpw.mods.bootstraplauncher").ifPresent(m -> { - try { - implAddOpensMH.invokeWithArguments(m, "cpw.mods.bootstraplauncher", ModuleUtil.class.getModule()); - } catch (Throwable t) { - throw new RuntimeException(t); - } - }); + public static Class setupBootstrapLauncher(Class mainClass) throws Throwable { + if (!mainClass.getModule().isOpen(mainClass.getPackageName(), ModuleUtil.class.getModule())) { + TypeToAdd.OPENS.implAddMH.invokeWithArguments(mainClass.getModule(), mainClass.getPackageName(), ModuleUtil.class.getModule()); + } + return mainClass; } } diff --git a/settings.gradle b/settings.gradle index 2d5aa2f..b15e75e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'ForgeWrapper' +include 'common' include 'converter' include 'jigsaw' include 'legacy' 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 98e8dda..2740b76 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java @@ -1,11 +1,8 @@ package io.github.zekerzhayard.forgewrapper.installer; import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; import io.github.zekerzhayard.forgewrapper.installer.util.ModuleUtil; @@ -14,7 +11,7 @@ public static void bootstrap(List jvmArgs, String minecraftJar, String l // Replace all placeholders List replacedJvmArgs = new ArrayList<>(); for (String arg : jvmArgs) { - replacedJvmArgs.add(arg.replace("${classpath}", System.getProperty("java.class.path").replace(File.separator, "/")).replace("${classpath_separator}", File.pathSeparator).replace("${library_directory}", libraryDir)); + replacedJvmArgs.add(arg.replace("${classpath}", System.getProperty("java.class.path").replace(File.separator, "/")).replace("${classpath_separator}", File.pathSeparator).replace("${library_directory}", libraryDir).replace("${version_name}", minecraftJar.substring(0, minecraftJar.lastIndexOf('.')))); } jvmArgs = replacedJvmArgs; @@ -47,21 +44,8 @@ public static void bootstrap(List jvmArgs, String minecraftJar, String l String[] prop = arg.substring(2).split("=", 2); if (prop[0].equals("ignoreList")) { - // The default ignoreList is too broad and may cause some problems, so we define it more precisely. - String[] ignores = (prop[1] + ",NewLaunch.jar,ForgeWrapper-," + minecraftJar).split(","); - List ignoreList = new ArrayList<>(); - for (String classPathName : System.getProperty("java.class.path").replace(File.separator, "/").split(File.pathSeparator)) { - Path classPath = Paths.get(classPathName); - String fileName = classPath.getFileName().toString(); - if (Stream.of(ignores).anyMatch(fileName::contains)) { - String absolutePath = classPath.toAbsolutePath().toString(); - if (absolutePath.contains(",")) { - absolutePath = absolutePath.substring(absolutePath.lastIndexOf(",")); - } - ignoreList.add(absolutePath.replace(File.separator, "/")); - } - } - System.setProperty(prop[0], String.join(",", ignoreList)); + // The default ignoreList may cause some problems, so we define it more precisely. + System.setProperty(prop[0], prop[1] + ",NewLaunch.jar,ForgeWrapper-," + minecraftJar); } else { System.setProperty(prop[0], prop[1]); } @@ -73,6 +57,5 @@ public static void bootstrap(List jvmArgs, String minecraftJar, String l } ModuleUtil.addExports(addExports); ModuleUtil.addOpens(addOpens); - ModuleUtil.setupBootstrapLauncher(); } } 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 99ead7c..2729ef5 100644 --- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java +++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java @@ -5,13 +5,13 @@ import java.net.URLClassLoader; import java.nio.file.Path; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import cpw.mods.modlauncher.Launcher; import io.github.zekerzhayard.forgewrapper.installer.detector.DetectorLoader; import io.github.zekerzhayard.forgewrapper.installer.detector.IFileDetector; +import io.github.zekerzhayard.forgewrapper.installer.util.ModuleUtil; public class Main { public static void main(String[] args) throws Throwable { @@ -49,7 +49,8 @@ public static void main(String[] args) throws Throwable { } } - Class.forName(detector.getMainClass(forgeFullVersion)).getMethod("main", String[].class).invoke(null, new Object[] { args }); + Class mainClass = ModuleUtil.setupBootstrapLauncher(Class.forName(detector.getMainClass(forgeFullVersion))); + mainClass.getMethod("main", String[].class).invoke(null, new Object[] { args }); } // https://github.com/MinecraftForge/Installer/blob/fe18a164b5ebb15b5f8f33f6a149cc224f446dc2/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java#L287-L303 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 2f8c976..2cb62cc 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 @@ -12,7 +12,6 @@ import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -20,7 +19,6 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; 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 0b37945..7461b50 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 @@ -15,7 +15,8 @@ public static void addOpens(List opens) { // nothing to do with Java 8 } - public static void setupBootstrapLauncher() { + public static Class setupBootstrapLauncher(Class mainClass) { // nothing to do with Java 8 + return mainClass; } }