Make auto-installing more flexible
This commit is contained in:
@@ -29,16 +29,16 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class HMCLGameLauncher extends DefaultLauncher {
|
public final class HMCLGameLauncher extends DefaultLauncher {
|
||||||
|
|
||||||
public HMCLGameLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options) {
|
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||||
this(repository, versionId, authInfo, options, null);
|
this(repository, version, authInfo, options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HMCLGameLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||||
this(repository, versionId, authInfo, options, listener, true);
|
this(repository, version, authInfo, options, listener, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HMCLGameLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||||
super(repository, versionId, authInfo, options, listener, daemon);
|
super(repository, version, authInfo, options, listener, daemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.game;
|
|||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.jackhuang.hmcl.download.DependencyManager;
|
import org.jackhuang.hmcl.download.DependencyManager;
|
||||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
|
||||||
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;
|
||||||
@@ -90,7 +89,7 @@ public final class HMCLModpackInstallTask extends Task<Void> {
|
|||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json");
|
String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json");
|
||||||
Version version = JsonUtils.GSON.fromJson(json, Version.class).setId(name).setJar(null);
|
Version version = JsonUtils.GSON.fromJson(json, Version.class).setId(name).setJar(null);
|
||||||
dependencies.add(new VersionJsonSaveTask(repository, version));
|
dependencies.add(repository.save(version));
|
||||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/minecraft", modpack, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/minecraft", modpack, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,22 @@ import org.jackhuang.hmcl.auth.AuthenticationException;
|
|||||||
import org.jackhuang.hmcl.auth.CredentialExpiredException;
|
import org.jackhuang.hmcl.auth.CredentialExpiredException;
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.MaintainTask;
|
import org.jackhuang.hmcl.download.MaintainTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.LibrariesUniqueTask;
|
||||||
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
||||||
import org.jackhuang.hmcl.launch.*;
|
import org.jackhuang.hmcl.launch.NotDecompressingNativesException;
|
||||||
|
import org.jackhuang.hmcl.launch.PermissionException;
|
||||||
|
import org.jackhuang.hmcl.launch.ProcessCreationException;
|
||||||
|
import org.jackhuang.hmcl.launch.ProcessListener;
|
||||||
import org.jackhuang.hmcl.mod.CurseCompletionException;
|
import org.jackhuang.hmcl.mod.CurseCompletionException;
|
||||||
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
||||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||||
import org.jackhuang.hmcl.task.*;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
|
import org.jackhuang.hmcl.task.TaskListener;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.DialogController;
|
import org.jackhuang.hmcl.ui.DialogController;
|
||||||
import org.jackhuang.hmcl.ui.LogWindow;
|
import org.jackhuang.hmcl.ui.LogWindow;
|
||||||
@@ -56,7 +63,13 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@@ -119,7 +132,7 @@ public final class LauncherHelper {
|
|||||||
private void launch0() {
|
private void launch0() {
|
||||||
HMCLGameRepository repository = profile.getRepository();
|
HMCLGameRepository repository = profile.getRepository();
|
||||||
DefaultDependencyManager dependencyManager = profile.getDependency();
|
DefaultDependencyManager dependencyManager = profile.getDependency();
|
||||||
Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion));
|
Version version = MaintainTask.maintain(LibrariesUniqueTask.unique(repository.getResolvedVersion(selectedVersion)));
|
||||||
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
||||||
|
|
||||||
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
||||||
@@ -159,7 +172,7 @@ public final class LauncherHelper {
|
|||||||
})
|
})
|
||||||
.thenApplyAsync(authInfo -> new HMCLGameLauncher(
|
.thenApplyAsync(authInfo -> new HMCLGameLauncher(
|
||||||
repository,
|
repository,
|
||||||
selectedVersion,
|
version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version,
|
||||||
authInfo,
|
authInfo,
|
||||||
setting.toLaunchOptions(profile.getGameDir()),
|
setting.toLaunchOptions(profile.getGameDir()),
|
||||||
launcherVisibility == LauncherVisibility.CLOSE
|
launcherVisibility == LauncherVisibility.CLOSE
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.download.RemoteVersion;
|
|||||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||||
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.DownloadException;
|
import org.jackhuang.hmcl.task.DownloadException;
|
||||||
@@ -56,10 +55,10 @@ public final class InstallerWizardProvider implements WizardProvider {
|
|||||||
this.gameVersion = gameVersion;
|
this.gameVersion = gameVersion;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
||||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version.resolve(profile.getRepository()));
|
||||||
forge = analyzer.get(FORGE).map(Library::getVersion).orElse(null);
|
forge = analyzer.getVersion(FORGE).orElse(null);
|
||||||
liteLoader = analyzer.get(LITELOADER).map(Library::getVersion).orElse(null);
|
liteLoader = analyzer.getVersion(LITELOADER).orElse(null);
|
||||||
optiFine = analyzer.get(OPTIFINE).map(Library::getVersion).orElse(null);
|
optiFine = analyzer.getVersion(OPTIFINE).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Profile getProfile() {
|
public Profile getProfile() {
|
||||||
|
|||||||
@@ -19,16 +19,13 @@ package org.jackhuang.hmcl.ui.download;
|
|||||||
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||||
import org.jackhuang.hmcl.download.MaintainTask;
|
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.download.InstallerWizardProvider.alertFailureMessage;
|
import static org.jackhuang.hmcl.ui.download.InstallerWizardProvider.alertFailureMessage;
|
||||||
@@ -39,14 +36,14 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
|||||||
private final String gameVersion;
|
private final String gameVersion;
|
||||||
private final Version version;
|
private final Version version;
|
||||||
private final String libraryId;
|
private final String libraryId;
|
||||||
private final Library oldLibrary;
|
private final String oldLibraryVersion;
|
||||||
|
|
||||||
public UpdateInstallerWizardProvider(Profile profile, String gameVersion, Version version, String libraryId, Library oldLibrary) {
|
public UpdateInstallerWizardProvider(Profile profile, String gameVersion, Version version, String libraryId, String oldLibraryVersion) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.gameVersion = gameVersion;
|
this.gameVersion = gameVersion;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.libraryId = libraryId;
|
this.libraryId = libraryId;
|
||||||
this.oldLibrary = oldLibrary;
|
this.oldLibraryVersion = oldLibraryVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,9 +57,7 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
|||||||
|
|
||||||
// We remove library but not save it,
|
// We remove library but not save it,
|
||||||
// so if installation failed will not break down current version.
|
// so if installation failed will not break down current version.
|
||||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
return profile.getDependency().removeLibraryWithoutSavingAsync(version.getId(), libraryId)
|
||||||
newList.remove(oldLibrary);
|
|
||||||
return new MaintainTask(version.setLibraries(newList))
|
|
||||||
.thenComposeAsync(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get(libraryId)))
|
.thenComposeAsync(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get(libraryId)))
|
||||||
.thenComposeAsync(profile.getRepository().refreshVersionsAsync());
|
.thenComposeAsync(profile.getRepository().refreshVersionsAsync());
|
||||||
}
|
}
|
||||||
@@ -73,7 +68,7 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
|||||||
switch (step) {
|
switch (step) {
|
||||||
case 0:
|
case 0:
|
||||||
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, provider, libraryId, () -> {
|
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, provider, libraryId, () -> {
|
||||||
Controllers.confirmDialog(i18n("install.change_version.confirm", i18n("install.installer." + libraryId), oldLibrary.getVersion(), ((RemoteVersion) settings.get(libraryId)).getSelfVersion()),
|
Controllers.confirmDialog(i18n("install.change_version.confirm", i18n("install.installer." + libraryId), oldLibraryVersion, ((RemoteVersion) settings.get(libraryId)).getSelfVersion()),
|
||||||
i18n("install.change_version"), controller::onFinish, controller::onCancel);
|
i18n("install.change_version"), controller::onFinish, controller::onCancel);
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -18,7 +18,10 @@
|
|||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
@@ -26,6 +29,10 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
|||||||
import org.jackhuang.hmcl.game.GameVersion;
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||||
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
|
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
|
||||||
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
||||||
@@ -33,10 +40,6 @@ import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
|
|||||||
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
|
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class GameItem extends Control {
|
public class GameItem extends Control {
|
||||||
|
|
||||||
private static final ThreadPoolExecutor POOL_VERSION_RESOLVE = threadPool("VersionResolve", true, 1, 1, TimeUnit.SECONDS);
|
private static final ThreadPoolExecutor POOL_VERSION_RESOLVE = threadPool("VersionResolve", true, 1, 1, TimeUnit.SECONDS);
|
||||||
@@ -56,9 +59,9 @@ public class GameItem extends Control {
|
|||||||
.thenAcceptAsync(game -> {
|
.thenAcceptAsync(game -> {
|
||||||
StringBuilder libraries = new StringBuilder(game);
|
StringBuilder libraries = new StringBuilder(game);
|
||||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getVersion(id));
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getVersion(id));
|
||||||
analyzer.get(FORGE).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)forge", ""))));
|
analyzer.getVersion(FORGE).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.replaceAll("(?i)forge", ""))));
|
||||||
analyzer.get(LITELOADER).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)liteloader", ""))));
|
analyzer.getVersion(LITELOADER).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.replaceAll("(?i)liteloader", ""))));
|
||||||
analyzer.get(OPTIFINE).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)optifine", ""))));
|
analyzer.getVersion(OPTIFINE).ifPresent(library -> libraries.append(", ").append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.replaceAll("(?i)optifine", ""))));
|
||||||
subtitle.set(libraries.toString());
|
subtitle.set(libraries.toString());
|
||||||
}, Platform::runLater)
|
}, Platform::runLater)
|
||||||
.exceptionally(handleUncaught);
|
.exceptionally(handleUncaught);
|
||||||
|
|||||||
@@ -21,24 +21,25 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.download.MaintainTask;
|
|
||||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
|
||||||
import org.jackhuang.hmcl.game.GameVersion;
|
import org.jackhuang.hmcl.game.GameVersion;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
import org.jackhuang.hmcl.task.TaskListener;
|
import org.jackhuang.hmcl.task.TaskListener;
|
||||||
import org.jackhuang.hmcl.ui.*;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
|
import org.jackhuang.hmcl.ui.InstallerItem;
|
||||||
|
import org.jackhuang.hmcl.ui.ListPageBase;
|
||||||
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
|
import org.jackhuang.hmcl.ui.ToolbarListPageSkin;
|
||||||
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
||||||
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
|
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -68,38 +69,35 @@ public class InstallerListPage extends ListPageBase<InstallerItem> {
|
|||||||
public void loadVersion(Profile profile, String versionId) {
|
public void loadVersion(Profile profile, String versionId) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.versionId = versionId;
|
this.versionId = versionId;
|
||||||
this.version = profile.getRepository().getResolvedVersion(versionId);
|
this.version = profile.getRepository().getVersion(versionId);
|
||||||
this.gameVersion = null;
|
this.gameVersion = null;
|
||||||
|
|
||||||
Task.supplyAsync(() -> {
|
Task.supplyAsync(() -> {
|
||||||
gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse(null);
|
gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse(null);
|
||||||
|
|
||||||
return LibraryAnalyzer.analyze(version);
|
return LibraryAnalyzer.analyze(profile.getRepository().getResolvedVersion(versionId));
|
||||||
}).thenAcceptAsync(Schedulers.javafx(), analyzer -> {
|
}).thenAcceptAsync(Schedulers.javafx(), analyzer -> {
|
||||||
Function<Library, Consumer<InstallerItem>> removeAction = library -> x -> {
|
Function<String, Consumer<InstallerItem>> removeAction = libraryId -> x -> {
|
||||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
profile.getDependency().removeLibraryAsync(version.getId(), libraryId)
|
||||||
newList.remove(library);
|
|
||||||
new MaintainTask(version.setLibraries(newList))
|
|
||||||
.thenComposeAsync(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
|
|
||||||
.withComposeAsync(profile.getRepository().refreshVersionsAsync())
|
.withComposeAsync(profile.getRepository().refreshVersionsAsync())
|
||||||
.withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
|
.withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
|
||||||
.start();
|
.start();
|
||||||
};
|
};
|
||||||
|
|
||||||
itemsProperty().clear();
|
itemsProperty().clear();
|
||||||
analyzer.get(FORGE).ifPresent(library -> itemsProperty().add(
|
analyzer.getVersion(FORGE).ifPresent(libraryVersion -> itemsProperty().add(
|
||||||
new InstallerItem("Forge", library.getVersion(), () -> {
|
new InstallerItem("Forge", libraryVersion, () -> {
|
||||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "forge", library));
|
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "forge", libraryVersion));
|
||||||
}, removeAction.apply(library))));
|
}, removeAction.apply("forge"))));
|
||||||
analyzer.get(LITELOADER).ifPresent(library -> itemsProperty().add(
|
analyzer.getVersion(LITELOADER).ifPresent(libraryVersion -> itemsProperty().add(
|
||||||
new InstallerItem("LiteLoader", library.getVersion(), () -> {
|
new InstallerItem("LiteLoader", libraryVersion, () -> {
|
||||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "liteloader", library));
|
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "liteloader", libraryVersion));
|
||||||
}, removeAction.apply(library))));
|
}, removeAction.apply("liteloader"))));
|
||||||
analyzer.get(OPTIFINE).ifPresent(library -> itemsProperty().add(
|
analyzer.getVersion(OPTIFINE).ifPresent(libraryVersion -> itemsProperty().add(
|
||||||
new InstallerItem("OptiFine", library.getVersion(), () -> {
|
new InstallerItem("OptiFine", libraryVersion, () -> {
|
||||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "optifine", library));
|
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "optifine", libraryVersion));
|
||||||
}, removeAction.apply(library))));
|
}, removeAction.apply("optifine"))));
|
||||||
analyzer.get(FABRIC).ifPresent(library -> itemsProperty().add(new InstallerItem("Fabric", library.getVersion(), null, null)));
|
analyzer.getVersion(FABRIC).ifPresent(libraryVersion -> itemsProperty().add(new InstallerItem("Fabric", libraryVersion, null, null)));
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,18 +19,22 @@ package org.jackhuang.hmcl.download;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
||||||
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
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.LiteLoaderInstallTask;
|
||||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: This class has no state.
|
* Note: This class has no state.
|
||||||
@@ -90,6 +94,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
|
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);
|
VersionList<?> versionList = getVersionList(libraryId);
|
||||||
return versionList.loadAsync(gameVersion, getDownloadProvider())
|
return versionList.loadAsync(gameVersion, getDownloadProvider())
|
||||||
.thenComposeAsync(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
|
.thenComposeAsync(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
|
||||||
@@ -98,6 +104,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
|
public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
|
||||||
|
if (oldVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved");
|
||||||
|
|
||||||
Task<Version> task;
|
Task<Version> task;
|
||||||
if (libraryVersion instanceof ForgeRemoteVersion)
|
if (libraryVersion instanceof ForgeRemoteVersion)
|
||||||
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
|
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
|
||||||
@@ -108,9 +116,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
else
|
else
|
||||||
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
||||||
return task
|
return task
|
||||||
.thenComposeAsync(LibrariesUniqueTask::new)
|
.thenApplyAsync(oldVersion::addPatch)
|
||||||
.thenComposeAsync(MaintainTask::new)
|
.thenComposeAsync(repository::save);
|
||||||
.thenComposeAsync(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +125,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
return version -> installLibraryAsync(version, libraryVersion);
|
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
|
return Task
|
||||||
.composeAsync(() -> {
|
.composeAsync(() -> {
|
||||||
try {
|
try {
|
||||||
@@ -133,8 +142,54 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
|
|
||||||
throw new UnsupportedOperationException("Library cannot be recognized");
|
throw new UnsupportedOperationException("Library cannot be recognized");
|
||||||
})
|
})
|
||||||
.thenComposeAsync(LibrariesUniqueTask::new)
|
.thenApplyAsync(oldVersion::addPatch)
|
||||||
.thenComposeAsync(MaintainTask::new)
|
.thenComposeAsync(repository::save);
|
||||||
.thenComposeAsync(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
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.game.Version;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
@@ -53,7 +56,7 @@ public class DefaultGameBuilder extends GameBuilder {
|
|||||||
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenComposeAsync(Task.allOf(
|
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenComposeAsync(Task.allOf(
|
||||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
|
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.
|
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);
|
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.Library;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public final class LibraryAnalyzer {
|
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;
|
this.libraries = libraries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Library> get(LibraryType type) {
|
public Optional<String> getVersion(LibraryType type) {
|
||||||
return Optional.ofNullable(libraries.get(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) {
|
public boolean has(LibraryType type) {
|
||||||
@@ -48,7 +57,7 @@ public final class LibraryAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static LibraryAnalyzer analyze(Version version) {
|
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()) {
|
for (Library library : version.getLibraries()) {
|
||||||
String groupId = library.getGroupId();
|
String groupId = library.getGroupId();
|
||||||
@@ -56,7 +65,16 @@ public final class LibraryAnalyzer {
|
|||||||
|
|
||||||
for (LibraryType type : LibraryType.values()) {
|
for (LibraryType type : LibraryType.values()) {
|
||||||
if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,16 +84,18 @@ public final class LibraryAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum LibraryType {
|
public enum LibraryType {
|
||||||
FORGE(true, Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
|
FORGE(true, "net.minecraftforge", Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
|
||||||
LITELOADER(true, Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
|
LITELOADER(true, "com.mumfrey.liteloader", Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
|
||||||
OPTIFINE(false, Pattern.compile("(net\\.)?optifine"), Pattern.compile(".*")),
|
OPTIFINE(false, "net.optifine", Pattern.compile("(net\\.)?optifine"), Pattern.compile(".*")),
|
||||||
FABRIC(true, Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader"));
|
FABRIC(true, "net.fabricmc", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader"));
|
||||||
|
|
||||||
private final Pattern group, artifact;
|
|
||||||
private final boolean modLoader;
|
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.modLoader = modLoader;
|
||||||
|
this.patchId = patchId;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.artifact = artifact;
|
this.artifact = artifact;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ public final class ForgeInstallTask extends Task<Version> {
|
|||||||
@Override
|
@Override
|
||||||
public void postExecute() throws Exception {
|
public void postExecute() throws Exception {
|
||||||
Files.deleteIfExists(installer);
|
Files.deleteIfExists(installer);
|
||||||
setResult(dependency.getResult());
|
setResult(dependency.getResult()
|
||||||
|
.setPriority(10000)
|
||||||
|
.setId("net.minecraftforge")
|
||||||
|
.setVersion(remote.getSelfVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -102,6 +105,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Install Forge library from existing local file.
|
* 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 dependencyManager game repository
|
||||||
* @param version version.json
|
* @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.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
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.task.Task;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
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.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
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.Attributes;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -258,15 +268,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve the version
|
setResult(forgeVersion);
|
||||||
SimpleVersionProvider provider = new SimpleVersionProvider();
|
|
||||||
provider.addVersion(version);
|
|
||||||
|
|
||||||
setResult(forgeVersion
|
|
||||||
.setInheritsFrom(version.getId())
|
|
||||||
.resolve(provider).setJar(null)
|
|
||||||
.setId(version.getId()).setLogging(Collections.emptyMap()));
|
|
||||||
|
|
||||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion));
|
dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion));
|
||||||
|
|
||||||
FileUtils.deleteDirectory(temp.toFile());
|
FileUtils.deleteDirectory(temp.toFile());
|
||||||
|
|||||||
@@ -18,15 +18,21 @@
|
|||||||
package org.jackhuang.hmcl.download.forge;
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
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.task.Task;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
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.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
@@ -75,15 +81,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
|||||||
IOUtils.copyTo(is, os);
|
IOUtils.copyTo(is, os);
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve the version
|
setResult(installProfile.getVersionInfo());
|
||||||
SimpleVersionProvider provider = new SimpleVersionProvider();
|
|
||||||
provider.addVersion(version);
|
|
||||||
|
|
||||||
setResult(installProfile.getVersionInfo()
|
|
||||||
.setInheritsFrom(version.getId())
|
|
||||||
.resolve(provider).setJar(null)
|
|
||||||
.setId(version.getId()).setLogging(Collections.emptyMap()));
|
|
||||||
|
|
||||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo()));
|
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ public class LibrariesUniqueTask extends Task<Version> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
|
setResult(unique(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Version unique(Version version) {
|
||||||
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
||||||
|
|
||||||
SimpleMultimap<String, Library> multimap = new SimpleMultimap<String, Library>(HashMap::new, LinkedList::new);
|
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;
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||||
@@ -67,16 +68,16 @@ public final class LiteLoaderInstallTask extends Task<Version> {
|
|||||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||||
);
|
);
|
||||||
|
|
||||||
Version tempVersion = version.setLibraries(Lang.merge(remote.getLibraries(), Collections.singleton(library)));
|
setResult(new Version("com.mumfrey.liteloader",
|
||||||
|
remote.getSelfVersion(),
|
||||||
// --tweakClass will be added in MaintainTask
|
20000,
|
||||||
setResult(version
|
new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"),
|
||||||
.setMainClass("net.minecraft.launchwrapper.Launch")
|
"net.minecraft.launchwrapper.Launch",
|
||||||
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
|
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
|
.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.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
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.task.Task;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
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 org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jenkinsci.constant_pool_scanner.ConstantPool;
|
import org.jenkinsci.constant_pool_scanner.ConstantPool;
|
||||||
import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||||
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||||
@@ -34,7 +40,12 @@ import java.io.IOException;
|
|||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
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;
|
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 Path installer;
|
||||||
private final List<Task<?>> dependents = new LinkedList<>();
|
private final List<Task<?>> dependents = new LinkedList<>();
|
||||||
private final List<Task<?>> dependencies = 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) {
|
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
|
||||||
this(dependencyManager, version, remoteVersion, null);
|
this(dependencyManager, version, remoteVersion, null);
|
||||||
@@ -61,6 +75,39 @@ public final class OptiFineInstallTask extends Task<Version> {
|
|||||||
this.version = version;
|
this.version = version;
|
||||||
this.remote = remoteVersion;
|
this.remote = remoteVersion;
|
||||||
this.installer = installer;
|
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
|
@Override
|
||||||
@@ -80,35 +127,42 @@ public final class OptiFineInstallTask extends Task<Version> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws IOException {
|
public void execute() throws IOException {
|
||||||
if (!Arrays.asList("net.minecraft.client.main.Main",
|
List<Library> libraries = new LinkedList<>();
|
||||||
"net.minecraft.launchwrapper.Launch")
|
libraries.add(optiFineLibrary);
|
||||||
.contains(version.getMainClass()))
|
|
||||||
throw new UnsupportedOptiFineInstallationException();
|
|
||||||
|
|
||||||
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(
|
Library launchWrapper = new Library("optifine", "launchwrapper-of", launchWrapperVersion);
|
||||||
"optifine", "OptiFine", remoteVersion, null, null,
|
|
||||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
|
||||||
"optifine/OptiFine/" + remoteVersion + "/OptiFine-" + remoteVersion + ".jar",
|
|
||||||
remote.getUrl()))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (installer != null) {
|
if (Files.exists(launchWrapperJar)) {
|
||||||
FileUtils.copyFile(installer, dependencyManager.getGameRepository().getLibraryFile(version, library).toPath());
|
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<>();
|
if (!hasLaunchWrapper) {
|
||||||
libraries.add(library);
|
|
||||||
|
|
||||||
if (version.getMainClass() == null || !version.getMainClass().startsWith("net.minecraft.launchwrapper."))
|
|
||||||
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
|
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
|
||||||
|
}
|
||||||
|
|
||||||
// --tweakClass will be added in MaintainTask
|
setResult(new Version(
|
||||||
setResult(version
|
"net.optifine",
|
||||||
.setLibraries(Lang.merge(version.getLibraries(), libraries))
|
remote.getSelfVersion(),
|
||||||
.setMainClass("net.minecraft.launchwrapper.Launch")
|
30000,
|
||||||
);
|
new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"),
|
||||||
|
"net.minecraft.launchwrapper.Launch",
|
||||||
|
libraries
|
||||||
|
));
|
||||||
|
|
||||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(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.Immutable;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
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;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,16 +51,18 @@ public final class Arguments {
|
|||||||
this.jvm = jvm;
|
this.jvm = jvm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public List<Argument> getGame() {
|
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) {
|
public Arguments withGame(List<Argument> game) {
|
||||||
return new Arguments(game, jvm);
|
return new Arguments(game, jvm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public List<Argument> getJvm() {
|
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) {
|
public Arguments withJvm(List<Argument> jvm) {
|
||||||
@@ -86,7 +93,9 @@ public final class Arguments {
|
|||||||
else if (b == null)
|
else if (b == null)
|
||||||
return a;
|
return a;
|
||||||
else
|
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) {
|
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 class ClassicVersion extends Version {
|
||||||
|
|
||||||
public ClassicVersion() {
|
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,
|
null, "net.minecraft.client.Minecraft", null, null, null, null,
|
||||||
Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")),
|
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 {
|
private static class ClassicLibrary extends Library {
|
||||||
|
|||||||
@@ -18,8 +18,17 @@
|
|||||||
package org.jackhuang.hmcl.game;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
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.mod.ModManager;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
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.logging.Level;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -368,6 +383,10 @@ public class DefaultGameRepository implements GameRepository {
|
|||||||
return assetsDir;
|
return assetsDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<Version> save(Version version) {
|
||||||
|
return new VersionJsonSaveTask(this, version);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isLoaded() {
|
public boolean isLoaded() {
|
||||||
return versions != null;
|
return versions != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,28 @@
|
|||||||
package org.jackhuang.hmcl.game;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
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.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.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -32,6 +49,8 @@ import java.util.logging.Level;
|
|||||||
public class Version implements Comparable<Version>, Validation {
|
public class Version implements Comparable<Version>, Validation {
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
|
private final String version;
|
||||||
|
private final Integer priority;
|
||||||
private final String minecraftArguments;
|
private final String minecraftArguments;
|
||||||
private final Arguments arguments;
|
private final Arguments arguments;
|
||||||
private final String mainClass;
|
private final String mainClass;
|
||||||
@@ -47,13 +66,20 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
private final Date time;
|
private final Date time;
|
||||||
private final Date releaseTime;
|
private final Date releaseTime;
|
||||||
private final int minimumLauncherVersion;
|
private final int minimumLauncherVersion;
|
||||||
private final boolean hidden;
|
private final Boolean hidden;
|
||||||
|
private final List<Version> patches;
|
||||||
|
|
||||||
private transient final boolean resolved;
|
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.resolved = resolved;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.version = version;
|
||||||
|
this.priority = priority;
|
||||||
this.minecraftArguments = minecraftArguments;
|
this.minecraftArguments = minecraftArguments;
|
||||||
this.arguments = arguments;
|
this.arguments = arguments;
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
@@ -61,15 +87,16 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
this.jar = jar;
|
this.jar = jar;
|
||||||
this.assetIndex = assetIndex;
|
this.assetIndex = assetIndex;
|
||||||
this.assets = assets;
|
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.compatibilityRules = compatibilityRules == null ? null : new LinkedList<>(compatibilityRules);
|
||||||
this.downloads = downloads == null ? null : new HashMap<>(downloads);
|
this.downloads = downloads == null ? null : new HashMap<>(downloads);
|
||||||
this.logging = logging == null ? null : new HashMap<>(logging);
|
this.logging = logging == null ? null : new HashMap<>(logging);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.time = time == null ? new Date() : (Date) time.clone();
|
this.time = time == null ? null : (Date) time.clone();
|
||||||
this.releaseTime = releaseTime == null ? new Date() : (Date) releaseTime.clone();
|
this.releaseTime = releaseTime == null ? null : (Date) releaseTime.clone();
|
||||||
this.minimumLauncherVersion = minimumLauncherVersion;
|
this.minimumLauncherVersion = minimumLauncherVersion;
|
||||||
this.hidden = hidden;
|
this.hidden = hidden;
|
||||||
|
this.patches = patches == null ? null : patches.isEmpty() ? null : new LinkedList<>(patches);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getMinecraftArguments() {
|
public Optional<String> getMinecraftArguments() {
|
||||||
@@ -92,8 +119,22 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return id;
|
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() {
|
public ReleaseType getType() {
|
||||||
return type;
|
return type == null ? ReleaseType.UNKNOWN : type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getReleaseTime() {
|
public Date getReleaseTime() {
|
||||||
@@ -113,7 +154,15 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHidden() {
|
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() {
|
public Map<DownloadType, LoggingInfo> getLogging() {
|
||||||
@@ -153,22 +202,12 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
return resolve(provider, new HashSet<>()).setResolved();
|
return resolve(provider, new HashSet<>()).setResolved();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Version resolve(VersionProvider provider, Set<String> resolvedSoFar) throws VersionNotFoundException {
|
protected Version merge(Version parent) {
|
||||||
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);
|
|
||||||
return new Version(
|
return new Version(
|
||||||
true,
|
true,
|
||||||
id,
|
id,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
minecraftArguments == null ? parent.minecraftArguments : minecraftArguments,
|
minecraftArguments == null ? parent.minecraftArguments : minecraftArguments,
|
||||||
Arguments.merge(parent.arguments, arguments),
|
Arguments.merge(parent.arguments, arguments),
|
||||||
mainClass == null ? parent.mainClass : mainClass,
|
mainClass == null ? parent.mainClass : mainClass,
|
||||||
@@ -184,43 +223,90 @@ public class Version implements Comparable<Version>, Validation {
|
|||||||
time,
|
time,
|
||||||
releaseTime,
|
releaseTime,
|
||||||
Math.max(minimumLauncherVersion, parent.minimumLauncherVersion),
|
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() {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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
|
@Override
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import org.jackhuang.hmcl.util.StringUtils;
|
|||||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,13 +41,13 @@ public final class VersionLibraryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Version build() {
|
public Version build() {
|
||||||
|
Version ret = version;
|
||||||
if (useMcArgs) {
|
if (useMcArgs) {
|
||||||
// Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping,
|
// Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping,
|
||||||
// so we regenerate the minecraftArgument without escaping.
|
// so we regenerate the minecraftArgument without escaping.
|
||||||
return version.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
|
ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
|
||||||
} else {
|
|
||||||
return version.setArguments(version.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, Collections.emptyList())));
|
|
||||||
}
|
}
|
||||||
|
return ret.setArguments(ret.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTweakClass(String target) {
|
public void removeTweakClass(String target) {
|
||||||
@@ -63,30 +61,26 @@ public final class VersionLibraryBuilder {
|
|||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
for (int i = 0; i + 1 < game.size(); ++i) {
|
|
||||||
Argument arg0 = game.get(i);
|
for (int i = 0; i + 1 < game.size(); ++i) {
|
||||||
Argument arg1 = game.get(i + 1);
|
Argument arg0 = game.get(i);
|
||||||
if (arg0 instanceof StringArgument && arg1 instanceof StringArgument) {
|
Argument arg1 = game.get(i + 1);
|
||||||
// We need to preserve the tokens
|
if (arg0 instanceof StringArgument && arg1 instanceof StringArgument) {
|
||||||
String arg0Str = arg0.toString();
|
// We need to preserve the tokens
|
||||||
String arg1Str = arg1.toString();
|
String arg0Str = arg0.toString();
|
||||||
if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) {
|
String arg1Str = arg1.toString();
|
||||||
game.remove(i);
|
if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) {
|
||||||
game.remove(i);
|
game.remove(i);
|
||||||
--i;
|
game.remove(i);
|
||||||
}
|
--i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addArgument(String... args) {
|
public void addArgument(String... args) {
|
||||||
if (useMcArgs) {
|
for (String arg : args)
|
||||||
mcArgs.addAll(Arrays.asList(args));
|
game.add(new StringArgument(arg));
|
||||||
} else {
|
|
||||||
for (String arg : args)
|
|
||||||
game.add(new StringArgument(arg));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,15 @@
|
|||||||
package org.jackhuang.hmcl.launch;
|
package org.jackhuang.hmcl.launch;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.Argument;
|
||||||
import org.jackhuang.hmcl.util.*;
|
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.gson.UUIDTypeAdapter;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.io.Unzipper;
|
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.OperatingSystem;
|
||||||
import org.jackhuang.hmcl.util.platform.Platform;
|
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.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 java.util.function.Supplier;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
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 class DefaultLauncher extends Launcher {
|
||||||
|
|
||||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options) {
|
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||||
this(repository, versionId, authInfo, options, null);
|
this(repository, version, authInfo, options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||||
this(repository, versionId, authInfo, options, listener, true);
|
this(repository, version, authInfo, options, listener, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultLauncher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) {
|
||||||
super(repository, versionId, authInfo, options, listener, daemon);
|
super(repository, version, authInfo, options, listener, daemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommandBuilder generateCommandLine(File nativeFolder) throws IOException {
|
private CommandBuilder generateCommandLine(File nativeFolder) throws IOException {
|
||||||
@@ -144,13 +160,13 @@ public class DefaultLauncher extends Launcher {
|
|||||||
|
|
||||||
res.add(version.getMainClass());
|
res.add(version.getMainClass());
|
||||||
|
|
||||||
|
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
|
||||||
|
|
||||||
Map<String, Boolean> features = getFeatures();
|
Map<String, Boolean> features = getFeatures();
|
||||||
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getGame).orElseGet(this::getDefaultGameArguments), configuration, features));
|
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())
|
if (authInfo.getArguments() != null && authInfo.getArguments().getGame() != null && !authInfo.getArguments().getGame().isEmpty())
|
||||||
res.addAll(Arguments.parseArguments(authInfo.getArguments().getGame(), configuration, features));
|
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())) {
|
if (StringUtils.isNotBlank(options.getServerIp())) {
|
||||||
String[] args = options.getServerIp().split(":");
|
String[] args = options.getServerIp().split(":");
|
||||||
res.add("--server");
|
res.add("--server");
|
||||||
@@ -255,7 +271,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ManagedProcess launch() throws IOException, InterruptedException {
|
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
|
// To guarantee that when failed to generate launch command line, we will not call pre-launch command
|
||||||
List<String> rawCommandLine = generateCommandLine(nativeFolder).asList();
|
List<String> rawCommandLine = generateCommandLine(nativeFolder).asList();
|
||||||
@@ -287,7 +303,7 @@ public class DefaultLauncher extends Launcher {
|
|||||||
public void makeLaunchScript(File scriptFile) throws IOException {
|
public void makeLaunchScript(File scriptFile) throws IOException {
|
||||||
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
||||||
|
|
||||||
File nativeFolder = repository.getNativeDirectory(versionId);
|
File nativeFolder = repository.getNativeDirectory(version.getId());
|
||||||
decompressNatives(nativeFolder);
|
decompressNatives(nativeFolder);
|
||||||
|
|
||||||
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
|
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
|
||||||
|
|||||||
@@ -33,30 +33,27 @@ import java.io.IOException;
|
|||||||
public abstract class Launcher {
|
public abstract class Launcher {
|
||||||
|
|
||||||
protected final GameRepository repository;
|
protected final GameRepository repository;
|
||||||
protected final String versionId;
|
|
||||||
protected final Version version;
|
protected final Version version;
|
||||||
protected final AuthInfo authInfo;
|
protected final AuthInfo authInfo;
|
||||||
protected final LaunchOptions options;
|
protected final LaunchOptions options;
|
||||||
protected final ProcessListener listener;
|
protected final ProcessListener listener;
|
||||||
protected final boolean daemon;
|
protected final boolean daemon;
|
||||||
|
|
||||||
public Launcher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options) {
|
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) {
|
||||||
this(repository, versionId, authInfo, options, null);
|
this(repository, version, authInfo, options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Launcher(GameRepository repository, String versionId, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) {
|
||||||
this(repository, versionId, authInfo, options, listener, true);
|
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.repository = repository;
|
||||||
this.versionId = versionId;
|
this.version = version;
|
||||||
this.authInfo = authInfo;
|
this.authInfo = authInfo;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.daemon = daemon;
|
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.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.GameBuilder;
|
import org.jackhuang.hmcl.download.GameBuilder;
|
||||||
import org.jackhuang.hmcl.download.MaintainTask;
|
import org.jackhuang.hmcl.download.MaintainTask;
|
||||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
|
||||||
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.game.VersionLibraryBuilder;
|
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)));
|
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;
|
package org.jackhuang.hmcl.util.platform;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
|
||||||
|
|
||||||
public final class CommandBuilder {
|
public final class CommandBuilder {
|
||||||
private final OperatingSystem os;
|
private final OperatingSystem os;
|
||||||
private List<Item> raw = new LinkedList<>();
|
private List<Item> raw = new LinkedList<>();
|
||||||
@@ -39,9 +39,9 @@ public final class CommandBuilder {
|
|||||||
|
|
||||||
private String parse(String s) {
|
private String parse(String s) {
|
||||||
if (OperatingSystem.WINDOWS == os) {
|
if (OperatingSystem.WINDOWS == os) {
|
||||||
return parseWindows(s);
|
return parseBatch(s);
|
||||||
} else {
|
} else {
|
||||||
return parseBash(s);
|
return parseShell(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,9 +96,14 @@ public final class CommandBuilder {
|
|||||||
this.arg = arg;
|
this.arg = arg;
|
||||||
this.parse = parse;
|
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\"^&<>|";
|
String escape = " \t\"^&<>|";
|
||||||
if (StringUtils.containsOne(s, escape.toCharArray()))
|
if (StringUtils.containsOne(s, escape.toCharArray()))
|
||||||
// The argument has not been quoted, add quotes.
|
// 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 escaping = " \t\"!#$&'()*,;<=>?[\\]^`{|}~";
|
||||||
String escaped = "\"$&`";
|
String escaped = "\"$&`";
|
||||||
if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0 || StringUtils.containsOne(s, escaping.toCharArray())) {
|
if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0 || StringUtils.containsOne(s, escaping.toCharArray())) {
|
||||||
|
|||||||
Reference in New Issue
Block a user