Make auto-installing more flexible
This commit is contained in:
@@ -19,18 +19,22 @@ package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.*;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Note: This class has no state.
|
||||
@@ -90,6 +94,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
@Override
|
||||
public Task<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
|
||||
if (version.isResolved()) throw new IllegalArgumentException("Version should not be resolved");
|
||||
|
||||
VersionList<?> versionList = getVersionList(libraryId);
|
||||
return versionList.loadAsync(gameVersion, getDownloadProvider())
|
||||
.thenComposeAsync(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
|
||||
@@ -98,6 +104,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
@Override
|
||||
public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
|
||||
if (oldVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved");
|
||||
|
||||
Task<Version> task;
|
||||
if (libraryVersion instanceof ForgeRemoteVersion)
|
||||
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
|
||||
@@ -108,9 +116,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
else
|
||||
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
||||
return task
|
||||
.thenComposeAsync(LibrariesUniqueTask::new)
|
||||
.thenComposeAsync(MaintainTask::new)
|
||||
.thenComposeAsync(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
||||
.thenApplyAsync(oldVersion::addPatch)
|
||||
.thenComposeAsync(repository::save);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +125,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
return version -> installLibraryAsync(version, libraryVersion);
|
||||
}
|
||||
|
||||
public Task installLibraryAsync(Version oldVersion, Path installer) {
|
||||
public Task<Version> installLibraryAsync(Version oldVersion, Path installer) {
|
||||
if (oldVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved");
|
||||
|
||||
return Task
|
||||
.composeAsync(() -> {
|
||||
try {
|
||||
@@ -133,8 +142,54 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
throw new UnsupportedOperationException("Library cannot be recognized");
|
||||
})
|
||||
.thenComposeAsync(LibrariesUniqueTask::new)
|
||||
.thenComposeAsync(MaintainTask::new)
|
||||
.thenComposeAsync(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
||||
.thenApplyAsync(oldVersion::addPatch)
|
||||
.thenComposeAsync(repository::save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove installed library.
|
||||
* Will try to remove libraries and patches.
|
||||
*
|
||||
* @param versionId version id
|
||||
* @param libraryId forge/liteloader/optifine
|
||||
* @return task to remove the specified library
|
||||
*/
|
||||
public Task<Version> removeLibraryWithoutSavingAsync(String versionId, String libraryId) {
|
||||
Version version = repository.getVersion(versionId); // to ensure version is not resolved
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
|
||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
||||
|
||||
switch (libraryId) {
|
||||
case "forge":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.FORGE, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById("net.minecraftforge");
|
||||
break;
|
||||
case "liteloader":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.LITELOADER, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById("com.mumfrey.liteloader");
|
||||
break;
|
||||
case "optifine":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.OPTIFINE, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById("net.optifine");
|
||||
break;
|
||||
case "fabric":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.FABRIC, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById("net.fabricmc");
|
||||
break;
|
||||
}
|
||||
return new MaintainTask(version.setLibraries(newList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove installed library.
|
||||
* Will try to remove libraries and patches.
|
||||
*
|
||||
* @param versionId version id
|
||||
* @param libraryId forge/liteloader/optifine
|
||||
* @return task to remove the specified library
|
||||
*/
|
||||
public Task<Version> removeLibraryAsync(String versionId, String libraryId) {
|
||||
return removeLibraryWithoutSavingAsync(versionId, libraryId).thenComposeAsync(repository::save);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.download.game.*;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
@@ -53,7 +56,7 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenComposeAsync(Task.allOf(
|
||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
|
||||
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
|
||||
).withComposeAsync(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
|
||||
).withComposeAsync(dependencyManager.getGameRepository().save(version))); // using [with] because download failure here are tolerant.
|
||||
|
||||
Task<Version> libraryTask = vanillaTask.thenSupplyAsync(() -> version);
|
||||
|
||||
|
||||
@@ -19,22 +19,31 @@ package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class LibraryAnalyzer {
|
||||
private final Map<LibraryType, Library> libraries;
|
||||
private final Map<LibraryType, Pair<Library, String>> libraries;
|
||||
|
||||
private LibraryAnalyzer(Map<LibraryType, Library> libraries) {
|
||||
private LibraryAnalyzer(Map<LibraryType, Pair<Library, String>> libraries) {
|
||||
this.libraries = libraries;
|
||||
}
|
||||
|
||||
public Optional<Library> get(LibraryType type) {
|
||||
return Optional.ofNullable(libraries.get(type));
|
||||
public Optional<String> getVersion(LibraryType type) {
|
||||
return Optional.ofNullable(libraries.get(type)).map(Pair::getValue);
|
||||
}
|
||||
|
||||
public void ifPresent(LibraryType type, BiConsumer<Library, String> consumer) {
|
||||
if (libraries.containsKey(type)) {
|
||||
Pair<Library, String> value = libraries.get(type);
|
||||
consumer.accept(value.getKey(), value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has(LibraryType type) {
|
||||
@@ -48,7 +57,7 @@ public final class LibraryAnalyzer {
|
||||
}
|
||||
|
||||
public static LibraryAnalyzer analyze(Version version) {
|
||||
Map<LibraryType, Library> libraries = new EnumMap<>(LibraryType.class);
|
||||
Map<LibraryType, Pair<Library, String>> libraries = new EnumMap<>(LibraryType.class);
|
||||
|
||||
for (Library library : version.getLibraries()) {
|
||||
String groupId = library.getGroupId();
|
||||
@@ -56,7 +65,16 @@ public final class LibraryAnalyzer {
|
||||
|
||||
for (LibraryType type : LibraryType.values()) {
|
||||
if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) {
|
||||
libraries.put(type, library);
|
||||
libraries.put(type, Pair.pair(library, library.getVersion()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Version patch : version.getPatches()) {
|
||||
for (LibraryType type : LibraryType.values()) {
|
||||
if (type.patchId.equals(patch.getId())) {
|
||||
libraries.put(type, Pair.pair(null, patch.getVersion()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -66,16 +84,18 @@ public final class LibraryAnalyzer {
|
||||
}
|
||||
|
||||
public enum LibraryType {
|
||||
FORGE(true, Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
|
||||
LITELOADER(true, Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
|
||||
OPTIFINE(false, Pattern.compile("(net\\.)?optifine"), Pattern.compile(".*")),
|
||||
FABRIC(true, Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader"));
|
||||
FORGE(true, "net.minecraftforge", Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
|
||||
LITELOADER(true, "com.mumfrey.liteloader", Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
|
||||
OPTIFINE(false, "net.optifine", Pattern.compile("(net\\.)?optifine"), Pattern.compile(".*")),
|
||||
FABRIC(true, "net.fabricmc", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader"));
|
||||
|
||||
private final Pattern group, artifact;
|
||||
private final boolean modLoader;
|
||||
private final String patchId;
|
||||
private final Pattern group, artifact;
|
||||
|
||||
LibraryType(boolean modLoader, Pattern group, Pattern artifact) {
|
||||
LibraryType(boolean modLoader, String patchId, Pattern group, Pattern artifact) {
|
||||
this.modLoader = modLoader;
|
||||
this.patchId = patchId;
|
||||
this.group = group;
|
||||
this.artifact = artifact;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,10 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
Files.deleteIfExists(installer);
|
||||
setResult(dependency.getResult());
|
||||
setResult(dependency.getResult()
|
||||
.setPriority(10000)
|
||||
.setId("net.minecraftforge")
|
||||
.setVersion(remote.getSelfVersion()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,6 +105,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
|
||||
/**
|
||||
* Install Forge library from existing local file.
|
||||
* This method will try to identify this installer whether it is in old or new format.
|
||||
*
|
||||
* @param dependencyManager game repository
|
||||
* @param version version.json
|
||||
|
||||
@@ -20,7 +20,10 @@ package org.jackhuang.hmcl.download.forge;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
@@ -40,7 +43,14 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -258,15 +268,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
// resolve the version
|
||||
SimpleVersionProvider provider = new SimpleVersionProvider();
|
||||
provider.addVersion(version);
|
||||
|
||||
setResult(forgeVersion
|
||||
.setInheritsFrom(version.getId())
|
||||
.resolve(provider).setJar(null)
|
||||
.setId(version.getId()).setLogging(Collections.emptyMap()));
|
||||
|
||||
setResult(forgeVersion);
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion));
|
||||
|
||||
FileUtils.deleteDirectory(temp.toFile());
|
||||
|
||||
@@ -18,15 +18,21 @@
|
||||
package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -75,15 +81,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
IOUtils.copyTo(is, os);
|
||||
}
|
||||
|
||||
// resolve the version
|
||||
SimpleVersionProvider provider = new SimpleVersionProvider();
|
||||
provider.addVersion(version);
|
||||
|
||||
setResult(installProfile.getVersionInfo()
|
||||
.setInheritsFrom(version.getId())
|
||||
.resolve(provider).setJar(null)
|
||||
.setId(version.getId()).setLogging(Collections.emptyMap()));
|
||||
|
||||
setResult(installProfile.getVersionInfo());
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ public class LibrariesUniqueTask extends Task<Version> {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
setResult(unique(version));
|
||||
}
|
||||
|
||||
public static Version unique(Version version) {
|
||||
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
||||
|
||||
SimpleMultimap<String, Library> multimap = new SimpleMultimap<String, Library>(HashMap::new, LinkedList::new);
|
||||
@@ -91,6 +95,6 @@ public class LibrariesUniqueTask extends Task<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
setResult(version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList())));
|
||||
return version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||
@@ -67,16 +68,16 @@ public final class LiteLoaderInstallTask extends Task<Version> {
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||
);
|
||||
|
||||
Version tempVersion = version.setLibraries(Lang.merge(remote.getLibraries(), Collections.singleton(library)));
|
||||
|
||||
// --tweakClass will be added in MaintainTask
|
||||
setResult(version
|
||||
.setMainClass("net.minecraft.launchwrapper.Launch")
|
||||
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
|
||||
setResult(new Version("com.mumfrey.liteloader",
|
||||
remote.getSelfVersion(),
|
||||
20000,
|
||||
new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"),
|
||||
"net.minecraft.launchwrapper.Launch",
|
||||
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
|
||||
);
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(tempVersion));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(getResult().getLibraries())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,11 +19,17 @@ package org.jackhuang.hmcl.download.optifine;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantPool;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||
@@ -34,7 +40,12 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.getOrDefault;
|
||||
|
||||
@@ -51,6 +62,9 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
private final Path installer;
|
||||
private final List<Task<?>> dependents = new LinkedList<>();
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
private final File dest;
|
||||
|
||||
private final Library optiFineLibrary;
|
||||
|
||||
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
|
||||
this(dependencyManager, version, remoteVersion, null);
|
||||
@@ -61,6 +75,39 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
this.version = version;
|
||||
this.remote = remoteVersion;
|
||||
this.installer = installer;
|
||||
|
||||
String mavenVersion = remote.getGameVersion() + "_" + remote.getSelfVersion();
|
||||
|
||||
optiFineLibrary = new Library(
|
||||
"optifine", "OptiFine", mavenVersion, null, null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
||||
"optifine/OptiFine/" + mavenVersion + "/OptiFine-" + mavenVersion + ".jar",
|
||||
remote.getUrl()))
|
||||
);
|
||||
|
||||
dest = dependencyManager.getGameRepository().getLibraryFile(version, optiFineLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPreExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
if (!Arrays.asList("net.minecraft.client.main.Main",
|
||||
"net.minecraft.launchwrapper.Launch")
|
||||
.contains(version.getMainClass()))
|
||||
throw new UnsupportedOptiFineInstallationException();
|
||||
|
||||
|
||||
if (installer == null) {
|
||||
dependents.add(new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), dest)
|
||||
.setCacheRepository(dependencyManager.getCacheRepository())
|
||||
.setCaching(true));
|
||||
} else {
|
||||
FileUtils.copyFile(installer, dest.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,35 +127,42 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
if (!Arrays.asList("net.minecraft.client.main.Main",
|
||||
"net.minecraft.launchwrapper.Launch")
|
||||
.contains(version.getMainClass()))
|
||||
throw new UnsupportedOptiFineInstallationException();
|
||||
List<Library> libraries = new LinkedList<>();
|
||||
libraries.add(optiFineLibrary);
|
||||
|
||||
String remoteVersion = remote.getGameVersion() + "_" + remote.getSelfVersion();
|
||||
// Install launch wrapper modified by OptiFine
|
||||
boolean hasLaunchWrapper = false;
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest.toPath())) {
|
||||
Path launchWrapperVersionText = fs.getPath("launchwrapper-of.txt");
|
||||
if (Files.exists(launchWrapperVersionText)) {
|
||||
String launchWrapperVersion = FileUtils.readText(launchWrapperVersionText).trim();
|
||||
Path launchWrapperJar = fs.getPath("launchwrapper-of-" + launchWrapperVersion + ".jar");
|
||||
|
||||
Library library = new Library(
|
||||
"optifine", "OptiFine", remoteVersion, null, null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
||||
"optifine/OptiFine/" + remoteVersion + "/OptiFine-" + remoteVersion + ".jar",
|
||||
remote.getUrl()))
|
||||
);
|
||||
Library launchWrapper = new Library("optifine", "launchwrapper-of", launchWrapperVersion);
|
||||
|
||||
if (installer != null) {
|
||||
FileUtils.copyFile(installer, dependencyManager.getGameRepository().getLibraryFile(version, library).toPath());
|
||||
if (Files.exists(launchWrapperJar)) {
|
||||
File launchWrapperFile = dependencyManager.getGameRepository().getLibraryFile(version, launchWrapper);
|
||||
FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile());
|
||||
FileUtils.copyFile(launchWrapperJar, launchWrapperFile.toPath());
|
||||
|
||||
hasLaunchWrapper = true;
|
||||
libraries.add(0, launchWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Library> libraries = new LinkedList<>();
|
||||
libraries.add(library);
|
||||
|
||||
if (version.getMainClass() == null || !version.getMainClass().startsWith("net.minecraft.launchwrapper."))
|
||||
if (!hasLaunchWrapper) {
|
||||
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
|
||||
}
|
||||
|
||||
// --tweakClass will be added in MaintainTask
|
||||
setResult(version
|
||||
.setLibraries(Lang.merge(version.getLibraries(), libraries))
|
||||
.setMainClass("net.minecraft.launchwrapper.Launch")
|
||||
);
|
||||
setResult(new Version(
|
||||
"net.optifine",
|
||||
remote.getSelfVersion(),
|
||||
30000,
|
||||
new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"),
|
||||
"net.minecraft.launchwrapper.Launch",
|
||||
libraries
|
||||
));
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(libraries)));
|
||||
}
|
||||
|
||||
@@ -21,8 +21,13 @@ import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -46,16 +51,18 @@ public final class Arguments {
|
||||
this.jvm = jvm;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Argument> getGame() {
|
||||
return game == null ? Collections.emptyList() : Collections.unmodifiableList(game);
|
||||
return game == null ? null : Collections.unmodifiableList(game);
|
||||
}
|
||||
|
||||
public Arguments withGame(List<Argument> game) {
|
||||
return new Arguments(game, jvm);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Argument> getJvm() {
|
||||
return jvm == null ? Collections.emptyList() : Collections.unmodifiableList(jvm);
|
||||
return jvm == null ? null : Collections.unmodifiableList(jvm);
|
||||
}
|
||||
|
||||
public Arguments withJvm(List<Argument> jvm) {
|
||||
@@ -86,7 +93,9 @@ public final class Arguments {
|
||||
else if (b == null)
|
||||
return a;
|
||||
else
|
||||
return new Arguments(Lang.merge(a.game, b.game), Lang.merge(a.jvm, b.jvm));
|
||||
return new Arguments(
|
||||
a.game == null && b.game == null ? null : Lang.merge(a.game, b.game),
|
||||
a.jvm == null && b.jvm == null ? null : Lang.merge(a.jvm, b.jvm));
|
||||
}
|
||||
|
||||
public static List<String> parseStringArguments(List<String> arguments, Map<String, String> keys) {
|
||||
|
||||
@@ -29,10 +29,10 @@ import java.util.Date;
|
||||
public class ClassicVersion extends Version {
|
||||
|
||||
public ClassicVersion() {
|
||||
super(true, "Classic", "${auth_player_name} ${auth_session} --workDir ${game_directory}",
|
||||
super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}",
|
||||
null, "net.minecraft.client.Minecraft", null, null, null, null,
|
||||
Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")),
|
||||
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false);
|
||||
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false, null);
|
||||
}
|
||||
|
||||
private static class ClassicLibrary extends Library {
|
||||
|
||||
@@ -18,8 +18,17 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.event.*;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.GameJsonParseFailedEvent;
|
||||
import org.jackhuang.hmcl.event.LoadedOneVersionEvent;
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
|
||||
import org.jackhuang.hmcl.event.RemoveVersionEvent;
|
||||
import org.jackhuang.hmcl.event.RenameVersionEvent;
|
||||
import org.jackhuang.hmcl.mod.ModManager;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@@ -27,7 +36,13 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -368,6 +383,10 @@ public class DefaultGameRepository implements GameRepository {
|
||||
return assetsDir;
|
||||
}
|
||||
|
||||
public Task<Version> save(Version version) {
|
||||
return new VersionJsonSaveTask(this, version);
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return versions != null;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,28 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -32,6 +49,8 @@ import java.util.logging.Level;
|
||||
public class Version implements Comparable<Version>, Validation {
|
||||
|
||||
private final String id;
|
||||
private final String version;
|
||||
private final Integer priority;
|
||||
private final String minecraftArguments;
|
||||
private final Arguments arguments;
|
||||
private final String mainClass;
|
||||
@@ -47,13 +66,20 @@ public class Version implements Comparable<Version>, Validation {
|
||||
private final Date time;
|
||||
private final Date releaseTime;
|
||||
private final int minimumLauncherVersion;
|
||||
private final boolean hidden;
|
||||
private final Boolean hidden;
|
||||
private final List<Version> patches;
|
||||
|
||||
private transient final boolean resolved;
|
||||
|
||||
public Version(boolean resolved, String id, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, int minimumLauncherVersion, boolean hidden) {
|
||||
public Version(String id, String version, int priority, Arguments arguments, String mainClass, List<Library> libraries) {
|
||||
this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, libraries, null, null, null, null, null, null, 0, null, null);
|
||||
}
|
||||
|
||||
public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, int minimumLauncherVersion, Boolean hidden, List<Version> patches) {
|
||||
this.resolved = resolved;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.priority = priority;
|
||||
this.minecraftArguments = minecraftArguments;
|
||||
this.arguments = arguments;
|
||||
this.mainClass = mainClass;
|
||||
@@ -61,15 +87,16 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this.jar = jar;
|
||||
this.assetIndex = assetIndex;
|
||||
this.assets = assets;
|
||||
this.libraries = libraries == null ? new LinkedList<>() : new LinkedList<>(libraries);
|
||||
this.libraries = libraries == null ? Collections.emptyList() : new LinkedList<>(libraries);
|
||||
this.compatibilityRules = compatibilityRules == null ? null : new LinkedList<>(compatibilityRules);
|
||||
this.downloads = downloads == null ? null : new HashMap<>(downloads);
|
||||
this.logging = logging == null ? null : new HashMap<>(logging);
|
||||
this.type = type;
|
||||
this.time = time == null ? new Date() : (Date) time.clone();
|
||||
this.releaseTime = releaseTime == null ? new Date() : (Date) releaseTime.clone();
|
||||
this.time = time == null ? null : (Date) time.clone();
|
||||
this.releaseTime = releaseTime == null ? null : (Date) releaseTime.clone();
|
||||
this.minimumLauncherVersion = minimumLauncherVersion;
|
||||
this.hidden = hidden;
|
||||
this.patches = patches == null ? null : patches.isEmpty() ? null : new LinkedList<>(patches);
|
||||
}
|
||||
|
||||
public Optional<String> getMinecraftArguments() {
|
||||
@@ -92,8 +119,22 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of the patch.
|
||||
* Exists only when this version object represents a patch.
|
||||
* Example: 0.5.0.33 for fabric-loader, 28.0.46 for minecraft-forge.
|
||||
*/
|
||||
@Nullable
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority == null ? Integer.MIN_VALUE : priority;
|
||||
}
|
||||
|
||||
public ReleaseType getType() {
|
||||
return type;
|
||||
return type == null ? ReleaseType.UNKNOWN : type;
|
||||
}
|
||||
|
||||
public Date getReleaseTime() {
|
||||
@@ -113,7 +154,15 @@ public class Version implements Comparable<Version>, Validation {
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
return hidden == null ? false : hidden;
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public List<Version> getPatches() {
|
||||
return patches == null ? Collections.emptyList() : patches;
|
||||
}
|
||||
|
||||
public Map<DownloadType, LoggingInfo> getLogging() {
|
||||
@@ -153,22 +202,12 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return resolve(provider, new HashSet<>()).setResolved();
|
||||
}
|
||||
|
||||
protected Version resolve(VersionProvider provider, Set<String> resolvedSoFar) throws VersionNotFoundException {
|
||||
if (inheritsFrom == null) {
|
||||
return this.jar == null ? this.setJar(id) : this;
|
||||
}
|
||||
|
||||
// To maximize the compatibility.
|
||||
if (!resolvedSoFar.add(id)) {
|
||||
Logging.LOG.log(Level.WARNING, "Found circular dependency versions: " + resolvedSoFar);
|
||||
return this;
|
||||
}
|
||||
|
||||
// It is supposed to auto install an version in getVersion.
|
||||
Version parent = provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar);
|
||||
protected Version merge(Version parent) {
|
||||
return new Version(
|
||||
true,
|
||||
id,
|
||||
null,
|
||||
null,
|
||||
minecraftArguments == null ? parent.minecraftArguments : minecraftArguments,
|
||||
Arguments.merge(parent.arguments, arguments),
|
||||
mainClass == null ? parent.mainClass : mainClass,
|
||||
@@ -184,43 +223,90 @@ public class Version implements Comparable<Version>, Validation {
|
||||
time,
|
||||
releaseTime,
|
||||
Math.max(minimumLauncherVersion, parent.minimumLauncherVersion),
|
||||
hidden);
|
||||
hidden,
|
||||
Lang.merge(parent.patches, patches));
|
||||
}
|
||||
|
||||
protected Version resolve(VersionProvider provider, Set<String> resolvedSoFar) throws VersionNotFoundException {
|
||||
Version thisVersion;
|
||||
|
||||
if (inheritsFrom == null) {
|
||||
thisVersion = this.jar == null ? this.setJar(id) : this;
|
||||
} else {
|
||||
// To maximize the compatibility.
|
||||
if (!resolvedSoFar.add(id)) {
|
||||
Logging.LOG.log(Level.WARNING, "Found circular dependency versions: " + resolvedSoFar);
|
||||
thisVersion = this.jar == null ? this.setJar(id) : this;
|
||||
} else {
|
||||
// It is supposed to auto install an version in getVersion.
|
||||
thisVersion = merge(provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar));
|
||||
}
|
||||
}
|
||||
|
||||
if (patches != null && !patches.isEmpty()) {
|
||||
// Assume patches themselves do not have patches recursively.
|
||||
List<Version> sortedPatches = patches.stream()
|
||||
.sorted(Comparator.comparing(Version::getPriority))
|
||||
.collect(Collectors.toList());
|
||||
for (Version patch : sortedPatches) {
|
||||
thisVersion = patch.setJar(null).merge(thisVersion);
|
||||
}
|
||||
}
|
||||
|
||||
return thisVersion.setId(id);
|
||||
}
|
||||
|
||||
private Version setResolved() {
|
||||
return new Version(true, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setId(String id) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setVersion(String version) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setPriority(Integer priority) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setMinecraftArguments(String minecraftArguments) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setArguments(Arguments arguments) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setMainClass(String mainClass) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setInheritsFrom(String inheritsFrom) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setJar(String jar) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setLibraries(List<Library> libraries) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version setLogging(Map<DownloadType, LoggingInfo> logging) {
|
||||
return new Version(resolved, id, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
}
|
||||
|
||||
public Version addPatch(Version patch) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, Lang.merge(patches, Collections.singleton(patch)));
|
||||
}
|
||||
|
||||
public Version removePatchById(String patchId) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden,
|
||||
patches == null ? null : patches.stream().filter(patch -> !patchId.equals(patch.getId())).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,8 +21,6 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -43,13 +41,13 @@ public final class VersionLibraryBuilder {
|
||||
}
|
||||
|
||||
public Version build() {
|
||||
Version ret = version;
|
||||
if (useMcArgs) {
|
||||
// Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping,
|
||||
// so we regenerate the minecraftArgument without escaping.
|
||||
return version.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
|
||||
} else {
|
||||
return version.setArguments(version.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, Collections.emptyList())));
|
||||
ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
|
||||
}
|
||||
return ret.setArguments(ret.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, null)));
|
||||
}
|
||||
|
||||
public void removeTweakClass(String target) {
|
||||
@@ -63,30 +61,26 @@ public final class VersionLibraryBuilder {
|
||||
--i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i + 1 < game.size(); ++i) {
|
||||
Argument arg0 = game.get(i);
|
||||
Argument arg1 = game.get(i + 1);
|
||||
if (arg0 instanceof StringArgument && arg1 instanceof StringArgument) {
|
||||
// We need to preserve the tokens
|
||||
String arg0Str = arg0.toString();
|
||||
String arg1Str = arg1.toString();
|
||||
if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) {
|
||||
game.remove(i);
|
||||
game.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i + 1 < game.size(); ++i) {
|
||||
Argument arg0 = game.get(i);
|
||||
Argument arg1 = game.get(i + 1);
|
||||
if (arg0 instanceof StringArgument && arg1 instanceof StringArgument) {
|
||||
// We need to preserve the tokens
|
||||
String arg0Str = arg0.toString();
|
||||
String arg1Str = arg1.toString();
|
||||
if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) {
|
||||
game.remove(i);
|
||||
game.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addArgument(String... args) {
|
||||
if (useMcArgs) {
|
||||
mcArgs.addAll(Arrays.asList(args));
|
||||
} else {
|
||||
for (String arg : args)
|
||||
game.add(new StringArgument(arg));
|
||||
}
|
||||
for (String arg : args)
|
||||
game.add(new StringArgument(arg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,15 @@
|
||||
package org.jackhuang.hmcl.launch;
|
||||
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.game.Argument;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.Unzipper;
|
||||
@@ -29,9 +36,18 @@ import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -43,16 +59,16 @@ import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
*/
|
||||
public class DefaultLauncher extends Launcher {
|
||||
|
||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options) {
|
||||
this(repository, versionId, authInfo, options, null);
|
||||
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||
this(repository, version, authInfo, options, null);
|
||||
}
|
||||
|
||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||
this(repository, versionId, authInfo, options, listener, true);
|
||||
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||
this(repository, version, authInfo, options, listener, true);
|
||||
}
|
||||
|
||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||
super(repository, versionId, authInfo, options, listener, daemon);
|
||||
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||
super(repository, version, authInfo, options, listener, daemon);
|
||||
}
|
||||
|
||||
private CommandBuilder generateCommandLine(File nativeFolder) throws IOException {
|
||||
@@ -144,13 +160,13 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
res.add(version.getMainClass());
|
||||
|
||||
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
|
||||
|
||||
Map<String, Boolean> features = getFeatures();
|
||||
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getGame).orElseGet(this::getDefaultGameArguments), configuration, features));
|
||||
if (authInfo.getArguments() != null && authInfo.getArguments().getGame() != null && !authInfo.getArguments().getGame().isEmpty())
|
||||
res.addAll(Arguments.parseArguments(authInfo.getArguments().getGame(), configuration, features));
|
||||
|
||||
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
|
||||
|
||||
if (StringUtils.isNotBlank(options.getServerIp())) {
|
||||
String[] args = options.getServerIp().split(":");
|
||||
res.add("--server");
|
||||
@@ -255,7 +271,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
@Override
|
||||
public ManagedProcess launch() throws IOException, InterruptedException {
|
||||
File nativeFolder = repository.getNativeDirectory(versionId);
|
||||
File nativeFolder = repository.getNativeDirectory(version.getId());
|
||||
|
||||
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
|
||||
List<String> rawCommandLine = generateCommandLine(nativeFolder).asList();
|
||||
@@ -287,7 +303,7 @@ public class DefaultLauncher extends Launcher {
|
||||
public void makeLaunchScript(File scriptFile) throws IOException {
|
||||
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
||||
|
||||
File nativeFolder = repository.getNativeDirectory(versionId);
|
||||
File nativeFolder = repository.getNativeDirectory(version.getId());
|
||||
decompressNatives(nativeFolder);
|
||||
|
||||
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
|
||||
|
||||
@@ -33,30 +33,27 @@ import java.io.IOException;
|
||||
public abstract class Launcher {
|
||||
|
||||
protected final GameRepository repository;
|
||||
protected final String versionId;
|
||||
protected final Version version;
|
||||
protected final AuthInfo authInfo;
|
||||
protected final LaunchOptions options;
|
||||
protected final ProcessListener listener;
|
||||
protected final boolean daemon;
|
||||
|
||||
public Launcher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options) {
|
||||
this(repository, versionId, authInfo, options, null);
|
||||
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||
this(repository, version, authInfo, options, null);
|
||||
}
|
||||
|
||||
public Launcher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||
this(repository, versionId, authInfo, options, listener, true);
|
||||
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||
this(repository, version, authInfo, options, listener, true);
|
||||
}
|
||||
|
||||
public Launcher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||
this.repository = repository;
|
||||
this.versionId = versionId;
|
||||
this.version = version;
|
||||
this.authInfo = authInfo;
|
||||
this.options = options;
|
||||
this.listener = listener;
|
||||
this.daemon = daemon;
|
||||
|
||||
version = repository.getResolvedVersion(versionId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
|
||||
@@ -155,7 +154,7 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies.add(new MaintainTask(version).thenComposeAsync(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion)));
|
||||
dependencies.add(new MaintainTask(version).thenComposeAsync(repository::save));
|
||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
public final class CommandBuilder {
|
||||
private final OperatingSystem os;
|
||||
private List<Item> raw = new LinkedList<>();
|
||||
@@ -39,9 +39,9 @@ public final class CommandBuilder {
|
||||
|
||||
private String parse(String s) {
|
||||
if (OperatingSystem.WINDOWS == os) {
|
||||
return parseWindows(s);
|
||||
return parseBatch(s);
|
||||
} else {
|
||||
return parseBash(s);
|
||||
return parseShell(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +96,14 @@ public final class CommandBuilder {
|
||||
this.arg = arg;
|
||||
this.parse = parse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return parse ? (OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS ? parseBatch(arg) : parseShell(arg)) : arg;
|
||||
}
|
||||
}
|
||||
|
||||
private static String parseWindows(String s) {
|
||||
private static String parseBatch(String s) {
|
||||
String escape = " \t\"^&<>|";
|
||||
if (StringUtils.containsOne(s, escape.toCharArray()))
|
||||
// The argument has not been quoted, add quotes.
|
||||
@@ -111,7 +116,7 @@ public final class CommandBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private static String parseBash(String s) {
|
||||
private static String parseShell(String s) {
|
||||
String escaping = " \t\"!#$&'()*,;<=>?[\\]^`{|}~";
|
||||
String escaped = "\"$&`";
|
||||
if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0 || StringUtils.containsOne(s, escaping.toCharArray())) {
|
||||
|
||||
Reference in New Issue
Block a user