summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZekerZhayard <trees1000@qq.com>2024-03-01 14:51:26 +0800
committerZekerZhayard <trees1000@qq.com>2024-03-01 14:51:26 +0800
commit3c6712d64a42e4ec200909912e72749499aaca79 (patch)
tree11ea3f3a119e9afa1223ed452882c0cd0bbcaf2a
parenta3413eb5c46f0205b0aa7c0fd8cc326dbebcced2 (diff)
downloadProject-Tick-3c6712d64a42e4ec200909912e72749499aaca79.tar.gz
Project-Tick-3c6712d64a42e4ec200909912e72749499aaca79.zip
Thanks to LexManos, he replaced all installers with the new version, so the codes for compatibility with the old installer do not need to exist.
The task of checking for extra files is now done by the installer instead of ForgeWrapper itself, thus avoiding some inaccurate checking.
-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);
- }
- }
-}