add: stages of modpack installation

This commit is contained in:
huanghongxun
2020-02-20 14:03:11 +08:00
parent 91682aeafa
commit 5c25ab3eda
19 changed files with 202 additions and 217 deletions

View File

@@ -102,7 +102,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
return versionList.loadAsync(gameVersion)
.thenComposeAsync(() -> installLibraryAsync(baseVersion, versionList.getVersion(gameVersion, libraryVersion)
.orElseThrow(() -> new IOException("Remote library " + libraryId + " has no version " + libraryVersion))))
.withStage("hmcl.install." + libraryId);
.withStage(String.format("hmcl.install.%s:%s", libraryId, libraryVersion));
}
@Override
@@ -112,7 +112,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
return removeLibraryAsync(baseVersion.resolvePreservingPatches(repository), libraryVersion.getLibraryId())
.thenComposeAsync(version -> libraryVersion.getInstallTask(this, version))
.thenApplyAsync(baseVersion::addPatch)
.thenComposeAsync(repository::save).withStage("hmcl.install." + libraryVersion.getLibraryId());
.thenComposeAsync(repository::save).withStage(String.format("hmcl.install.%s:%s", libraryVersion.getLibraryId(), libraryVersion.getSelfVersion()));
}
public Task<Version> installLibraryAsync(Version oldVersion, Path installer) {

View File

@@ -21,6 +21,8 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
@@ -47,19 +49,27 @@ public class DefaultGameBuilder extends GameBuilder {
@Override
public Task<?> buildAsync() {
List<String> stages = new ArrayList<>();
Task<Version> libraryTask = Task.supplyAsync(() -> new Version(name));
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, "game", gameVersion));
stages.add("hmcl.install.game:" + gameVersion);
stages.add("hmcl.install.assets");
for (Map.Entry<String, String> entry : toolVersions.entrySet())
for (Map.Entry<String, String> entry : toolVersions.entrySet()) {
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue()));
stages.add(String.format("hmcl.install.%s:%s", entry.getKey(), entry.getValue()));
}
for (RemoteVersion remoteVersion : remoteVersions)
for (RemoteVersion remoteVersion : remoteVersions) {
libraryTask = libraryTask.thenComposeAsync(version -> dependencyManager.installLibraryAsync(version, remoteVersion));
stages.add(String.format("hmcl.install.%s:%s", remoteVersion.getLibraryId(), remoteVersion.getSelfVersion()));
}
return libraryTask.whenComplete(exception -> {
if (exception != null)
dependencyManager.getGameRepository().removeVersionFromDisk(name);
});
}).withStagesHint(stages);
}
private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) {

View File

@@ -31,7 +31,7 @@ import java.util.LinkedList;
import java.util.List;
/**
*
* Task to download Minecraft jar
* @author huangyuhui
*/
public final class GameDownloadTask extends Task<Void> {

View File

@@ -69,11 +69,13 @@ public class GameInstallTask extends Task<Version> {
setResult(patch);
Version version = new Version(this.version.getId()).addPatch(patch);
dependencies.add(new GameDownloadTask(dependencyManager, remote.getGameVersion(), version)
.thenComposeAsync(Task.allOf(
dependencies.add(Task.allOf(
new GameDownloadTask(dependencyManager, remote.getGameVersion(), version),
Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true),
new GameLibrariesTask(dependencyManager, version, true)
).withStage("hmcl.install.assets").withComposeAsync(gameRepository.save(version))));
).withStage("hmcl.install.assets")
).withComposeAsync(gameRepository.save(version)));
}
}

View File

@@ -32,11 +32,12 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Install a downloaded CurseForge modpack.
@@ -105,7 +106,8 @@ public final class CurseInstallTask extends Task<Void> {
} catch (JsonParseException | IOException ignore) {
}
this.config = config;
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), manifest.getOverrides(), any -> true, config));
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), manifest.getOverrides(), any -> true, config).withStage("hmcl.modpack"));
dependents.add(new CurseCompletionTask(dependencyManager, name, manifest).withStage("hmcl.modpack.download"));
}
@Override
@@ -133,8 +135,15 @@ public final class CurseInstallTask extends Task<Void> {
File root = repository.getVersionRoot(name);
FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(manifest));
dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest).withStage("hmcl.modpack.download"));
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)).withStage("hmcl.modpack.unzip"));
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack", "hmcl.modpack.download")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "Curse";

View File

@@ -43,6 +43,8 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*
@@ -127,9 +129,9 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(modpack.getEncoding()).build()) {
if (Files.exists(fs.getPath("/" + manifest.getName() + "/.minecraft")))
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/" + manifest.getName() + "/.minecraft", any -> true, config));
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/" + manifest.getName() + "/.minecraft", any -> true, config).withStage("hmcl.modpack"));
else if (Files.exists(fs.getPath("/" + manifest.getName() + "/minecraft")))
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", any -> true, config));
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", any -> true, config).withStage("hmcl.modpack"));
}
}
@@ -173,7 +175,15 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
}
dependencies.add(repository.save(version));
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)).withStage("hmcl.modpack"));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "MultiMC";

