From a39a23f938d09201f8d8e2d6eff485aaa8c1d0c6 Mon Sep 17 00:00:00 2001 From: Burning_TNT Date: Sun, 14 Sep 2025 20:50:53 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E6=97=A0=E6=B3=95=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E5=B8=A6=E6=9C=89=20Fabric=20=E7=9A=84=E9=9D=9E=E6=A0=87?= =?UTF-8?q?=E5=87=86=20MMC=20=E6=95=B4=E5=90=88=E5=8C=85=20(#4034)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复无法安装带有 Fabric 的非标准 MMC 整合包的漏洞 - 在安装时写入当前安装器实现信息和启动器信息,以在后续调试时获得更多信息 Fix #4049 --- .gitignore | 4 +- .../jackhuang/hmcl/game/ModpackHelper.java | 6 + HMCLCore/build.gradle.kts | 13 ++ .../java/org/jackhuang/hmcl/game/Library.java | 4 + .../hmcl/mod/multimc/MultiMCComponents.java | 57 ++++++++- .../mod/multimc/MultiMCInstancePatch.java | 13 ++ .../multimc/MultiMCModpackInstallTask.java | 78 +++++++++--- .../HMCLTransformerDiscoveryService-1.0.jar | Bin 2243 -> 0 bytes .../HMCLMultiMCBootstrap/build.gradle.kts | 15 +++ .../jackhuang/hmcl/HMCLMultiMCBootstrap.java | 116 ++++++++++++++++++ settings.gradle.kts | 6 +- 11 files changed, 290 insertions(+), 22 deletions(-) delete mode 100644 HMCLCore/src/main/resources/assets/game/HMCLTransformerDiscoveryService-1.0.jar create mode 100644 minecraft/libraries/HMCLMultiMCBootstrap/build.gradle.kts create mode 100644 minecraft/libraries/HMCLMultiMCBootstrap/src/main/java/org/jackhuang/hmcl/HMCLMultiMCBootstrap.java diff --git a/.gitignore b/.gitignore index fc25cc840..082849356 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,8 @@ hmcl-exported-logs-* /HMCL/build/ /HMCLCore/build/ /HMCLBoot/build/ -/HMCLTransformerDiscoveryService/build/ /minecraft/libraries/HMCLTransformerDiscoveryService/build/ +/minecraft/libraries/HMCLMultiMCBootstrap/build/ /buildSrc/build/ # idea @@ -30,12 +30,14 @@ hmcl-exported-logs-* /HMCL/out/ /HMCLCore/out/ /minecraft/libraries/HMCLTransformerDiscoveryService/out/ +/minecraft/libraries/HMCLMultiMCBootstrap/out/ # eclipse /bin/ /HMCL/bin/ /HMCLCore/bin/ /minecraft/libraries/HMCLTransformerDiscoveryService/bin/ +/minecraft/libraries/HMCLMultiMCBootstrap/bin/ .classpath .project .settings diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 7d189d253..ff8763ac7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -19,11 +19,13 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; +import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.mod.curse.CurseModpackProvider; import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackProvider; import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackProvider; +import org.jackhuang.hmcl.mod.multimc.MultiMCComponents; import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; import org.jackhuang.hmcl.mod.multimc.MultiMCModpackProvider; import org.jackhuang.hmcl.mod.server.ServerModpackManifest; @@ -69,6 +71,10 @@ public final class ModpackHelper { pair(HMCLModpackProvider.INSTANCE.getName(), HMCLModpackProvider.INSTANCE) ); + static { + MultiMCComponents.setImplementation(Metadata.FULL_TITLE); + } + @Nullable public static ModpackProvider getProviderByType(String type) { return providers.get(type); diff --git a/HMCLCore/build.gradle.kts b/HMCLCore/build.gradle.kts index e1a8c9212..86ca2bde9 100644 --- a/HMCLCore/build.gradle.kts +++ b/HMCLCore/build.gradle.kts @@ -32,3 +32,16 @@ dependencies { testImplementation(libs.jna.platform) testImplementation(libs.jimfs) } + +tasks.processResources { + listOf( + "HMCLTransformerDiscoveryService", + "HMCLMultiMCBootstrap" + ).map { project(":$it").tasks["jar"] as Jar }.forEach { task -> + dependsOn(task) + + into("assets/game") { + from(task.outputs.files) + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java index ae5ce6864..dc253ca8e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java @@ -241,6 +241,10 @@ public class Library implements Comparable, Validation { return hint; } + public Library withoutCommunityFields() { + return new Library(artifact, url, downloads, checksums, extract, natives, rules, null, null); + } + /** * Available when hint is "local" * diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java index c9452d01f..b4c9c78a9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java @@ -4,7 +4,9 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.util.io.NetworkUtils; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public final class MultiMCComponents { @@ -12,6 +14,41 @@ public final class MultiMCComponents { private MultiMCComponents() { } + private static final Map INSTALLER_PROFILE = new ConcurrentHashMap<>(); + + static { + // Please append a phrase below while fixing bugs or implementing new features for Instance Format transformer + INSTALLER_PROFILE.put("Patches", "recursive install, fabric & quilt intermediary"); + + // Check whether MultiMCComponents is 'org.jackhuang.hmcl.mod.multimc.MultiMCComponents'. + // We use a base64-encoded value here to prevent string literals from being replaced by IDE if users trigger the 'Refactor' feature. + if (new String( + Base64.getDecoder().decode("b3JnLmphY2todWFuZy5obWNsLm1vZC5tdWx0aW1jLk11bHRpTUNDb21wb25lbnRz"), + StandardCharsets.UTF_8 + ).equals(MultiMCComponents.class.getName())) { + INSTALLER_PROFILE.put("Implementation", "Probably vanilla. Class location is not modified (org.jackhuang.hmcl.mod.multimc.MultiMCComponents)."); + } else { + INSTALLER_PROFILE.put("Implementation", "Not vanilla. Class location is " + MultiMCComponents.class.getName()); + } + } + + public static void setImplementation(String implementation) { + INSTALLER_PROFILE.put("Implementation", implementation); + } + + public static String getInstallerProfile() { + StringBuilder builder = new StringBuilder(); + for (Map.Entry entry : INSTALLER_PROFILE.entrySet()) { + builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + + if (builder.length() != 0) { + builder.setLength(builder.length() - 1); + } + + return builder.toString(); + } + private static final Map ID_TYPE = new HashMap<>(); static { @@ -46,7 +83,25 @@ public final class MultiMCComponents { return PAIRS; } - public static URI getMetaURL(String componentID, String version) { + public static URI getMetaURL(String componentID, String version, String mcVersion) { + if (version == null) { + switch (componentID) { + case "org.lwjgl": { + version = "2.9.1"; + break; + } + case "org.lwjgl3": { + version = "3.1.2"; + break; + } + case "net.fabricmc.intermediary": + case "org.quiltmc.hashed": { + version = mcVersion; + break; + } + } + } + return NetworkUtils.toURI(String.format("https://meta.multimc.org/v1/%s/%s.json", componentID, version)); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java index 176a0c32c..51483e4a3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java @@ -37,6 +37,7 @@ import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonMap; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.logging.Logger; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -45,6 +46,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -54,6 +56,8 @@ import java.util.stream.Collectors; */ @Immutable public final class MultiMCInstancePatch { + public static final Library BOOTSTRAP_LIBRARY = new Library(new Artifact("org.jackhuang.hmcl", "mmc-bootstrap", "1.0")); + private final int formatVersion; @SerializedName("uid") @@ -390,6 +394,15 @@ public final class MultiMCInstancePatch { } } + { + libraries.add(0, BOOTSTRAP_LIBRARY); + jvmArguments.add(new StringArgument("-Dhmcl.mmc.bootstrap=" + NetworkUtils.withQuery("hmcl:///bootstrap_profile_v1/", Map.of( + "main_class", mainClass, + "installer", MultiMCComponents.getInstallerProfile() + )))); + mainClass = "org.jackhuang.hmcl.HMCLMultiMCBootstrap"; + } + Version version = new Version(versionID) .setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments)) .setMainClass(mainClass) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java index de756c11a..e3db99fc5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java @@ -19,11 +19,14 @@ package org.jackhuang.hmcl.mod.multimc; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.download.MaintainTask; import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.mod.MinecraftInstanceTask; import org.jackhuang.hmcl.mod.Modpack; @@ -38,14 +41,16 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -54,16 +59,20 @@ import java.util.Objects; *

A task transforming MultiMC Modpack Scheme to Official Launcher Scheme. * The transforming process contains 7 stage: *

    - *
  • General Setup
  • - *
  • Load Components
  • - *
  • Resolve Json-Patch
  • - *
  • Build Artifact
  • - *
  • Copy Embedded Files
  • - *
  • Assemble Game
  • - *
  • Download Game
  • - *
  • Apply JAR mods
  • + *
  • General Setup: Compute checksum and copy 'overrides' files.
  • + *
  • Load Components: Parse all local Json-Patch and prepare to fetch others from Internet.
  • + *
  • Resolve Json-Patch: Fetch remote Json-Patch and their dependencies.
  • + *
  • Build Artifact: Transform Json-Patch to Official Scheme lossily, without original structure.
  • + *
  • Copy Embedded Files: Copy embedded libraries and icon.
  • + *
  • Assemble Game: Prepare to download main jar, libraries and assets.
  • + *
  • Download Game: Download files.
  • + *
  • Apply JAR mods: Apply JAR mods into main jar.
  • *
* See codes below for detailed implementation. + * + * @implNote To guarantee all features of MultiMC Modpack Scheme is super hard. + * As f*** MMC never provides a detailed API docs, most codes below is guessed from its source code. + * FUNCTIONS OF GAMES MIGHT NOT BE COMPLETELY THE SAME WITH MMC. *

*/ public final class MultiMCModpackInstallTask extends Task { @@ -133,10 +142,23 @@ public final class MultiMCModpackInstallTask extends Task> patches = new ArrayList<>(); - for (MultiMCManifest.MultiMCManifestComponent component : Objects.requireNonNull( + List components = Objects.requireNonNull( Objects.requireNonNull(manifest.getMmcPack(), "mmc-pack.json").getComponents(), "components" - )) { + ); + List> patches = new ArrayList<>(); + + String mcVersion = null; + for (MultiMCManifest.MultiMCManifestComponent component : components) { + if (MultiMCComponents.getComponent(component.getUid()) == LibraryAnalyzer.LibraryType.MINECRAFT) { + mcVersion = component.getVersion(); + break; + } + } + if (mcVersion == null) { + throw new IllegalStateException("Cannot load modpacks without Minecraft."); + } + + for (MultiMCManifest.MultiMCManifestComponent component : components) { String componentID = Objects.requireNonNull(component.getUid(), "Component ID"); Path patchPath = root.resolve(String.format("patches/%s.json", componentID)); @@ -149,20 +171,22 @@ public final class MultiMCModpackInstallTask extends Task patch)); // TODO: Task.completed has unclear compatibility issue. } else { patches.add( - new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion())) + new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion(), mcVersion)) .thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s)) ); } } - dependents.add(new MMCInstancePatchesAssembleTask(patches)); + dependents.add(new MMCInstancePatchesAssembleTask(patches, mcVersion)); } } private static final class MMCInstancePatchesAssembleTask extends Task> { private final List> patches; + private final String mcVersion; - public MMCInstancePatchesAssembleTask(List> patches) { + public MMCInstancePatchesAssembleTask(List> patches, String mcVersion) { this.patches = patches; + this.mcVersion = mcVersion; } @Override @@ -172,7 +196,7 @@ public final class MultiMCModpackInstallTask extends Task existed = new HashMap<>(); + Map existed = new LinkedHashMap<>(); for (Task patch : patches) { MultiMCInstancePatch result = patch.getResult(); @@ -186,7 +210,7 @@ public final class MultiMCModpackInstallTask extends Task task = new GetTask(MultiMCComponents.getMetaURL( - componentID, Lang.requireNonNullElse(require.getEqualsVersion(), require.getSuggests()) + componentID, Lang.requireNonNullElse(require.getEqualsVersion(), require.getSuggests()), mcVersion )).thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s)); task.run(); @@ -231,6 +255,26 @@ public final class MultiMCModpackInstallTask extends TaskCTC}qCg$N+EsUW$BR4q*!vcTp@ zH?_zmvp6}wEVZaIIJKxOGdWc+IVZ8Wcy3sBxU{Q8omz~TnYX8=h^X$RWhM(!H-@ck z3YBuW;BZkP?p5xlnJUb)@0ONNo>lMq+H%%68QZ#VKW5wP+br#=kh!An#JMxp@1M?m zzIV=_pWiRvXS{RFS$494dm-oP#TCrkOH@8C^Apeaz3|sdXmv&W@z|OLW%5}Omka#e z#a_SKvoJCEuzS{S)23Wm`NzV%mb}(dWs+as+iH=tWv`K(Fd*PO|b9kIKNojvx6)}C^2B+)!20q_5B}<$=z3K4b zS^fs$^G|=z(qFp!$*W0cSGOwpZ-2OKx@2D3u9t60BwO}92rYHyu1sW6sZ~3&E%^C1 zomC#v_K}CUm%g6xYJodbRi>SaSNRU{jlP{}5|LZqgzfvXC;GGQM$J&AOV2m#UGhk( zf>B>;M^7g2ciuONw+<}-;p2SofSp~s$KB$)6AFs+-CIPrOZ4zRR%q4F_-WKy$sGSM zo0p%bra9lVCjCM6pM-|R2MQjVOn>rP$5Kj%*_DG?b&BM=dFNR~W6Ry9SUS!PG~Fh$ zEq>3HbA4==CRwEH_WQXM}YMeBD@icB9rI^X3~()tBzPdwsFP z=92cKl-`wJp0ysY^;11#AX2eyiH3Pq=#}O;-`QE4_dff*$MLVR%ysF*LCY?y)H%*S zbfu7G_uV?%9xGQXU(GJVBev(4-J5;3#7a+4Un~DvTDG&t^7(?(9kp-TEP0f+WAe+_ zOX7pimc*vU-tKd2exy?JIBCzMmk*z~O#YJ*R+A8M?#tyjj+|%P-)Svhf$l2_TQs3)KlQMpGx1SUH zy3tqJ%q-^Rv9CIZ|F)_ByPqnvY5!ZN@43@%m&LU;%~)j|Q@`$efbO^Jhh?_jP?1>n z;`=12YhTn8CVVs5t<0Hl=~RYn)F06|g1JI77QQb~+0!)rW%d!Siq>-r{_4c3>ZZml zFm-%=^|*)4u><-NIY}=xB_DHcVVNaS*>sWRUqfBZqsk@*hU*1_nM_+e1lGF->K&gK z6?i4DudXL=YO2-J)u%EW#n$c!?q=Svc(di=ksF15-yZue=~Ld}*;LEO9O$vNsfYK@ z0SP1ZhsO+@CiyI}Z*A?Ed-TW0FUy~s?yCLUvV5lpx5V5XH;x=R?_txmBcs^V+cD2A zV)}#2t0rG@?vOuu^tP_peZ{%f72@yzG9yY^^g>SvSSG=X<6=mOQ;boHT{eCjV+K@d z0>pk;btD&*>*eOB6oZJI#L~RvjMO4MsCkJ6nR=dhO1RVdUe`~1`J6ka;d{pW`sowC z=PrK!Q2LEi)9Z}3-)a3*zPd#xb^Olhd+V;^@zd2h?c?_pT-Y-*i7?}?WPr{D0s)4% zjvyND#{h3sZRk}FNHGX3Y19OgaBWBx5V8sA!3nBoAOK`>8ITF8b@1r`)jALW(lHCj z1nEH3LHP87>L3UJ={*5tqUr_LNazNj*GMoUmNfpxZU9mB6S^%RQ(;v;D2+h?$eI1D nNWltEap?NdQyk2MC5;EM>Zc_A1$eWvfz0OsLNB1BlfgUyP_HXA diff --git a/minecraft/libraries/HMCLMultiMCBootstrap/build.gradle.kts b/minecraft/libraries/HMCLMultiMCBootstrap/build.gradle.kts new file mode 100644 index 000000000..08e0c9f28 --- /dev/null +++ b/minecraft/libraries/HMCLMultiMCBootstrap/build.gradle.kts @@ -0,0 +1,15 @@ +version = "1.0" + +tasks.compileJava { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" +} + +tasks.jar { + manifest { + attributes( + "Created-By" to "Copyright(c) 2013-2025 huangyuhui.", + "Implementation-Version" to project.version + ) + } +} diff --git a/minecraft/libraries/HMCLMultiMCBootstrap/src/main/java/org/jackhuang/hmcl/HMCLMultiMCBootstrap.java b/minecraft/libraries/HMCLMultiMCBootstrap/src/main/java/org/jackhuang/hmcl/HMCLMultiMCBootstrap.java new file mode 100644 index 000000000..b91a82f8f --- /dev/null +++ b/minecraft/libraries/HMCLMultiMCBootstrap/src/main/java/org/jackhuang/hmcl/HMCLMultiMCBootstrap.java @@ -0,0 +1,116 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2020 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Scanner; + +public final class HMCLMultiMCBootstrap { + private HMCLMultiMCBootstrap() { + } + + public static void main(String[] args) throws Throwable { + String profile = System.getProperty("hmcl.mmc.bootstrap"); + if (profile == null) { + launchLegacy(args); + return; + } + + URI uri = URI.create(profile); + if (Objects.equals(uri.getPath(), "/bootstrap_profile_v1/")) { + launchV1(parseQuery(uri.getRawQuery()), args); + } + } + + private static void launchV1(Map arguments, String[] args) throws Throwable { + String mainClass = arguments.get("main_class"); + String installerInfo = arguments.get("installer"); + + launch(installerInfo, mainClass, args); + } + + private static void launchLegacy(String[] args) throws Throwable { + String mainClass = new String(Base64.getUrlDecoder().decode(System.getProperty("hmcl.mmc.bootstrap.main")), StandardCharsets.UTF_8); + String installerInfo = new String(Base64.getUrlDecoder().decode(System.getProperty("hmcl.mmc.bootstrap.installer")), StandardCharsets.UTF_8); + + launch(installerInfo, mainClass, args); + } + + private static void launch(String installerInfo, String mainClass, String[] args) throws Throwable { + System.out.println("This version is installed by HMCLCore's MultiMC combat layer."); + System.out.println("Installer Properties:"); + System.out.println(installerInfo); + System.out.println("Main Class: " + mainClass); + System.out.println("GAME MAY CRASH DUE TO BUGS. TEST YOUR GAME ON OFFICIAL MMC BEFORE REPORTING BUGS TO AUTHORS."); + + Method[] methods = Class.forName(mainClass).getMethods(); + for (Method method : methods) { + // https://docs.oracle.com/javase/specs/jls/se21/html/jls-12.html#jls-12.1.4 + if ("main".equals(method.getName()) && + Modifier.isStatic(method.getModifiers()) && + method.getReturnType() == void.class && + method.getParameterCount() == 1 && + method.getParameters()[0].getType() == String[].class + ) { + method.invoke(null, (Object) args); + return; + } + } + + throw new IllegalArgumentException("Cannot find method 'main(String[])' in " + mainClass); + } + + private static Map parseQuery(String queryParameterString) { + if (queryParameterString == null) return Collections.emptyMap(); + + Map result = new HashMap<>(); + + try (Scanner scanner = new Scanner(queryParameterString)) { + scanner.useDelimiter("&"); + while (scanner.hasNext()) { + String[] nameValue = scanner.next().split("="); + if (nameValue.length == 0 || nameValue.length > 2) { + throw new IllegalArgumentException("bad query string"); + } + + String name = decodeURL(nameValue[0]); + String value = nameValue.length == 2 ? decodeURL(nameValue[1]) : null; + result.put(name, value); + } + } + return result; + } + + private static String decodeURL(String value) { + try { + return URLDecoder.decode(value, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7831e3da1..a1a710b39 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,11 +2,11 @@ rootProject.name = "HMCL3" include( "HMCL", "HMCLCore", - "HMCLBoot", - "HMCLTransformerDiscoveryService" + "HMCLBoot" ) -val minecraftLibraries = listOf("HMCLTransformerDiscoveryService") +val minecraftLibraries = listOf("HMCLTransformerDiscoveryService", "HMCLMultiMCBootstrap") +include(minecraftLibraries) for (library in minecraftLibraries) { project(":$library").projectDir = file("minecraft/libraries/$library")