Refactor Task

This commit is contained in:
huanghongxun
2019-02-24 23:09:17 +08:00
parent dc1df98277
commit 8f2a1030bc
72 changed files with 841 additions and 935 deletions

View File

@@ -30,7 +30,7 @@ import java.util.List;
/** /**
* Export the game to a mod pack file. * Export the game to a mod pack file.
*/ */
public class HMCLModpackExportTask extends Task { public class HMCLModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
private final String version; private final String version;
private final List<String> whitelist; private final List<String> whitelist;

View File

@@ -36,13 +36,13 @@ import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public final class HMCLModpackInstallTask extends Task { public final class HMCLModpackInstallTask extends Task<Void> {
private final File zipFile; private final File zipFile;
private final String name; private final String name;
private final HMCLGameRepository repository; private final HMCLGameRepository repository;
private final Modpack modpack; private final Modpack modpack;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>(); private final List<Task<?>> dependents = new LinkedList<>();
public HMCLModpackInstallTask(Profile profile, File zipFile, Modpack modpack, String name) { public HMCLModpackInstallTask(Profile profile, File zipFile, Modpack modpack, String name) {
DependencyManager dependency = profile.getDependency(); DependencyManager dependency = profile.getDependency();
@@ -77,12 +77,12 @@ public final class HMCLModpackInstallTask extends Task {
} }
@Override @Override
public List<Task> getDependencies() { public List<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }
@Override @Override
public List<Task> getDependents() { public List<Task<?>> getDependents() {
return dependents; return dependents;
} }

View File

@@ -121,15 +121,15 @@ public final class LauncherHelper {
Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion)); Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion));
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)); Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)) TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.then(() -> { .thenCompose(() -> {
if (setting.isNotCheckGame()) if (setting.isNotCheckGame())
return null; return null;
else else
return dependencyManager.checkGameCompletionAsync(version); return dependencyManager.checkGameCompletionAsync(version);
}) })
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))) .thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))
.then(() -> { .thenCompose(() -> {
try { try {
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
if ("Curse".equals(configuration.getType())) if ("Curse".equals(configuration.getType()))
@@ -140,8 +140,8 @@ public final class LauncherHelper {
return null; return null;
} }
}) })
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))) .thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))
.thenCompose(() -> Task.ofResult(i18n("account.methods"), () -> { .thenSupply(i18n("account.methods"), () -> {
try { try {
return account.logIn(); return account.logIn();
} catch (CredentialExpiredException e) { } catch (CredentialExpiredException e) {
@@ -151,7 +151,7 @@ public final class LauncherHelper {
LOG.warning("Authentication failed, try playing offline: " + e); LOG.warning("Authentication failed, try playing offline: " + e);
return account.playOffline().orElseThrow(() -> e); return account.playOffline().orElseThrow(() -> e);
} }
})) })
.thenApply(Schedulers.javafx(), authInfo -> { .thenApply(Schedulers.javafx(), authInfo -> {
emitStatus(LoadingState.LAUNCHING); emitStatus(LoadingState.LAUNCHING);
return authInfo; return authInfo;
@@ -199,7 +199,7 @@ public final class LauncherHelper {
final AtomicInteger finished = new AtomicInteger(0); final AtomicInteger finished = new AtomicInteger(0);
@Override @Override
public void onFinished(Task task) { public void onFinished(Task<?> task) {
finished.incrementAndGet(); finished.incrementAndGet();
int runningTasks = executor.getRunningTasks(); int runningTasks = executor.getRunningTasks();
Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks)); Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks));
@@ -416,7 +416,7 @@ public final class LauncherHelper {
} }
} }
private static class LaunchTask<T> extends TaskResult<T> { private static class LaunchTask<T> extends Task<T> {
private final ExceptionalSupplier<T, Exception> supplier; private final ExceptionalSupplier<T, Exception> supplier;
public LaunchTask(ExceptionalSupplier<T, Exception> supplier) { public LaunchTask(ExceptionalSupplier<T, Exception> supplier) {

View File

@@ -86,7 +86,7 @@ public final class ModpackHelper {
throw new UnsupportedModpackException(); throw new UnsupportedModpackException();
} }
public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) { public static Task<Void> getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
profile.getRepository().markVersionAsModpack(name); profile.getRepository().markVersionAsModpack(name);
ExceptionalRunnable<?> success = () -> { ExceptionalRunnable<?> success = () -> {
@@ -117,11 +117,11 @@ public final class ModpackHelper {
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
.whenComplete(Schedulers.defaultScheduler(), success, failure) .whenComplete(Schedulers.defaultScheduler(), success, failure)
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)); .thenCompose(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack); else throw new IllegalStateException("Unrecognized modpack: " + modpack);
} }
public static Task getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException { public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
Modpack modpack = ModpackHelper.readModpackManifest(zipFile.toPath(), charset); Modpack modpack = ModpackHelper.readModpackManifest(zipFile.toPath(), charset);
switch (configuration.getType()) { switch (configuration.getType()) {

View File

@@ -26,7 +26,7 @@ import org.jackhuang.hmcl.task.Task;
import java.util.Objects; import java.util.Objects;
public final class MultiMCInstallVersionSettingTask extends Task { public final class MultiMCInstallVersionSettingTask extends Task<Void> {
private final Profile profile; private final Profile profile;
private final MultiMCInstanceConfiguration manifest; private final MultiMCInstanceConfiguration manifest;
private final String version; private final String version;

View File

@@ -205,7 +205,7 @@ public final class Controllers {
dialog(i18n("launcher.cache_directory.invalid")); dialog(i18n("launcher.cache_directory.invalid"));
} }
Task.of(JavaVersion::initialize).start(); Task.runAsync(JavaVersion::initialize).start();
scene = new Scene(decorator.getDecorator(), 800, 519); scene = new Scene(decorator.getDecorator(), 800, 519);
scene.getStylesheets().setAll(config().getTheme().getStylesheets()); scene.getStylesheets().setAll(config().getTheme().getStylesheets());

View File

@@ -373,7 +373,7 @@ public final class FXUtils {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Deprecated @Deprecated
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum> property) { public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum<?>> property) {
unbindEnum(comboBox); unbindEnum(comboBox);
ChangeListener<Number> listener = (a, b, newValue) -> ChangeListener<Number> listener = (a, b, newValue) ->
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]); ((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);

View File

@@ -118,12 +118,12 @@ public final class LeftPaneController extends AdvancedListBox {
if (repository.getVersionCount() == 0) { if (repository.getVersionCount() == 0) {
File modpackFile = new File("modpack.zip").getAbsoluteFile(); File modpackFile = new File("modpack.zip").getAbsoluteFile();
if (modpackFile.exists()) { if (modpackFile.exists()) {
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath())) Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding)) .thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenAccept(modpack -> { .thenAccept(modpack -> {
AtomicReference<Region> region = new AtomicReference<>(); AtomicReference<Region> region = new AtomicReference<>();
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor(); .withRun(Schedulers.javafx(), this::checkAccount).executor();
region.set(Controllers.taskDialog(executor, i18n("modpack.installing"))); region.set(Controllers.taskDialog(executor, i18n("modpack.installing")));
executor.start(); executor.start();
}).start(); }).start();

View File

@@ -61,7 +61,7 @@ public class AccountLoginPane extends StackPane {
String password = txtPassword.getText(); String password = txtPassword.getText();
progressBar.setVisible(true); progressBar.setVisible(true);
lblCreationWarning.setText(""); lblCreationWarning.setText("");
Task.ofResult(() -> oldAccount.logInWithPassword(password)) Task.supplyAsync(() -> oldAccount.logInWithPassword(password))
.whenComplete(Schedulers.javafx(), authInfo -> { .whenComplete(Schedulers.javafx(), authInfo -> {
success.accept(authInfo); success.accept(authInfo);
fireEvent(new DialogCloseEvent()); fireEvent(new DialogCloseEvent());

View File

@@ -195,7 +195,7 @@ public class AddAccountPane extends StackPane {
AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem(); AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem();
Object additionalData = getAuthAdditionalData(); Object additionalData = getAuthAdditionalData();
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData)) Task.supplyAsync(() -> factory.create(new Selector(), username, password, additionalData))
.whenComplete(Schedulers.javafx(), account -> { .whenComplete(Schedulers.javafx(), account -> {
int oldIndex = Accounts.getAccounts().indexOf(account); int oldIndex = Accounts.getAccounts().indexOf(account);
if (oldIndex == -1) { if (oldIndex == -1) {

View File

@@ -104,7 +104,7 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
nextPane.showSpinner(); nextPane.showSpinner();
addServerPane.setDisable(true); addServerPane.setDisable(true);
Task.of(() -> { Task.runAsync(() -> {
serverBeingAdded = AuthlibInjectorServer.locateServer(url); serverBeingAdded = AuthlibInjectorServer.locateServer(url);
}).whenComplete(Schedulers.javafx(), (isDependentSucceeded, exception) -> { }).whenComplete(Schedulers.javafx(), (isDependentSucceeded, exception) -> {
addServerPane.setDisable(false); addServerPane.setDisable(false);

View File

@@ -42,7 +42,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class TaskListPane extends StackPane { public final class TaskListPane extends StackPane {
private final AdvancedListBox listBox = new AdvancedListBox(); private final AdvancedListBox listBox = new AdvancedListBox();
private final Map<Task, ProgressListNode> nodes = new HashMap<>(); private final Map<Task<?>, ProgressListNode> nodes = new HashMap<>();
private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper(); private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper();
private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper(); private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper();
@@ -72,12 +72,12 @@ public final class TaskListPane extends StackPane {
} }
@Override @Override
public void onReady(Task task) { public void onReady(Task<?> task) {
Platform.runLater(() -> totTasks.set(totTasks.getValue() + 1)); Platform.runLater(() -> totTasks.set(totTasks.getValue() + 1));
} }
@Override @Override
public void onRunning(Task task) { public void onRunning(Task<?> task) {
if (!task.getSignificance().shouldShow()) if (!task.getSignificance().shouldShow())
return; return;
@@ -113,7 +113,7 @@ public final class TaskListPane extends StackPane {
} }
@Override @Override
public void onFinished(Task task) { public void onFinished(Task<?> task) {
ProgressListNode node = nodes.remove(task); ProgressListNode node = nodes.remove(task);
if (node == null) if (node == null)
return; return;
@@ -125,7 +125,7 @@ public final class TaskListPane extends StackPane {
} }
@Override @Override
public void onFailed(Task task, Throwable throwable) { public void onFailed(Task<?> task, Throwable throwable) {
ProgressListNode node = nodes.remove(task); ProgressListNode node = nodes.remove(task);
if (node == null) if (node == null)
return; return;
@@ -142,7 +142,7 @@ public final class TaskListPane extends StackPane {
private final Label title = new Label(); private final Label title = new Label();
private final Label state = new Label(); private final Label state = new Label();
public ProgressListNode(Task task) { public ProgressListNode(Task<?> task) {
bar.progressProperty().bind(task.progressProperty()); bar.progressProperty().bind(task.progressProperty());
title.setText(task.getName()); title.setText(task.getName());
state.textProperty().bind(task.messageProperty()); state.textProperty().bind(task.messageProperty());

View File

@@ -27,7 +27,6 @@ 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;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -90,7 +89,7 @@ public final class InstallerWizardProvider implements WizardProvider {
settings.put("success_message", i18n("install.success")); settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next)); settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
TaskResult<Version> ret = Task.ofResult(() -> version); Task<Version> ret = Task.supplyAsync(() -> version);
if (settings.containsKey("forge")) if (settings.containsKey("forge"))
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge"))); ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
@@ -101,7 +100,7 @@ public final class InstallerWizardProvider implements WizardProvider {
if (settings.containsKey("optifine")) if (settings.containsKey("optifine"))
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine"))); ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync()); return ret.thenCompose(profile.getRepository().refreshVersionsAsync());
} }
@Override @Override

View File

@@ -71,7 +71,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile); settings.put(PROFILE, profile);
} }
private Task finishModpackInstallingAsync(Map<String, Object> settings) { private Task<Void> finishModpackInstallingAsync(Map<String, Object> settings) {
if (!settings.containsKey(ModpackPage.MODPACK_FILE)) if (!settings.containsKey(ModpackPage.MODPACK_FILE))
return null; return null;
@@ -93,7 +93,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
return null; return null;
} else { } else {
return ModpackHelper.getInstallTask(profile, selected, name, modpack) return ModpackHelper.getInstallTask(profile, selected, name, modpack)
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); .thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
} }
} }

View File

@@ -109,7 +109,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
} }
spinnerPane.showSpinner(); spinnerPane.showSpinner();
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath())) Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
.thenApply(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding)) .thenApply(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.whenComplete(Schedulers.javafx(), manifest -> { .whenComplete(Schedulers.javafx(), manifest -> {
spinnerPane.hideSpinner(); spinnerPane.hideSpinner();

View File

@@ -43,7 +43,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile); settings.put(PROFILE, profile);
} }
private Task finishVersionDownloadingAsync(Map<String, Object> settings) { private Task<Void> finishVersionDownloadingAsync(Map<String, Object> settings) {
GameBuilder builder = profile.getDependency().gameBuilder(); GameBuilder builder = profile.getDependency().gameBuilder();
String name = (String) settings.get("name"); String name = (String) settings.get("name");
@@ -60,7 +60,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
builder.version((RemoteVersion) settings.get("optifine")); builder.version((RemoteVersion) settings.get("optifine"));
return builder.buildAsync().whenComplete((a, b) -> profile.getRepository().refreshVersions()) return builder.buildAsync().whenComplete((a, b) -> profile.getRepository().refreshVersions())
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); .thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
} }
@Override @Override

View File

@@ -60,8 +60,8 @@ public final class ExportWizardProvider implements WizardProvider {
List<File> launcherJar = Launcher.getCurrentJarFiles(); List<File> launcherJar = Launcher.getCurrentJarFiles();
boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER) && launcherJar != null; boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER) && launcherJar != null;
return new Task() { return new Task<Void>() {
Task dependency = null; Task<?> dependency = null;
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
@@ -80,7 +80,7 @@ public final class ExportWizardProvider implements WizardProvider {
), tempModpack); ), tempModpack);
if (includeLauncher) { if (includeLauncher) {
dependency = dependency.then(Task.of(() -> { dependency = dependency.thenRun(() -> {
try (Zipper zip = new Zipper(modpackFile.toPath())) { try (Zipper zip = new Zipper(modpackFile.toPath())) {
Config exported = new Config(); Config exported = new Config();
@@ -109,12 +109,12 @@ public final class ExportWizardProvider implements WizardProvider {
for (File jar : launcherJar) for (File jar : launcherJar)
zip.putFile(jar, jar.getName()); zip.putFile(jar, jar.getName());
} }
})); });
} }
} }
@Override @Override
public Collection<? extends Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency); return Collections.singleton(dependency);
} }
}; };

View File

@@ -54,9 +54,9 @@ public class InstallerListPage extends ListPage<InstallerItem> {
LinkedList<Library> newList = new LinkedList<>(version.getLibraries()); LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(library); newList.remove(library);
new MaintainTask(version.setLibraries(newList)) new MaintainTask(version.setLibraries(newList))
.then(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion)) .thenCompose(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
.with(profile.getRepository().refreshVersionsAsync()) .withCompose(profile.getRepository().refreshVersionsAsync())
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))) .withRun(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
.start(); .start();
}; };

View File

@@ -85,7 +85,7 @@ public final class ModListPage extends Control {
public void loadMods(ModManager modManager) { public void loadMods(ModManager modManager) {
this.modManager = modManager; this.modManager = modManager;
Task.ofResult(() -> { Task.supplyAsync(() -> {
synchronized (ModListPage.this) { synchronized (ModListPage.this) {
JFXUtilities.runInFX(() -> loadingProperty().set(true)); JFXUtilities.runInFX(() -> loadingProperty().set(true));
modManager.refreshMods(); modManager.refreshMods();
@@ -111,7 +111,7 @@ public final class ModListPage extends Control {
List<String> succeeded = new LinkedList<>(); List<String> succeeded = new LinkedList<>();
List<String> failed = new LinkedList<>(); List<String> failed = new LinkedList<>();
if (res == null) return; if (res == null) return;
Task.of(() -> { Task.runAsync(() -> {
for (File file : res) { for (File file : res) {
try { try {
modManager.addMod(file); modManager.addMod(file);
@@ -123,7 +123,7 @@ public final class ModListPage extends Control {
// Actually addMod will not throw exceptions because FileChooser has already filtered files. // Actually addMod will not throw exceptions because FileChooser has already filtered files.
} }
} }
}).with(Task.of(Schedulers.javafx(), () -> { }).withRun(Schedulers.javafx(), () -> {
List<String> prompt = new LinkedList<>(); List<String> prompt = new LinkedList<>();
if (!succeeded.isEmpty()) if (!succeeded.isEmpty())
prompt.add(i18n("mods.add.success", String.join(", ", succeeded))); prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
@@ -131,7 +131,7 @@ public final class ModListPage extends Control {
prompt.add(i18n("mods.add.failed", String.join(", ", failed))); prompt.add(i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), i18n("mods.add")); Controllers.dialog(String.join("\n", prompt), i18n("mods.add"));
loadMods(modManager); loadMods(modManager);
})).start(); }).start();
} }
public void setParentTab(JFXTabPane parentTab) { public void setParentTab(JFXTabPane parentTab) {

View File

@@ -118,7 +118,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.smoothScrolling(scroll); FXUtils.smoothScrolling(scroll);
Task.ofResult(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> { Task.supplyAsync(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> {
javaItem.loadChildren(list.stream() javaItem.loadChildren(list.stream()
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit", .map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion)) javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
@@ -273,7 +273,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
VersionSetting versionSetting = lastVersionSetting; VersionSetting versionSetting = lastVersionSetting;
if (versionSetting == null) if (versionSetting == null)
return; return;
Task.ofResult(versionSetting::getJavaVersion) Task.supplyAsync(versionSetting::getJavaVersion)
.thenAccept(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion) .thenAccept(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path"))) .map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
.start(); .start();

View File

@@ -74,6 +74,6 @@ public class WorldExportPage extends WizardSinglePage {
@Override @Override
protected Object finish() { protected Object finish() {
return Task.of(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get())); return Task.runAsync(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get()));
} }
} }

View File

@@ -50,7 +50,7 @@ public class WorldListPage extends ListPage<WorldListItem> {
this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves"); this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves");
setLoading(true); setLoading(true);
Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList())) Task.supplyAsync(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.whenComplete(Schedulers.javafx(), (result, isDependentSucceeded, exception) -> { .whenComplete(Schedulers.javafx(), (result, isDependentSucceeded, exception) -> {
setLoading(false); setLoading(false);
if (isDependentSucceeded) if (isDependentSucceeded)
@@ -72,10 +72,10 @@ public class WorldListPage extends ListPage<WorldListItem> {
private void installWorld(File zipFile) { private void installWorld(File zipFile) {
// Only accept one world file because user is required to confirm the new world name // Only accept one world file because user is required to confirm the new world name
// Or too many input dialogs are popped. // Or too many input dialogs are popped.
Task.ofResult(() -> new World(zipFile.toPath())) Task.supplyAsync(() -> new World(zipFile.toPath()))
.whenComplete(Schedulers.javafx(), world -> { .whenComplete(Schedulers.javafx(), world -> {
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> { Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
Task.of(() -> world.install(savesDir, name)) Task.runAsync(() -> world.install(savesDir, name))
.whenComplete(Schedulers.javafx(), () -> { .whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name)))); itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
resolve.run(); resolve.run();

View File

@@ -32,8 +32,8 @@ public interface AbstractWizardDisplayer extends WizardDisplayer {
Queue<Object> getCancelQueue(); Queue<Object> getCancelQueue();
@Override @Override
default void handleTask(Map<String, Object> settings, Task task) { default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor(); TaskExecutor executor = task.withRun(Schedulers.javafx(), this::navigateToSuccess).executor();
TaskListPane pane = new TaskListPane(); TaskListPane pane = new TaskListPane();
pane.setExecutor(executor); pane.setExecutor(executor);
navigateTo(pane, Navigation.NavigationDirection.FINISH); navigateTo(pane, Navigation.NavigationDirection.FINISH);

View File

@@ -35,7 +35,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer { public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer {
@Override @Override
default void handleTask(Map<String, Object> settings, Task task) { default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> { TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> {
it.fireEvent(new DialogCloseEvent()); it.fireEvent(new DialogCloseEvent());
onEnd(); onEnd();

View File

@@ -106,7 +106,7 @@ public class WizardController implements Navigation {
public void onFinish() { public void onFinish() {
Object result = provider.finish(settings); Object result = provider.finish(settings);
if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT); if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
else if (result instanceof Task) displayer.handleTask(settings, ((Task) result)); else if (result instanceof Task<?>) displayer.handleTask(settings, ((Task<?>) result));
else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result); else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result);
} }

View File

@@ -27,5 +27,5 @@ public interface WizardDisplayer {
void onEnd(); void onEnd();
void onCancel(); void onCancel();
void navigateTo(Node page, Navigation.NavigationDirection nav); void navigateTo(Node page, Navigation.NavigationDirection nav);
void handleTask(Map<String, Object> settings, Task task); void handleTask(Map<String, Object> settings, Task<?> task);
} }

View File

@@ -99,7 +99,7 @@ public final class UpdateHandler {
return; return;
} }
Task task = new HMCLDownloadTask(version, downloaded); Task<?> task = new HMCLDownloadTask(version, downloaded);
TaskExecutor executor = task.executor(); TaskExecutor executor = task.executor();
Controllers.taskDialog(executor, i18n("message.downloading")); Controllers.taskDialog(executor, i18n("message.downloading"));

View File

@@ -26,9 +26,7 @@ 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.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalFunction;
/** /**
@@ -69,9 +67,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
} }
@Override @Override
public Task checkGameCompletionAsync(Version version) { public Task<?> checkGameCompletionAsync(Version version) {
return new ParallelTask( return Task.allOf(
Task.ofThen(() -> { Task.composeAsync(() -> {
if (!repository.getVersionJar(version).exists()) if (!repository.getVersionJar(version).exists())
return new GameDownloadTask(this, null, version); return new GameDownloadTask(this, null, version);
else else
@@ -83,12 +81,12 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
} }
@Override @Override
public Task checkLibraryCompletionAsync(Version version) { public Task<?> checkLibraryCompletionAsync(Version version) {
return new GameLibrariesTask(this, version); return new GameLibrariesTask(this, version);
} }
@Override @Override
public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) { public Task<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
VersionList<?> versionList = getVersionList(libraryId); VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(gameVersion, getDownloadProvider()) return versionList.loadAsync(gameVersion, getDownloadProvider())
.thenCompose(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion) .thenCompose(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
@@ -96,8 +94,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
} }
@Override @Override
public TaskResult<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) { public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
TaskResult<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);
else if (libraryVersion instanceof LiteLoaderRemoteVersion) else if (libraryVersion instanceof LiteLoaderRemoteVersion)
@@ -113,7 +111,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
} }
public ExceptionalFunction<Version, TaskResult<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) { public ExceptionalFunction<Version, Task<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
return version -> installLibraryAsync(version, libraryVersion); return version -> installLibraryAsync(version, libraryVersion);
} }
} }

View File

@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*; import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -48,16 +46,16 @@ public class DefaultGameBuilder extends GameBuilder {
} }
@Override @Override
public Task buildAsync() { public Task<?> buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenCompose(rawJson -> { return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenCompose(rawJson -> {
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class); Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null); Version version = original.setId(name).setJar(null);
Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask( Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenCompose(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.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant. ).withCompose(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
TaskResult<Version> libraryTask = vanillaTask.thenSupply(() -> version); Task<Version> libraryTask = vanillaTask.thenSupply(() -> version);
if (toolVersions.containsKey("forge")) if (toolVersions.containsKey("forge"))
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge")); libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge"));
@@ -76,11 +74,11 @@ public class DefaultGameBuilder extends GameBuilder {
}); });
} }
private ExceptionalFunction<Version, TaskResult<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) { private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) {
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId)); return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId));
} }
protected Task downloadGameAsync(String gameVersion, Version version) { protected Task<Void> downloadGameAsync(String gameVersion, Version version) {
return new GameDownloadTask(dependencyManager, gameVersion, version); return new GameDownloadTask(dependencyManager, gameVersion, version);
} }

View File

@@ -46,7 +46,7 @@ public interface DependencyManager {
* *
* @return the task to check game completion. * @return the task to check game completion.
*/ */
Task checkGameCompletionAsync(Version version); Task<?> checkGameCompletionAsync(Version version);
/** /**
* Check if the game is complete. * Check if the game is complete.
@@ -54,7 +54,7 @@ public interface DependencyManager {
* *
* @return the task to check game completion. * @return the task to check game completion.
*/ */
Task checkLibraryCompletionAsync(Version version); Task<?> checkLibraryCompletionAsync(Version version);
/** /**
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
@@ -71,7 +71,7 @@ public interface DependencyManager {
* @param libraryVersion the version of being installed library. * @param libraryVersion the version of being installed library.
* @return the task to install the specific library. * @return the task to install the specific library.
*/ */
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion); Task<?> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/** /**
* Install a library to a version. * Install a library to a version.
@@ -81,7 +81,7 @@ public interface DependencyManager {
* @param libraryVersion the remote version of being installed library. * @param libraryVersion the remote version of being installed library.
* @return the task to install the specific library. * @return the task to install the specific library.
*/ */
Task installLibraryAsync(Version version, RemoteVersion libraryVersion); Task<?> installLibraryAsync(Version version, RemoteVersion libraryVersion);
/** /**
* Get registered version list. * Get registered version list.

View File

@@ -72,5 +72,5 @@ public abstract class GameBuilder {
/** /**
* @return the task that can build thw whole Minecraft environment * @return the task that can build thw whole Minecraft environment
*/ */
public abstract Task buildAsync(); public abstract Task<?> buildAsync();
} }

View File

@@ -18,9 +18,9 @@
package org.jackhuang.hmcl.download; package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.task.Task;
public class MaintainTask extends TaskResult<Version> { public class MaintainTask extends Task<Version> {
private final Version version; private final Version version;

View File

@@ -62,19 +62,19 @@ public abstract class VersionList<T extends RemoteVersion> {
* @param downloadProvider DownloadProvider * @param downloadProvider DownloadProvider
* @return the task to reload the remote version list. * @return the task to reload the remote version list.
*/ */
public abstract Task refreshAsync(DownloadProvider downloadProvider); public abstract Task<?> refreshAsync(DownloadProvider downloadProvider);
/** /**
* @param gameVersion the remote version depends on * @param gameVersion the remote version depends on
* @param downloadProvider DownloadProvider * @param downloadProvider DownloadProvider
* @return the task to reload the remote version list. * @return the task to reload the remote version list.
*/ */
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) { public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
return refreshAsync(downloadProvider); return refreshAsync(downloadProvider);
} }
public Task loadAsync(DownloadProvider downloadProvider) { public Task<?> loadAsync(DownloadProvider downloadProvider) {
return Task.ofThen(() -> { return Task.composeAsync(() -> {
lock.readLock().lock(); lock.readLock().lock();
boolean loaded; boolean loaded;
@@ -87,8 +87,8 @@ public abstract class VersionList<T extends RemoteVersion> {
}); });
} }
public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) { public Task<?> loadAsync(String gameVersion, DownloadProvider downloadProvider) {
return Task.ofThen(() -> { return Task.composeAsync(() -> {
lock.readLock().lock(); lock.readLock().lock();
boolean loaded; boolean loaded;

View File

@@ -46,21 +46,21 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
} }
@Override @Override
public Task loadAsync(DownloadProvider downloadProvider) { public Task<?> loadAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list."); throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list."); throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
} }
@Override @Override
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) { public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL("https://bmclapi2.bangbang93.com/forge/minecraft/" + gameVersion)); final GetTask task = new GetTask(NetworkUtils.toURL("https://bmclapi2.bangbang93.com/forge/minecraft/" + gameVersion));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -34,14 +33,14 @@ import java.util.Collections;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class ForgeInstallTask extends TaskResult<Version> { public final class ForgeInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final Version version; private final Version version;
private Path installer; private Path installer;
private final ForgeRemoteVersion remote; private final ForgeRemoteVersion remote;
private Task dependent; private Task<Void> dependent;
private TaskResult<Version> dependency; private Task<Version> dependency;
public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) { public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
@@ -73,20 +72,15 @@ public final class ForgeInstallTask extends TaskResult<Version> {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(dependent); return Collections.singleton(dependent);
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency); return Collections.singleton(dependency);
} }
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override @Override
public void execute() { public void execute() {
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0)

View File

@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
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;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -49,19 +48,19 @@ import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex; import static org.jackhuang.hmcl.util.Hex.encodeHex;
import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Logging.LOG;
public class ForgeNewInstallTask extends TaskResult<Version> { public class ForgeNewInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository gameRepository; private final DefaultGameRepository gameRepository;
private final Version version; private final Version 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 ForgeNewInstallProfile profile; private ForgeNewInstallProfile profile;
private Version forgeVersion; private Version forgeVersion;
public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) { ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.gameRepository = dependencyManager.getGameRepository(); this.gameRepository = dependencyManager.getGameRepository();
this.version = version; this.version = version;
@@ -80,12 +79,12 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@Override @Override
public List<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -20,7 +20,6 @@ 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.*;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
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;
@@ -31,21 +30,21 @@ import java.util.*;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
public class ForgeOldInstallTask extends TaskResult<Version> { public class ForgeOldInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final Version version; private final Version version;
private final Path installer; private final Path installer;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
public ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) { ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version; this.version = version;
this.installer = installer; this.installer = installer;
} }
@Override @Override
public List<Task> getDependencies() { public List<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -45,12 +45,12 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST))); final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -38,14 +38,14 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class GameAssetDownloadTask extends Task { public final class GameAssetDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager; private final AbstractDependencyManager dependencyManager;
private final Version version; private final Version version;
private final AssetIndexInfo assetIndexInfo; private final AssetIndexInfo assetIndexInfo;
private final File assetIndexFile; private final File assetIndexFile;
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<>();
/** /**
* Constructor. * Constructor.
@@ -64,12 +64,12 @@ public final class GameAssetDownloadTask extends Task {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File; import java.io.File;
@@ -34,11 +33,11 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class GameAssetIndexDownloadTask extends Task { public final class GameAssetIndexDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager; private final AbstractDependencyManager dependencyManager;
private final Version version; private final Version version;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
/** /**
* Constructor. * Constructor.
@@ -53,7 +52,7 @@ public final class GameAssetIndexDownloadTask extends Task {
} }
@Override @Override
public List<Task> getDependencies() { public List<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File; import java.io.File;
import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -33,11 +34,11 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class GameDownloadTask extends Task { public final class GameDownloadTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final String gameVersion; private final String gameVersion;
private final Version version; private final Version version;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) { public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
@@ -48,7 +49,7 @@ public final class GameDownloadTask extends Task {
} }
@Override @Override
public List<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -32,12 +32,12 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class GameLibrariesTask extends Task { public final class GameLibrariesTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager; private final AbstractDependencyManager dependencyManager;
private final Version version; private final Version version;
private final List<Library> libraries; private final List<Library> libraries;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
/** /**
* Constructor. * Constructor.
@@ -64,7 +64,7 @@ public final class GameLibrariesTask extends Task {
} }
@Override @Override
public List<Task> getDependencies() { public List<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -55,11 +55,11 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL())); GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.CompatibilityRule; import org.jackhuang.hmcl.game.CompatibilityRule;
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.task.TaskResult; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap; import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -31,7 +31,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class LibrariesUniqueTask extends TaskResult<Version> { public class LibrariesUniqueTask extends Task<Version> {
private final Version version; private final Version version;
public LibrariesUniqueTask(Version version) { public LibrariesUniqueTask(Version version) {

View File

@@ -45,7 +45,7 @@ import java.util.logging.Level;
import static org.jackhuang.hmcl.util.DigestUtils.digest; import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex; import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class LibraryDownloadTask extends Task { public class LibraryDownloadTask extends Task<Void> {
private FileDownloadTask task; private FileDownloadTask task;
protected final File jar; protected final File jar;
protected final DefaultCacheRepository cacheRepository; protected final DefaultCacheRepository cacheRepository;
@@ -74,7 +74,7 @@ public class LibraryDownloadTask extends Task {
} }
@Override @Override
public Collection<? extends Task> getDependents() { public Collection<Task<?>> getDependents() {
if (cached) return Collections.emptyList(); if (cached) return Collections.emptyList();
else return Collections.singleton(task); else return Collections.singleton(task);
} }

View File

@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.util.Collection; import java.util.Collection;
@@ -33,11 +32,11 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class VersionJsonDownloadTask extends TaskResult<String> { public final class VersionJsonDownloadTask extends Task<String> {
private final String gameVersion; private final String gameVersion;
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
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 VersionList<?> gameVersionList; private final VersionList<?> gameVersionList;
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) { public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
@@ -52,12 +51,12 @@ public final class VersionJsonDownloadTask extends TaskResult<String> {
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }

View File

@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download.game;
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.task.TaskResult; 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;
@@ -30,7 +30,7 @@ import java.io.File;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class VersionJsonSaveTask extends TaskResult<Version> { public final class VersionJsonSaveTask extends Task<Version> {
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
private final Version version; private final Version version;

View File

@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** /**
* *
@@ -52,11 +51,11 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST))); GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo; import org.jackhuang.hmcl.game.LibraryDownloadInfo;
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.task.TaskResult;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import java.util.Collection; import java.util.Collection;
@@ -36,13 +35,13 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class LiteLoaderInstallTask extends TaskResult<Version> { public final class LiteLoaderInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final Version version; private final Version version;
private final LiteLoaderRemoteVersion remote; private final LiteLoaderRemoteVersion remote;
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<>();
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) { public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
@@ -51,12 +50,12 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** /**
* *
@@ -52,11 +51,11 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST))); GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -46,11 +46,11 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
} }
@Override @Override
public Task refreshAsync(DownloadProvider downloadProvider) { public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi2.bangbang93.com/optifine/versionlist")); GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi2.bangbang93.com/optifine/versionlist"));
return new Task() { return new Task<Void>() {
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(task); return Collections.singleton(task);
} }

View File

@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo; import org.jackhuang.hmcl.game.LibraryDownloadInfo;
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.task.TaskResult;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import java.util.Collection; import java.util.Collection;
@@ -35,13 +34,13 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class OptiFineInstallTask extends TaskResult<Version> { public final class OptiFineInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final Version version; private final Version version;
private final OptiFineRemoteVersion remote; private final OptiFineRemoteVersion remote;
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<>();
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) { public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
@@ -50,12 +49,12 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -77,8 +77,8 @@ public interface GameRepository extends VersionProvider {
*/ */
void refreshVersions(); void refreshVersions();
default Task refreshVersionsAsync() { default Task<Void> refreshVersionsAsync() {
return Task.of(this::refreshVersions); return Task.runAsync(this::refreshVersions);
} }
/** /**

View File

@@ -43,14 +43,14 @@ import java.util.stream.Collectors;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class CurseCompletionTask extends Task { public final class CurseCompletionTask extends Task<Void> {
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
private final ModManager modManager; private final ModManager modManager;
private final String version; private final String version;
private CurseManifest manifest = null; private CurseManifest manifest = null;
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<>();
/** /**
* Constructor. * Constructor.
@@ -86,12 +86,12 @@ public final class CurseCompletionTask extends Task {
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@@ -147,7 +147,7 @@ public final class CurseCompletionTask extends Task {
// Let this task fail if the curse manifest has not been completed. // Let this task fail if the curse manifest has not been completed.
// But continue other downloads. // But continue other downloads.
if (!flag.get() || notFound.get()) if (!flag.get() || notFound.get())
dependencies.add(Task.of(() -> { dependencies.add(Task.runAsync(() -> {
if (notFound.get()) if (notFound.get())
throw new CurseCompletionException(new FileNotFoundException()); throw new CurseCompletionException(new FileNotFoundException());
else else

View File

@@ -38,7 +38,7 @@ import java.util.List;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class CurseInstallTask extends Task { public final class CurseInstallTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager; private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
@@ -48,8 +48,8 @@ public final class CurseInstallTask extends Task {
private final String name; private final String name;
private final File run; private final File run;
private final ModpackConfiguration<CurseManifest> config; private final ModpackConfiguration<CurseManifest> config;
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<>();
/** /**
* Constructor. * Constructor.
@@ -99,12 +99,12 @@ public final class CurseInstallTask extends Task {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return dependents; return dependents;
} }
@Override @Override
public Collection<Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }

View File

@@ -33,7 +33,7 @@ import java.util.List;
import static org.jackhuang.hmcl.util.DigestUtils.digest; import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex; import static org.jackhuang.hmcl.util.Hex.encodeHex;
public final class MinecraftInstanceTask<T> extends Task { public final class MinecraftInstanceTask<T> extends Task<Void> {
private final File zipFile; private final File zipFile;
private final Charset encoding; private final Charset encoding;

View File

@@ -31,7 +31,7 @@ import java.util.function.Predicate;
import static org.jackhuang.hmcl.util.DigestUtils.digest; import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex; import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class ModpackInstallTask<T> extends Task { public class ModpackInstallTask<T> extends Task<Void> {
private final File modpackFile; private final File modpackFile;
private final File dest; private final File dest;

View File

@@ -26,14 +26,14 @@ import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
public class ModpackUpdateTask extends Task { public class ModpackUpdateTask extends Task<Void> {
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
private final String id; private final String id;
private final Task updateTask; private final Task<?> updateTask;
private final Path backupFolder; private final Path backupFolder;
public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) { public ModpackUpdateTask(DefaultGameRepository repository, String id, Task<?> updateTask) {
this.repository = repository; this.repository = repository;
this.id = id; this.id = id;
this.updateTask = updateTask; this.updateTask = updateTask;
@@ -49,7 +49,7 @@ public class ModpackUpdateTask extends Task {
} }
@Override @Override
public Collection<? extends Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return Collections.singleton(updateTask); return Collections.singleton(updateTask);
} }

View File

@@ -46,15 +46,15 @@ import java.util.Optional;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public final class MultiMCModpackInstallTask extends Task { public final class MultiMCModpackInstallTask extends Task<Void> {
private final File zipFile; private final File zipFile;
private final Modpack modpack; private final Modpack modpack;
private final MultiMCInstanceConfiguration manifest; private final MultiMCInstanceConfiguration manifest;
private final String name; private final String name;
private final DefaultGameRepository repository; private final DefaultGameRepository repository;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>(); private final List<Task<?>> dependents = new LinkedList<>();
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) { public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
this.zipFile = zipFile; this.zipFile = zipFile;
@@ -91,7 +91,7 @@ public final class MultiMCModpackInstallTask extends Task {
} }
@Override @Override
public List<Task> getDependencies() { public List<Task<?>> getDependencies() {
return dependencies; return dependencies;
} }
@@ -126,7 +126,7 @@ public final class MultiMCModpackInstallTask extends Task {
} }
@Override @Override
public List<Task> getDependents() { public List<Task<?>> getDependents() {
return dependents; return dependents;
} }

View File

@@ -1,74 +0,0 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.Collection;
import java.util.Collections;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class CoupleTask extends Task {
private final boolean relyingOnDependents;
private final Task pred;
private Task succ;
private final ExceptionalSupplier<Task, ?> supplier;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before supplier.
* @param supplier a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
*/
CoupleTask(Task pred, ExceptionalSupplier<Task, ?> supplier, boolean relyingOnDependents) {
this.pred = pred;
this.supplier = supplier;
this.relyingOnDependents = relyingOnDependents;
setSignificance(TaskSignificance.MODERATE);
setName(supplier.toString());
}
@Override
public void execute() throws Exception {
setName(supplier.toString());
succ = supplier.get();
}
@Override
public Collection<Task> getDependents() {
return pred == null ? Collections.emptySet() : Collections.singleton(pred);
}
@Override
public Collection<Task> getDependencies() {
return succ == null ? Collections.emptySet() : Collections.singleton(succ);
}
@Override
public boolean isRelyingOnDependents() {
return relyingOnDependents;
}
}

View File

@@ -47,7 +47,7 @@ import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public class FileDownloadTask extends Task { public class FileDownloadTask extends Task<Void> {
public static class IntegrityCheck { public static class IntegrityCheck {
private String algorithm; private String algorithm;

View File

@@ -36,33 +36,27 @@ import static java.nio.charset.StandardCharsets.UTF_8;
/** /**
* *
* @author huang * @author huangyuhui
*/ */
public final class GetTask extends TaskResult<String> { public final class GetTask extends Task<String> {
private final URL url; private final URL url;
private final Charset charset; private final Charset charset;
private final int retry; private final int retry;
private final String id;
private CacheRepository repository = CacheRepository.getInstance(); private CacheRepository repository = CacheRepository.getInstance();
public GetTask(URL url) { public GetTask(URL url) {
this(url, ID); this(url, UTF_8);
} }
public GetTask(URL url, String id) { public GetTask(URL url, Charset charset) {
this(url, id, UTF_8); this(url, charset, 5);
} }
public GetTask(URL url, String id, Charset charset) { public GetTask(URL url, Charset charset, int retry) {
this(url, id, charset, 5);
}
public GetTask(URL url, String id, Charset charset, int retry) {
this.url = url; this.url = url;
this.charset = charset; this.charset = charset;
this.retry = retry; this.retry = retry;
this.id = id;
setName(url.toString()); setName(url.toString());
} }
@@ -138,9 +132,4 @@ public final class GetTask extends TaskResult<String> {
throw new DownloadException(url, exception); throw new DownloadException(url, exception);
} }
/**
* The default task result ID.
*/
public static final String ID = "http_get";
} }

View File

@@ -1,57 +0,0 @@
/*
* 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.task;
import java.util.Arrays;
import java.util.Collection;
/**
* The tasks that provides a way to execute tasks parallelly.
* Fails when some of {@link #tasks} failed.
*
* @author huangyuhui
*/
public final class ParallelTask extends Task {
private final Collection<Task> tasks;
/**
* Constructor.
*
* @param tasks the tasks that can be executed parallelly.
*/
public ParallelTask(Task... tasks) {
this.tasks = Arrays.asList(tasks);
setSignificance(TaskSignificance.MINOR);
}
public ParallelTask(Collection<Task> tasks) {
this.tasks = tasks;
setSignificance(TaskSignificance.MINOR);
}
@Override
public void execute() {
}
@Override
public Collection<Task> getDependents() {
return tasks;
}
}

View File

@@ -1,52 +0,0 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
/**
*
* @author huangyuhui
*/
class SimpleTask extends Task {
private final ExceptionalRunnable<?> closure;
private final Scheduler scheduler;
public SimpleTask(String name, ExceptionalRunnable<?> closure, Scheduler scheduler) {
this.closure = closure;
this.scheduler = scheduler;
if (name == null) {
setSignificance(TaskSignificance.MINOR);
setName(closure.toString());
} else {
setName(name);
}
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
closure.run();
}
}

View File

@@ -1,46 +0,0 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.concurrent.Callable;
/**
*
* @author huangyuhui
*/
class SimpleTaskResult<V> extends TaskResult<V> {
private final Callable<V> callable;
public SimpleTaskResult(Callable<V> callable) {
this.callable = callable;
}
public SimpleTaskResult(ExceptionalSupplier<V, ?> supplier) {
this.callable = supplier.toCallable();
}
@Override
public void execute() throws Exception {
setResult(callable.call());
}
}

View File

@@ -27,13 +27,16 @@ import org.jackhuang.hmcl.util.InvocationDispatcher;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ReflectionHelper; import org.jackhuang.hmcl.util.ReflectionHelper;
import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.function.ExceptionalRunnable; import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
/** /**
@@ -41,15 +44,15 @@ import java.util.logging.Level;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public abstract class Task { public abstract class Task<T> {
private final EventManager<TaskEvent> onDone = new EventManager<>(); private final EventManager<TaskEvent> onDone = new EventManager<>();
private TaskSignificance significance = TaskSignificance.MAJOR;
/** /**
* True if not logging when executing this task. * True if not logging when executing this task.
*/ */
private TaskSignificance significance = TaskSignificance.MAJOR;
public final TaskSignificance getSignificance() { public final TaskSignificance getSignificance() {
return significance; return significance;
} }
@@ -58,6 +61,7 @@ public abstract class Task {
this.significance = significance; this.significance = significance;
} }
// state
private TaskState state = TaskState.READY; private TaskState state = TaskState.READY;
public TaskState getState() { public TaskState getState() {
@@ -68,6 +72,7 @@ public abstract class Task {
this.state = state; this.state = state;
} }
// last exception
private Exception lastException; private Exception lastException;
public Exception getLastException() { public Exception getLastException() {
@@ -85,6 +90,7 @@ public abstract class Task {
return Schedulers.defaultScheduler(); return Schedulers.defaultScheduler();
} }
// dependents succeeded
private boolean dependentsSucceeded = false; private boolean dependentsSucceeded = false;
public boolean isDependentsSucceeded() { public boolean isDependentsSucceeded() {
@@ -95,6 +101,7 @@ public abstract class Task {
dependentsSucceeded = true; dependentsSucceeded = true;
} }
// dependencies succeeded
private boolean dependenciesSucceeded = false; private boolean dependenciesSucceeded = false;
public boolean isDependenciesSucceeded() { public boolean isDependenciesSucceeded() {
@@ -123,17 +130,49 @@ public abstract class Task {
return true; return true;
} }
// name
private String name = getClass().getName(); private String name = getClass().getName();
public String getName() { public String getName() {
return name; return name;
} }
public Task setName(String name) { public Task<T> setName(String name) {
this.name = name; this.name = name;
return this; return this;
} }
// result
private T result;
private Consumer<T> resultConsumer;
/**
* Returns the result of this task.
*
* The result will be generated only if the execution is completed.
*/
public T getResult() {
return result;
}
protected void setResult(T result) {
this.result = result;
if (resultConsumer != null)
resultConsumer.accept(result);
}
/**
* Sync the result of this task by given action.
*
* @param action the action to perform when result of this task changed
* @return this Task
*/
public Task<T> storeTo(Consumer<T> action) {
this.resultConsumer = action;
return this;
}
// execution
public boolean doPreExecute() { public boolean doPreExecute() {
return false; return false;
} }
@@ -171,7 +210,7 @@ public abstract class Task {
/** /**
* The collection of sub-tasks that should execute **before** this task running. * The collection of sub-tasks that should execute **before** this task running.
*/ */
public Collection<? extends Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.emptySet(); return Collections.emptySet();
} }
@@ -179,7 +218,7 @@ public abstract class Task {
* The collection of sub-tasks that should execute **after** this task running. * The collection of sub-tasks that should execute **after** this task running.
* Will not be executed if execution fails. * Will not be executed if execution fails.
*/ */
public Collection<? extends Task> getDependencies() { public Collection<Task<?>> getDependencies() {
return Collections.emptySet(); return Collections.emptySet();
} }
@@ -232,15 +271,15 @@ public abstract class Task {
if (getSignificance().shouldLog()) if (getSignificance().shouldLog())
Logging.LOG.log(Level.FINE, "Executing task: " + getName()); Logging.LOG.log(Level.FINE, "Executing task: " + getName());
for (Task task : getDependents()) for (Task<?> task : getDependents())
doSubTask(task); doSubTask(task);
execute(); execute();
for (Task task : getDependencies()) for (Task<?> task : getDependencies())
doSubTask(task); doSubTask(task);
onDone.fireEvent(new TaskEvent(this, this, false)); onDone.fireEvent(new TaskEvent(this, this, false));
} }
private void doSubTask(Task task) throws Exception { private void doSubTask(Task<?> task) throws Exception {
message.bind(task.message); message.bind(task.message);
progress.bind(task.progress); progress.bind(task.progress);
task.run(); task.run();
@@ -273,61 +312,240 @@ public abstract class Task {
return executor().test(); return executor().test();
} }
public final Task then(Task b) { /**
return then(convert(b)); * Returns a new Task that, when this task completes
} * normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied function.
public final Task then(ExceptionalSupplier<Task, ?> b) { *
return new CoupleTask(this, b, true); * @param fn the function to use to compute the value of the returned Task
* @param <U> the function's return type
* @return the new Task
*/
public <U, E extends Exception> Task<U> thenApply(ExceptionalFunction<T, U, E> fn) {
return thenApply(Schedulers.defaultScheduler(), fn);
} }
/** /**
* Returns a new TaskResult that, when this task completes * Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned Task
* @param <U> the function's return type
* @return the new Task
*/
public <U, E extends Exception> Task<U> thenApply(Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return thenApply(getCaller(), scheduler, fn);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param name the name of this new Task for displaying
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned Task
* @param <U> the function's return type
* @return the new Task
*/
public <U, E extends Exception> Task<U> thenApply(String name, Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return new UniApply<>(name, scheduler, fn);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the
* returned Task
* @return the new Task
*/
public <E extends Exception> Task<Void> thenAccept(ExceptionalConsumer<T, E> action) {
return thenAccept(Schedulers.defaultScheduler(), action);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> thenAccept(Scheduler scheduler, ExceptionalConsumer<T, E> action) {
return thenAccept(getCaller(), scheduler, action);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param name the name of this new Task for displaying
* @param action the action to perform before completing the returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> thenAccept(String name, Scheduler scheduler, ExceptionalConsumer<T, E> action) {
return thenApply(name, scheduler, result -> {
action.accept(result);
return null;
});
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the default Scheduler.
*
* @param action the action to perform before completing the
* returned Task
* @return the new Task
*/
public <E extends Exception> Task<Void> thenRun(ExceptionalRunnable<E> action) {
return thenRun(Schedulers.defaultScheduler(), action);
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the supplied Scheduler.
*
* @param action the action to perform before completing the
* returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> thenRun(Scheduler scheduler, ExceptionalRunnable<E> action) {
return thenRun(getCaller(), scheduler, action);
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the supplied Scheduler.
*
* @param name the name of this new Task for displaying
* @param action the action to perform before completing the
* returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> thenRun(String name, Scheduler scheduler, ExceptionalRunnable<E> action) {
return thenApply(name, scheduler, ignore -> {
action.run();
return null;
});
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the default Scheduler. * normally, is executed using the default Scheduler.
* *
* @param fn the function to use to compute the value of the returned TaskResult * @param fn the function to use to compute the value of the returned Task
* @param <U> the function's return type * @param <U> the function's return type
* @return the new TaskResult * @return the new Task
*/ */
public final <U> TaskResult<U> thenSupply(Callable<U> fn) { public final <U> Task<U> thenSupply(Callable<U> fn) {
return thenCompose(() -> Task.ofResult(fn)); return thenCompose(() -> Task.supplyAsync(fn));
} }
/** /**
* Returns a new TaskResult that, when this task completes * Returns a new Task that, when this task completes
* normally, is executed using the default Scheduler.
*
* @param name the name of this new Task for displaying
* @param fn the function to use to compute the value of the returned Task
* @param <U> the function's return type
* @return the new Task
*/
public final <U> Task<U> thenSupply(String name, Callable<U> fn) {
return thenCompose(() -> Task.supplyAsync(name, fn));
}
/**
* Returns a new Task that, when this task completes
* normally, is executed. * normally, is executed.
* *
* @param fn the function returning a new TaskResult * @param other the another Task
* @param <U> the type of the returned TaskResult's result * @param <U> the type of the returned Task's result
* @return the TaskResult * @return the Task
*/ */
public final <U> TaskResult<U> thenCompose(ExceptionalSupplier<TaskResult<U>, ?> fn) { public final <U> Task<U> thenCompose(Task<U> other) {
return new TaskResult<U>() { return thenCompose(() -> other);
TaskResult<U> then; }
@Override /**
public Collection<? extends Task> getDependents() { * Returns a new Task that, when this task completes
return Collections.singleton(Task.this); * normally, is executed.
} *
* @param fn the function returning a new Task
@Override * @param <U> the type of the returned Task's result
public void execute() throws Exception { * @return the Task
then = fn.get().storeTo(this::setResult); */
} public final <U> Task<U> thenCompose(ExceptionalSupplier<Task<U>, ?> fn) {
return new UniCompose<>(fn, true);
@Override
public Collection<? extends Task> getDependencies() {
return then == null ? Collections.emptyList() : Collections.singleton(then);
}
};
} }
public final Task with(Task b) { /**
return with(convert(b)); * Returns a new Task that, when this task completes
* normally, is executed with result of this task as the argument
* to the supplied function.
*
* @param fn the function returning a new Task
* @param <U> the type of the returned Task's result
* @return the Task
*/
public <U, E extends Exception> Task<U> thenCompose(ExceptionalFunction<T, Task<U>, E> fn) {
return new UniCompose<>(fn, true);
} }
public final <E extends Exception> Task with(ExceptionalSupplier<Task, E> b) { public final <U> Task<U> withCompose(Task<U> other) {
return new CoupleTask(this, b, false); return withCompose(() -> other);
}
public final <U, E extends Exception> Task<U> withCompose(ExceptionalSupplier<Task<U>, E> fn) {
return new UniCompose<>(fn, false);
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the default Scheduler.
*
* @param action the action to perform before completing the
* returned Task
* @return the new Task
*/
public <E extends Exception> Task<Void> withRun(ExceptionalRunnable<E> action) {
return withRun(Schedulers.defaultScheduler(), action);
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the supplied Scheduler.
*
* @param action the action to perform before completing the
* returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> withRun(Scheduler scheduler, ExceptionalRunnable<E> action) {
return withRun(getCaller(), scheduler, action);
}
/**
* Returns a new Task that, when this task completes
* normally, executes the given action using the supplied Scheduler.
*
* @param name the name of this new Task for displaying
* @param action the action to perform before completing the
* returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task<Void> withRun(String name, Scheduler scheduler, ExceptionalRunnable<E> action) {
return new UniCompose<>(() -> Task.runAsync(name, scheduler, action), false);
} }
/** /**
@@ -344,7 +562,7 @@ public abstract class Task {
* @param action the action to perform * @param action the action to perform
* @return the new Task * @return the new Task
*/ */
public final Task whenComplete(FinalizedCallback action) { public final Task<Void> whenComplete(FinalizedCallback action) {
return whenComplete(Schedulers.defaultScheduler(), action); return whenComplete(Schedulers.defaultScheduler(), action);
} }
@@ -364,8 +582,8 @@ public abstract class Task {
* @param scheduler the executor to use for asynchronous execution * @param scheduler the executor to use for asynchronous execution
* @return the new Task * @return the new Task
*/ */
public final Task whenComplete(Scheduler scheduler, FinalizedCallback action) { public final Task<Void> whenComplete(Scheduler scheduler, FinalizedCallback action) {
return new Task() { return new Task<Void>() {
{ {
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }
@@ -389,7 +607,7 @@ public abstract class Task {
} }
@Override @Override
public Collection<Task> getDependents() { public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this); return Collections.singleton(Task.this);
} }
@@ -400,6 +618,25 @@ public abstract class Task {
}.setName(getCaller()); }.setName(getCaller());
} }
/**
* Returns a new Task with the same exception as this task, that executes
* the given action when this task completes.
*
* <p>When this task is complete, the given action is invoked with the
* result (or {@code null} if none), a boolean value represents the
* execution status of this task, and the exception (or {@code null}
* if none) of this task as arguments. The returned task is completed
* when the action returns. If the supplied action itself encounters an
* exception, then the returned task exceptionally completes with this
* exception unless this task also completed exceptionally.
*
* @param action the action to perform
* @return the new Task
*/
public Task<Void> whenComplete(Scheduler scheduler, FinalizedCallbackWithResult<T> action) {
return whenComplete(scheduler, ((isDependentSucceeded, exception) -> action.execute(getResult(), isDependentSucceeded, exception)));
}
/** /**
* Returns a new Task with the same exception as this task, that executes * Returns a new Task with the same exception as this task, that executes
* the given actions when this task completes. * the given actions when this task completes.
@@ -415,7 +652,7 @@ public abstract class Task {
* @param failure the action to perform when this task exceptionally returned * @param failure the action to perform when this task exceptionally returned
* @return the new Task * @return the new Task
*/ */
public final <E1 extends Exception, E2 extends Exception> Task whenComplete(Scheduler scheduler, ExceptionalRunnable<E1> success, ExceptionalConsumer<Exception, E2> failure) { public final <E1 extends Exception, E2 extends Exception> Task<Void> whenComplete(Scheduler scheduler, ExceptionalRunnable<E1> success, ExceptionalConsumer<Exception, E2> failure) {
return whenComplete(scheduler, (isDependentSucceeded, exception) -> { return whenComplete(scheduler, (isDependentSucceeded, exception) -> {
if (isDependentSucceeded) { if (isDependentSucceeded) {
if (success != null) if (success != null)
@@ -433,44 +670,114 @@ public abstract class Task {
}); });
} }
public static Task of(ExceptionalRunnable<?> closure) { /**
return of(Schedulers.defaultScheduler(), closure); * Returns a new Task with the same exception as this task, that executes
* the given actions when this task completes.
*
* <p>When this task is complete, the given success action is invoked with
* the result, the given failure action is invoked with the exception of
* this task. The returned task is completed when the action returns. If
* the supplied action itself encounters an exception, then the returned
* task exceptionally completes with this exception unless this task also
* completed exceptionally.
*
* @param success the action to perform when this task successfully completed
* @param failure the action to perform when this task exceptionally returned
* @return the new Task
*/
public <E1 extends Exception, E2 extends Exception> Task<Void> whenComplete(Scheduler scheduler, ExceptionalConsumer<T, E1> success, ExceptionalConsumer<Exception, E2> failure) {
return whenComplete(scheduler, () -> success.accept(getResult()), failure);
} }
public static Task of(String name, ExceptionalRunnable<?> closure) { public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
return of(name, Schedulers.defaultScheduler(), closure); return runAsync(Schedulers.defaultScheduler(), closure);
} }
public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) { public static Task<Void> runAsync(String name, ExceptionalRunnable<?> closure) {
return of(getCaller(), scheduler, closure); return runAsync(name, Schedulers.defaultScheduler(), closure);
} }
public static Task of(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) { public static Task<Void> runAsync(Scheduler scheduler, ExceptionalRunnable<?> closure) {
return new SimpleTask(name, closure, scheduler); return runAsync(getCaller(), scheduler, closure);
} }
public static Task ofThen(ExceptionalSupplier<Task, ?> b) { public static Task<Void> runAsync(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) {
return new CoupleTask(null, b, true); return new SimpleTask<>(closure.toCallable(), scheduler).setName(name);
} }
public static <V> TaskResult<V> ofResult(Callable<V> callable) { public static <T> Task<T> composeAsync(ExceptionalSupplier<Task<T>, ?> fn) {
return ofResult(getCaller(), callable); return new Task<T>() {
} Task<T> then;
public static <V> TaskResult<V> ofResult(String name, Callable<V> callable) {
return new SimpleTaskResult<>(callable).setName(name);
}
private static ExceptionalSupplier<Task, ?> convert(Task t) {
return new ExceptionalSupplier<Task, Exception>() {
@Override @Override
public Task get() { public void execute() throws Exception {
return t; then = fn.get();
if (then != null)
then.storeTo(this::setResult);
} }
@Override @Override
public String toString() { public Collection<Task<?>> getDependencies() {
return t.getName(); return then == null ? Collections.emptySet() : Collections.singleton(then);
}
};
}
public static <V> Task<V> supplyAsync(Callable<V> callable) {
return supplyAsync(getCaller(), callable);
}
public static <V> Task<V> supplyAsync(Scheduler scheduler, Callable<V> callable) {
return supplyAsync(getCaller(), scheduler, callable);
}
public static <V> Task<V> supplyAsync(String name, Callable<V> callable) {
return supplyAsync(name, Schedulers.defaultScheduler(), callable);
}
public static <V> Task<V> supplyAsync(String name, Scheduler scheduler, Callable<V> callable) {
return new SimpleTask<>(callable, scheduler).setName(name);
}
/**
* Returns a new Task that is completed when all of the given Tasks
* complete. If any of the given Tasks complete exceptionally,
* then the returned Task also does so. Otherwise, the results, if
* any, of the given Tasks are not reflected in the returned Task,
* but may be obtained by inspecting them individually. If no Tasks
* are provided, returns a Task completed with the value {@code null}.
*
* @param tasks the Tasks
* @return a new Task that is completed when all of the given Tasks complete
*/
public static Task<Void> allOf(Task<?>... tasks) {
return allOf(Arrays.asList(tasks));
}
/**
* Returns a new Task that is completed when all of the given Tasks
* complete. If any of the given Tasks complete exceptionally,
* then the returned Task also does so. Otherwise, the results, if
* any, of the given Tasks are not reflected in the returned Task,
* but may be obtained by inspecting them individually. If no Tasks
* are provided, returns a Task completed with the value {@code null}.
*
* @param tasks the Tasks
* @return a new Task that is completed when all of the given Tasks complete
*/
public static Task<Void> allOf(Collection<Task<?>> tasks) {
return new Task<Void>() {
{
setSignificance(TaskSignificance.MINOR);
}
@Override
public void execute() {
}
@Override
public Collection<Task<?>> getDependents() {
return tasks;
} }
}; };
} }
@@ -501,7 +808,118 @@ public abstract class Task {
void execute(boolean isDependentSucceeded, Exception exception) throws Exception; void execute(boolean isDependentSucceeded, Exception exception) throws Exception;
} }
static String getCaller() { public interface FinalizedCallbackWithResult<T> {
void execute(T result, boolean isDependentSucceeded, Exception exception) throws Exception;
}
private static String getCaller() {
return ReflectionHelper.getCaller(packageName -> !"org.jackhuang.hmcl.task".equals(packageName)).toString(); return ReflectionHelper.getCaller(packageName -> !"org.jackhuang.hmcl.task".equals(packageName)).toString();
} }
private static final class SimpleTask<T> extends Task<T> {
private final Callable<T> callable;
private final Scheduler scheduler;
SimpleTask(Callable<T> callable, Scheduler scheduler) {
this.callable = callable;
this.scheduler = scheduler;
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
setResult(callable.call());
}
}
private class UniApply<R> extends Task<R> {
private final Scheduler scheduler;
private final ExceptionalFunction<T, R, ?> callable;
UniApply(String name, Scheduler scheduler, ExceptionalFunction<T, R, ?> callable) {
this.scheduler = scheduler;
this.callable = callable;
setName(name);
}
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
setResult(callable.apply(Task.this.getResult()));
}
}
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
private final class UniCompose<U> extends Task<U> {
private final boolean relyingOnDependents;
private Task<U> succ;
private final ExceptionalFunction<T, Task<U>, ?> fn;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param fn a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
*/
UniCompose(ExceptionalSupplier<Task<U>, ?> fn, boolean relyingOnDependents) {
this(result -> fn.get(), relyingOnDependents);
}
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param fn a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
*/
UniCompose(ExceptionalFunction<T, Task<U>, ?> fn, boolean relyingOnDependents) {
this.fn = fn;
this.relyingOnDependents = relyingOnDependents;
setSignificance(TaskSignificance.MODERATE);
setName(fn.toString());
}
@Override
public void execute() throws Exception {
setName(fn.toString());
succ = fn.apply(Task.this.getResult());
if (succ != null)
succ.storeTo(this::setResult);
}
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public Collection<Task<?>> getDependencies() {
return succ == null ? Collections.emptySet() : Collections.singleton(succ);
}
@Override
public boolean isRelyingOnDependents() {
return relyingOnDependents;
}
}
} }

View File

@@ -25,16 +25,16 @@ import org.jackhuang.hmcl.event.Event;
*/ */
public class TaskEvent extends Event { public class TaskEvent extends Event {
private final Task task; private final Task<?> task;
private final boolean failed; private final boolean failed;
public TaskEvent(Object source, Task task, boolean failed) { public TaskEvent(Object source, Task<?> task, boolean failed) {
super(source); super(source);
this.task = task; this.task = task;
this.failed = failed; this.failed = failed;
} }
public Task getTask() { public Task<?> getTask() {
return task; return task;
} }

View File

@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
@@ -32,15 +31,13 @@ import java.util.logging.Level;
*/ */
public final class TaskExecutor { public final class TaskExecutor {
private final Task firstTask; private final Task<?> firstTask;
private final List<TaskListener> taskListeners = new LinkedList<>(); private final List<TaskListener> taskListeners = new LinkedList<>();
private boolean canceled = false;
private Exception lastException; private Exception lastException;
private final AtomicInteger totTask = new AtomicInteger(0); private final AtomicInteger totTask = new AtomicInteger(0);
private final ConcurrentLinkedQueue<Future<?>> workerQueue = new ConcurrentLinkedQueue<>(); private CompletableFuture<Boolean> future;
private Scheduler scheduler = Schedulers.newThread();
public TaskExecutor(Task task) { public TaskExecutor(Task<?> task) {
this.firstTask = task; this.firstTask = task;
} }
@@ -48,222 +45,204 @@ public final class TaskExecutor {
taskListeners.add(taskListener); taskListeners.add(taskListener);
} }
public boolean isCanceled() {
return canceled;
}
public Exception getLastException() { public Exception getLastException() {
return lastException; return lastException;
} }
public void setScheduler(Scheduler scheduler) {
this.scheduler = Objects.requireNonNull(scheduler);
}
public TaskExecutor start() { public TaskExecutor start() {
taskListeners.forEach(TaskListener::onStart); taskListeners.forEach(TaskListener::onStart);
workerQueue.add(scheduler.schedule(() -> { future = executeTasks(Collections.singleton(firstTask))
boolean flag = executeTasks(Collections.singleton(firstTask)); .thenApplyAsync(exception -> {
taskListeners.forEach(it -> it.onStop(flag, this)); boolean success = exception == null;
})); taskListeners.forEach(it -> it.onStop(success, this));
return success;
})
.exceptionally(e -> {
Lang.handleUncaughtException(e instanceof UncheckedThrowable ? e.getCause() : e);
return false;
});
return this; return this;
} }
public boolean test() { public boolean test() {
taskListeners.forEach(TaskListener::onStart); start();
AtomicBoolean flag = new AtomicBoolean(true);
Future<?> future = scheduler.schedule(() -> {
flag.set(executeTasks(Collections.singleton(firstTask)));
taskListeners.forEach(it -> it.onStop(flag.get(), this));
});
workerQueue.add(future);
try { try {
future.get(); return future.get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} catch (ExecutionException | CancellationException e) { } catch (ExecutionException | CancellationException e) {
} }
return flag.get(); return false;
} }
/** /**
* Cancel the subscription ant interrupt all tasks. * Cancel the subscription ant interrupt all tasks.
*/ */
public synchronized void cancel() { public synchronized void cancel() {
canceled = true; if (future == null) {
throw new IllegalStateException("Cannot cancel a not started TaskExecutor");
while (!workerQueue.isEmpty()) {
Future<?> future = workerQueue.poll();
if (future != null)
future.cancel(true);
} }
future.cancel(true);
} }
private boolean executeTasks(Collection<? extends Task> tasks) throws InterruptedException { private CompletableFuture<Exception> executeTasks(Collection<Task<?>> tasks) {
if (tasks.isEmpty()) if (tasks == null || tasks.isEmpty())
return true; return CompletableFuture.completedFuture(null);
totTask.addAndGet(tasks.size()); return CompletableFuture.completedFuture(null)
AtomicBoolean success = new AtomicBoolean(true); .thenComposeAsync(unused -> {
CountDownLatch latch = new CountDownLatch(tasks.size()); totTask.addAndGet(tasks.size());
for (Task task : tasks) {
if (canceled)
return false;
Invoker invoker = new Invoker(task, latch, success);
try {
Future<?> future = scheduler.schedule(invoker);
if (future != null)
workerQueue.add(future);
} catch (RejectedExecutionException e) {
throw new InterruptedException();
}
}
if (canceled) return CompletableFuture.allOf(tasks.stream()
return false; .map(task -> CompletableFuture.completedFuture(null)
.thenComposeAsync(unused2 -> executeTask(task))
).toArray(CompletableFuture<?>[]::new));
})
.thenApplyAsync(unused -> (Exception) null)
.exceptionally(throwable -> {
if (throwable instanceof Exception) {
return (Exception) throwable;
} else {
// If an error occurred, we just rethrow it.
throw new UncheckedThrowable(throwable);
}
});
}
private static void scheduleTo(Scheduler scheduler, ExceptionalRunnable<?> runnable) {
scheduleTo(scheduler, runnable, null);
}
private static void scheduleTo(Scheduler scheduler, ExceptionalRunnable<?> runnable, Runnable finalize) {
try { try {
latch.await(); scheduler.schedule(runnable).get();
} catch (ExecutionException e) {
if (e.getCause() instanceof Exception)
rethrow(e.getCause());
else
throw new UncheckedException(e);
} catch (InterruptedException e) { } catch (InterruptedException e) {
return false; throw new UncheckedException(e);
} finally {
if (finalize != null)
finalize.run();
} }
return success.get() && !canceled;
} }
private boolean executeTask(Task task) { private CompletableFuture<Object> executeTask(Task<?> task) {
if (canceled) { return CompletableFuture.completedFuture(null).thenComposeAsync(unused -> {
task.setState(Task.TaskState.FAILED); task.setState(Task.TaskState.READY);
task.setLastException(new CancellationException());
return false;
}
task.setState(Task.TaskState.READY); if (task.getSignificance().shouldLog())
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
if (task.getSignificance().shouldLog()) taskListeners.forEach(it -> it.onReady(task));
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
taskListeners.forEach(it -> it.onReady(task));
boolean flag = false;
try {
if (task.doPreExecute()) { if (task.doPreExecute()) {
try { scheduleTo(task.getScheduler(), task::preExecute);
task.getScheduler().schedule(task::preExecute).get();
} catch (ExecutionException e) {
if (e.getCause() instanceof Exception)
throw (Exception) e.getCause();
else
throw e;
}
} }
Collection<? extends Task> dependents = task.getDependents(); return executeTasks(task.getDependents());
boolean doDependentsSucceeded = executeTasks(dependents); }).thenComposeAsync(dependentsException -> {
Exception dependentsException = dependents.stream().map(Task::getLastException).filter(Objects::nonNull).findAny().orElse(null); boolean isDependentsSucceeded = dependentsException == null;
if (!doDependentsSucceeded && task.isRelyingOnDependents() || canceled) {
if (!isDependentsSucceeded && task.isRelyingOnDependents()) {
task.setLastException(dependentsException); task.setLastException(dependentsException);
throw new CancellationException(); rethrow(dependentsException);
} }
if (doDependentsSucceeded) if (isDependentsSucceeded)
task.setDependentsSucceeded(); task.setDependentsSucceeded();
try { scheduleTo(task.getScheduler(), () -> {
task.getScheduler().schedule(() -> { task.setState(Task.TaskState.RUNNING);
task.setState(Task.TaskState.RUNNING); taskListeners.forEach(it -> it.onRunning(task));
taskListeners.forEach(it -> it.onRunning(task)); task.execute();
task.execute(); }, () -> task.setState(Task.TaskState.EXECUTED));
}).get();
} catch (ExecutionException e) {
if (e.getCause() instanceof Exception)
throw (Exception) e.getCause();
else
throw e;
} finally {
task.setState(Task.TaskState.EXECUTED);
}
Collection<? extends Task> dependencies = task.getDependencies(); return executeTasks(task.getDependencies());
boolean doDependenciesSucceeded = executeTasks(dependencies); }).thenApplyAsync(dependenciesException -> {
Exception dependenciesException = dependencies.stream().map(Task::getLastException).filter(Objects::nonNull).findAny().orElse(null); boolean isDependenciesSucceeded = dependenciesException == null;
if (doDependenciesSucceeded) if (isDependenciesSucceeded)
task.setDependenciesSucceeded(); task.setDependenciesSucceeded();
if (task.doPostExecute()) { if (task.doPostExecute()) {
try { scheduleTo(task.getScheduler(), task::postExecute);
task.getScheduler().schedule(task::postExecute).get();
} catch (ExecutionException e) {
if (e.getCause() instanceof Exception)
throw (Exception) e.getCause();
else
throw e;
}
} }
if (!doDependenciesSucceeded && task.isRelyingOnDependencies()) { if (!isDependenciesSucceeded && task.isRelyingOnDependencies()) {
Logging.LOG.severe("Subtasks failed for " + task.getName()); Logging.LOG.severe("Subtasks failed for " + task.getName());
task.setLastException(dependenciesException); task.setLastException(dependenciesException);
throw new CancellationException(); rethrow(dependenciesException);
} }
flag = true;
if (task.getSignificance().shouldLog()) { if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINER, "Task finished: " + task.getName()); Logging.LOG.log(Level.FINER, "Task finished: " + task.getName());
} }
task.onDone().fireEvent(new TaskEvent(this, task, false)); task.onDone().fireEvent(new TaskEvent(this, task, false));
taskListeners.forEach(it -> it.onFinished(task)); taskListeners.forEach(it -> it.onFinished(task));
} catch (InterruptedException e) {
task.setLastException(e); task.setState(Task.TaskState.SUCCEEDED);
if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); return null;
} }).exceptionally(throwable -> {
task.onDone().fireEvent(new TaskEvent(this, task, true)); if (!(throwable instanceof Exception))
taskListeners.forEach(it -> it.onFailed(task, e)); throw new UncheckedThrowable(throwable);
} catch (CancellationException | RejectedExecutionException e) { Exception e = throwable instanceof UncheckedException ? (Exception) throwable.getCause() : (Exception) throwable;
if (task.getLastException() == null) if (e instanceof InterruptedException) {
task.setLastException(e); task.setLastException(e);
} catch (Exception e) { if (task.getSignificance().shouldLog()) {
task.setLastException(e); Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
lastException = e; }
if (task.getSignificance().shouldLog()) { task.onDone().fireEvent(new TaskEvent(this, task, true));
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); taskListeners.forEach(it -> it.onFailed(task, e));
} else if (e instanceof CancellationException || e instanceof RejectedExecutionException) {
if (task.getLastException() == null)
task.setLastException(e);
} else {
task.setLastException(e);
lastException = e;
if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
}
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));
} }
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e)); task.setState(Task.TaskState.FAILED);
}
task.setState(flag ? Task.TaskState.SUCCEEDED : Task.TaskState.FAILED); throw new UncheckedException(e);
return flag; });
} }
public int getRunningTasks() { public int getRunningTasks() {
return totTask.get(); return totTask.get();
} }
private class Invoker implements ExceptionalRunnable<Exception> { private static class UncheckedException extends RuntimeException {
private final Task task; UncheckedException(Exception exception) {
private final CountDownLatch latch; super(exception);
private final AtomicBoolean success;
public Invoker(Task task, CountDownLatch latch, AtomicBoolean success) {
this.task = task;
this.latch = latch;
this.success = success;
} }
}
@Override private static class UncheckedThrowable extends RuntimeException {
public void run() {
try { UncheckedThrowable(Throwable throwable) {
Thread.currentThread().setName(task.getName()); super(throwable);
if (!executeTask(task))
success.set(false);
} finally {
latch.countDown();
}
} }
}
private static void rethrow(Throwable e) {
if (e instanceof RuntimeException) { // including UncheckedException and UncheckedThrowable
throw (RuntimeException) e;
} else if (e instanceof Exception) {
throw new UncheckedException((Exception) e);
} else {
throw new UncheckedThrowable(e);
}
} }
} }

View File

@@ -28,16 +28,16 @@ public abstract class TaskListener implements EventListener {
public void onStart() { public void onStart() {
} }
public void onReady(Task task) { public void onReady(Task<?> task) {
} }
public void onRunning(Task task) { public void onRunning(Task<?> task) {
} }
public void onFinished(Task task) { public void onFinished(Task<?> task) {
} }
public void onFailed(Task task, Throwable throwable) { public void onFailed(Task<?> task, Throwable throwable) {
onFinished(task); onFinished(task);
} }

View File

@@ -1,247 +0,0 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
/**
* A task that has a result.
*
* @author huangyuhui
*/
public abstract class TaskResult<T> extends Task {
private T result;
private Consumer<T> resultConsumer;
@Override
public TaskResult<T> setName(String name) {
super.setName(name);
return this;
}
/**
* Returns the result of this task.
*
* The result will be generated only if the execution is completed.
*/
public T getResult() {
return result;
}
protected void setResult(T result) {
this.result = result;
if (resultConsumer != null)
resultConsumer.accept(result);
}
/**
* Sync the result of this task by given action.
*
* @param action the action to perform when result of this task changed
* @return this TaskResult
*/
public TaskResult<T> storeTo(Consumer<T> action) {
this.resultConsumer = action;
return this;
}
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed with result of this task as the argument
* to the supplied function.
*
* @param fn the function returning a new TaskResult
* @param <U> the type of the returned TaskResult's result
* @return the TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenCompose(ExceptionalFunction<T, TaskResult<U>, E> fn) {
return new TaskResult<U>() {
TaskResult<U> then;
@Override
public Collection<? extends Task> getDependents() {
return Collections.singleton(TaskResult.this);
}
@Override
public void execute() throws Exception {
then = fn.apply(TaskResult.this.getResult()).storeTo(this::setResult);
}
@Override
public Collection<? extends Task> getDependencies() {
return then == null ? Collections.emptyList() : Collections.singleton(then);
}
};
}
/**
* Returns a new Task that, when this task completes
* normally, is executed with this task as the argument
* to the supplied function.
*
* @param fn the function returning a new Task
* @return the Task
*/
public <E extends Exception> Task then(ExceptionalFunction<T, Task, E> fn) {
return new CoupleTask(this, () -> fn.apply(getResult()), true);
}
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(ExceptionalFunction<T, U, E> fn) {
return thenApply(Schedulers.defaultScheduler(), fn);
}
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return thenApply(getCaller(), scheduler, fn);
}
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param name the name of this new TaskResult for displaying
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(String name, Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return new Subtask<>(name, scheduler, fn);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the
* returned Task
* @return the new Task
*/
public <E extends Exception> Task thenAccept(ExceptionalConsumer<T, E> action) {
return thenAccept(Schedulers.defaultScheduler(), action);
}
/**
* Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task thenAccept(Scheduler scheduler, ExceptionalConsumer<T, E> action) {
return new CoupleTask(this, () -> Task.of(scheduler, () -> action.accept(getResult())), true);
}
/**
* Returns a new Task with the same exception as this task, that executes
* the given actions when this task completes.
*
* <p>When this task is complete, the given success action is invoked with
* the result, the given failure action is invoked with the exception of
* this task. The returned task is completed when the action returns. If
* the supplied action itself encounters an exception, then the returned
* task exceptionally completes with this exception unless this task also
* completed exceptionally.
*
* @param success the action to perform when this task successfully completed
* @param failure the action to perform when this task exceptionally returned
* @return the new Task
*/
public <E1 extends Exception, E2 extends Exception> Task whenComplete(Scheduler scheduler, ExceptionalConsumer<T, E1> success, ExceptionalConsumer<Exception, E2> failure) {
return whenComplete(scheduler, () -> success.accept(getResult()), failure);
}
/**
* Returns a new Task with the same exception as this task, that executes
* the given action when this task completes.
*
* <p>When this task is complete, the given action is invoked with the
* result (or {@code null} if none), a boolean value represents the
* execution status of this task, and the exception (or {@code null}
* if none) of this task as arguments. The returned task is completed
* when the action returns. If the supplied action itself encounters an
* exception, then the returned task exceptionally completes with this
* exception unless this task also completed exceptionally.
*
* @param action the action to perform
* @return the new Task
*/
public Task whenComplete(Scheduler scheduler, FinalizedCallback<T> action) {
return whenComplete(scheduler, ((isDependentSucceeded, exception) -> action.execute(getResult(), isDependentSucceeded, exception)));
}
private class Subtask<R> extends TaskResult<R> {
private final Scheduler scheduler;
private final ExceptionalFunction<T, R, ?> callable;
public Subtask(String name, Scheduler scheduler, ExceptionalFunction<T, R, ?> callable) {
this.scheduler = scheduler;
this.callable = callable;
setName(name);
}
@Override
public Collection<? extends Task> getDependents() {
return Collections.singleton(TaskResult.this);
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
setResult(callable.apply(TaskResult.this.getResult()));
}
}
public interface FinalizedCallback<V> {
void execute(V result, boolean isDependentSucceeded, Exception exception) throws Exception;
}
}

View File

@@ -27,15 +27,11 @@ public interface ExceptionalRunnable<E extends Exception> {
void run() throws E; void run() throws E;
default Callable<?> toCallable() { default Callable<Void> toCallable() {
return () -> { return () -> {
run(); run();
return null; return null;
}; };
} }
static ExceptionalRunnable<?> fromRunnable(Runnable r) {
return r::run;
}
} }

View File

@@ -0,0 +1,18 @@
package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.task.Task;
import org.junit.Assert;
import org.junit.Test;
public class TaskTest {
@Test
public void testWhenComplete() {
Task.composeAsync(() -> Task.allOf(
Task.allOf(Task.runAsync(() -> {
throw new Exception();
}))
)).whenComplete(((isDependentSucceeded, exception) -> {
Assert.assertFalse(isDependentSucceeded);
})).test();
}
}