mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-08-03 19:36:53 -04:00
parent
88a7243b32
commit
7d7dbb9eb5
@ -281,7 +281,13 @@ public class InstallerItem extends Control {
|
|||||||
|
|
||||||
for (InstallerItem item : all) {
|
for (InstallerItem item : all) {
|
||||||
if (!item.resolvedStateProperty.isBound()) {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +391,10 @@ public class InstallerItem extends Control {
|
|||||||
if (control.id.equals(MINECRAFT.getPatchId())) {
|
if (control.id.equals(MINECRAFT.getPatchId())) {
|
||||||
removeButton.setVisible(false);
|
removeButton.setVisible(false);
|
||||||
} else {
|
} 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.managedProperty().bind(removeButton.visibleProperty());
|
||||||
removeButton.setOnAction(e -> {
|
removeButton.setOnAction(e -> {
|
||||||
|
@ -124,7 +124,7 @@ public final class FabricInstallTask extends Task<Version> {
|
|||||||
libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.intermediary.maven), "https://maven.fabricmc.net/", null));
|
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));
|
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 {
|
public static class FabricInfo {
|
||||||
|
@ -410,7 +410,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
|||||||
dependencyManager.checkLibraryCompletionAsync(forgeVersion, true)));
|
dependencyManager.checkLibraryCompletionAsync(forgeVersion, true)));
|
||||||
|
|
||||||
setResult(forgeVersion
|
setResult(forgeVersion
|
||||||
.setPriority(30000)
|
.setPriority(Version.PRIORITY_LOADER)
|
||||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||||
.setVersion(selfVersion));
|
.setVersion(selfVersion));
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setResult(installProfile.getVersionInfo()
|
setResult(installProfile.getVersionInfo()
|
||||||
.setPriority(30000)
|
.setPriority(Version.PRIORITY_LOADER)
|
||||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||||
.setVersion(selfVersion));
|
.setVersion(selfVersion));
|
||||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true));
|
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true));
|
||||||
|
@ -65,7 +65,7 @@ public class GameInstallTask extends Task<Version> {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
Version patch = JsonUtils.fromNonNullJson(downloadTask.getResult(), Version.class)
|
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);
|
setResult(patch);
|
||||||
|
|
||||||
Version version = new Version(this.version.getId()).addPatch(patch);
|
Version version = new Version(this.version.getId()).addPatch(patch);
|
||||||
|
@ -406,7 +406,7 @@ public class NeoForgeOldInstallTask extends Task<Version> {
|
|||||||
dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true)));
|
dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true)));
|
||||||
|
|
||||||
setResult(neoForgeVersion
|
setResult(neoForgeVersion
|
||||||
.setPriority(30000)
|
.setPriority(Version.PRIORITY_LOADER)
|
||||||
.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId())
|
.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId())
|
||||||
.setVersion(selfVersion));
|
.setVersion(selfVersion));
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ public final class QuiltInstallTask extends Task<Version> {
|
|||||||
libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.intermediary.maven), getMavenRepositoryByGroup(quiltInfo.intermediary.maven), null));
|
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));
|
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) {
|
private static String getMavenRepositoryByGroup(String maven) {
|
||||||
|
@ -79,8 +79,12 @@ public final class Arguments {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Arguments addJVMArguments(List<String> jvmArguments) {
|
public Arguments addJVMArguments(List<String> jvmArguments) {
|
||||||
List<Argument> list = jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList());
|
return addJVMArgumentsDirect(jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList()));
|
||||||
return new Arguments(getGame(), Lang.merge(getJvm(), list));
|
}
|
||||||
|
|
||||||
|
// 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) {
|
public static Arguments merge(Arguments a, Arguments b) {
|
||||||
|
@ -95,10 +95,15 @@ public class DefaultGameRepository implements GameRepository {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getLibraryFile(Version version, Library lib) {
|
public File getLibraryFile(Version version, Library lib) {
|
||||||
if ("local".equals(lib.getHint()) && lib.getFileName() != null)
|
if ("local".equals(lib.getHint())) {
|
||||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
if (lib.getFileName() != null) {
|
||||||
else
|
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
||||||
return new File(getLibrariesDirectory(version), lib.getPath());
|
}
|
||||||
|
|
||||||
|
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getArtifact().getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File(getLibrariesDirectory(version), lib.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getArtifactFile(Version version, Artifact artifact) {
|
public Path getArtifactFile(Version version, Artifact artifact) {
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import org.jackhuang.hmcl.util.Constants;
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||||
import org.jackhuang.hmcl.util.gson.Validation;
|
import org.jackhuang.hmcl.util.gson.Validation;
|
||||||
@ -28,10 +29,7 @@ import org.jackhuang.hmcl.util.platform.Architecture;
|
|||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that describes a Minecraft dependency.
|
* A class that describes a Minecraft dependency.
|
||||||
@ -40,6 +38,40 @@ import java.util.Optional;
|
|||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public class Library implements Comparable<Library>, Validation {
|
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")
|
@SerializedName("name")
|
||||||
private final Artifact artifact;
|
private final Artifact artifact;
|
||||||
@ -93,13 +125,27 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getClassifier() {
|
public String getClassifier() {
|
||||||
if (artifact.getClassifier() == null)
|
if (artifact.getClassifier() == null) {
|
||||||
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS.getMojangName()))
|
if (natives != null) {
|
||||||
return natives.get(OperatingSystem.CURRENT_OS.getMojangName()).replace("${arch}", Architecture.SYSTEM_ARCH.getBits().getBit());
|
for (String nativeDescriptor : POSSIBLE_NATIVE_DESCRIPTORS) {
|
||||||
else
|
String nd = natives.get(nativeDescriptor);
|
||||||
return null;
|
if (nd != null) {
|
||||||
else
|
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();
|
return artifact.getClassifier();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtractRules getExtract() {
|
public ExtractRules getExtract() {
|
||||||
@ -111,10 +157,17 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNative() {
|
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 (downloads != null) {
|
||||||
if (isNative())
|
if (isNative())
|
||||||
return downloads.getClassifiers().get(getClassifier());
|
return downloads.getClassifiers().get(getClassifier());
|
||||||
@ -125,6 +178,10 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Artifact getArtifact() {
|
||||||
|
return artifact;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||||
if (temp != null && temp.getPath() != null)
|
if (temp != null && temp.getPath() != null)
|
||||||
@ -137,12 +194,28 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||||
String path = getPath();
|
String path = getPath();
|
||||||
return new LibraryDownloadInfo(path,
|
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.getSha1() : null,
|
||||||
temp != null ? temp.getSize() : 0
|
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() {
|
public boolean hasDownloadURL() {
|
||||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||||
if (temp != null) return temp.getUrl() != null;
|
if (temp != null) return temp.getUrl() != null;
|
||||||
@ -159,6 +232,7 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hint for how to locate the library file.
|
* Hint for how to locate the library file.
|
||||||
|
*
|
||||||
* @return null for default, "local" for location in version/<version>/libraries/filename
|
* @return null for default, "local" for location in version/<version>/libraries/filename
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -168,6 +242,7 @@ public class Library implements Comparable<Library>, Validation {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Available when hint is "local"
|
* Available when hint is "local"
|
||||||
|
*
|
||||||
* @return the filename of the local library in version/<version>/libraries/$filename
|
* @return the filename of the local library in version/<version>/libraries/$filename
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -24,7 +24,6 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class OSRestriction {
|
public final class OSRestriction {
|
||||||
@ -57,19 +56,28 @@ public final class OSRestriction {
|
|||||||
this.arch = arch;
|
this.arch = arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getArch() {
|
|
||||||
return arch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean allow() {
|
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);
|
OperatingSystem os = OperatingSystem.parseOSName(name);
|
||||||
if (os != OperatingSystem.UNKNOWN
|
if (os != OperatingSystem.UNKNOWN
|
||||||
&& os != OperatingSystem.CURRENT_OS
|
&& os != OperatingSystem.CURRENT_OS
|
||||||
@ -85,5 +93,4 @@ public final class OSRestriction {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,11 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
|||||||
@Immutable
|
@Immutable
|
||||||
public class Version implements Comparable<Version>, Validation {
|
public class Version implements Comparable<Version>, Validation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches with higher priority can override info from other patches, such as mainClass.
|
||||||
|
*/
|
||||||
|
public static final int PRIORITY_MC = 0, PRIORITY_LOADER = 30000;
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final String version;
|
private final String version;
|
||||||
private final Integer priority;
|
private final Integer priority;
|
||||||
@ -302,7 +307,8 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
|
|
||||||
if (inheritsFrom == null) {
|
if (inheritsFrom == null) {
|
||||||
if (isRoot()) {
|
if (isRoot()) {
|
||||||
thisVersion = new Version(id).setPatches(patches);
|
// TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions.
|
||||||
|
thisVersion = patches != null ? new Version(id).setPatches(patches) : this;
|
||||||
} else {
|
} else {
|
||||||
thisVersion = this;
|
thisVersion = this;
|
||||||
}
|
}
|
||||||
@ -368,7 +374,7 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return thisVersion.setId(id).setJar(resolve(provider).getJar());
|
return thisVersion.setId(id).setJar(resolve(provider).getJar());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Version markAsResolved() {
|
public Version markAsResolved() {
|
||||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,6 +386,10 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Version setRoot(Boolean root) {
|
||||||
|
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
|
}
|
||||||
|
|
||||||
public Version setId(String id) {
|
public Version setId(String id) {
|
||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
@ -396,6 +406,10 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Version setJavaVersion(GameJavaVersion javaVersion) {
|
||||||
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
|
}
|
||||||
|
|
||||||
public Version setArguments(Arguments arguments) {
|
public Version setArguments(Arguments arguments) {
|
||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
@ -412,10 +426,18 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Version setAssetIndex(AssetIndexInfo assetIndex) {
|
||||||
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
|
}
|
||||||
|
|
||||||
public Version setLibraries(List<Library> libraries) {
|
public Version setLibraries(List<Library> libraries) {
|
||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Version setDownload(JsonMap<DownloadType, DownloadInfo> downloads) {
|
||||||
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
|
}
|
||||||
|
|
||||||
public Version setLogging(Map<DownloadType, LoggingInfo> logging) {
|
public Version setLogging(Map<DownloadType, LoggingInfo> logging) {
|
||||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,12 @@
|
|||||||
package org.jackhuang.hmcl.game.tlauncher;
|
package org.jackhuang.hmcl.game.tlauncher;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.Artifact;
|
||||||
|
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||||
|
import org.jackhuang.hmcl.game.ExtractRules;
|
||||||
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package org.jackhuang.hmcl.mod.multimc;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public final class MultiMCComponents {
|
||||||
|
|
||||||
|
private MultiMCComponents() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, LibraryAnalyzer.LibraryType> ID_TYPE = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
ID_TYPE.put("net.minecraft", LibraryAnalyzer.LibraryType.MINECRAFT);
|
||||||
|
ID_TYPE.put("net.minecraftforge", LibraryAnalyzer.LibraryType.FORGE);
|
||||||
|
ID_TYPE.put("net.neoforged", LibraryAnalyzer.LibraryType.NEO_FORGE);
|
||||||
|
ID_TYPE.put("com.mumfrey.liteloader", LibraryAnalyzer.LibraryType.LITELOADER);
|
||||||
|
ID_TYPE.put("net.fabricmc.fabric-loader", LibraryAnalyzer.LibraryType.FABRIC);
|
||||||
|
ID_TYPE.put("org.quiltmc.quilt-loader", LibraryAnalyzer.LibraryType.QUILT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<LibraryAnalyzer.LibraryType, String> TYPE_ID =
|
||||||
|
ID_TYPE.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
|
||||||
|
|
||||||
|
private static final Collection<Map.Entry<String, LibraryAnalyzer.LibraryType>> PAIRS = Collections.unmodifiableCollection(ID_TYPE.entrySet());
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (TYPE_ID.isEmpty()) {
|
||||||
|
throw new AssertionError("Please make sure TYPE_ID and PAIRS is initialized after ID_TYPE!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getComponent(LibraryAnalyzer.LibraryType type) {
|
||||||
|
return TYPE_ID.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LibraryAnalyzer.LibraryType getComponent(String type) {
|
||||||
|
return ID_TYPE.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Map.Entry<String, LibraryAnalyzer.LibraryType>> getPairs() {
|
||||||
|
return PAIRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URL getMetaURL(String componentID, String version) {
|
||||||
|
return NetworkUtils.toURL(String.format("https://meta.multimc.org/v1/%s/%s.json", componentID, version));
|
||||||
|
}
|
||||||
|
}
|
@ -17,80 +17,408 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.mod.multimc;
|
package org.jackhuang.hmcl.mod.multimc;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
|
import org.jackhuang.hmcl.game.Argument;
|
||||||
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
|
import org.jackhuang.hmcl.game.Artifact;
|
||||||
|
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||||
|
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||||
|
import org.jackhuang.hmcl.game.DownloadType;
|
||||||
|
import org.jackhuang.hmcl.game.GameJavaVersion;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.game.OSRestriction;
|
||||||
|
import org.jackhuang.hmcl.game.RuledArgument;
|
||||||
|
import org.jackhuang.hmcl.game.StringArgument;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
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.logging.Logger;
|
||||||
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class MultiMCInstancePatch {
|
public final class MultiMCInstancePatch {
|
||||||
|
private final int formatVersion;
|
||||||
|
|
||||||
private final String name;
|
@SerializedName("uid")
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
@SerializedName("version")
|
||||||
private final String version;
|
private final String version;
|
||||||
|
|
||||||
@SerializedName("mcVersion")
|
@SerializedName("assetIndex")
|
||||||
private final String gameVersion;
|
private final AssetIndexInfo assetIndex;
|
||||||
|
|
||||||
|
@SerializedName("minecraftArguments")
|
||||||
|
private final String minecraftArguments;
|
||||||
|
|
||||||
|
@SerializedName("+jvmArgs")
|
||||||
|
private final List<String> jvmArgs;
|
||||||
|
|
||||||
|
@SerializedName("mainClass")
|
||||||
private final String mainClass;
|
private final String mainClass;
|
||||||
private final String fileId;
|
|
||||||
|
@SerializedName("compatibleJavaMajors")
|
||||||
|
private final int[] javaMajors;
|
||||||
|
|
||||||
|
@SerializedName("mainJar")
|
||||||
|
private final Library mainJar;
|
||||||
|
|
||||||
|
@SerializedName("+traits")
|
||||||
|
private final List<String> traits;
|
||||||
|
|
||||||
@SerializedName("+tweakers")
|
@SerializedName("+tweakers")
|
||||||
private final List<String> tweakers;
|
private final List<String> tweakers;
|
||||||
|
|
||||||
@SerializedName("+libraries")
|
@SerializedName(value = "+libraries")
|
||||||
private final List<Library> _libraries;
|
private final List<Library> libraries0;
|
||||||
|
@SerializedName(value = "libraries")
|
||||||
|
private final List<Library> libraries1;
|
||||||
|
@SerializedName(value = "mavenFiles")
|
||||||
|
private final List<Library> mavenFiles;
|
||||||
|
|
||||||
@SerializedName("libraries")
|
@SerializedName("jarMods")
|
||||||
private final List<Library> libraries;
|
private final List<Library> jarMods;
|
||||||
|
|
||||||
public MultiMCInstancePatch() {
|
public MultiMCInstancePatch(int formatVersion, String id, String version, AssetIndexInfo assetIndex, String minecraftArguments, List<String> jvmArgs, String mainClass, int[] javaMajors, Library mainJar, List<String> traits, List<String> tweakers, List<Library> libraries0, List<Library> libraries1, List<Library> mavenFiles, List<Library> jarMods) {
|
||||||
this("", "", "", "", "", Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
this.formatVersion = formatVersion;
|
||||||
}
|
this.id = id;
|
||||||
|
|
||||||
public MultiMCInstancePatch(String name, String version, String gameVersion, String mainClass, String fileId, List<String> tweakers, List<Library> _libraries, List<Library> libraries) {
|
|
||||||
this.name = name;
|
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.gameVersion = gameVersion;
|
this.assetIndex = assetIndex;
|
||||||
|
this.minecraftArguments = minecraftArguments;
|
||||||
|
this.jvmArgs = jvmArgs;
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
this.fileId = fileId;
|
this.javaMajors = javaMajors;
|
||||||
this.tweakers = new ArrayList<>(tweakers);
|
this.mainJar = mainJar;
|
||||||
this._libraries = new ArrayList<>(_libraries);
|
this.traits = traits;
|
||||||
this.libraries = new ArrayList<>(libraries);
|
this.tweakers = tweakers;
|
||||||
|
this.libraries0 = libraries0;
|
||||||
|
this.libraries1 = libraries1;
|
||||||
|
this.mavenFiles = mavenFiles;
|
||||||
|
this.jarMods = jarMods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public int getFormatVersion() {
|
||||||
return name;
|
return formatVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getID() {
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGameVersion() {
|
public AssetIndexInfo getAssetIndex() {
|
||||||
return gameVersion;
|
return assetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMinecraftArguments() {
|
||||||
|
return minecraftArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getJvmArgs() {
|
||||||
|
return nonNullOrEmpty(jvmArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMainClass() {
|
public String getMainClass() {
|
||||||
return mainClass;
|
return mainClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileId() {
|
public int[] getJavaMajors() {
|
||||||
return fileId;
|
return javaMajors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Library getMainJar() {
|
||||||
|
return mainJar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTraits() {
|
||||||
|
return nonNullOrEmpty(traits);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getTweakers() {
|
public List<String> getTweakers() {
|
||||||
return Collections.unmodifiableList(tweakers);
|
return nonNullOrEmpty(tweakers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Library> getLibraries() {
|
public List<Library> getLibraries() {
|
||||||
return Lang.merge(_libraries, libraries);
|
List<Library> list = new ArrayList<>();
|
||||||
|
if (libraries0 != null) {
|
||||||
|
list.addAll(libraries0);
|
||||||
|
}
|
||||||
|
if (libraries1 != null) {
|
||||||
|
list.addAll(libraries1);
|
||||||
|
}
|
||||||
|
return nonNullOrEmpty(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Library> getMavenOnlyFiles() {
|
||||||
|
return nonNullOrEmpty(mavenFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Library> getJarMods() {
|
||||||
|
return nonNullOrEmpty(jarMods);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> nonNullOrEmpty(List<T> value) {
|
||||||
|
return value != null && !value.isEmpty() ? value : Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T, K> List<T> dropDuplicate(List<T> original, Function<T, K> mapper) {
|
||||||
|
Set<K> values = new HashSet<>();
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
|
||||||
|
for (T item : original) {
|
||||||
|
if (values.add(mapper.apply(item))) {
|
||||||
|
result.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MultiMCInstancePatch read(String componentID, String text) {
|
||||||
|
try {
|
||||||
|
return JsonUtils.fromNonNullJson(text, MultiMCInstancePatch.class);
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
throw new IllegalArgumentException("Illegal Json-Patch: " + componentID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ResolvedInstance {
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
|
private final String gameVersion;
|
||||||
|
|
||||||
|
private final Library mainJar;
|
||||||
|
|
||||||
|
private final List<String> jarModFileNames;
|
||||||
|
private final List<Library> mavenOnlyFiles;
|
||||||
|
|
||||||
|
public ResolvedInstance(Version version, String gameVersion, Library mainJar, List<String> jarModFileNames, List<Library> mavenOnlyFiles) {
|
||||||
|
this.version = version;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.mainJar = mainJar;
|
||||||
|
this.jarModFileNames = jarModFileNames;
|
||||||
|
this.mavenOnlyFiles = mavenOnlyFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Library getMainJar() {
|
||||||
|
return mainJar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getJarModFileNames() {
|
||||||
|
return jarModFileNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Library> getMavenOnlyFiles() {
|
||||||
|
return mavenOnlyFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Core methods transforming MultiMCModpack to Official Version Scheme.</p>
|
||||||
|
*
|
||||||
|
* <p>Mose of the information can be transformed in a lossless manner, except for some inputs.
|
||||||
|
* See to do marks below for more information</p>
|
||||||
|
*
|
||||||
|
* @param patches List of all Json-Patch.
|
||||||
|
* @param versionID the version ID. Used when constructing a Version.
|
||||||
|
* @return The resolved instance.
|
||||||
|
*/
|
||||||
|
public static ResolvedInstance resolveArtifact(List<MultiMCInstancePatch> patches, String versionID) {
|
||||||
|
if (patches.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Empty components.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MultiMCInstancePatch patch : patches) {
|
||||||
|
if (patch.getFormatVersion() != 1) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
String.format("Unsupported JSON-Patch[%s] format version: %d", patch.getID(), patch.getFormatVersion())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
|
||||||
|
List<String> minecraftArguments;
|
||||||
|
ArrayList<Argument> jvmArguments = new ArrayList<>(Arguments.DEFAULT_JVM_ARGUMENTS);
|
||||||
|
String mainClass;
|
||||||
|
AssetIndexInfo assetIndex;
|
||||||
|
int[] javaMajors;
|
||||||
|
Library mainJar;
|
||||||
|
List<String> traits;
|
||||||
|
List<String> tweakers;
|
||||||
|
List<Library> libraries;
|
||||||
|
List<Library> mavenOnlyFiles;
|
||||||
|
List<String> jarModFileNames;
|
||||||
|
|
||||||
|
{
|
||||||
|
MultiMCInstancePatch last = patches.get(patches.size() - 1);
|
||||||
|
minecraftArguments = last.getMinecraftArguments() == null ? null : StringUtils.tokenize(last.getMinecraftArguments());
|
||||||
|
mainClass = last.getMainClass();
|
||||||
|
assetIndex = last.getAssetIndex();
|
||||||
|
javaMajors = last.getJavaMajors();
|
||||||
|
mainJar = last.getMainJar();
|
||||||
|
traits = last.getTraits();
|
||||||
|
tweakers = last.getTweakers();
|
||||||
|
libraries = last.getLibraries();
|
||||||
|
mavenOnlyFiles = last.getMavenOnlyFiles();
|
||||||
|
jarModFileNames = last.getJarMods().stream().map(Library::getFileName).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = patches.size() - 2; i >= 0; i--) {
|
||||||
|
MultiMCInstancePatch patch = patches.get(i);
|
||||||
|
if (minecraftArguments == null & patch.getMinecraftArguments() != null) {
|
||||||
|
minecraftArguments = StringUtils.tokenize(patch.getMinecraftArguments());
|
||||||
|
}
|
||||||
|
for (String jvmArg : patch.getJvmArgs()) {
|
||||||
|
jvmArguments.add(new StringArgument(jvmArg));
|
||||||
|
}
|
||||||
|
mainClass = Lang.requireNonNullElse(mainClass, patch.getMainClass());
|
||||||
|
assetIndex = Lang.requireNonNullElse(patch.getAssetIndex(), assetIndex);
|
||||||
|
javaMajors = Lang.requireNonNullElse(patch.getJavaMajors(), javaMajors);
|
||||||
|
mainJar = Lang.requireNonNullElse(patch.getMainJar(), mainJar);
|
||||||
|
traits = Lang.merge(patch.getTraits(), traits);
|
||||||
|
tweakers = Lang.merge(patch.getTweakers(), tweakers);
|
||||||
|
libraries = Lang.merge(patch.getLibraries(), libraries);
|
||||||
|
mavenOnlyFiles = Lang.merge(patch.getMavenOnlyFiles(), mavenOnlyFiles);
|
||||||
|
jarModFileNames = Lang.merge(patch.getJarMods().stream().map(Library::getFileName).collect(Collectors.toList()), jarModFileNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClass = Lang.requireNonNullElse(mainClass, "net.minecraft.client.Minecraft");
|
||||||
|
|
||||||
|
if (minecraftArguments == null) {
|
||||||
|
minecraftArguments = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// '--tweakClass' can't be the last argument.
|
||||||
|
for (int i = minecraftArguments.size() - 2; i >= 0; i--) {
|
||||||
|
if ("--tweakClass".equals(minecraftArguments.get(i))) {
|
||||||
|
tweakers.add(minecraftArguments.get(i + 1));
|
||||||
|
|
||||||
|
minecraftArguments.remove(i);
|
||||||
|
minecraftArguments.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traits = dropDuplicate(traits, Function.identity());
|
||||||
|
tweakers = dropDuplicate(tweakers, Function.identity());
|
||||||
|
libraries = dropDuplicate(libraries, Library::getName);
|
||||||
|
jarModFileNames = dropDuplicate(jarModFileNames, Function.identity());
|
||||||
|
|
||||||
|
for (String tweaker : tweakers) {
|
||||||
|
minecraftArguments.add("--tweakClass");
|
||||||
|
minecraftArguments.add(tweaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String trait : traits) {
|
||||||
|
switch (trait) {
|
||||||
|
case "FirstThreadOnMacOS": {
|
||||||
|
jvmArguments.add(new RuledArgument(
|
||||||
|
Collections.singletonList(
|
||||||
|
new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.MACOS))
|
||||||
|
),
|
||||||
|
Collections.singletonList("-XstartOnFirstThread")
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "XR:Initial": // Flag for chat report. See https://discord.com/channels/132965178051526656/134843027553255425/1380885829702127616
|
||||||
|
case "texturepacks": // HMCL hasn't support checking whether a game version supports texture packs.
|
||||||
|
case "no-texturepacks": {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
message.append(" - Trait: ").append(trait).append('\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Library library : libraries) {
|
||||||
|
Artifact artifact = library.getArtifact();
|
||||||
|
if ("io.github.zekerzhayard".equals(artifact.getGroup()) && "ForgeWrapper".equals(artifact.getName())) {
|
||||||
|
jvmArguments.add(new StringArgument("-Dforgewrapper.librariesDir=${library_directory}"));
|
||||||
|
jvmArguments.add(new StringArgument("-Dforgewrapper.minecraft=${primary_jar}"));
|
||||||
|
|
||||||
|
for (Library lib : libraries) {
|
||||||
|
Artifact ar = lib.getArtifact();
|
||||||
|
if ("net.neoforged".equals(ar.getGroup()) && "neoforge".equals(ar.getName()) && "installer".equals(ar.getClassifier()) ||
|
||||||
|
"net.minecraftforge".equals(ar.getGroup()) && "forge".equals(ar.getName()) && "installer".equals(ar.getClassifier())
|
||||||
|
) {
|
||||||
|
jvmArguments.add(new StringArgument("-Dforgewrapper.installer=${library_directory}/" + ar.getPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Version version = new Version(versionID)
|
||||||
|
.setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments))
|
||||||
|
.setMainClass(mainClass)
|
||||||
|
.setLibraries(libraries)
|
||||||
|
.setAssetIndex(assetIndex)
|
||||||
|
.setDownload(new JsonMap<>(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo())));
|
||||||
|
|
||||||
|
/* TODO: Official Version-Json can only store one pre-defined GameJavaVersion, including 8, 11, 16, 17 and 21.
|
||||||
|
An array of all suitable java versions are NOT supported.
|
||||||
|
For compatibility with official launcher and any other launchers, a transform is made between int[] and GameJavaVersion. */
|
||||||
|
javaMajors:
|
||||||
|
if (javaMajors != null) {
|
||||||
|
javaMajors = javaMajors.clone();
|
||||||
|
Arrays.sort(javaMajors);
|
||||||
|
|
||||||
|
for (int i = javaMajors.length - 1; i >= 0; i--) {
|
||||||
|
GameJavaVersion jv = GameJavaVersion.get(javaMajors[i]);
|
||||||
|
if (jv != null) {
|
||||||
|
version = version.setJavaVersion(jv);
|
||||||
|
break javaMajors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message.append(" - Java Version Range: ").append(Arrays.toString(javaMajors)).append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
version = version.markAsResolved();
|
||||||
|
|
||||||
|
String gameVersion = null;
|
||||||
|
for (MultiMCInstancePatch patch : patches) {
|
||||||
|
if (MultiMCComponents.getComponent(patch.getID()) == LibraryAnalyzer.LibraryType.MINECRAFT) {
|
||||||
|
gameVersion = patch.getVersion();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.length() != 0) {
|
||||||
|
if (message.charAt(message.length() - 1) == '\n') {
|
||||||
|
message.setLength(message.length() - 1);
|
||||||
|
}
|
||||||
|
Logger.LOG.warning("Cannot fully parse MultiMC modpack with following unsupported features: \n" + message);
|
||||||
|
}
|
||||||
|
return new ResolvedInstance(version, gameVersion, mainJar, jarModFileNames, mavenOnlyFiles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import java.io.IOException;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@ -74,17 +75,16 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
|||||||
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
||||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId), gameVersion);
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId), gameVersion);
|
||||||
List<MultiMCManifest.MultiMCManifestComponent> components = new ArrayList<>();
|
List<MultiMCManifest.MultiMCManifestComponent> components = new ArrayList<>();
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, "net.minecraft", gameVersion));
|
components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion));
|
||||||
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
|
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.minecraftforge", forgeVersion)));
|
for (Map.Entry<String, LibraryAnalyzer.LibraryType> pair : MultiMCComponents.getPairs()) {
|
||||||
analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion ->
|
if (pair.getValue().isModLoader()) {
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.neoforged", neoForgeVersion)));
|
analyzer.getVersion(pair.getValue()).ifPresent(
|
||||||
analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion ->
|
v -> components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, pair.getKey(), v))
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "com.mumfrey.liteloader", liteLoaderVersion)));
|
);
|
||||||
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
}
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.fabricmc.fabric-loader", fabricVersion)));
|
}
|
||||||
analyzer.getVersion(QUILT).ifPresent(quiltVersion ->
|
|
||||||
components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "org.quiltmc.quilt-loader", quiltVersion)));
|
|
||||||
MultiMCManifest mmcPack = new MultiMCManifest(1, components);
|
MultiMCManifest mmcPack = new MultiMCManifest(1, components);
|
||||||
zip.putTextFile(JsonUtils.GSON.toJson(mmcPack), "mmc-pack.json");
|
zip.putTextFile(JsonUtils.GSON.toJson(mmcPack), "mmc-pack.json");
|
||||||
|
|
||||||
|
@ -19,21 +19,26 @@ package org.jackhuang.hmcl.mod.multimc;
|
|||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.GameBuilder;
|
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||||
import org.jackhuang.hmcl.game.Arguments;
|
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.DefaultGameRepository;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||||
import org.jackhuang.hmcl.mod.Modpack;
|
import org.jackhuang.hmcl.mod.Modpack;
|
||||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||||
|
import org.jackhuang.hmcl.task.GetTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -41,79 +46,47 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* <p>A task transforming MultiMC Modpack Scheme to Official Launcher Scheme.
|
||||||
* @author huangyuhui
|
* 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.
|
||||||
|
* See codes below for detailed implementation.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final class MultiMCModpackInstallTask extends Task<Void> {
|
public final class MultiMCModpackInstallTask extends Task<MultiMCInstancePatch.ResolvedInstance> {
|
||||||
|
|
||||||
private final File zipFile;
|
private final File zipFile;
|
||||||
private final Modpack modpack;
|
private final Modpack modpack;
|
||||||
private final MultiMCInstanceConfiguration manifest;
|
private final MultiMCInstanceConfiguration manifest;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final DefaultGameRepository repository;
|
private final DefaultGameRepository repository;
|
||||||
private final List<Task<?>> dependencies = new ArrayList<>(1);
|
private final List<Task<MultiMCInstancePatch>> patches = new ArrayList<>();
|
||||||
private final List<Task<?>> dependents = new ArrayList<>(4);
|
private final List<Task<?>> dependents = new ArrayList<>();
|
||||||
|
private final List<Task<?>> dependencies = new ArrayList<>();
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
|
||||||
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
|
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
|
||||||
this.zipFile = zipFile;
|
this.zipFile = zipFile;
|
||||||
this.modpack = modpack;
|
this.modpack = modpack;
|
||||||
this.manifest = manifest;
|
this.manifest = manifest;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
this.repository = dependencyManager.getGameRepository();
|
this.repository = dependencyManager.getGameRepository();
|
||||||
|
|
||||||
File json = repository.getModpackConfiguration(name);
|
File json = repository.getModpackConfiguration(name);
|
||||||
if (repository.hasVersion(name) && !json.exists())
|
if (repository.hasVersion(name) && !json.exists())
|
||||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||||
|
|
||||||
GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion());
|
|
||||||
|
|
||||||
if (manifest.getMmcPack() != null) {
|
|
||||||
Optional<MultiMCManifest.MultiMCManifestComponent> forge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.minecraftforge")).findAny();
|
|
||||||
forge.ifPresent(c -> {
|
|
||||||
if (c.getVersion() != null)
|
|
||||||
builder.version("forge", c.getVersion());
|
|
||||||
});
|
|
||||||
|
|
||||||
Optional<MultiMCManifest.MultiMCManifestComponent> neoForge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.neoforged")).findAny();
|
|
||||||
neoForge.ifPresent(c -> {
|
|
||||||
if (c.getVersion() != null)
|
|
||||||
builder.version("neoforge", c.getVersion());
|
|
||||||
});
|
|
||||||
|
|
||||||
Optional<MultiMCManifest.MultiMCManifestComponent> liteLoader = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("com.mumfrey.liteloader")).findAny();
|
|
||||||
liteLoader.ifPresent(c -> {
|
|
||||||
if (c.getVersion() != null)
|
|
||||||
builder.version("liteloader", c.getVersion());
|
|
||||||
});
|
|
||||||
|
|
||||||
Optional<MultiMCManifest.MultiMCManifestComponent> fabric = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.fabricmc.fabric-loader")).findAny();
|
|
||||||
fabric.ifPresent(c -> {
|
|
||||||
if (c.getVersion() != null)
|
|
||||||
builder.version("fabric", c.getVersion());
|
|
||||||
});
|
|
||||||
|
|
||||||
Optional<MultiMCManifest.MultiMCManifestComponent> quilt = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("org.quiltmc.quilt-loader")).findAny();
|
|
||||||
quilt.ifPresent(c -> {
|
|
||||||
if (c.getVersion() != null)
|
|
||||||
builder.version("quilt", c.getVersion());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dependents.add(builder.buildAsync());
|
|
||||||
onDone().register(event -> {
|
onDone().register(event -> {
|
||||||
if (event.isFailed())
|
if (event.isFailed())
|
||||||
repository.removeVersionFromDisk(name);
|
repository.removeVersionFromDisk(name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Task<?>> getDependencies() {
|
|
||||||
return dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doPreExecute() {
|
public boolean doPreExecute() {
|
||||||
return true;
|
return true;
|
||||||
@ -121,85 +94,85 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preExecute() throws Exception {
|
public void preExecute() throws Exception {
|
||||||
File run = repository.getRunDirectory(name);
|
// Stage #0: General Setup
|
||||||
File json = repository.getModpackConfiguration(name);
|
{
|
||||||
|
File run = repository.getRunDirectory(name);
|
||||||
|
File json = repository.getModpackConfiguration(name);
|
||||||
|
|
||||||
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
|
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
|
||||||
try {
|
try {
|
||||||
if (json.exists()) {
|
if (json.exists()) {
|
||||||
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
|
||||||
|
|
||||||
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
|
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
|
||||||
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
|
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
|
||||||
|
}
|
||||||
|
} catch (JsonParseException | IOException ignore) {
|
||||||
}
|
}
|
||||||
} catch (JsonParseException | IOException ignore) {
|
|
||||||
|
String mcDirectory;
|
||||||
|
try (FileSystem fs = openModpack()) {
|
||||||
|
mcDirectory = getRootPath(fs).resolve(".minecraft").toAbsolutePath().normalize().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Optimize unbearably slow ModpackInstallTask
|
||||||
|
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(mcDirectory), any -> true, config).withStage("hmcl.modpack"));
|
||||||
|
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String subDirectory;
|
// Stage #1: Load all related Json-Patch from meta maven or local mod pack.
|
||||||
|
|
||||||
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(modpack.getEncoding()).build()) {
|
try (FileSystem fs = openModpack()) {
|
||||||
// /.minecraft
|
Path root = getRootPath(fs);
|
||||||
if (Files.exists(fs.getPath("/.minecraft"))) {
|
|
||||||
subDirectory = "/.minecraft";
|
for (MultiMCManifest.MultiMCManifestComponent component : Objects.requireNonNull(
|
||||||
// /minecraft
|
Objects.requireNonNull(manifest.getMmcPack(), "mmc-pack.json").getComponents(), "components"
|
||||||
} else if (Files.exists(fs.getPath("/minecraft"))) {
|
)) {
|
||||||
subDirectory = "/minecraft";
|
String componentID = Objects.requireNonNull(component.getUid(), "Component ID");
|
||||||
// /[name]/.minecraft
|
Path patchPath = root.resolve(String.format("patches/%s.json", componentID));
|
||||||
} else if (Files.exists(fs.getPath("/" + manifest.getName() + "/.minecraft"))) {
|
|
||||||
subDirectory = "/" + manifest.getName() + "/.minecraft";
|
Task<String> task;
|
||||||
// /[name]/minecraft
|
if (Files.exists(patchPath)) {
|
||||||
} else if (Files.exists(fs.getPath("/" + manifest.getName() + "/minecraft"))) {
|
if (!Files.isRegularFile(patchPath)) {
|
||||||
subDirectory = "/" + manifest.getName() + "/minecraft";
|
throw new IllegalArgumentException("Json-Patch isn't a file: " + componentID);
|
||||||
} else {
|
}
|
||||||
subDirectory = "/" + manifest.getName() + "/.minecraft";
|
|
||||||
|
// TODO: Task.completed has unclear compatibility issue.
|
||||||
|
String text = FileUtils.readText(patchPath, StandardCharsets.UTF_8);
|
||||||
|
task = Task.supplyAsync(() -> text);
|
||||||
|
} else {
|
||||||
|
task = new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Task<MultiMCInstancePatch> task2 = task.thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s));
|
||||||
|
patches.add(task2);
|
||||||
|
dependents.add(task2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(subDirectory), any -> true, config).withStage("hmcl.modpack"));
|
|
||||||
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(subDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Task<?>> getDependents() {
|
public List<Task<?>> getDependents() {
|
||||||
|
// Stage #2: Resolve all Json-Patch
|
||||||
return dependents;
|
return dependents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
Version version = repository.readVersionJson(name);
|
// Stage #3: Build Json-Patch artifact.
|
||||||
|
MultiMCInstancePatch.ResolvedInstance artifact = MultiMCInstancePatch.resolveArtifact(patches.stream()
|
||||||
|
.map(value -> Objects.requireNonNull(value.getResult(), "MultiMCInstancePatch"))
|
||||||
|
.collect(Collectors.toList()), name
|
||||||
|
);
|
||||||
|
|
||||||
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setAutoDetectEncoding(true).build()) {
|
// Stage #4: Copy embedded files.
|
||||||
Path root = MultiMCModpackProvider.getRootPath(fs.getPath("/"));
|
try (FileSystem fs = openModpack()) {
|
||||||
Path patches = root.resolve("patches");
|
Path root = getRootPath(fs);
|
||||||
|
|
||||||
if (Files.exists(patches)) {
|
|
||||||
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(patches)) {
|
|
||||||
for (Path patchJson : directoryStream) {
|
|
||||||
if (patchJson.toString().endsWith(".json")) {
|
|
||||||
// If json is malformed, we should stop installing this modpack instead of skipping it.
|
|
||||||
MultiMCInstancePatch multiMCPatch = JsonUtils.GSON.fromJson(FileUtils.readText(patchJson), MultiMCInstancePatch.class);
|
|
||||||
|
|
||||||
List<String> arguments = new ArrayList<>();
|
|
||||||
for (String arg : multiMCPatch.getTweakers()) {
|
|
||||||
arguments.add("--tweakClass");
|
|
||||||
arguments.add(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
Version patch = new Version(multiMCPatch.getName(), multiMCPatch.getVersion(), 1, new Arguments().addGameArguments(arguments), multiMCPatch.getMainClass(), multiMCPatch.getLibraries());
|
|
||||||
version = version.addPatch(patch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Path libraries = root.resolve("libraries");
|
Path libraries = root.resolve("libraries");
|
||||||
if (Files.exists(libraries))
|
if (Files.exists(libraries))
|
||||||
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries"));
|
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries"));
|
||||||
|
|
||||||
Path jarmods = root.resolve("jarmods");
|
|
||||||
if (Files.exists(jarmods))
|
|
||||||
FileUtils.copyDirectory(jarmods, repository.getVersionRoot(name).toPath().resolve("jarmods"));
|
|
||||||
|
|
||||||
String iconKey = this.manifest.getIconKey();
|
String iconKey = this.manifest.getIconKey();
|
||||||
if (iconKey != null) {
|
if (iconKey != null) {
|
||||||
Path iconFile = root.resolve(iconKey + ".png");
|
Path iconFile = root.resolve(iconKey + ".png");
|
||||||
@ -209,6 +182,95 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies.add(repository.saveAsync(version));
|
// Stage #5: Assemble game files.
|
||||||
|
{
|
||||||
|
Version version = artifact.getVersion();
|
||||||
|
|
||||||
|
dependencies.add(repository.saveAsync(artifact.getVersion()));
|
||||||
|
dependencies.add(new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true));
|
||||||
|
dependencies.add(new GameLibrariesTask(
|
||||||
|
dependencyManager,
|
||||||
|
// TODO: check integrity of maven-only files when launching games?
|
||||||
|
version.setLibraries(Lang.merge(version.getLibraries(), artifact.getMavenOnlyFiles())),
|
||||||
|
true
|
||||||
|
));
|
||||||
|
|
||||||
|
Artifact mainJarArtifact = artifact.getMainJar().getArtifact();
|
||||||
|
String gameVersion = artifact.getGameVersion();
|
||||||
|
if (gameVersion != null &&
|
||||||
|
"com.mojang".equals(mainJarArtifact.getGroup()) &&
|
||||||
|
"minecraft".equals(mainJarArtifact.getName()) &&
|
||||||
|
Objects.equals(gameVersion, mainJarArtifact.getVersion()) &&
|
||||||
|
"client".equals(mainJarArtifact.getClassifier())
|
||||||
|
) {
|
||||||
|
dependencies.add(new GameDownloadTask(dependencyManager, gameVersion, version));
|
||||||
|
} else {
|
||||||
|
dependencies.add(new GameDownloadTask(dependencyManager, null, version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task<?>> getDependencies() {
|
||||||
|
// Stage #6: Download game files.
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doPostExecute() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postExecute() throws Exception {
|
||||||
|
MultiMCInstancePatch.ResolvedInstance artifact = Objects.requireNonNull(getResult(), "ResolvedInstance");
|
||||||
|
|
||||||
|
List<String> files = artifact.getJarModFileNames();
|
||||||
|
if (!isDependenciesSucceeded() || files.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage #7: Apply jar mods.
|
||||||
|
try (FileSystem fs = openModpack()) {
|
||||||
|
Path root = getRootPath(fs).resolve("jarmods");
|
||||||
|
|
||||||
|
try (FileSystem mc = CompressingUtils.writable(
|
||||||
|
repository.getVersionRoot(name).toPath().resolve(name + ".jar")
|
||||||
|
).setAutoDetectEncoding(true).build()) {
|
||||||
|
for (String fileName : files) {
|
||||||
|
try (FileSystem jm = CompressingUtils.readonly(root.resolve(fileName)).setAutoDetectEncoding(true).build()) {
|
||||||
|
FileUtils.copyDirectory(jm.getPath("/"), mc.getPath("/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystem openModpack() throws IOException {
|
||||||
|
return CompressingUtils.readonly(zipFile.toPath()).setAutoDetectEncoding(true).setEncoding(modpack.getEncoding()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean testPath(Path root) {
|
||||||
|
return Files.exists(root.resolve("instance.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path getRootPath(FileSystem fs) throws IOException {
|
||||||
|
Path root = fs.getPath("/");
|
||||||
|
|
||||||
|
if (testPath(root)) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
|
||||||
|
for (Path candidate : stream) {
|
||||||
|
if (testPath(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException("Not a valid MultiMC modpack");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class MultiMCModpackProvider implements ModpackProvider {
|
public final class MultiMCModpackProvider implements ModpackProvider {
|
||||||
public static final MultiMCModpackProvider INSTANCE = new MultiMCModpackProvider();
|
public static final MultiMCModpackProvider INSTANCE = new MultiMCModpackProvider();
|
||||||
@ -56,20 +54,6 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
|||||||
return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name));
|
return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean testPath(Path root) {
|
|
||||||
return Files.exists(root.resolve("instance.cfg"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Path getRootPath(Path root) throws IOException {
|
|
||||||
if (testPath(root)) return root;
|
|
||||||
try (Stream<Path> stream = Files.list(root)) {
|
|
||||||
Path candidate = stream.filter(Files::isDirectory).findAny()
|
|
||||||
.orElseThrow(() -> new IOException("Not a valid MultiMC modpack"));
|
|
||||||
if (testPath(candidate)) return candidate;
|
|
||||||
throw new IOException("Not a valid MultiMC modpack");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getRootEntryName(ZipArchiveReader file) throws IOException {
|
private static String getRootEntryName(ZipArchiveReader file) throws IOException {
|
||||||
final String instanceFileName = "instance.cfg";
|
final String instanceFileName = "instance.cfg";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user