summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.gradle11
-rw-r--r--common/build.gradle11
-rw-r--r--common/src/main/java/io/github/zekerzhayard/forgewrapper/util/CheckedLambdaUtil.java40
-rw-r--r--gradle.properties2
-rw-r--r--jigsaw/build.gradle4
-rw-r--r--jigsaw/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/ModuleUtil.java60
-rw-r--r--legacy/build.gradle22
-rw-r--r--legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/AbstractInstaller.java12
-rw-r--r--legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV0.java37
-rw-r--r--settings.gradle2
-rw-r--r--src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java19
-rw-r--r--src/main/java/io/github/zekerzhayard/forgewrapper/installer/Installer.java173
-rw-r--r--src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java61
-rw-r--r--src/main/java/io/github/zekerzhayard/forgewrapper/installer/detector/IFileDetector.java182
-rw-r--r--src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV1.java38
15 files changed, 246 insertions, 428 deletions
diff --git a/build.gradle b/build.gradle
index 45e6dee5d7..b0bcf5df22 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,9 +15,6 @@ group = "io.github.zekerzhayard"
archivesBaseName = rootProject.name
configurations {
- provided {
- implementation.extendsFrom provided
- }
multirelase {
implementation.extendsFrom multirelase
}
@@ -34,11 +31,9 @@ repositories {
dependencies {
compileOnly "com.google.code.gson:gson:2.8.7"
compileOnly "cpw.mods:modlauncher:8.0.9"
- compileOnly "net.minecraftforge:installer:2.1.9"
+ compileOnly "net.minecraftforge:installer:2.2.7"
compileOnly "net.sf.jopt-simple:jopt-simple:5.0.4"
- provided project(":common")
- provided project(":legacy")
multirelase project(":jigsaw")
}
@@ -60,10 +55,6 @@ jar {
"GitCommit": String.valueOf(System.getenv("GITHUB_SHA"))
])
- from configurations.provided.files.collect {
- zipTree(it)
- }
-
into "META-INF/versions/9", {
from configurations.multirelase.files.collect {
zipTree(it)
diff --git a/common/build.gradle b/common/build.gradle
deleted file mode 100644
index abd2997bba..0000000000
--- a/common/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-
-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
deleted file mode 100644
index c36b8b33a7..0000000000
--- a/common/src/main/java/io/github/zekerzhayard/forgewrapper/util/CheckedLambdaUtil.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package io.github.zekerzhayard.forgewrapper.util;
-
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-public class CheckedLambdaUtil {
- public static <T> Consumer<T> wrapConsumer(CheckedConsumer<T> consumer) {
- return consumer;
- }
-
- public static <T, U> BiConsumer<T, U> wrapBiConsumer(CheckedBiConsumer<T, U> biconsumer) {
- return biconsumer;
- }
-
- public interface CheckedConsumer<T> extends Consumer<T> {
- 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<T, U> extends BiConsumer<T, U> {
- 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/gradle.properties b/gradle.properties
index 4fb12126f0..96acf9205f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
org.gradle.daemon = false
-fw_version = 1.5.8
+fw_version = 1.6.0
diff --git a/jigsaw/build.gradle b/jigsaw/build.gradle
index 08c394afee..a555ec3bf3 100644
--- a/jigsaw/build.gradle
+++ b/jigsaw/build.gradle
@@ -26,7 +26,3 @@ 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 e15d39c410..09792b1624 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
@@ -12,6 +12,7 @@ import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -19,10 +20,8 @@ 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 {
@@ -53,17 +52,27 @@ public class ModuleUtil {
MethodHandle loadModuleMH = IMPL_LOOKUP.findVirtual(Class.forName("jdk.internal.loader.BuiltinClassLoader"), "loadModule", MethodType.methodType(void.class, ModuleReference.class));
// 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()));
+ List<String> roots = new ArrayList<>();
+ for (ModuleReference mref : finder.findAll()) {
+ String name = mref.descriptor().name();
+ if (!ModuleLayer.boot().findModule(name).isPresent()) {
+ loadModuleMH.invokeWithArguments(ClassLoader.getSystemClassLoader(), mref);
+ roots.add(name);
+ }
+ }
+ Configuration config = Configuration.resolveAndBind(finder, List.of(ModuleLayer.boot().configuration()), finder, roots);
// Copy the new config graph to boot module layer config
MethodHandle graphGetter = IMPL_LOOKUP.findGetter(Configuration.class, "graph", Map.class);
HashMap<ResolvedModule, Set<ResolvedModule>> graphMap = new HashMap<>((Map<ResolvedModule, Set<ResolvedModule>>) 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(CheckedLambdaUtil.wrapBiConsumer((k, v) -> {
- cfSetter.invokeWithArguments(k, ModuleLayer.boot().configuration());
- v.forEach(CheckedLambdaUtil.wrapConsumer(m -> cfSetter.invokeWithArguments(m, ModuleLayer.boot().configuration())));
- }));
+ for (Map.Entry<ResolvedModule, Set<ResolvedModule>> entry : graphMap.entrySet()) {
+ cfSetter.invokeWithArguments(entry.getKey(), ModuleLayer.boot().configuration());
+ for (ResolvedModule resolvedModule : entry.getValue()) {
+ cfSetter.invokeWithArguments(resolvedModule, ModuleLayer.boot().configuration());
+ }
+ }
graphMap.putAll((Map<ResolvedModule, Set<ResolvedModule>>) graphGetter.invokeWithArguments(ModuleLayer.boot().configuration()));
IMPL_LOOKUP.findSetter(Configuration.class, "graph", Map.class).invokeWithArguments(ModuleLayer.boot().configuration(), new HashMap<>(graphMap));
@@ -92,7 +101,17 @@ public class ModuleUtil {
// 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(CheckedLambdaUtil.wrapConsumer(bm -> implAddReadsMH.invokeWithArguments(m, bm))))));
+ for (ResolvedModule resolvedModule : config.modules()) {
+ Module module = ModuleLayer.boot().findModule(resolvedModule.name()).orElse(null);
+ if (module != null) {
+ for (ResolvedModule bootResolvedModule : oldBootModules) {
+ Module bootModule = ModuleLayer.boot().findModule(bootResolvedModule.name()).orElse(null);
+ if (bootModule != null) {
+ implAddReadsMH.invokeWithArguments(module, bootModule);
+ }
+ }
+ }
+ }
}
public static void addExports(List<String> exports) {
@@ -124,13 +143,26 @@ public class ModuleUtil {
}
void implAdd(List<String> 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)));
+ for (String extra : extras) {
+ ParserData data = ModuleUtil.parseModuleExtra(extra).orElse(null);
+ if (data != null) {
+ Module module = ModuleLayer.boot().findModule(data.module).orElse(null);
+ if (module != null) {
+ try {
+ if ("ALL-UNNAMED".equals(data.target)) {
+ this.implAddToAllUnnamedMH.invokeWithArguments(module, data.packages);
+ } else {
+ Module targetModule = ModuleLayer.boot().findModule(data.target).orElse(null);
+ if (targetModule != null) {
+ this.implAddMH.invokeWithArguments(module, data.packages, targetModule);
+ }
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
}
- }))));
+ }
}
}
diff --git a/legacy/build.gradle b/legacy/build.gradle
deleted file mode 100644
index 4778335e0d..0000000000
--- a/legacy/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-
-plugins {
- id "java"
- id "eclipse"
-}
-
-sourceCompatibility = targetCompatibility = 1.8
-compileJava {
- sourceCompatibility = targetCompatibility = 1.8
-}
-
-repositories {
- mavenCentral()
- maven {
- name = "forge"
- url = "https://maven.minecraftforge.net/"
- }
-}
-
-dependencies {
- compileOnly "net.minecraftforge:installer:2.0.24"
-}
diff --git a/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/AbstractInstaller.java b/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/AbstractInstaller.java
deleted file mode 100644
index 6b2d55072f..0000000000
--- a/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/AbstractInstaller.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.github.zekerzhayard.forgewrapper.installer.util;
-
-import java.io.File;
-
-import net.minecraftforge.installer.actions.ProgressCallback;
-import net.minecraftforge.installer.json.Install;
-
-public abstract class AbstractInstaller {
- public abstract Install loadInstallProfile();
-
- public abstract boolean runClientInstall(Install profile, ProgressCallback monitor, File libraryDir, File minecraftJar, File installerJar);
-}
diff --git a/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV0.java b/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV0.java
deleted file mode 100644
index ff1cd4373b..0000000000
--- a/legacy/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV0.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.github.zekerzhayard.forgewrapper.installer.util;
-
-import java.io.File;
-import java.util.function.Predicate;
-
-import net.minecraftforge.installer.actions.ClientInstall;
-import net.minecraftforge.installer.actions.ProgressCallback;
-import net.minecraftforge.installer.json.Install;
-import net.minecraftforge.installer.json.Util;
-
-public class InstallerV0 extends AbstractInstaller {
- @Override
- public Install loadInstallProfile() {
- return Util.loadInstallProfile();
- }
-
- @Override
- public boolean runClientInstall(Install profile, ProgressCallback monitor, File libraryDir, File minecraftJar, File installerJar) {
- return new ClientInstall4MultiMC(profile, monitor, libraryDir, minecraftJar).run(null, input -> true);
- }
-
- public static class ClientInstall4MultiMC extends ClientInstall {
- protected File libraryDir;
- protected File minecraftJar;
-
- public ClientInstall4MultiMC(Install profile, ProgressCallback monitor, File libraryDir, File minecraftJar) {
- super(profile, monitor);
- this.libraryDir = libraryDir;
- this.minecraftJar = minecraftJar;
- }
-
- @Override
- public boolean run(File target, Predicate<String> optionals) {
- return this.processors.process(this.libraryDir, this.minecraftJar);
- }
- }
-}
diff --git a/settings.gradle b/settings.gradle
index 34c563d573..3444232309 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,3 @@
rootProject.name = 'ForgeWrapper'
-include 'common'
-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 3604371a14..90d3325570 100644
--- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java
+++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Bootstrap.java
@@ -7,11 +7,12 @@ import java.util.List;
import io.github.zekerzhayard.forgewrapper.installer.util.ModuleUtil;
public class Bootstrap {
- public static void bootstrap(List<String> jvmArgs, String minecraftJar, String libraryDir) throws Throwable {
+ public static void bootstrap(String[] jvmArgs, String minecraftJar, String libraryDir) throws Throwable {
// Replace all placeholders
- List<String> 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).replace("${version_name}", minecraftJar.substring(0, minecraftJar.lastIndexOf('.'))));
+ String[] replacedJvmArgs = new String[jvmArgs.length];
+ for (int i = 0; i < jvmArgs.length; i++) {
+ String arg = jvmArgs[i];
+ replacedJvmArgs[i] = 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;
@@ -27,23 +28,23 @@ public class Bootstrap {
String modulePath = null;
List<String> addExports = new ArrayList<>();
List<String> addOpens = new ArrayList<>();
- for (int i = 0; i < jvmArgs.size(); i++) {
- String arg = jvmArgs.get(i);
+ for (int i = 0; i < jvmArgs.length; i++) {
+ String arg = jvmArgs[i];
if (arg.equals("-p") || arg.equals("--module-path")) {
- modulePath = jvmArgs.get(i + 1);
+ modulePath = jvmArgs[i + 1];
} else if (arg.startsWith("--module-path=")) {
modulePath = arg.split("=", 2)[1];
}
if (arg.equals("--add-exports")) {
- addExports.add(jvmArgs.get(i + 1));
+ addExports.add(jvmArgs[i + 1]);
} else if (arg.startsWith("--add-exports=")) {
addExports.add(arg.split("=", 2)[1]);
}
if (arg.equals("--add-opens")) {
- addOpens.add(jvmArgs.get(i + 1));
+ addOpens.add(jvmArgs[i + 1]);
} else if (arg.startsWith("--add-opens=")) {
addOpens.add(arg.split("=", 2)[1]);
}
diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Installer.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Installer.java
index 278688e773..10895b13fd 100644
--- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Installer.java
+++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Installer.java
@@ -1,20 +1,47 @@
package io.github.zekerzhayard.forgewrapper.installer;
import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
-import io.github.zekerzhayard.forgewrapper.installer.util.AbstractInstaller;
-import io.github.zekerzhayard.forgewrapper.installer.util.InstallerV0;
-import io.github.zekerzhayard.forgewrapper.installer.util.InstallerV1;
+import net.minecraftforge.installer.DownloadUtils;
+import net.minecraftforge.installer.actions.PostProcessors;
import net.minecraftforge.installer.actions.ProgressCallback;
+import net.minecraftforge.installer.json.Artifact;
import net.minecraftforge.installer.json.Install;
import net.minecraftforge.installer.json.InstallV1;
import net.minecraftforge.installer.json.Util;
+import net.minecraftforge.installer.json.Version;
public class Installer {
- public static boolean install(File libraryDir, File minecraftJar, File installerJar) {
- AbstractInstaller installer = createInstaller();
+ private static InstallV1Wrapper wrapper;
+ private static InstallV1Wrapper getWrapper(File librariesDir) {
+ if (wrapper == null) {
+ wrapper = new InstallV1Wrapper(Util.loadInstallProfile(), librariesDir);
+ }
+ return wrapper;
+ }
+
+ public static Map<String, Object> getData(File librariesDir) {
+ Map<String, Object> data = new HashMap<>();
+ Version0 version = Version0.loadVersion(getWrapper(librariesDir));
+ data.put("mainClass", version.getMainClass());
+ data.put("jvmArgs", version.getArguments().getJvm());
+ data.put("extraLibraries", getExtraLibraries(version));
+ return data;
+ }
+
+ public static boolean install(File libraryDir, File minecraftJar, File installerJar) throws Throwable {
ProgressCallback monitor = ProgressCallback.withOutputs(System.out);
- Install profile = installer.loadInstallProfile();
if (System.getProperty("java.net.preferIPv4Stack") == null) {
System.setProperty("java.net.preferIPv4Stack", "true");
}
@@ -23,21 +50,129 @@ public class Installer {
String jvmVersion = System.getProperty("java.vm.version", "missing jvm version");
monitor.message(String.format("JVM info: %s - %s - %s", vendor, javaVersion, jvmVersion));
monitor.message("java.net.preferIPv4Stack=" + System.getProperty("java.net.preferIPv4Stack"));
- return installer.runClientInstall(profile, monitor, libraryDir, minecraftJar, installerJar);
+ monitor.message("Current Time: " + new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()));
+
+ // MinecraftForge has removed all old installers since 2024/2/27, but they still exist in NeoForge.
+ PostProcessors processors = new PostProcessors(wrapper, true, monitor);
+ Method processMethod = PostProcessors.class.getMethod("process", File.class, File.class, File.class, File.class);
+ if (boolean.class.equals(processMethod.getReturnType())) {
+ return (boolean) processMethod.invoke(processors, libraryDir, minecraftJar, libraryDir.getParentFile(), installerJar);
+ } else {
+ return processMethod.invoke(processors, libraryDir, minecraftJar, libraryDir.getParentFile(), installerJar) != null;
+ }
+ }
+
+ // Some libraries in the version json are not available via direct download,
+ // so they are not available in the MultiMC meta json,
+ // so wee need to get them manually.
+ private static List<String> getExtraLibraries(Version0 version) {
+ List<String> paths = new ArrayList<>();
+ for (Version.Library library : version.getLibraries()) {
+ Version.LibraryDownload artifact = library.getDownloads().getArtifact();
+ if (artifact.getUrl().isEmpty()) {
+ paths.add(artifact.getPath());
+ }
+ }
+ return paths;
+ }
+
+ public static class InstallV1Wrapper extends InstallV1 {
+ protected Map<String, List<Processor>> processors = new HashMap<>();
+ protected File librariesDir;
+
+ public InstallV1Wrapper(InstallV1 v1, File librariesDir) {
+ super(v1);
+ this.serverJarPath = v1.getServerJarPath();
+ this.librariesDir = librariesDir;
+ }
+
+ @Override
+ public List<Processor> getProcessors(String side) {
+ List<Processor> processor = this.processors.get(side);
+ if (processor == null) {
+ checkProcessorFiles(processor = super.getProcessors(side), super.getData("client".equals(side)), this.librariesDir);
+ this.processors.put(side, processor);
+ }
+ return processor;
+ }
+
+ private static void checkProcessorFiles(List<Processor> processors, Map<String, String> data, File base) {
+ Map<String, File> artifactData = new HashMap<>();
+ for (Map.Entry<String, String> entry : data.entrySet()) {
+ String value = entry.getValue();
+ if (value.charAt(0) == '[' && value.charAt(value.length() - 1) == ']') {
+ artifactData.put("{" + entry.getKey() + "}", Artifact.from(value.substring(1, value.length() - 1)).getLocalPath(base));
+ }
+ }
+
+ Map<Processor, Map<String, String>> outputsMap = new HashMap<>();
+ label:
+ for (Processor processor : processors) {
+ Map<String, String> outputs = new HashMap<>();
+ if (processor.getOutputs().isEmpty()) {
+ String[] args = processor.getArgs();
+ for (int i = 0; i < args.length; i++) {
+ for (Map.Entry<String, File> entry : artifactData.entrySet()) {
+ if (args[i].contains(entry.getKey())) {
+ // We assume that all files that exist but don't have the sha1 checksum are valid.
+ if (entry.getValue().exists()) {
+ outputs.put(entry.getKey(), DownloadUtils.getSha1(entry.getValue()));
+ } else {
+ outputsMap.clear();
+ break label;
+ }
+ }
+ }
+ }
+ outputsMap.put(processor, outputs);
+ }
+ }
+ for (Map.Entry<Processor, Map<String, String>> entry : outputsMap.entrySet()) {
+ setOutputs(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private static Field outputsField;
+ private static void setOutputs(Processor processor, Map<String, String> outputs) {
+ try {
+ if (outputsField == null) {
+ outputsField = Processor.class.getDeclaredField("outputs");
+ outputsField.setAccessible(true);
+ }
+ outputsField.set(processor, outputs);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
}
- private static AbstractInstaller createInstaller() {
- try {
- Class<?> installerClass = Util.class.getMethod("loadInstallProfile").getReturnType();
- if (installerClass.equals(Install.class)) {
- return new InstallerV0();
- } else if (installerClass.equals(InstallV1.class)) {
- return new InstallerV1();
- } else {
- throw new IllegalArgumentException("Unable to determine the installer version. (" + installerClass + ")");
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
+ public static class Version0 extends Version {
+
+ public static Version0 loadVersion(Install profile) {
+ try (InputStream stream = Util.class.getResourceAsStream(profile.getJson())) {
+ return Util.GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Version0.class);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ protected String mainClass;
+ protected Version0.Arguments arguments;
+
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ public Version0.Arguments getArguments() {
+ return arguments;
+ }
+
+ public static class Arguments {
+ protected String[] jvm;
+
+ public String[] getJvm() {
+ return jvm;
+ }
}
}
}
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 37876a6de4..f15a2a842c 100644
--- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java
+++ b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/Main.java
@@ -3,8 +3,10 @@ package io.github.zekerzhayard.forgewrapper.installer;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -14,6 +16,7 @@ import io.github.zekerzhayard.forgewrapper.installer.detector.IFileDetector;
import io.github.zekerzhayard.forgewrapper.installer.util.ModuleUtil;
public class Main {
+ @SuppressWarnings("unchecked")
public static void main(String[] args) throws Throwable {
// --fml.neoForgeVersion 20.2.20-beta --fml.fmlVersion 1.0.2 --fml.mcVersion 1.20.2 --fml.neoFormVersion 20231019.002635 --launchTarget forgeclient
@@ -30,40 +33,44 @@ public class Main {
String forgeFullVersion = isNeoForge ? forgeVersion : mcVersion + "-" + forgeVersion;
IFileDetector detector = DetectorLoader.loadDetector();
- try {
- Bootstrap.bootstrap(detector.getJvmArgs(forgeGroup, forgeArtifact, forgeFullVersion), detector.getMinecraftJar(mcVersion).getFileName().toString(), detector.getLibraryDir().toAbsolutePath().toString());
- } catch (Throwable ignored) {
- // Avoid this bunch of hacks that nuke the whole wrapper.
+ // Check installer jar.
+ Path installerJar = detector.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
+ if (!isFile(installerJar)) {
+ throw new RuntimeException("Unable to detect the forge installer!");
}
- if (!detector.checkExtraFiles(forgeGroup, forgeArtifact, forgeFullVersion)) {
- System.out.println("Some extra libraries are missing! Running the installer to generate them now.");
- // Check installer jar.
- Path installerJar = detector.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
- if (!IFileDetector.isFile(installerJar)) {
- throw new RuntimeException("Unable to detect the forge installer!");
- }
+ // Check vanilla Minecraft jar.
+ Path minecraftJar = detector.getMinecraftJar(mcVersion);
+ if (!isFile(minecraftJar)) {
+ throw new RuntimeException("Unable to detect the Minecraft jar!");
+ }
+
+ try (URLClassLoader ucl = URLClassLoader.newInstance(new URL[] {
+ Main.class.getProtectionDomain().getCodeSource().getLocation(),
+ Launcher.class.getProtectionDomain().getCodeSource().getLocation(),
+ installerJar.toUri().toURL()
+ }, ModuleUtil.getPlatformClassLoader())) {
+ Class<?> installer = ucl.loadClass("io.github.zekerzhayard.forgewrapper.installer.Installer");
- // Check vanilla Minecraft jar.
- Path minecraftJar = detector.getMinecraftJar(mcVersion);
- if (!IFileDetector.isFile(minecraftJar)) {
- throw new RuntimeException("Unable to detect the Minecraft jar!");
+ Map<String, Object> data = (Map<String, Object>) installer.getMethod("getData", File.class).invoke(null, detector.getLibraryDir().toFile());
+ try {
+ Bootstrap.bootstrap((String[]) data.get("jvmArgs"), detector.getMinecraftJar(mcVersion).getFileName().toString(), detector.getLibraryDir().toAbsolutePath().toString());
+ } catch (Throwable t) {
+ // Avoid this bunch of hacks that nuke the whole wrapper.
+ t.printStackTrace();
}
- try (URLClassLoader ucl = URLClassLoader.newInstance(new URL[] {
- Main.class.getProtectionDomain().getCodeSource().getLocation(),
- Launcher.class.getProtectionDomain().getCodeSource().getLocation(),
- installerJar.toUri().toURL()
- }, ModuleUtil.getPlatformClassLoader())) {
- Class<?> installer = ucl.loadClass("io.github.zekerzhayard.forgewrapper.installer.Installer");
- if (!(boolean) installer.getMethod("install", File.class, File.class, File.class).invoke(null, detector.getLibraryDir().toFile(), minecraftJar.toFile(), installerJar.toFile())) {
- return;
- }
+ if (!((boolean) installer.getMethod("install", File.class, File.class, File.class).invoke(null, detector.getLibraryDir().toFile(), minecraftJar.toFile(), installerJar.toFile()))) {
+ return;
}
+
+ ModuleUtil.setupClassPath(detector.getLibraryDir(), (List<String>) data.get("extraLibraries"));
+ Class<?> mainClass = ModuleUtil.setupBootstrapLauncher(Class.forName((String) data.get("mainClass")));
+ mainClass.getMethod("main", String[].class).invoke(null, new Object[] {args});
}
+ }
- 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 });
+ private static boolean isFile(Path path) {
+ return path != null && Files.isRegularFile(path);
}
}
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 67d3e1d4a1..29b7179194 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
@@ -1,33 +1,10 @@
package io.github.zekerzhayard.forgewrapper.installer.detector;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.math.BigInteger;
import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
import cpw.mods.modlauncher.Launcher;
public interface IFileDetector {
@@ -85,163 +62,4 @@ public interface IFileDetector {
}
return null;
}
-
- /**
- * @param forgeGroup Forge package group (e.g. net.minecraftforge).
- * @param forgeArtifact Forge package artifact (e.g. forge).
- * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
- * @return The list of jvm args.
- */
- default List<String> getJvmArgs(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
- return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> {
- JsonElement element = e.getAsJsonObject().get("arguments").getAsJsonObject().get("jvm");
- List<String> args = new ArrayList<>();
- if (!element.equals(JsonNull.INSTANCE)) {
- element.getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString()));
- }
- return args;
- });
- }
-
- default List<String> getExtraLibraries(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
- return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> {
- List<String> 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).
- * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
- * @return The main class.
- */
- default String getMainClass(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
- return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> e.getAsJsonObject().getAsJsonPrimitive("mainClass").getAsString());
- }
-
- /**
- * @param forgeGroup Forge package group (e.g. net.minecraftforge).
- * @param forgeArtifact Forge package artifact (e.g. forge).
- * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
- * @return The json object in the-installer-jar-->install_profile.json-->data-->xxx-->client.
- */
- default JsonObject getInstallProfileExtraData(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
- return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "install_profile.json", e -> e.getAsJsonObject().getAsJsonObject("data"));
- }
-
- @SuppressWarnings("deprecation")
- default <R> R getDataFromInstaller(String forgeGroup, String forgeArtifact, String forgeFullVersion, String entry, Function<JsonElement, R> function) {
- Path installer = this.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
- if (isFile(installer)) {
- try (ZipFile zf = new ZipFile(installer.toFile())) {
- ZipEntry ze = zf.getEntry(entry);
- if (ze != null) {
- try (
- InputStream is = zf.getInputStream(ze);
- InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)
- ) {
- return function.apply(new JsonParser().parse(isr));
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else {
- throw new RuntimeException("Unable to detect the forge installer!");
- }
- return null;
- }
-
- /**
- * Check all cached files.
- * @param forgeGroup Forge package group (e.g. net.minecraftforge).
- * @param forgeArtifact Forge package artifact (e.g. forge).
- * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
- * @return True represents all files are ready.
- */
- default boolean checkExtraFiles(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
- JsonObject jo = this.getInstallProfileExtraData(forgeGroup, forgeArtifact, forgeFullVersion);
- if (jo != null) {
- Map<String, Path> libsMap = new HashMap<>();
- Map<String, String> hashMap = new HashMap<>();
-
- // Get all "data/<name>/client" elements.
- Pattern artifactPattern = Pattern.compile("^\\[(?<groupId>[^:]*):(?<artifactId>[^:]*):(?<version>[^:@]*)(:(?<classifier>[^@]*))?(@(?<type>[^]]*))?]$");
- for (Map.Entry<String, JsonElement> entry : jo.entrySet()) {
- String clientStr = entry.getValue().getAsJsonObject().get("client").getAsString();
- if (entry.getKey().endsWith("_SHA")) {
- Pattern p = Pattern.compile("^'(?<sha1>[A-Za-z0-9]{40})'$");
- Matcher m = p.matcher(clientStr);
- if (m.find()) {
- hashMap.put(entry.getKey(), m.group("sha1"));
- }
- } else {
- Matcher m = artifactPattern.matcher(clientStr);
- if (m.find()) {
- String groupId = nullToDefault(m.group("groupId"), "");
- String artifactId = nullToDefault(m.group("artifactId"), "");
- String version = nullToDefault(m.group("version"), "");
- String classifier = nullToDefault(m.group("classifier"), "");
- String type = nullToDefault(m.group("type"), "jar");
- libsMap.put(entry.getKey(), this.getLibraryDir()
- .resolve(groupId.replace('.', File.separatorChar))
- .resolve(artifactId)
- .resolve(version)
- .resolve(artifactId + "-" + version + (classifier.isEmpty() ? "" : "-") + classifier + "." + type).toAbsolutePath());
- }
- }
- }
-
- // Check all cached libraries.
- boolean checked = true;
- for (Map.Entry<String, Path> entry : libsMap.entrySet()) {
- checked = checkExtraFile(entry.getValue(), hashMap.get(entry.getKey() + "_SHA"));
- if (!checked) {
- System.out.println("Missing: " + entry.getValue());
- break;
- }
- }
- return checked;
- }
- // Skip installing process if installer profile doesn't exist.
- return true;
- }
-
- /**
- * Check the exact file.
- * @param path The path of the file to check.
- * @param sha1 The sha1 defined in installer.
- * @return True represents the file is ready.
- */
- static boolean checkExtraFile(Path path, String sha1) {
- 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 String getFileSHA1(Path path) {
- try {
- StringBuilder sha1 = new StringBuilder(new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(path))).toString(16));
- while (sha1.length() < 40) {
- sha1.insert(0, "0");
- }
- return sha1.toString().toLowerCase(Locale.ENGLISH);
- } catch (IOException | NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- static String nullToDefault(String string, String defaultValue) {
- return string == null ? defaultValue : string;
- }
}
diff --git a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV1.java b/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV1.java
deleted file mode 100644
index 014a6240ea..0000000000
--- a/src/main/java/io/github/zekerzhayard/forgewrapper/installer/util/InstallerV1.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.zekerzhayard.forgewrapper.installer.util;
-
-import java.io.File;
-import java.util.function.Predicate;
-
-import net.minecraftforge.installer.actions.ClientInstall;
-import net.minecraftforge.installer.actions.ProgressCallback;
-import net.minecraftforge.installer.json.Install;
-import net.minecraftforge.installer.json.InstallV1;
-import net.minecraftforge.installer.json.Util;
-
-public class InstallerV1 extends AbstractInstaller {
- @Override
- public Install loadInstallProfile() {
- return Util.loadInstallProfile();
- }
-
- @Override
- public boolean runClientInstall(Install profile, ProgressCallback monitor, File libraryDir, File minecraftJar, File installerJar) {
- return new ClientInstall4MultiMC(profile, monitor, libraryDir, minecraftJar).run(null, input -> true, installerJar);
- }
-
- public static class ClientInstall4MultiMC extends ClientInstall {
- protected File libraryDir;
- protected File minecraftJar;
-
- public ClientInstall4MultiMC(Install profile, ProgressCallback monitor, File libraryDir, File minecraftJar) {
- super(profile instanceof InstallV1 ? (InstallV1) profile : new InstallV1(profile), monitor);
- this.libraryDir = libraryDir;
- this.minecraftJar = minecraftJar;
- }
-
- @Override
- public boolean run(File target, Predicate<String> optionals, File installer) {
- return this.processors.process(this.libraryDir, this.minecraftJar, this.libraryDir.getParentFile(), installer);
- }
- }
-}