Support changing game version

This commit is contained in:
huanghongxun 2019-08-19 16:05:40 +08:00
parent a2ac3c1f18
commit 11e66ac13c
15 changed files with 125 additions and 48 deletions

View File

@ -26,11 +26,14 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameInstallTask;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
import org.jackhuang.hmcl.mod.ModpackInstallTask;
import org.jackhuang.hmcl.mod.ModpackUpdateTask;
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
@ -86,6 +89,8 @@ public final class TaskListPane extends StackPane {
if (task instanceof GameAssetDownloadTask) {
task.setName(i18n("assets.download_all"));
} else if (task instanceof GameInstallTask) {
task.setName(i18n("install.installer.install", i18n("install.installer.game")));
} else if (task instanceof ForgeInstallTask) {
task.setName(i18n("install.installer.install", i18n("install.installer.forge")));
} else if (task instanceof LiteLoaderInstallTask) {

View File

@ -34,6 +34,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
import static org.jackhuang.hmcl.util.Lang.threadPool;
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
@ -60,6 +61,7 @@ public class GameItem extends Control {
StringBuilder libraries = new StringBuilder(game);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(id));
analyzer.forEachLibrary((libraryId, libraryVersion) -> {
if (libraryId.equals(MINECRAFT.getPatchId())) return;
if (I18n.hasKey("install.installer." + libraryId)) {
libraries.append(", ").append(i18n("install.installer." + libraryId));
if (libraryVersion != null)

View File

@ -88,13 +88,14 @@ public class InstallerListPage extends ListPageBase<InstallerItem> {
itemsProperty().clear();
analyzer.forEachLibrary((libraryId, libraryVersion) -> {
String title = I18n.hasKey("install.installer." + libraryId) ? i18n("install.installer." + libraryId) : libraryId;
if (Lang.test(() -> profile.getDependency().getVersionList(libraryId)))
Consumer<InstallerItem> action = "game".equals(libraryId) ? null : removeAction.apply(libraryId);
if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId)))
itemsProperty().add(
new InstallerItem(title, libraryVersion, () -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
}, removeAction.apply(libraryId)));
new InstallerItem(title, libraryVersion, () -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
}, action));
else
itemsProperty().add(new InstallerItem(title, libraryVersion, null, removeAction.apply(libraryId)));
itemsProperty().add(new InstallerItem(title, libraryVersion, null, action));
});
}).start();
}

View File

@ -21,7 +21,6 @@ import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
@ -77,8 +76,7 @@ public class Versions {
}
public static void updateGameAssets(Profile profile, String version) {
Version resolvedVersion = profile.getRepository().getResolvedVersion(version);
TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), resolvedVersion, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY)
TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY)
.executor();
Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"));
executor.start();

View File

@ -68,7 +68,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public Task<?> checkGameCompletionAsync(Version version) {
public Task<?> checkGameCompletionAsync(Version original) {
Version version = original.resolve(repository);
return Task.allOf(
Task.composeAsync(() -> {
if (!repository.getVersionJar(version).exists())

View File

@ -17,14 +17,9 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameDownloadTask;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import java.util.Map;
@ -52,24 +47,16 @@ public class DefaultGameBuilder extends GameBuilder {
@Override
public Task<?> buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenComposeAsync(rawJson -> {
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null);
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenComposeAsync(Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).withComposeAsync(dependencyManager.getGameRepository().save(version))); // using [with] because download failure here are tolerant.
Task<Version> libraryTask = Task.supplyAsync(() -> new Version(name));
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, "game", gameVersion));
Task<Version> libraryTask = vanillaTask.thenSupplyAsync(() -> version);
for (Map.Entry<String, String> entry : toolVersions.entrySet())
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue()));
for (Map.Entry<String, String> entry : toolVersions.entrySet())
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue()));
for (RemoteVersion remoteVersion : remoteVersions)
libraryTask = libraryTask.thenComposeAsync(dependencyManager.installLibraryAsync(remoteVersion));
for (RemoteVersion remoteVersion : remoteVersions)
libraryTask = libraryTask.thenComposeAsync(dependencyManager.installLibraryAsync(remoteVersion));
return libraryTask;
}).whenComplete(exception -> {
return libraryTask.whenComplete(exception -> {
if (exception != null)
dependencyManager.getGameRepository().removeVersionFromDisk(name);
});
@ -78,9 +65,4 @@ public class DefaultGameBuilder extends GameBuilder {
private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) {
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, libraryVersion);
}
protected Task<Void> downloadGameAsync(String gameVersion, Version version) {
return new GameDownloadTask(dependencyManager, gameVersion, version);
}
}

