add: allow installing OptiFine 1.14.4 and Forge in same game version

This commit is contained in:
huanghongxun
2020-05-03 19:21:54 +08:00
parent b53a3ebcc2
commit 7c510f9fb8
26 changed files with 176 additions and 27 deletions

View File

@@ -198,4 +198,8 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return libraryVersion;
}
}
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";
}

View File

@@ -24,18 +24,22 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
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;
public class MaintainTask extends Task<Version> {
private final GameRepository repository;
@@ -60,13 +64,15 @@ public class MaintainTask extends Task<Version> {
String mainClass = version.resolve(null).getMainClass();
if (mainClass != null && mainClass.contains("launchwrapper")) {
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true));
if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) {
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);
} else {
// Vanilla Minecraft does not need maintain
// Forge 1.13 support not implemented, not compatible with OptiFine currently.
// Fabric does not need maintain, nothing compatible with fabric now.
return maintainOptiFineLibrary(repository, unique(version));
return maintainOptiFineLibrary(repository, unique(version), false);
}
}
@@ -116,7 +122,34 @@ public class MaintainTask extends Task<Version> {
return mainClass == null ? ret : ret.setMainClass(mainClass);
}
private static Version maintainOptiFineLibrary(GameRepository repository, Version version) {
private static Version maintainGameWithModLauncher(GameRepository repository, Version version) {
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
if (!libraryAnalyzer.has(FORGE)) return version;
if (libraryAnalyzer.has(OPTIFINE)) {
Library hmclTransformerDiscoveryService = new Library(new Artifact("org.jackhuang.hmcl", "transformer-discovery-service", "1.0"));
Optional<Library> optiFine = version.getLibraries().stream().filter(library -> library.is("optifine", "OptiFine")).findAny();
boolean libraryExisting = version.getLibraries().stream().anyMatch(library -> library.is("org.jackhuang.hmcl", "transformer-discovery-service"));
optiFine.ifPresent(library -> {
builder.addJvmArgument("-Dhmcl.transformer.candidates=${libraries_directory}/" + library.getPath());
if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService);
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
try {
Files.createDirectories(libraryPath.getParent());
Files.copy(MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar"),
libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to unpack HMCLTransformerDiscoveryService", e);
}
});
}
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());
@@ -135,7 +168,7 @@ public class MaintainTask extends Task<Version> {
// Although we have altered priority of OptiFine higher than Forge,
// there still exists a situation that Forge is installed without patch.
// Here we manually alter the position of OptiFine library in classpath.
libraries.add(newLibrary);
if (!remove) libraries.add(newLibrary);
}
}

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.DependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.VersionMismatchException;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.GameVersion;
@@ -104,7 +105,7 @@ public final class ForgeInstallTask extends Task<Version> {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) {
// Forge 1.13 is not compatible with any other libraries.
if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"cpw.mods.modlauncher.Launcher".equals(originalMainClass))
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass))
throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
} else {
// Forge 1.12 and older versions is compatible with vanilla and launchwrapper.

View File

@@ -69,7 +69,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
remote.getSelfVersion(),
60000,
new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"),
"net.minecraft.launchwrapper.Launch",
LibraryAnalyzer.LAUNCH_WRAPPER_MAIN,
Lang.merge(remote.getLibraries(), Collections.singleton(library)))
.setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration
);

View File

@@ -121,7 +121,7 @@ public final class OptiFineInstallTask extends Task<Version> {
@Override
public void execute() throws Exception {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"net.minecraft.launchwrapper.Launch".equals(originalMainClass))
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass))
throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
List<Library> libraries = new LinkedList<>();
@@ -183,9 +183,9 @@ public final class OptiFineInstallTask extends Task<Version> {
setResult(new Version(
LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(),
remote.getSelfVersion(),
90000,
10000,
new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"),
"net.minecraft.launchwrapper.Launch",
LibraryAnalyzer.LAUNCH_WRAPPER_MAIN,
libraries
));

View File

@@ -96,12 +96,17 @@ public class DefaultGameRepository implements GameRepository {
return versions.values();
}
@Override
public File getLibrariesDirectory(Version version) {
return new File(getBaseDirectory(), "libraries");
}
@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(getBaseDirectory(), "libraries/" + lib.getPath());
return new File(getLibrariesDirectory(version), lib.getPath());
}
public Path getArtifactFile(Version version, Artifact artifact) {

View File

@@ -99,6 +99,8 @@ public interface GameRepository extends VersionProvider {
*/
File getRunDirectory(String id);
File getLibrariesDirectory(Version version);
/**
* Get the library file in disk.
* This method allows versions and libraries that are not loaded by this game repository.

View File

@@ -20,8 +20,7 @@ package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.CommandBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
/**
*
@@ -31,12 +30,17 @@ public final class VersionLibraryBuilder {
private final Version version;
private final List<String> mcArgs;
private final List<Argument> game;
private final List<Argument> jvm;
private final List<Library> libraries;
private final boolean useMcArgs;
private boolean jvmChanged = false;
public VersionLibraryBuilder(Version version) {
this.version = version;
this.libraries = new ArrayList<>(version.getLibraries());
this.mcArgs = version.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null);
this.game = version.getArguments().map(Arguments::getGame).map(ArrayList::new).orElseGet(ArrayList::new);
this.jvm = new ArrayList<>(version.getArguments().map(Arguments::getJvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS));
this.useMcArgs = mcArgs != null;
}
@@ -47,7 +51,10 @@ public final class VersionLibraryBuilder {
// so we regenerate the minecraftArgument without escaping.
ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
}
return ret.setArguments(ret.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, null)));
return ret.setArguments(ret.getArguments()
.map(args -> args.withGame(game))
.map(args -> jvmChanged ? args.withJvm(jvm) : args).orElse(new Arguments(game, jvmChanged ? jvm : null)))
.setLibraries(libraries);
}
public void removeTweakClass(String target) {
@@ -138,8 +145,18 @@ public final class VersionLibraryBuilder {
}
}
public void addArgument(String... args) {
public void addGameArgument(String... args) {
for (String arg : args)
game.add(new StringArgument(arg));
}
public void addJvmArgument(String... args) {
jvmChanged = true;
for (String arg : args)
jvm.add(new StringArgument(arg));
}
public void addLibrary(Library library) {
libraries.add(library);
}
}

View File

@@ -153,6 +153,7 @@ public class DefaultLauncher extends Launcher {
configuration.put("${natives_directory}", nativeFolder.getAbsolutePath());
configuration.put("${game_assets}", gameAssets.getAbsolutePath());
configuration.put("${assets_root}", gameAssets.getAbsolutePath());
configuration.put("${libraries_directory}", repository.getLibrariesDirectory(version).getAbsolutePath());
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration));
if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty())