Skip to content

Fix #3540 MultiMC 整合包安装问题 #3547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Jun 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f655bba
Translate exception message.
burningtnt Jan 25, 2025
303bdb8
Fix #3540: MultiMC json-patch native data.
burningtnt Jan 27, 2025
9545b7f
Fix: Invalid game arguments.
burningtnt Jan 28, 2025
259fed6
Fix: MultiMC json-patch priority
burningtnt Jan 28, 2025
c0eb622
Fix: Read java version from MultiMC Modpack.
burningtnt Jan 29, 2025
8150267
Fix: checkstyle
burningtnt Jan 29, 2025
6af675c
Merge remote-tracking branch 'refs/remotes/official/main' into fix354…
burningtnt Apr 5, 2025
c86e85d
Refactor: 100% (maybe) support for MultiMC modpacks.
burningtnt Apr 12, 2025
399204d
Fix: MultiMC Json-Patch $.libraries should override all existing libr…
burningtnt Apr 13, 2025
1fe71e3
Fix: typo
burningtnt Apr 13, 2025
dda4338
Fix: OSRestriction should accept things like 'osx-arm64'.
burningtnt Apr 13, 2025
f0dd1a9
Merge remote-tracking branch 'official/main' into fix3540/multimc-pat…
burningtnt Apr 13, 2025
31bc7c1
Code cleanup.
burningtnt Apr 13, 2025
f70b0a3
Fix: Deal with JarMods.
burningtnt Apr 14, 2025
511bc8c
Fix: Json-Patch should accept $.minecraftArguments. Copy MultiMC suck…
burningtnt Apr 18, 2025
cf54161
More hardcoded versions :(
burningtnt Apr 19, 2025
5defa46
Fix: Hardcoded versioning is only used for vacant version info. Suppo…
burningtnt Apr 19, 2025
c9b0a8f
Fix: Support assetIndex.
burningtnt Apr 24, 2025
60cba55
Code cleanup.
burningtnt Apr 25, 2025
f488adb
Code cleanup.
burningtnt May 11, 2025
7d3558a
Merge remote-tracking branch 'official/main' into fix3540/multimc-pat…
burningtnt May 22, 2025
896a23e
Merge remote-tracking branch 'official/main' into fix3540/multimc-pat…
burningtnt May 31, 2025
7a14048
Refactor 99% codes.
burningtnt Jun 2, 2025
9609ea5
Enhance compatibility with other launcher.
burningtnt Jun 2, 2025
9540e8a
Fix.
burningtnt Jun 2, 2025
a471fa9
Code cleanup.
burningtnt Jun 6, 2025
b654ccb
Support more traits.
burningtnt Jun 7, 2025
1d7da63
Merge remote-tracking branch 'official/main' into fix3540/multimc-pat…
burningtnt Jun 7, 2025
4ef72f2
Support XR:Initial
burningtnt Jun 7, 2025
d7c3f47
Merge remote-tracking branch 'official/main' into fix3540/multimc-pat…
burningtnt Jun 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,13 @@ public InstallerItemGroup(String gameVersion, Style style) {

for (InstallerItem item : all) {
if (!item.resolvedStateProperty.isBound()) {
item.resolvedStateProperty.bind(item.versionProperty);
item.resolvedStateProperty.bind(Bindings.createObjectBinding(() -> {
InstalledState itemVersion = item.versionProperty.get();
if (itemVersion != null) {
return itemVersion;
}
return InstallableState.INSTANCE;
}, item.versionProperty));
}
}

Expand Down Expand Up @@ -385,7 +391,10 @@ private static final class InstallerItemSkin extends SkinBase<InstallerItem> {
if (control.id.equals(MINECRAFT.getPatchId())) {
removeButton.setVisible(false);
} else {
removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> control.resolvedStateProperty.get() instanceof InstalledState, control.resolvedStateProperty));
removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
State state = control.resolvedStateProperty.get();
return state instanceof InstalledState && !((InstalledState) state).external;
}, control.resolvedStateProperty));
}
removeButton.managedProperty().bind(removeButton.visibleProperty());
removeButton.setOnAction(e -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private Version getPatch(FabricInfo fabricInfo, String gameVersion, String loade
libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.intermediary.maven), "https://maven.fabricmc.net/", null));
libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.loader.maven), "https://maven.fabricmc.net/", null));

return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries);
return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries);
}

public static class FabricInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ public void execute() throws Exception {
dependencyManager.checkLibraryCompletionAsync(forgeVersion, true)));

