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")