View File

@@ -34,6 +34,8 @@ import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ServerModpackLocalInstallTask extends Task<Void> {
@@ -79,7 +81,7 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
}
} catch (JsonParseException | IOException ignore) {
}
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/overrides", any -> true, config));
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/overrides", any -> true, config).withStage("hmcl.modpack"));
}
@Override
@@ -94,7 +96,15 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
@Override
public void execute() throws Exception {
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/overrides", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/overrides", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "Server";

View File

@@ -24,6 +24,7 @@ import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.util.InvocationDispatcher;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ReflectionHelper;
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
@@ -34,12 +35,15 @@ import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Disposable task.
@@ -91,13 +95,17 @@ public abstract class Task<T> {
}
/**
* You must initialize stage in preExecute.
* You must initialize stage in constructor.
* @param stage the stage
*/
final void setStage(String stage) {
this.stage = stage;
}
public List<String> getStages() {
return getStage() == null ? Collections.emptyList() : Collections.singletonList(getStage());
}
// state
private TaskState state = TaskState.READY;
@@ -688,6 +696,11 @@ public abstract class Task<T> {
public boolean isRelyingOnDependents() {
return false;
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}.setExecutor(executor).setName(getCaller());
}
@@ -763,11 +776,31 @@ public abstract class Task<T> {
}
public Task<T> withStage(String stage) {
StageTask<T> task = new StageTask<>(this);
StageTask task = new StageTask();
task.setStage(stage);
return task;
}
public Task<T> withStagesHint(List<String> stages) {
return new Task<T>() {
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public void execute() throws Exception {
setResult(Task.this.getResult());
}
@Override
public List<String> getStages() {
return stages;
}
};
}
public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
return runAsync(Schedulers.defaultScheduler(), closure);
}
@@ -803,6 +836,11 @@ public abstract class Task<T> {
public Collection<Task<?>> getDependencies() {
return then == null ? Collections.emptySet() : Collections.singleton(then);
}
@Override
public List<String> getStages() {
return Lang.merge(super.getStages(), then == null ? null : then.getStages());
}
}.setName(name);
}
@@ -862,6 +900,11 @@ public abstract class Task<T> {
public Collection<Task<?>> getDependents() {
return tasks;
}
@Override
public List<String> getStages() {
return tasks.stream().flatMap(task -> task.getStages().stream()).collect(Collectors.toList());
}
};
}
@@ -929,6 +972,11 @@ public abstract class Task<T> {
public void execute() throws Exception {
setResult(callable.apply(Task.this.getResult()));
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
/**
@@ -988,22 +1036,30 @@ public abstract class Task<T> {
public boolean isRelyingOnDependents() {
return relyingOnDependents;
}
@Override
public List<String> getStages() {
return Stream.of(Task.this.getStages(), super.getStages(), succ == null ? Collections.<String>emptyList() : succ.getStages())
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}
public static class StageTask<T> extends Task<T> {
private final Task<T> task;
StageTask(Task<T> task) {
this.task = task;
}
public class StageTask extends Task<T> {
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
return Collections.singleton(Task.this);
}
@Override
public void execute() throws Exception {
setResult(task.getResult());
setResult(Task.this.getResult());
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
}

View File

@@ -30,10 +30,11 @@ public abstract class TaskExecutor {
protected final AtomicInteger totTask = new AtomicInteger(0);
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
protected Exception exception;
private TaskStages stages = TaskStages.EMPTY;
private final List<String> stages;
public TaskExecutor(Task<?> task) {
this.firstTask = task;
this.stages = task.getStages();
}
public void addTaskListener(TaskListener taskListener) {
@@ -66,11 +67,7 @@ public abstract class TaskExecutor {
return totTask.get();
}
public TaskStages getStages() {
public List<String> getStages() {
return stages;
}
public void setStages(TaskStages stages) {
this.stages = stages;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 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.task;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TaskStages {
public static final TaskStages EMPTY = new TaskStages();
private final List<String> stages = new ArrayList<>();
private final Map<String, String> localization = new HashMap<>();
protected void addStage(String stage, String localizedMessage) {
stages.add(stage);
localization.put(stage, localizedMessage);
}
public List<String> getStages() {
return stages;
}
public String localize(String stage) {
return localization.get(stage);
}
}

View File

@@ -112,6 +112,12 @@ public final class Lang {
return operator.apply(a, b);
}
public static <T> List<T> removingDuplicates(List<T> list) {
LinkedHashSet<T> set = new LinkedHashSet<>(list.size());
set.addAll(list);
return new ArrayList<>(set);
}
/**
* Join two collections into one list.
*