setResult(forgeVersion
.setPriority(30000)
.setPriority(Version.PRIORITY_LOADER)
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
.setVersion(selfVersion));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void execute() throws Exception {
}

setResult(installProfile.getVersionInfo()
.setPriority(30000)
.setPriority(Version.PRIORITY_LOADER)
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
.setVersion(selfVersion));
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public boolean isRelyingOnDependencies() {
@Override
public void execute() throws Exception {
Version patch = JsonUtils.fromNonNullJson(downloadTask.getResult(), Version.class)
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(0);
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(Version.PRIORITY_MC);
setResult(patch);

Version version = new Version(this.version.getId()).addPatch(patch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public void execute() throws Exception {
dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true)));

setResult(neoForgeVersion
.setPriority(30000)
.setPriority(Version.PRIORITY_LOADER)
.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId())
.setVersion(selfVersion));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private Version getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderV
libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.intermediary.maven), getMavenRepositoryByGroup(quiltInfo.intermediary.maven), null));
libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.loader.maven), getMavenRepositoryByGroup(quiltInfo.loader.maven), null));

return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries);
return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries);
}

private static String getMavenRepositoryByGroup(String maven) {
Expand Down
8 changes: 6 additions & 2 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/game/Arguments.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ public Arguments addJVMArguments(String... jvmArguments) {
}

public Arguments addJVMArguments(List<String> jvmArguments) {
List<Argument> list = jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList());
return new Arguments(getGame(), Lang.merge(getJvm(), list));
return addJVMArgumentsDirect(jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList()));
}

// TODO: How to distinguish addJVMArgumentsDirect from addJVMArguments? Naming is hard :)
public Arguments addJVMArgumentsDirect(List<Argument> jvmArguments) {
return new Arguments(getGame(), Lang.merge(getJvm(), jvmArguments));
}