View File

@ -56,7 +56,9 @@ public class MaintainTask extends Task<Version> {
if (version.getInheritsFrom() != null)
throw new IllegalArgumentException("MaintainTask requires independent game version");
if (version.resolve(null).getMainClass().contains("launchwrapper")) {
String mainClass = version.resolve(null).getMainClass();
if (mainClass != null && mainClass.contains("launchwrapper")) {
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version)));
} else {
// Vanilla Minecraft does not need maintain

View File

@ -51,16 +51,16 @@ public final class GameAssetDownloadTask extends Task<Void> {
* Constructor.
*
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
* @param version the game version
*/
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex) {
this.dependencyManager = dependencyManager;
this.version = version;
this.assetIndexInfo = version.getAssetIndex();
this.version = version.resolve(dependencyManager.getGameRepository());
this.assetIndexInfo = this.version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
if (!assetIndexFile.exists() || forceDownloadingIndex)
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, version));
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version));
}
@Override

View File

@ -43,7 +43,7 @@ public final class GameDownloadTask extends Task<Void> {
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.version = version.resolve(dependencyManager.getGameRepository());
setSignificance(TaskSignificance.MODERATE);
}

View File

@ -0,0 +1,79 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
public class GameInstallTask extends Task<Version> {
private final DefaultGameRepository gameRepository;
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final GameRemoteVersion remote;
private final VersionJsonDownloadTask downloadTask;
private final List<Task<?>> dependencies = new LinkedList<>();
public GameInstallTask(DefaultDependencyManager dependencyManager, Version version, GameRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameRepository = dependencyManager.getGameRepository();
this.version = version;
this.remote = remoteVersion;
this.downloadTask = new VersionJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager);
}
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(downloadTask);
}
@Override
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override
public void execute() throws Exception {
Version patch = JsonUtils.GSON.fromJson(downloadTask.getResult(), Version.class)
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(0);
setResult(patch);
Version version = new Version(this.version.getId()).addPatch(patch);
dependencies.add(new GameDownloadTask(dependencyManager, remote.getGameVersion(), version)
.thenComposeAsync(Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
new GameLibrariesTask(dependencyManager, version)
).withComposeAsync(gameRepository.save(version))));
}
}

View File

@ -43,17 +43,17 @@ public final class GameLibrariesTask extends Task<Void> {
* Constructor.
*
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
* @param version the game version
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
this(dependencyManager, version, version.getLibraries());
this(dependencyManager, version, version.resolve(dependencyManager.getGameRepository()).getLibraries());
}
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
* @param version the game version
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, List<Library> libraries) {
this.dependencyManager = dependencyManager;

View File

@ -17,8 +17,11 @@
*/
package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.ReleaseType;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Immutable;
import java.util.Date;
@ -47,6 +50,11 @@ public final class GameRemoteVersion extends RemoteVersion {
return type;
}
@Override
public Task<Version> getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) {
return new GameInstallTask(dependencyManager, baseVersion, this);
}
@Override
public int compareTo(RemoteVersion o) {
if (!(o instanceof GameRemoteVersion))

View File

@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
@ -48,7 +47,7 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
protected Collection<GameRemoteVersion> getVersionsImpl(String gameVersion) {
lock.readLock().lock();
try {
return StringUtils.isBlank(gameVersion) ? versions.values() : versions.get(gameVersion);
return versions.values();
} finally {
lock.readLock().unlock();
}

View File

@ -62,7 +62,7 @@ public final class VersionJsonDownloadTask extends Task<String> {
@Override
public void execute() {
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion)
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult));

View File

@ -160,7 +160,7 @@ public class DefaultGameRepository implements GameRepository {
}
if (fromVersion.getId().equals(fromVersion.getJar()))
fromVersion = fromVersion.setJar(to);
fromVersion = fromVersion.setJar(null);
FileUtils.writeText(toJson, JsonUtils.GSON.toJson(fromVersion.setId(to)));
return true;
} catch (IOException | JsonParseException | VersionNotFoundException e) {