Merge pull request #1 from phit/master

Merge upstream updates
This commit is contained in:
Petr Mrázek 2024-01-30 09:40:07 +01:00 committed by GitHub
commit 7531db2b71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 100 additions and 338 deletions

View file

@ -1,54 +0,0 @@
plugins {
id "java"
id "eclipse"
}
version = "${rootProject.fw_version}${-> getVersionSuffix()}"
group = "io.github.zekerzhayard"
archivesBaseName = rootProject.name + "Converter"
sourceCompatibility = targetCompatibility = 1.8
compileJava {
sourceCompatibility = targetCompatibility = 1.8
}
configurations {
provided {
implementation.extendsFrom provided
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly "com.google.code.gson:gson:2.8.7"
provided rootProject
}
jar {
manifest.attributes rootProject.jar.manifest.attributes
manifest.attributes([
"Main-Class": "io.github.zekerzhayard.forgewrapper.converter.Main"
])
from configurations.provided.files.collect {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
zipTree(it)
}
}
processResources {
inputs.property "version", project.version
from(sourceSets.main.resources.srcDirs) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
include "patches/net.minecraftforge.json"
expand "version": project.version
}
from(sourceSets.main.resources.srcDirs) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude "patches/net.minecraftforge.json"
}
}

View file

@ -1,157 +0,0 @@
package io.github.zekerzhayard.forgewrapper.converter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class Converter {
public static void convert(Path installerPath, Path targetDir, Path multimcDir) throws Exception {
JsonObject installer = getJsonFromZip(installerPath, "version.json");
JsonObject installProfile = getJsonFromZip(installerPath, "install_profile.json");
List<String> arguments = getAdditionalArgs(installer);
String mcVersion = arguments.get(arguments.indexOf("--fml.mcVersion") + 1);
String forgeVersion = arguments.get(arguments.indexOf("--fml.forgeVersion") + 1);
String forgeFullVersion = "forge-" + mcVersion + "-" + forgeVersion;
StringBuilder wrapperVersion = new StringBuilder();
JsonObject pack = convertPackJson(mcVersion);
JsonObject patches = convertPatchesJson(installer, installProfile, mcVersion, forgeVersion, wrapperVersion);
Files.createDirectories(targetDir);
// Copy mmc-pack.json and instance.cfg to <instance> folder.
Path instancePath = targetDir.resolve(forgeFullVersion);
Files.createDirectories(instancePath);
Files.copy(new ByteArrayInputStream(pack.toString().getBytes(StandardCharsets.UTF_8)), instancePath.resolve("mmc-pack.json"), StandardCopyOption.REPLACE_EXISTING);
Files.copy(new ByteArrayInputStream(("InstanceType=OneSix\nname=" + forgeFullVersion).getBytes(StandardCharsets.UTF_8)), instancePath.resolve("instance.cfg"), StandardCopyOption.REPLACE_EXISTING);
// Copy ForgeWrapper to <instance>/libraries folder.
Path librariesPath = instancePath.resolve("libraries");
Files.createDirectories(librariesPath);
Files.copy(Paths.get(Converter.class.getProtectionDomain().getCodeSource().getLocation().toURI()), librariesPath.resolve(wrapperVersion.toString()), StandardCopyOption.REPLACE_EXISTING);
// Copy net.minecraftforge.json to <instance>/patches folder.
Path patchesPath = instancePath.resolve("patches");
Files.createDirectories(patchesPath);
Files.copy(new ByteArrayInputStream(patches.toString().getBytes(StandardCharsets.UTF_8)), patchesPath.resolve("net.minecraftforge.json"), StandardCopyOption.REPLACE_EXISTING);
// Copy forge installer to MultiMC/libraries/net/minecraftforge/forge/<mcVersion>-<forgeVersion> folder.
if (multimcDir != null) {
Path targetInstallerPath = multimcDir.resolve("libraries").resolve("net").resolve("minecraftforge").resolve("forge").resolve(forgeVersion);
Files.createDirectories(targetInstallerPath);
Files.copy(installerPath, targetInstallerPath.resolve(forgeFullVersion + "-installer.jar"), StandardCopyOption.REPLACE_EXISTING);
}
}
public static List<String> getAdditionalArgs(JsonObject installer) {
List<String> args = new ArrayList<>();
getElement(installer.getAsJsonObject("arguments"), "game").getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString()));
return args;
}
public static JsonObject getJsonFromZip(Path path, String json) {
try {
ZipFile zf = new ZipFile(path.toFile());
ZipEntry versionFile = zf.getEntry(json);
if (versionFile == null) {
throw new RuntimeException("The zip file is invalid!");
}
InputStreamReader isr = new InputStreamReader(zf.getInputStream(versionFile), StandardCharsets.UTF_8);
return JsonParser.parseReader(isr).getAsJsonObject();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// Convert mmc-pack.json:
// - Replace Minecraft version
private static JsonObject convertPackJson(String mcVersion) {
JsonObject pack = JsonParser.parseReader(new InputStreamReader(Converter.class.getResourceAsStream("/mmc-pack.json"))).getAsJsonObject();
for (JsonElement component : getElement(pack, "components").getAsJsonArray()) {
JsonObject componentObject = component.getAsJsonObject();
JsonElement version = getElement(componentObject, "version");
if (!version.isJsonNull() && getElement(componentObject, "uid").getAsString().equals("net.minecraft")) {
componentObject.addProperty("version", mcVersion);
}
}
return pack;
}
// Convert patches/net.minecraftforge.json:
// - Add libraries
// - Add forge-launcher url
// - Replace Minecraft & Forge versions
private static JsonObject convertPatchesJson(JsonObject installer, JsonObject installProfile, String mcVersion, String forgeVersion, StringBuilder wrapperVersion) {
JsonObject patches = JsonParser.parseReader(new InputStreamReader(Converter.class.getResourceAsStream("/patches/net.minecraftforge.json"))).getAsJsonObject();
JsonArray mavenFiles = getElement(patches, "mavenFiles").getAsJsonArray();
JsonArray libraries = getElement(patches, "libraries").getAsJsonArray();
String minecraftArguments = String.join(" ", getElement(patches, "minecraftArguments").getAsString(), "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", String.join(" ", getAdditionalArgs(installer))).trim();
patches.addProperty("minecraftArguments", minecraftArguments);
for (JsonElement mavenFile : mavenFiles) {
String name = getElement(mavenFile.getAsJsonObject(), "name").getAsString();
mavenFile.getAsJsonObject().addProperty("name", name.replace("{VERSION}", mcVersion).replace("{FORGE_VERSION}", forgeVersion));
}
for (JsonElement lib : libraries) {
String name = getElement(lib.getAsJsonObject(), "name").getAsString();
if (name.startsWith("io.github.zekerzhayard:ForgeWrapper:")) {
wrapperVersion.append(getElement(lib.getAsJsonObject(), "MMC-filename").getAsString());
}
}
Map<String, String> additionalUrls = new HashMap<>();
String path = String.format("net/minecraftforge/forge/%s-%s/forge-%s-%s", mcVersion, forgeVersion, mcVersion, forgeVersion);
additionalUrls.put(path + "-universal.jar", "https://maven.minecraftforge.net/" + path + "-universal.jar");
transformLibraries(getElement(installProfile, "libraries").getAsJsonArray(), mavenFiles, additionalUrls);
additionalUrls.clear();
additionalUrls.put(path + ".jar", "https://maven.minecraftforge.net/" + path + "-launcher.jar");
transformLibraries(getElement(installer, "libraries").getAsJsonArray(), libraries, additionalUrls);
patches.addProperty("version", forgeVersion);
for (JsonElement require : getElement(patches, "requires").getAsJsonArray()) {
JsonObject requireObject = require.getAsJsonObject();
if (getElement(requireObject, "uid").getAsString().equals("net.minecraft")) {
requireObject.addProperty("equals", mcVersion);
}
}
return patches;
}
private static JsonElement getElement(JsonObject object, String property) {
Optional<Map.Entry<String, JsonElement>> first = object.entrySet().stream().filter(e -> e.getKey().equals(property)).findFirst();
if (first.isPresent()) {
return first.get().getValue();
}
return JsonNull.INSTANCE;
}
private static void transformLibraries(JsonArray source, JsonArray target, Map<String, String> additionalUrls) {
for (JsonElement lib : source) {
JsonObject artifact = getElement(getElement(lib.getAsJsonObject(), "downloads").getAsJsonObject(), "artifact").getAsJsonObject();
String path = getElement(artifact, "path").getAsString();
if (additionalUrls.containsKey(path)) {
artifact.getAsJsonObject().addProperty("url", additionalUrls.get(path));
}
target.add(lib);
}
}
}

View file

@ -1,53 +0,0 @@
package io.github.zekerzhayard.forgewrapper.converter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
Path installer = null, instance = Paths.get("."), multimc = null;
try {
HashMap<String, String> argsMap = parseArgs(args);
installer = Paths.get(argsMap.get("--installer"));
if (argsMap.containsKey("--instance")) {
instance = Paths.get(argsMap.get("--instance"));
multimc = instance.getParent();
}
} catch (Exception e) {
System.out.println("Invalid arguments! Use: java -jar <ForgeWrapper.jar> --installer=<forge-installer.jar> [--instance=<instance-path>]");
throw new RuntimeException(e);
}
try {
URLClassLoader ucl = URLClassLoader.newInstance(new URL[] {
Converter.class.getProtectionDomain().getCodeSource().getLocation(),
installer.toUri().toURL()
}, null);
ucl.loadClass("io.github.zekerzhayard.forgewrapper.converter.Converter").getMethod("convert", Path.class, Path.class, Path.class).invoke(null, installer, instance, multimc);
System.out.println("Successfully install Forge for MultiMC!");
} catch (Exception e) {
System.out.println("Failed to install Forge!");
throw new RuntimeException(e);
}
}
/**
* @return installer -- The path of forge installer.<br/>
* instance -- The instance folder of MultiMC.<br/>
* cursepack -- The version of cursepacklocator.<br/>
*/
private static HashMap<String, String> parseArgs(String[] args) {
HashMap<String, String> map = new HashMap<>();
for (String arg : args) {
String[] params = arg.split("=", 2);
map.put(params[0], params[1]);
}
if (!map.containsKey("--installer")) {
throw new IllegalArgumentException();
}
return map;
}
}