public static Arguments merge(Arguments a, Arguments b) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,15 @@ public File getLibrariesDirectory(Version version) {

@Override
public File getLibraryFile(Version version, Library lib) {
if ("local".equals(lib.getHint()) && lib.getFileName() != null)
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
else
return new File(getLibrariesDirectory(version), lib.getPath());
if ("local".equals(lib.getHint())) {
if (lib.getFileName() != null) {
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
}

return new File(getVersionRoot(version.getId()), "libraries/" + lib.getArtifact().getFileName());
}

return new File(getLibrariesDirectory(version), lib.getPath());
}

public Path getArtifactFile(Version version, Artifact artifact) {
Expand Down
103 changes: 89 additions & 14 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@
*/
package org.jackhuang.hmcl.game;

import com.google.gson.*;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.*;

/**
* A class that describes a Minecraft dependency.
Expand All @@ -40,6 +38,40 @@
*/
@Immutable
public class Library implements Comparable<Library>, Validation {
/**
* <p>A possible native descriptors can be: [variant-]os[-key]</p>
*
* <p>
* Variant can be empty string, 'native', or 'natives'.
* Key can be empty string, system arch, or system arch bit count.
* </p>
*/
private static final String[] POSSIBLE_NATIVE_DESCRIPTORS;

static {
String[] keys = {
"",
Architecture.SYSTEM_ARCH.name().toLowerCase(Locale.ROOT),
Architecture.SYSTEM_ARCH.getBits().getBit()
}, variants = {"", "native", "natives"};

POSSIBLE_NATIVE_DESCRIPTORS = new String[keys.length * variants.length];
StringBuilder builder = new StringBuilder();
for (int i = 0; i < keys.length; i++) {
for (int j = 0; j < variants.length; j++) {
if (!variants[j].isEmpty()) {
builder.append(variants[j]).append('-');
}
builder.append(OperatingSystem.CURRENT_OS.getMojangName());
if (!keys[i].isEmpty()) {
builder.append('-').append(keys[i]);
}

POSSIBLE_NATIVE_DESCRIPTORS[i * variants.length + j] = builder.toString();
builder.setLength(0);
}
}
}

@SerializedName("name")
private final Artifact artifact;
Expand Down Expand Up @@ -93,13 +125,27 @@ public String getVersion() {
}

public String getClassifier() {
if (artifact.getClassifier() == null)
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS.getMojangName()))
return natives.get(OperatingSystem.CURRENT_OS.getMojangName()).replace("${arch}", Architecture.SYSTEM_ARCH.getBits().getBit());
else
return null;
else
if (artifact.getClassifier() == null) {
if (natives != null) {
for (String nativeDescriptor : POSSIBLE_NATIVE_DESCRIPTORS) {
String nd = natives.get(nativeDescriptor);
if (nd != null) {
return nd.replace("${arch}", Architecture.SYSTEM_ARCH.getBits().getBit());
}
}
} else if (downloads != null && downloads.getClassifiers() != null) {
for (String nativeDescriptor : POSSIBLE_NATIVE_DESCRIPTORS) {
LibraryDownloadInfo info = downloads.getClassifiers().get(nativeDescriptor);
if (info != null) {
return nativeDescriptor;
}
}
}

return null;
} else {
return artifact.getClassifier();
}
}

public ExtractRules getExtract() {
Expand All @@ -111,10 +157,17 @@ public boolean appliesToCurrentEnvironment() {
}

public boolean isNative() {
return natives != null && appliesToCurrentEnvironment();
if (!appliesToCurrentEnvironment()) {
return false;
}
if (natives != null) {
return true;
}

return downloads != null && downloads.getClassifiers().keySet().stream().anyMatch(s -> s.startsWith("native"));
}

protected LibraryDownloadInfo getRawDownloadInfo() {
public LibraryDownloadInfo getRawDownloadInfo() {
if (downloads != null) {
if (isNative())
return downloads.getClassifiers().get(getClassifier());
Expand All @@ -125,6 +178,10 @@ protected LibraryDownloadInfo getRawDownloadInfo() {
}
}

public Artifact getArtifact() {
return artifact;
}

public String getPath() {
LibraryDownloadInfo temp = getRawDownloadInfo();
if (temp != null && temp.getPath() != null)
Expand All @@ -137,12 +194,28 @@ public LibraryDownloadInfo getDownload() {
LibraryDownloadInfo temp = getRawDownloadInfo();
String path = getPath();
return new LibraryDownloadInfo(path,
Optional.ofNullable(temp).map(LibraryDownloadInfo::getUrl).orElse(Optional.ofNullable(url).orElse(Constants.DEFAULT_LIBRARY_URL) + path),
computePath(temp, path),
temp != null ? temp.getSha1() : null,
temp != null ? temp.getSize() : 0
);
}

private String computePath(LibraryDownloadInfo raw, String path) {
if (raw != null) {
String url = raw.getUrl();
if (url != null) {
return url;
}
}

String repo = Lang.requireNonNullElse(url, Constants.DEFAULT_LIBRARY_URL);
if (!repo.endsWith("/")) {
repo += '/';
}

return repo + path;
}

public boolean hasDownloadURL() {
LibraryDownloadInfo temp = getRawDownloadInfo();
if (temp != null) return temp.getUrl() != null;
Expand All @@ -159,6 +232,7 @@ public List<CompatibilityRule> getRules() {

/**
* Hint for how to locate the library file.
*
* @return null for default, "local" for location in version/&lt;version&gt;/libraries/filename
*/
@Nullable
Expand All @@ -168,6 +242,7 @@ public String getHint() {

/**
* Available when hint is "local"
*
* @return the filename of the local library in version/&lt;version&gt;/libraries/$filename
*/
@Nullable
Expand Down
35 changes: 21 additions & 14 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/game/OSRestriction.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.regex.Pattern;

/**
*
* @author huangyuhui
*/
public final class OSRestriction {
Expand Down Expand Up @@ -57,19 +56,28 @@ public OSRestriction(String name, String version, String arch) {
this.arch = arch;
}

public String getName() {
return name;
}

public String getVersion() {
return version;
}

public String getArch() {
return arch;
}

public boolean allow() {
// Some modpacks directly use { name: "win-x86" }
if (name != null) {
String[] parts = name.split("-", 3);
if (parts.length == 2) {
OperatingSystem os = OperatingSystem.parseOSName(parts[0]);
Architecture arch = Architecture.parseArchName(parts[1]);

if (os != OperatingSystem.UNKNOWN && arch != Architecture.UNKNOWN) {
if (os != OperatingSystem.CURRENT_OS && !(os == OperatingSystem.LINUX && OperatingSystem.CURRENT_OS.isLinuxOrBSD())) {
return false;
}

if (arch != Architecture.SYSTEM_ARCH) {
return false;
}

return true;
}
}
}

OperatingSystem os = OperatingSystem.parseOSName(name);
if (os != OperatingSystem.UNKNOWN
&& os != OperatingSystem.CURRENT_OS
Expand All @@ -85,5 +93,4 @@ public boolean allow() {

return true;
}

}
Loading