mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-02-05 16:44:47 +08:00
fix: 1.17 Forge auto installation
This commit is contained in:
parent
14d1bccd14
commit
1dc4e3f730
@ -214,4 +214,5 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
public static final String VANILLA_MAIN = "net.minecraft.client.main.Main";
|
||||
public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch";
|
||||
public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher";
|
||||
public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher";
|
||||
}
|
||||
|
@ -17,29 +17,26 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.FORGE;
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.LITELOADER;
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.OPTIFINE;
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||
|
||||
public class MaintainTask extends Task<Version> {
|
||||
private final GameRepository repository;
|
||||
@ -68,7 +65,10 @@ public class MaintainTask extends Task<Version> {
|
||||
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true), false);
|
||||
} else if (mainClass != null && mainClass.equals(LibraryAnalyzer.MOD_LAUNCHER_MAIN)) {
|
||||
// Forge 1.13 and OptiFine
|
||||
return maintainOptiFineLibrary(repository, maintainGameWithModLauncher(repository, unique(version)), true);
|
||||
return maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(version)), true);
|
||||
} else if (mainClass != null && mainClass.equals(LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN)) {
|
||||
// Forge 1.17
|
||||
return maintainGameWithCpwBoostrapLauncher(repository, unique(version));
|
||||
} else {
|
||||
// Vanilla Minecraft does not need maintain
|
||||
// Fabric does not need maintain, nothing compatible with fabric now.
|
||||
@ -122,7 +122,7 @@ public class MaintainTask extends Task<Version> {
|
||||
return mainClass == null ? ret : ret.setMainClass(mainClass);
|
||||
}
|
||||
|
||||
private static Version maintainGameWithModLauncher(GameRepository repository, Version version) {
|
||||
private static Version maintainGameWithCpwModLauncher(GameRepository repository, Version version) {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
|
||||
|
||||
@ -149,6 +149,59 @@ public class MaintainTask extends Task<Version> {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static String updateIgnoreList(GameRepository repository, Version version, String ignoreList) {
|
||||
String[] ignores = ignoreList.split(",");
|
||||
List<String> newIgnoreList = new ArrayList<>();
|
||||
|
||||
// To resolve the the problem that name of primary jar may conflict with the module naming convention,
|
||||
// we need to manually ignore ${primary_jar}.
|
||||
newIgnoreList.add("${primary_jar}");
|
||||
|
||||
Path libraryDirectory = repository.getLibrariesDirectory(version).toPath().toAbsolutePath();
|
||||
|
||||
// The default ignoreList is too loose and may cause some problems, we replace them with the absolute version.
|
||||
// For example, if "client-extra" is in ignoreList, and game directory contains "client-extra" component, all
|
||||
// libraries will be ignored, which is not expected.
|
||||
for (String classpathName : repository.getClasspath(version)) {
|
||||
Path classpathFile = Paths.get(classpathName).toAbsolutePath();
|
||||
String fileName = classpathFile.getFileName().toString();
|
||||
if (Stream.of(ignores).anyMatch(fileName::contains)) {
|
||||
// This library should be ignored for Jigsaw module finding by Forge.
|
||||
String absolutePath;
|
||||
if (classpathFile.startsWith(libraryDirectory)) {
|
||||
// Note: It's assumed using "/" instead of File.separator in classpath
|
||||
absolutePath = "${library_directory}${file_separator}" + libraryDirectory.relativize(classpathFile).toString().replace(File.separator, "${file_separator}");
|
||||
} else {
|
||||
absolutePath = classpathFile.toString();
|
||||
}
|
||||
newIgnoreList.add(StringUtils.substringBefore(absolutePath, ","));
|
||||
}
|
||||
}
|
||||
return String.join(",", newIgnoreList);
|
||||
}
|
||||
|
||||
// Fix wrong configurations when launching 1.17+ with Forge.
|
||||
private static Version maintainGameWithCpwBoostrapLauncher(GameRepository repository, Version version) {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
|
||||
|
||||
if (!libraryAnalyzer.has(FORGE)) return version;
|
||||
|
||||
// The default ignoreList set by Forge installer does not fulfill our requirements
|
||||
List<Argument> jvm = builder.getMutableJvmArguments();
|
||||
for (int i = 0; i < jvm.size(); i++) {
|
||||
Argument jvmArg = jvm.get(i);
|
||||
if (jvmArg instanceof StringArgument) {
|
||||
String jvmArgStr = jvmArg.toString();
|
||||
if (jvmArgStr.startsWith("-DignoreList=")) {
|
||||
jvm.set(i, new StringArgument("-DignoreList=" + updateIgnoreList(repository, version, jvmArgStr.substring("-DignoreList=".length()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Version maintainOptiFineLibrary(GameRepository repository, Version version, boolean remove) {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
||||
|
@ -173,7 +173,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getMinecraft()))
|
||||
throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get());
|
||||
return new ForgeNewInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getPath().getVersion().replaceAll("(?i)forge", "")), installer);
|
||||
return new ForgeNewInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getVersion()), installer);
|
||||
} else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) {
|
||||
ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getInstall().getMinecraft()))
|
||||
|
@ -25,10 +25,7 @@ import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Immutable
|
||||
@ -37,15 +34,17 @@ public class ForgeNewInstallProfile implements Validation {
|
||||
private final int spec;
|
||||
private final String minecraft;
|
||||
private final String json;
|
||||
private final String version;
|
||||
private final Artifact path;
|
||||
private final List<Library> libraries;
|
||||
private final List<Processor> processors;
|
||||
private final Map<String, Datum> data;
|
||||
|
||||
public ForgeNewInstallProfile(int spec, String minecraft, String json, Artifact path, List<Library> libraries, List<Processor> processors, Map<String, Datum> data) {
|
||||
public ForgeNewInstallProfile(int spec, String minecraft, String json, String version, Artifact path, List<Library> libraries, List<Processor> processors, Map<String, Datum> data) {
|
||||
this.spec = spec;
|
||||
this.minecraft = minecraft;
|
||||
this.json = json;
|
||||
this.version = version;
|
||||
this.path = path;
|
||||
this.libraries = libraries;
|
||||
this.processors = processors;
|
||||
@ -74,12 +73,20 @@ public class ForgeNewInstallProfile implements Validation {
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return forge version.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maven artifact path for the main jar to install.
|
||||
* @return artifact path of the main jar.
|
||||
*/
|
||||
public Artifact getPath() {
|
||||
return path;
|
||||
public Optional<Artifact> getPath() {
|
||||
return Optional.ofNullable(path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +119,7 @@ public class ForgeNewInstallProfile implements Validation {
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException, TolerableValidationException {
|
||||
if (minecraft == null || json == null || path == null)
|
||||
if (minecraft == null || json == null || version == null)
|
||||
throw new JsonParseException("ForgeNewInstallProfile is malformed");
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,50 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
setSignificance(TaskSignificance.MINOR);
|
||||
}
|
||||
|
||||
private static String replaceTokens(Map<String, String> tokens, String value) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int x = 0; x < value.length(); x++) {
|
||||
char c = value.charAt(x);
|
||||
if (c == '\\') {
|
||||
if (x == value.length() - 1)
|
||||
throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value);
|
||||
buf.append(value.charAt(++x));
|
||||
} else if (c == '{' || c == '\'') {
|
||||
StringBuilder key = new StringBuilder();
|
||||
for (int y = x + 1; y <= value.length(); y++) {
|
||||
if (y == value.length())
|
||||
throw new IllegalArgumentException("Illegal pattern (Unclosed " + c + "): " + value);
|
||||
char d = value.charAt(y);
|
||||
if (d == '\\') {
|
||||
if (y == value.length() - 1)
|
||||
throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value);
|
||||
key.append(value.charAt(++y));
|
||||
} else {
|
||||
if (c == '{' && d == '}') {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
if (c == '\'' && d == '\'') {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
key.append(d);
|
||||
}
|
||||
}
|
||||
if (c == '\'') {
|
||||
buf.append(key);
|
||||
} else {
|
||||
if (!tokens.containsKey(key.toString()))
|
||||
throw new IllegalArgumentException("Illegal pattern: " + value + " Missing Key: " + key);
|
||||
buf.append(tokens.get(key.toString()));
|
||||
}
|
||||
} else {
|
||||
buf.append(c);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private <E extends Exception> String parseLiteral(String literal, Map<String, String> var, ExceptionalFunction<String, String, E> plainConverter) throws E {
|
||||
if (StringUtils.isSurrounded(literal, "{", "}"))
|
||||
return var.get(StringUtils.removeSurrounding(literal, "{", "}"));
|
||||
@ -84,7 +128,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
else if (StringUtils.isSurrounded(literal, "[", "]"))
|
||||
return gameRepository.getArtifactFile(version, Artifact.fromDescriptor(StringUtils.removeSurrounding(literal, "[", "]"))).toString();
|
||||
else
|
||||
return plainConverter.apply(literal);
|
||||
return plainConverter.apply(replaceTokens(var, literal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,10 +160,10 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Path mainJar = profile.getPath().getPath(fs.getPath("maven"));
|
||||
if (profile.getPath().isPresent()) {
|
||||
Path mainJar = profile.getPath().get().getPath(fs.getPath("maven"));
|
||||
if (Files.exists(mainJar)) {
|
||||
Path dest = gameRepository.getArtifactFile(version, profile.getPath());
|
||||
Path dest = gameRepository.getArtifactFile(version, profile.getPath().get());
|
||||
FileUtils.copyFile(mainJar, dest);
|
||||
}
|
||||
}
|
||||
@ -153,6 +197,10 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
|
||||
data.put("SIDE", "client");
|
||||
data.put("MINECRAFT_JAR", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
data.put("MINECRAFT_VERSION", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
data.put("ROOT", gameRepository.getBaseDirectory().getAbsolutePath());
|
||||
data.put("INSTALLER", installer.toAbsolutePath().toString());
|
||||
data.put("LIBRARY_DIR", gameRepository.getLibrariesDirectory(version).getAbsolutePath());
|
||||
|
||||
for (ForgeNewInstallProfile.Processor processor : processors) {
|
||||
Map<String, String> outputs = new HashMap<>();
|
||||
|
@ -21,7 +21,9 @@ import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Supports operations on versioning.
|
||||
@ -220,4 +222,14 @@ public interface GameRepository extends VersionProvider {
|
||||
*/
|
||||
File getLoggingObject(String version, String assetId, LoggingInfo loggingInfo);
|
||||
|
||||
default List<String> getClasspath(Version version) {
|
||||
List<String> classpath = new ArrayList<>();
|
||||
for (Library library : version.getLibraries())
|
||||
if (library.appliesToCurrentEnvironment() && !library.isNative()) {
|
||||
File f = getLibraryFile(version, library);
|
||||
if (f.exists() && f.isFile())
|
||||
classpath.add(f.getAbsolutePath());
|
||||
}
|
||||
return classpath;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ package org.jackhuang.hmcl.game;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -145,6 +146,11 @@ public final class VersionLibraryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public List<Argument> getMutableJvmArguments() {
|
||||
jvmChanged = true;
|
||||
return jvm;
|
||||
}
|
||||
|
||||
public void addGameArgument(String... args) {
|
||||
for (String arg : args)
|
||||
game.add(new StringArgument(arg));
|
||||
|
@ -160,13 +160,7 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<String> classpath = new LinkedList<>();
|
||||
for (Library library : version.getLibraries())
|
||||
if (library.appliesToCurrentEnvironment() && !library.isNative()) {
|
||||
File f = repository.getLibraryFile(version, library);
|
||||
if (f.exists() && f.isFile())
|
||||
classpath.add(f.getAbsolutePath());
|
||||
}
|
||||
List<String> classpath = repository.getClasspath(version);
|
||||
|
||||
File jar = repository.getVersionJar(version);
|
||||
if (!jar.exists() || !jar.isFile())
|
||||
@ -308,7 +302,9 @@ public class DefaultLauncher extends Launcher {
|
||||
// libraries_directory stands for historical reasons here. We don't know the official launcher
|
||||
// had already defined "library_directory" as the placeholder for path to ".minecraft/libraries"
|
||||
// when we propose this placeholder.
|
||||
pair("${libraries_directory}", repository.getLibrariesDirectory(version).getAbsolutePath())
|
||||
pair("${libraries_directory}", repository.getLibrariesDirectory(version).getAbsolutePath()),
|
||||
// file_separator is used in -DignoreList
|
||||
pair("${file_separator}", OperatingSystem.FILE_SEPARATOR)
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user