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 55869eb12..06baad358 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
@@ -45,6 +45,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -96,7 +97,10 @@ public final class MultiMCInstancePatch {
@SerializedName("jarMods")
private final List jarMods;
- public MultiMCInstancePatch(int formatVersion, String id, String version, AssetIndexInfo assetIndex, String minecraftArguments, List jvmArgs, String mainClass, int[] javaMajors, Library mainJar, List traits, List tweakers, List libraries0, List libraries1, List mavenFiles, List jarMods) {
+ @SerializedName("requires")
+ private final List requires;
+
+ public MultiMCInstancePatch(int formatVersion, String id, String version, AssetIndexInfo assetIndex, String minecraftArguments, List jvmArgs, String mainClass, int[] javaMajors, Library mainJar, List traits, List tweakers, List libraries0, List libraries1, List mavenFiles, List jarMods, List requires) {
this.formatVersion = formatVersion;
this.id = id;
this.version = version;
@@ -112,6 +116,7 @@ public final class MultiMCInstancePatch {
this.libraries1 = libraries1;
this.mavenFiles = mavenFiles;
this.jarMods = jarMods;
+ this.requires = requires;
}
public int getFormatVersion() {
@@ -177,6 +182,10 @@ public final class MultiMCInstancePatch {
return nonNullOrEmpty(jarMods);
}
+ public List getRequires() {
+ return nonNullOrEmpty(requires);
+ }
+
private static List nonNullOrEmpty(List value) {
return value != null && !value.isEmpty() ? value : Collections.emptyList();
}
@@ -257,6 +266,8 @@ public final class MultiMCInstancePatch {
}
for (MultiMCInstancePatch patch : patches) {
+ Objects.requireNonNull(patch, "patch");
+
if (patch.getFormatVersion() != 1) {
throw new UnsupportedOperationException(
String.format("Unsupported JSON-Patch[%s] format version: %d", patch.getID(), patch.getFormatVersion())
diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java
index 3fecb7ebd..a4ac2d1e4 100644
--- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java
+++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java
@@ -86,7 +86,7 @@ public final class MultiMCManifest {
return equalsVersion;
}
- public String getUid() {
+ public String getID() {
return uid;
}
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 64c531355..2efdf0275 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
@@ -44,16 +44,26 @@ import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
-import java.util.stream.Collectors;
/**
* 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 and Apply JAR mods.
+ *
+ * - General Setup
+ * - Load Components
+ * - Resolve Json-Patch
+ * - Build Artifact
+ * - Copy Embedded Files
+ * - Assemble Game
+ * - Download Game
+ * - Apply JAR mods
+ *
* See codes below for detailed implementation.
*
*/
@@ -64,7 +74,6 @@ public final class MultiMCModpackInstallTask extends Task> patches = new ArrayList<>();
private final List> dependents = new ArrayList<>();
private final List> dependencies = new ArrayList<>();
private final DefaultDependencyManager dependencyManager;
@@ -125,29 +134,74 @@ public final class MultiMCModpackInstallTask extends Task> patches = new ArrayList<>();
for (MultiMCManifest.MultiMCManifestComponent component : Objects.requireNonNull(
Objects.requireNonNull(manifest.getMmcPack(), "mmc-pack.json").getComponents(), "components"
)) {
String componentID = Objects.requireNonNull(component.getUid(), "Component ID");
Path patchPath = root.resolve(String.format("patches/%s.json", componentID));
- Task task;
if (Files.exists(patchPath)) {
if (!Files.isRegularFile(patchPath)) {
throw new IllegalArgumentException("Json-Patch isn't a file: " + componentID);
}
- // TODO: Task.completed has unclear compatibility issue.
- String text = FileUtils.readText(patchPath, StandardCharsets.UTF_8);
- task = Task.supplyAsync(() -> text);
+ MultiMCInstancePatch patch = MultiMCInstancePatch.read(componentID, FileUtils.readText(patchPath, StandardCharsets.UTF_8));
+ patches.add(Task.supplyAsync(() -> patch)); // TODO: Task.completed has unclear compatibility issue.
} else {
- task = new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion()));
+ patches.add(
+ new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion()))
+ .thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s))
+ );
+ }
+ }
+ dependents.add(new MMCInstancePatchesAssembleTask(patches));
+ }
+ }
+
+ private static final class MMCInstancePatchesAssembleTask extends Task> {
+ private final List> patches;
+
+ public MMCInstancePatchesAssembleTask(List> patches) {
+ this.patches = patches;
+ }
+
+ @Override
+ public Collection extends Task>> getDependents() {
+ return patches;
+ }
+
+ @Override
+ public void execute() throws Exception {
+ Map existed = new HashMap<>();
+ for (Task patch : patches) {
+ MultiMCInstancePatch result = patch.getResult();
+
+ existed.put(result.getID(), result);
+ }
+
+ checking:
+ while (true) {
+ for (MultiMCInstancePatch patch : existed.values()) {
+ for (MultiMCManifest.MultiMCManifestCachedRequires require : patch.getRequires()) {
+ String componentID = require.getID();
+ if (!existed.containsKey(componentID)) {
+ Task task = new GetTask(MultiMCComponents.getMetaURL(
+ componentID, Lang.requireNonNullElse(require.getEqualsVersion(), require.getSuggests())
+ )).thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s));
+ task.run();
+
+ MultiMCInstancePatch result = Objects.requireNonNull(task.getResult());
+ existed.put(result.getID(), result);
+ continue checking;
+ }
+ }
}
- Task task2 = task.thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s));
- patches.add(task2);
- dependents.add(task2);
+ break;
}
+
+ setResult(new ArrayList<>(existed.values()));
}
}
@@ -160,10 +214,15 @@ public final class MultiMCModpackInstallTask extends Task Objects.requireNonNull(value.getResult(), "MultiMCInstancePatch"))
- .collect(Collectors.toList()), name
- );
+ MultiMCInstancePatch.ResolvedInstance artifact = null;
+ for (int i = dependents.size() - 1; i >= 0; i--) {
+ Task> task = dependents.get(i);
+ if (task instanceof MMCInstancePatchesAssembleTask) {
+ artifact = MultiMCInstancePatch.resolveArtifact(((MMCInstancePatchesAssembleTask) task).getResult(), name);
+ break;
+ }
+ }
+ Objects.requireNonNull(artifact, "artifact");
// Stage #4: Copy embedded files.
try (FileSystem fs = openModpack()) {