View file

@ -1,13 +0,0 @@
{
"formatVersion": 1,
"components": [
{
"important": true,
"uid": "net.minecraft",
"version": "{VERSION}"
},
{
"uid": "net.minecraftforge"
}
]
}

View file

@ -1,28 +0,0 @@
{
"formatVersion": 1,
"mainClass": "io.github.zekerzhayard.forgewrapper.installer.Main",
"minecraftArguments": "",
"name": "Forge",
"requires": [
{
"equals": "{VERSION}",
"uid": "net.minecraft"
}
],
"type": "release",
"uid": "net.minecraftforge",
"version": "{FORGE_VERSION}",
"mavenFiles": [
{
"name": "net.minecraftforge:forge:{VERSION}-{FORGE_VERSION}:installer",
"url": "https://maven.minecraftforge.net/"
}
],
"libraries": [
{
"name": "io.github.zekerzhayard:ForgeWrapper:${version}",
"MMC-hint": "local",
"MMC-filename": "ForgeWrapper-${version}.jar"
}
]
}

View file

@ -1,4 +1,4 @@
org.gradle.daemon = false org.gradle.daemon = false
fw_version = mmc3 fw_version = mmc4

View file

@ -9,6 +9,7 @@ import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference; import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule; import java.lang.module.ResolvedModule;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap; import java.util.HashMap;
@ -159,6 +160,15 @@ public class ModuleUtil {
} }
} }
public static void setupClassPath(Path libraryDir, List<String> paths) throws Throwable {
Class<?> urlClassPathClass = Class.forName("jdk.internal.loader.URLClassPath");
Object ucp = IMPL_LOOKUP.findGetter(Class.forName("jdk.internal.loader.BuiltinClassLoader"), "ucp", urlClassPathClass).invokeWithArguments(ClassLoader.getSystemClassLoader());
MethodHandle addURLMH = IMPL_LOOKUP.findVirtual(urlClassPathClass, "addURL", MethodType.methodType(void.class, URL.class));
for (String path : paths) {
addURLMH.invokeWithArguments(ucp, libraryDir.resolve(path).toUri().toURL());
}
}
// ForgeWrapper need some extra settings to invoke BootstrapLauncher. // ForgeWrapper need some extra settings to invoke BootstrapLauncher.
public static Class<?> setupBootstrapLauncher(Class<?> mainClass) throws Throwable { public static Class<?> setupBootstrapLauncher(Class<?> mainClass) throws Throwable {
if (!mainClass.getModule().isOpen(mainClass.getPackageName(), ModuleUtil.class.getModule())) { if (!mainClass.getModule().isOpen(mainClass.getPackageName(), ModuleUtil.class.getModule())) {

View file

@ -4,6 +4,11 @@ plugins {
id "eclipse" id "eclipse"
} }
sourceCompatibility = targetCompatibility = 1.8
compileJava {
sourceCompatibility = targetCompatibility = 1.8
}
repositories { repositories {
mavenCentral() mavenCentral()
maven { maven {

View file

@ -1,6 +1,5 @@
rootProject.name = 'ForgeWrapper' rootProject.name = 'ForgeWrapper'
include 'common' include 'common'
include 'converter'
include 'legacy' include 'legacy'
include 'jigsaw' include 'jigsaw'

View file

@ -15,6 +15,15 @@ public class Bootstrap {
} }
jvmArgs = replacedJvmArgs; jvmArgs = replacedJvmArgs;
// Remove NewLaunch.jar from property to prevent Forge from adding it to the module path
StringBuilder newCP = new StringBuilder();
for (String path : System.getProperty("java.class.path").split(File.pathSeparator)) {
if (!path.endsWith("NewLaunch.jar")) {
newCP.append(path).append(File.pathSeparator);
}
}
System.setProperty("java.class.path", newCP.substring(0, newCP.length() - 1));
String modulePath = null; String modulePath = null;
List<String> addExports = new ArrayList<>(); List<String> addExports = new ArrayList<>();
List<String> addOpens = new ArrayList<>(); List<String> addOpens = new ArrayList<>();

View file

@ -15,22 +15,31 @@ import io.github.zekerzhayard.forgewrapper.installer.util.ModuleUtil;
public class Main { public class Main {
public static void main(String[] args) throws Throwable { 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
List<String> argsList = Stream.of(args).collect(Collectors.toList()); List<String> argsList = Stream.of(args).collect(Collectors.toList());
// NOTE: this is only true for NeoForge versions past 20.2.x
// early versions of NeoForge (for 1.20.1) are not supposed to be covered here
boolean isNeoForge = argsList.contains("--fml.neoForgeVersion");
String mcVersion = argsList.get(argsList.indexOf("--fml.mcVersion") + 1); String mcVersion = argsList.get(argsList.indexOf("--fml.mcVersion") + 1);
String forgeVersion = argsList.get(argsList.indexOf("--fml.forgeVersion") + 1); String forgeGroup = argsList.contains("--fml.forgeGroup") ? argsList.get(argsList.indexOf("--fml.forgeGroup") + 1) : "net.neoforged";
String forgeFullVersion = mcVersion + "-" + forgeVersion; String forgeArtifact = isNeoForge ? "neoforge" : "forge";
String forgeVersionKey = isNeoForge ? "--fml.neoForgeVersion" : "--fml.forgeVersion";
String forgeVersion = argsList.get(argsList.indexOf(forgeVersionKey) + 1);
String forgeFullVersion = isNeoForge ? forgeVersion : mcVersion + "-" + forgeVersion;
IFileDetector detector = DetectorLoader.loadDetector(); IFileDetector detector = DetectorLoader.loadDetector();
try { try {
Bootstrap.bootstrap(detector.getJvmArgs(forgeFullVersion), detector.getMinecraftJar(mcVersion).getFileName().toString(), detector.getLibraryDir().toAbsolutePath().toString()); Bootstrap.bootstrap(detector.getJvmArgs(forgeGroup, forgeArtifact, forgeFullVersion), detector.getMinecraftJar(mcVersion).getFileName().toString(), detector.getLibraryDir().toAbsolutePath().toString());
} catch (Throwable ignored) { } catch (Throwable ignored) {
// Avoid this bunch of hacks that nuke the whole wrapper. // Avoid this bunch of hacks that nuke the whole wrapper.
} }
if (!detector.checkExtraFiles(forgeFullVersion)) { if (!detector.checkExtraFiles(forgeGroup, forgeArtifact, forgeFullVersion)) {
System.out.println("Some extra libraries are missing! Running the installer to generate them now."); System.out.println("Some extra libraries are missing! Running the installer to generate them now.");
// Check installer jar. // Check installer jar.
Path installerJar = detector.getInstallerJar(forgeFullVersion); Path installerJar = detector.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
if (!IFileDetector.isFile(installerJar)) { if (!IFileDetector.isFile(installerJar)) {
throw new RuntimeException("Unable to detect the forge installer!"); throw new RuntimeException("Unable to detect the forge installer!");
} }
@ -53,7 +62,8 @@ public class Main {
} }
} }
Class<?> mainClass = ModuleUtil.setupBootstrapLauncher(Class.forName(detector.getMainClass(forgeFullVersion))); 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 }); mainClass.getMethod("main", String[].class).invoke(null, new Object[] { args });
} }
} }

View file

@ -61,10 +61,12 @@ public interface IFileDetector {
} }
/** /**
* @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). * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
* @return The forge installer jar path. It can also be defined by JVM argument "-Dforgewrapper.installer=&lt;installer-path&gt;". * @return The forge installer jar path. It can also be defined by JVM argument "-Dforgewrapper.installer=&lt;installer-path&gt;".
*/ */
default Path getInstallerJar(String forgeFullVersion) { default Path getInstallerJar(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
String installer = System.getProperty("forgewrapper.installer"); String installer = System.getProperty("forgewrapper.installer");
if (installer != null) { if (installer != null) {
return Paths.get(installer).toAbsolutePath(); return Paths.get(installer).toAbsolutePath();
@ -85,12 +87,14 @@ public interface IFileDetector {
} }
/** /**
* @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). * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
* @return The list of jvm args. * @return The list of jvm args.
*/ */
default List<String> getJvmArgs(String forgeFullVersion) { default List<String> getJvmArgs(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
return this.getDataFromInstaller(forgeFullVersion, "version.json", e -> { return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "version.json", e -> {
JsonElement element = getElement(e.getAsJsonObject().getAsJsonObject("arguments"), "jvm"); JsonElement element = e.getAsJsonObject().get("arguments").getAsJsonObject().get("jvm");
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
if (!element.equals(JsonNull.INSTANCE)) { if (!element.equals(JsonNull.INSTANCE)) {
element.getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString())); element.getAsJsonArray().iterator().forEachRemaining(je -> args.add(je.getAsString()));
@ -99,25 +103,42 @@ public interface IFileDetector {
}); });
} }
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). * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
* @return The main class. * @return The main class.
*/ */
default String getMainClass(String forgeFullVersion) { default String getMainClass(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
return this.getDataFromInstaller(forgeFullVersion, "version.json", e -> e.getAsJsonObject().getAsJsonPrimitive("mainClass").getAsString()); 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). * @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. * @return The json object in the-installer-jar-->install_profile.json-->data-->xxx-->client.
*/ */
default JsonObject getInstallProfileExtraData(String forgeFullVersion) { default JsonObject getInstallProfileExtraData(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
return this.getDataFromInstaller(forgeFullVersion, "install_profile.json", e -> e.getAsJsonObject().getAsJsonObject("data")); return this.getDataFromInstaller(forgeGroup, forgeArtifact, forgeFullVersion, "install_profile.json", e -> e.getAsJsonObject().getAsJsonObject("data"));
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
default <R> R getDataFromInstaller(String forgeFullVersion, String entry, Function<JsonElement, R> function) { default <R> R getDataFromInstaller(String forgeGroup, String forgeArtifact, String forgeFullVersion, String entry, Function<JsonElement, R> function) {
Path installer = this.getInstallerJar(forgeFullVersion); Path installer = this.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
if (isFile(installer)) { if (isFile(installer)) {
try (ZipFile zf = new ZipFile(installer.toFile())) { try (ZipFile zf = new ZipFile(installer.toFile())) {
ZipEntry ze = zf.getEntry(entry); ZipEntry ze = zf.getEntry(entry);
@ -140,11 +161,13 @@ public interface IFileDetector {
/** /**
* Check all cached files. * 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). * @param forgeFullVersion Forge full version (e.g. 1.14.4-28.2.0).
* @return True represents all files are ready. * @return True represents all files are ready.
*/ */
default boolean checkExtraFiles(String forgeFullVersion) { default boolean checkExtraFiles(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
JsonObject jo = this.getInstallProfileExtraData(forgeFullVersion); JsonObject jo = this.getInstallProfileExtraData(forgeGroup, forgeArtifact, forgeFullVersion);
if (jo != null) { if (jo != null) {
Map<String, Path> libsMap = new HashMap<>(); Map<String, Path> libsMap = new HashMap<>();
Map<String, String> hashMap = new HashMap<>(); Map<String, String> hashMap = new HashMap<>();
@ -152,7 +175,7 @@ public interface IFileDetector {
// Get all "data/<name>/client" elements. // Get all "data/<name>/client" elements.
Pattern artifactPattern = Pattern.compile("^\\[(?<groupId>[^:]*):(?<artifactId>[^:]*):(?<version>[^:@]*)(:(?<classifier>[^@]*))?(@(?<type>[^]]*))?]$"); Pattern artifactPattern = Pattern.compile("^\\[(?<groupId>[^:]*):(?<artifactId>[^:]*):(?<version>[^:@]*)(:(?<classifier>[^@]*))?(@(?<type>[^]]*))?]$");
for (Map.Entry<String, JsonElement> entry : jo.entrySet()) { for (Map.Entry<String, JsonElement> entry : jo.entrySet()) {
String clientStr = getElement(entry.getValue().getAsJsonObject(), "client").getAsString(); String clientStr = entry.getValue().getAsJsonObject().get("client").getAsString();
if (entry.getKey().endsWith("_SHA")) { if (entry.getKey().endsWith("_SHA")) {
Pattern p = Pattern.compile("^'(?<sha1>[A-Za-z0-9]{40})'$"); Pattern p = Pattern.compile("^'(?<sha1>[A-Za-z0-9]{40})'$");
Matcher m = p.matcher(clientStr); Matcher m = p.matcher(clientStr);
@ -171,7 +194,7 @@ public interface IFileDetector {
.resolve(groupId.replace('.', File.separatorChar)) .resolve(groupId.replace('.', File.separatorChar))
.resolve(artifactId) .resolve(artifactId)
.resolve(version) .resolve(version)
.resolve(artifactId + "-" + version + (classifier.equals("") ? "" : "-") + classifier + "." + type).toAbsolutePath()); .resolve(artifactId + "-" + version + (classifier.isEmpty() ? "" : "-") + classifier + "." + type).toAbsolutePath());
} }
} }
} }
@ -225,14 +248,6 @@ public interface IFileDetector {
return path != null && Files.isRegularFile(path); return path != null && Files.isRegularFile(path);
} }
static JsonElement getElement(JsonObject object, String property) {
Optional<Map.Entry<String, JsonElement>> first = object.entrySet().stream().filter(e -> e.getKey().equals(property)).findFirst();
if (first.isPresent()) {
return first.get().getValue();
}
return JsonNull.INSTANCE;
}
static String getFileSHA1(Path path) { static String getFileSHA1(Path path) {
try { try {
StringBuilder sha1 = new StringBuilder(new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(path))).toString(16)); StringBuilder sha1 = new StringBuilder(new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(path))).toString(16));

View file

@ -27,10 +27,16 @@ public class MultiMCFileDetector implements IFileDetector {
} }
@Override @Override
public Path getInstallerJar(String forgeFullVersion) { public Path getInstallerJar(String forgeGroup, String forgeArtifact, String forgeFullVersion) {
Path path = IFileDetector.super.getInstallerJar(forgeFullVersion); Path path = IFileDetector.super.getInstallerJar(forgeGroup, forgeArtifact, forgeFullVersion);
if (path == null) { if (path == null) {
return this.installerJar != null ? this.installerJar : (this.installerJar = this.getLibraryDir().resolve("net").resolve("minecraftforge").resolve("forge").resolve(forgeFullVersion).resolve("forge-" + forgeFullVersion + "-installer.jar").toAbsolutePath()); if (this.installerJar == null) {
Path installerBase = this.getLibraryDir();
for (String dir : forgeGroup.split("\\."))
installerBase = installerBase.resolve(dir);
this.installerJar = installerBase.resolve(forgeArtifact).resolve(forgeFullVersion).resolve(forgeArtifact + "-" + forgeFullVersion + "-installer.jar").toAbsolutePath();
}
return this.installerJar;
} }
return path; return path;
} }

View file

@ -1,5 +1,10 @@
package io.github.zekerzhayard.forgewrapper.installer.util; package io.github.zekerzhayard.forgewrapper.installer.util;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.List; import java.util.List;
public class ModuleUtil { public class ModuleUtil {
@ -15,6 +20,14 @@ public class ModuleUtil {
// nothing to do with Java 8 // nothing to do with Java 8
} }
public static void setupClassPath(Path libraryDir, List<String> paths) throws Throwable {
Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURLMethod.setAccessible(true);
for (String path : paths) {
addURLMethod.invoke(ClassLoader.getSystemClassLoader(), libraryDir.resolve(path).toUri().toURL());
}
}
public static Class<?> setupBootstrapLauncher(Class<?> mainClass) { public static Class<?> setupBootstrapLauncher(Class<?> mainClass) {
// nothing to do with Java 8 // nothing to do with Java 8
return mainClass; return mainClass;