add: more brief launching steps
This commit is contained in:
@@ -20,11 +20,7 @@ package org.jackhuang.hmcl.game;
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.CharacterDeletedException;
|
||||
import org.jackhuang.hmcl.auth.CredentialExpiredException;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
@@ -51,11 +47,7 @@ import org.jackhuang.hmcl.ui.LogWindow;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
@@ -68,16 +60,9 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -102,9 +87,11 @@ public final class LauncherHelper {
|
||||
this.setting = profile.getVersionSetting(selectedVersion);
|
||||
this.launcherVisibility = setting.getLauncherVisibility();
|
||||
this.showLogs = setting.isShowLogs();
|
||||
this.launchingStepsPane.setTitle(i18n("version.launch"));
|
||||
}
|
||||
|
||||
private final TaskExecutorDialogPane launchingStepsPane = new TaskExecutorDialogPane(it -> {});
|
||||
private CountDownLatch launchingLatch;
|
||||
|
||||
public void setTestMode() {
|
||||
launcherVisibility = LauncherVisibility.KEEP;
|
||||
@@ -140,15 +127,15 @@ public final class LauncherHelper {
|
||||
Version version = MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion));
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
||||
|
||||
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
||||
.thenComposeAsync(() -> {
|
||||
TaskExecutor executor = Task.runAsync(() -> {
|
||||
})
|
||||
.thenComposeAsync(() -> Task.composeAsync(() -> {
|
||||
if (setting.isNotCheckGame())
|
||||
return null;
|
||||
else
|
||||
return dependencyManager.checkGameCompletionAsync(version);
|
||||
})
|
||||
.thenRunAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))
|
||||
.thenComposeAsync(() -> {
|
||||
}).withStage("launch.state.dependencies"))
|
||||
.thenComposeAsync(() -> Task.composeAsync(null, () -> {
|
||||
try {
|
||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||
if ("Curse".equals(configuration.getType()))
|
||||
@@ -160,9 +147,8 @@ public final class LauncherHelper {
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.thenRunAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))
|
||||
.thenSupplyAsync(i18n("launch.state.logging_in"), () -> {
|
||||
}).withStage("launch.state.modpack"))
|
||||
.thenComposeAsync(() -> Task.supplyAsync((String) null, () -> {
|
||||
try {
|
||||
return account.logIn();
|
||||
} catch (CredentialExpiredException e) {
|
||||
@@ -172,31 +158,27 @@ public final class LauncherHelper {
|
||||
LOG.warning("Authentication failed, try playing offline: " + e);
|
||||
return account.playOffline().orElseThrow(() -> e);
|
||||
}
|
||||
})
|
||||
.thenApplyAsync(Schedulers.javafx(), authInfo -> {
|
||||
emitStatus(LoadingState.LAUNCHING);
|
||||
return authInfo;
|
||||
})
|
||||
.thenApplyAsync(authInfo -> new HMCLGameLauncher(
|
||||
repository,
|
||||
version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version,
|
||||
authInfo,
|
||||
setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()),
|
||||
launcherVisibility == LauncherVisibility.CLOSE
|
||||
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
||||
: new HMCLProcessListener(authInfo, gameVersion.isPresent())
|
||||
))
|
||||
.thenComposeAsync(launcher -> { // launcher is prev task's result
|
||||
}).withStage("launch.state.logging_in"))
|
||||
.thenComposeAsync(authInfo -> Task.supplyAsync((String) null, () -> {
|
||||
return new HMCLGameLauncher(
|
||||
repository,
|
||||
version.getPatches().isEmpty() ? repository.getResolvedVersion(selectedVersion) : version,
|
||||
authInfo,
|
||||
setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()),
|
||||
launcherVisibility == LauncherVisibility.CLOSE
|
||||
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
||||
: new HMCLProcessListener(authInfo, gameVersion.isPresent())
|
||||
);
|
||||
}).thenComposeAsync(launcher -> { // launcher is prev task's result
|
||||
if (scriptFile == null) {
|
||||
return new LaunchTask<>(launcher::launch).setName(i18n("version.launch"));
|
||||
return Task.supplyAsync((String) null, launcher::launch);
|
||||
} else {
|
||||
return new LaunchTask<ManagedProcess>(() -> {
|
||||
return Task.supplyAsync((String) null, () -> {
|
||||
launcher.makeLaunchScript(scriptFile);
|
||||
return null;
|
||||
}).setName(i18n("version.launch_script"));
|
||||
});
|
||||
}
|
||||
})
|
||||
.thenAcceptAsync(process -> { // process is LaunchTask's result
|
||||
}).thenAcceptAsync(process -> { // process is LaunchTask's result
|
||||
if (scriptFile == null) {
|
||||
PROCESSES.add(process);
|
||||
if (launcherVisibility == LauncherVisibility.CLOSE)
|
||||
@@ -212,19 +194,18 @@ public final class LauncherHelper {
|
||||
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
||||
});
|
||||
}
|
||||
})
|
||||
}).thenRunAsync(null, Schedulers.defaultScheduler(), () -> {
|
||||
launchingLatch = new CountDownLatch(1);
|
||||
launchingLatch.await();
|
||||
}).withStage("launch.state.waiting_launching"))
|
||||
.cancellableExecutor();
|
||||
|
||||
launchingStepsPane.setExecutor(executor, false);
|
||||
launchingStepsPane.setExecutor(executor, Lang.immutableListOf(
|
||||
"launch.state.dependencies",
|
||||
"launch.state.modpack",
|
||||
"launch.state.logging_in",
|
||||
"launch.state.waiting_launching"), false);
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
final AtomicInteger finished = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void onFinished(Task<?> task) {
|
||||
finished.incrementAndGet();
|
||||
int runningTasks = executor.getRunningTasks();
|
||||
Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(boolean success, TaskExecutor executor) {
|
||||
@@ -432,15 +413,6 @@ public final class LauncherHelper {
|
||||
onAccept.run();
|
||||
}
|
||||
|
||||
public void emitStatus(LoadingState state) {
|
||||
if (state == LoadingState.DONE) {
|
||||
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
launchingStepsPane.setTitle(state.getLocalizedMessage());
|
||||
launchingStepsPane.setSubtitle((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||
}
|
||||
|
||||
private void checkExit() {
|
||||
switch (launcherVisibility) {
|
||||
case HIDE_AND_REOPEN:
|
||||
@@ -465,19 +437,6 @@ public final class LauncherHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static class LaunchTask<T> extends Task<T> {
|
||||
private final ExceptionalSupplier<T, Exception> supplier;
|
||||
|
||||
public LaunchTask(ExceptionalSupplier<T, Exception> supplier) {
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
setResult(supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The managed process listener.
|
||||
* Guarantee that one [JavaProcess], one [HMCLProcessListener].
|
||||
@@ -534,7 +493,7 @@ public final class LauncherHelper {
|
||||
// these codes will be executed.
|
||||
if (Controllers.getStage() != null) {
|
||||
Controllers.getStage().hide();
|
||||
emitStatus(LoadingState.DONE);
|
||||
launchingLatch.countDown();
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -543,7 +502,7 @@ public final class LauncherHelper {
|
||||
break;
|
||||
case KEEP:
|
||||
Platform.runLater(() -> {
|
||||
emitStatus(LoadingState.DONE);
|
||||
launchingLatch.countDown();
|
||||
});
|
||||
break;
|
||||
case HIDE:
|
||||
@@ -552,7 +511,7 @@ public final class LauncherHelper {
|
||||
// these codes will be executed.
|
||||
if (Controllers.getStage() != null) {
|
||||
Controllers.getStage().close();
|
||||
emitStatus(LoadingState.DONE);
|
||||
launchingLatch.countDown();
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -248,18 +248,13 @@ public final class Controllers {
|
||||
return pane;
|
||||
}
|
||||
|
||||
public static Region taskDialog(TaskExecutor executor, String title) {
|
||||
return taskDialog(executor, title, "");
|
||||
public static TaskExecutorDialogPane taskDialog(TaskExecutor executor, String title) {
|
||||
return taskDialog(executor, title, null);
|
||||
}
|
||||
|
||||
public static Region taskDialog(TaskExecutor executor, String title, String subtitle) {
|
||||
return taskDialog(executor, title, subtitle, null);
|
||||
}
|
||||
|
||||
public static Region taskDialog(TaskExecutor executor, String title, String subtitle, Consumer<Region> onCancel) {
|
||||
public static TaskExecutorDialogPane taskDialog(TaskExecutor executor, String title, Consumer<Region> onCancel) {
|
||||
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(onCancel);
|
||||
pane.setTitle(title);
|
||||
pane.setSubtitle(subtitle);
|
||||
pane.setExecutor(executor);
|
||||
dialog(pane);
|
||||
return pane;
|
||||
|
||||
@@ -71,6 +71,10 @@ public final class SVG {
|
||||
return createSVGPath("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node dotsHorizontal(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M16,12A2,2 0 0,1 18,10A2,2 0 0,1 20,12A2,2 0 0,1 18,14A2,2 0 0,1 16,12M10,12A2,2 0 0,1 12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12M4,12A2,2 0 0,1 6,10A2,2 0 0,1 8,12A2,2 0 0,1 6,14A2,2 0 0,1 4,12Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node delete(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z", fill, width, height);
|
||||
}
|
||||
@@ -170,4 +174,8 @@ public final class SVG {
|
||||
public static Node check(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node arrowRight(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z", fill, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,22 +53,37 @@ public class AdvancedListBox extends ScrollPane {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox remove(Node child) {
|
||||
if (child instanceof Pane)
|
||||
container.getChildren().remove(child);
|
||||
public AdvancedListBox add(int index, Node child) {
|
||||
if (child instanceof Pane || child instanceof AdvancedListItem)
|
||||
container.getChildren().add(index, child);
|
||||
else {
|
||||
StackPane pane = null;
|
||||
for (Node node : container.getChildren())
|
||||
StackPane pane = new StackPane();
|
||||
pane.getStyleClass().add("advanced-list-box-item");
|
||||
pane.getChildren().setAll(child);
|
||||
container.getChildren().add(index, pane);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox remove(Node child) {
|
||||
container.getChildren().remove(indexOf(child));
|
||||
return this;
|
||||
}
|
||||
|
||||
public int indexOf(Node child) {
|
||||
if (child instanceof Pane) {
|
||||
return container.getChildren().indexOf(child);
|
||||
} else {
|
||||
for (int i = 0; i < container.getChildren().size(); ++i) {
|
||||
Node node = container.getChildren().get(i);
|
||||
if (node instanceof StackPane) {
|
||||
ObservableList<Node> list = ((StackPane) node).getChildren();
|
||||
if (list.size() == 1 && list.get(0) == child)
|
||||
pane = (StackPane) node;
|
||||
return i;
|
||||
}
|
||||
if (pane == null)
|
||||
throw new Error();
|
||||
container.getChildren().remove(pane);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox startCategory(String category) {
|
||||
|
||||
@@ -18,18 +18,19 @@
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -38,14 +39,11 @@ import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
public class TaskExecutorDialogPane extends StackPane {
|
||||
private TaskExecutor executor;
|
||||
private Consumer<Region> onCancel;
|
||||
private final Consumer<FileDownloadTask.SpeedEvent> speedEventHandler;
|
||||
|
||||
@FXML
|
||||
private JFXProgressBar progressBar;
|
||||
@FXML
|
||||
private Label lblTitle;
|
||||
@FXML
|
||||
private Label lblSubtitle;
|
||||
@FXML
|
||||
private Label lblProgress;
|
||||
@FXML
|
||||
private JFXButton btnCancel;
|
||||
@@ -62,10 +60,24 @@ public class TaskExecutorDialogPane extends StackPane {
|
||||
onCancel.accept(this);
|
||||
});
|
||||
|
||||
lblProgress.textProperty().bind(Bindings.createStringBinding(
|
||||
() -> taskListPane.finishedTasksProperty().get() + "/" + taskListPane.totTasksProperty().get(),
|
||||
taskListPane.finishedTasksProperty(), taskListPane.totTasksProperty()
|
||||
));
|
||||
speedEventHandler = speedEvent -> {
|
||||
String unit = "B/s";
|
||||
double speed = speedEvent.getSpeed();
|
||||
if (speed > 1024) {
|
||||
speed /= 1024;
|
||||
unit = "KB/s";
|
||||
}
|
||||
if (speed > 1024) {
|
||||
speed /= 1024;
|
||||
unit = "MB/s";
|
||||
}
|
||||
double finalSpeed = speed;
|
||||
String finalUnit = unit;
|
||||
Platform.runLater(() -> {
|
||||
lblProgress.setText(String.format("%.1f", finalSpeed) + " " + finalUnit);
|
||||
});
|
||||
};
|
||||
FileDownloadTask.speedEvent.channel(FileDownloadTask.SpeedEvent.class).registerWeak(speedEventHandler);
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor) {
|
||||
@@ -73,10 +85,18 @@ public class TaskExecutorDialogPane extends StackPane {
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor, boolean autoClose) {
|
||||
setExecutor(executor, Collections.emptyList(), autoClose);
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor, List<String> stages) {
|
||||
setExecutor(executor, stages, true);
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor, List<String> stages, boolean autoClose) {
|
||||
this.executor = executor;
|
||||
|
||||
if (executor != null) {
|
||||
taskListPane.setExecutor(executor);
|
||||
taskListPane.setExecutor(executor, stages);
|
||||
|
||||
if (autoClose)
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
@@ -100,25 +120,6 @@ public class TaskExecutorDialogPane extends StackPane {
|
||||
lblTitle.setText(currentState);
|
||||
}
|
||||
|
||||
public StringProperty subtitleProperty() {
|
||||
return lblSubtitle.textProperty();
|
||||
}
|
||||
|
||||
public String getSubtitle() {
|
||||
return lblSubtitle.getText();
|
||||
}
|
||||
|
||||
public void setSubtitle(String subtitle) {
|
||||
lblSubtitle.setText(subtitle);
|
||||
}
|
||||
|
||||
public void setProgress(double progress) {
|
||||
if (progress == Double.MAX_VALUE)
|
||||
progressBar.setVisible(false);
|
||||
else
|
||||
progressBar.setProgress(progress);
|
||||
}
|
||||
|
||||
public void setCancel(Consumer<Region> onCancel) {
|
||||
this.onCancel = onCancel;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@@ -37,20 +39,22 @@ import org.jackhuang.hmcl.mod.ModpackUpdateTask;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class TaskListPane extends StackPane {
|
||||
private final AdvancedListBox listBox = new AdvancedListBox();
|
||||
private final Map<Task<?>, ProgressListNode> nodes = new HashMap<>();
|
||||
private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper();
|
||||
private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper();
|
||||
private final List<StageNode> stageNodes = new ArrayList<>();
|
||||
|
||||
public TaskListPane() {
|
||||
listBox.setSpacing(0);
|
||||
@@ -58,33 +62,35 @@ public final class TaskListPane extends StackPane {
|
||||
getChildren().setAll(listBox);
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty finishedTasksProperty() {
|
||||
return finishedTasks.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty totTasksProperty() {
|
||||
return totTasks.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor) {
|
||||
setExecutor(executor, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void setExecutor(TaskExecutor executor, List<String> stages) {
|
||||
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
@Override
|
||||
public void onStart() {
|
||||
Platform.runLater(() -> {
|
||||
stageNodes.clear();
|
||||
listBox.clear();
|
||||
finishedTasks.set(0);
|
||||
totTasks.set(0);
|
||||
stageNodes.addAll(stages.stream().map(StageNode::new).collect(Collectors.toList()));
|
||||
stageNodes.forEach(listBox::add);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady(Task<?> task) {
|
||||
Platform.runLater(() -> totTasks.set(totTasks.getValue() + 1));
|
||||
if (task instanceof Task.StageTask) {
|
||||
Platform.runLater(() -> {
|
||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::begin);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunning(Task<?> task) {
|
||||
if (!task.getSignificance().shouldShow())
|
||||
if (!task.getSignificance().shouldShow() || task.getName() == null)
|
||||
return;
|
||||
|
||||
if (task instanceof GameAssetDownloadTask) {
|
||||
@@ -117,34 +123,77 @@ public final class TaskListPane extends StackPane {
|
||||
|
||||
ProgressListNode node = new ProgressListNode(task);
|
||||
nodes.put(task, node);
|
||||
Platform.runLater(() -> listBox.add(node));
|
||||
Platform.runLater(() -> {
|
||||
StageNode stageNode = stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().orElse(null);
|
||||
listBox.add(listBox.indexOf(stageNode) + 1, node);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(Task<?> task) {
|
||||
if (task instanceof Task.StageTask) {
|
||||
Platform.runLater(() -> {
|
||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::succeed);
|
||||
});
|
||||
}
|
||||
|
||||
ProgressListNode node = nodes.remove(task);
|
||||
if (node == null)
|
||||
return;
|
||||
node.unbind();
|
||||
Platform.runLater(() -> {
|
||||
listBox.remove(node);
|
||||
finishedTasks.set(finishedTasks.getValue() + 1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Task<?> task, Throwable throwable) {
|
||||
if (task instanceof Task.StageTask) {
|
||||
Platform.runLater(() -> {
|
||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::fail);
|
||||
});
|
||||
}
|
||||
ProgressListNode node = nodes.remove(task);
|
||||
if (node == null)
|
||||
return;
|
||||
Platform.runLater(() -> {
|
||||
node.setThrowable(throwable);
|
||||
finishedTasks.set(finishedTasks.getValue() + 1);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class StageNode extends BorderPane {
|
||||
private final String stage;
|
||||
private final Label title = new Label();
|
||||
private boolean started = false;
|
||||
|
||||
public StageNode(String stage) {
|
||||
this.stage = stage;
|
||||
|
||||
title.setText(i18n(stage));
|
||||
BorderPane.setAlignment(title, Pos.CENTER_LEFT);
|
||||
BorderPane.setMargin(title, new Insets(0, 0, 0, 8));
|
||||
setPadding(new Insets(0, 0, 8, 4));
|
||||
setCenter(title);
|
||||
setLeft(SVG.dotsHorizontal(Theme.blackFillBinding(), 14, 14));
|
||||
}
|
||||
|
||||
public void begin() {
|
||||
if (started) return;
|
||||
started = true;
|
||||
setLeft(FXUtils.limitingSize(SVG.arrowRight(Theme.blackFillBinding(), 14, 14), 14, 14));
|
||||
}
|
||||
|
||||
public void fail() {
|
||||
setLeft(FXUtils.limitingSize(SVG.close(Theme.blackFillBinding(), 14, 14), 14, 14));
|
||||
}
|
||||
|
||||
public void succeed() {
|
||||
setLeft(FXUtils.limitingSize(SVG.check(Theme.blackFillBinding(), 14, 14), 14, 14));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProgressListNode extends BorderPane {
|
||||
private final JFXProgressBar bar = new JFXProgressBar();
|
||||
private final Label title = new Label();
|
||||
|
||||
@@ -42,7 +42,6 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
});
|
||||
|
||||
pane.setTitle(i18n("message.doing"));
|
||||
pane.setProgress(Double.MAX_VALUE);
|
||||
if (settings.containsKey("title")) {
|
||||
Object title = settings.get("title");
|
||||
if (title instanceof StringProperty)
|
||||
@@ -51,14 +50,6 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
pane.setTitle((String) title);
|
||||
}
|
||||
|
||||
if (settings.containsKey("subtitle")) {
|
||||
Object subtitle = settings.get("subtitle");
|
||||
if (subtitle instanceof StringProperty)
|
||||
pane.subtitleProperty().bind((StringProperty) subtitle);
|
||||
else if (subtitle instanceof String)
|
||||
pane.setSubtitle((String) subtitle);
|
||||
}
|
||||
|
||||
runInFX(() -> {
|
||||
TaskExecutor executor = task.cancellableExecutor(new TaskListener() {
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXProgressBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
@@ -11,13 +10,9 @@
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane" FXUtils.limitWidth="500" FXUtils.limitHeight="300">
|
||||
<BorderPane>
|
||||
<top>
|
||||
<JFXProgressBar fx:id="progressBar" StackPane.alignment="TOP_CENTER" />
|
||||
</top>
|
||||
<center>
|
||||
<VBox alignment="TOP_CENTER" style="-fx-padding: 16px;">
|
||||
<Label fx:id="lblTitle" style="-fx-font-size: 20px;" />
|
||||
<Label fx:id="lblSubtitle" style="-fx-font-size: 14px;" />
|
||||
<VBox style="-fx-padding: 16px;">
|
||||
<Label fx:id="lblTitle" style="-fx-font-size: 14px; -fx-font-weight: BOLD;" />
|
||||
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
|
||||
<TaskListPane fx:id="taskListPane" />
|
||||
|
||||
@@ -179,10 +179,10 @@ launch.failed.decompressing_natives=無法解壓縮遊戲資源庫。
|
||||
launch.failed.download_library=無法下載遊戲相依元件 %s。
|
||||
launch.failed.executable_permission=無法為啟動檔案新增執行權限。
|
||||
launch.failed.exited_abnormally=遊戲非正常退出,請查看記錄檔案,或聯絡他人尋求幫助。
|
||||
launch.state.dependencies=正在處理遊戲相依元件
|
||||
launch.state.dependencies=處理遊戲相依元件
|
||||
launch.state.done=啟動完成
|
||||
launch.state.logging_in=登入中
|
||||
launch.state.modpack=正在下載必要檔案
|
||||
launch.state.logging_in=登入
|
||||
launch.state.modpack=下載必要檔案
|
||||
launch.state.waiting_launching=等待遊戲啟動
|
||||
launch.wrong_javadir=Java 路徑錯誤,將自動重設為預設 Java 路徑。
|
||||
|
||||
|
||||
@@ -180,10 +180,10 @@ launch.failed.decompressing_natives=未能解压游戏本地库。
|
||||
launch.failed.download_library=未能下载游戏依赖 %s.
|
||||
launch.failed.executable_permission=未能为启动文件添加执行权限。
|
||||
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||
launch.state.dependencies=正在处理游戏依赖
|
||||
launch.state.dependencies=处理游戏依赖
|
||||
launch.state.done=启动完成
|
||||
launch.state.logging_in=登录中
|
||||
launch.state.modpack=正在下载必要文件
|
||||
launch.state.logging_in=登录
|
||||
launch.state.modpack=下载必要文件
|
||||
launch.state.waiting_launching=等待游戏启动
|
||||
launch.wrong_javadir=错误的 Java 路径,将自动重置为默认 Java 路径。
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
@@ -42,7 +45,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
@Override
|
||||
public TaskExecutor start() {
|
||||
taskListeners.forEach(TaskListener::onStart);
|
||||
future = executeTasks(Collections.singleton(firstTask))
|
||||
future = executeTasks(null, Collections.singleton(firstTask))
|
||||
.thenApplyAsync(exception -> {
|
||||
boolean success = exception == null;
|
||||
|
||||
@@ -95,7 +98,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
future.cancel(true);
|
||||
}
|
||||
|
||||
private CompletableFuture<Exception> executeTasks(Collection<Task<?>> tasks) {
|
||||
private CompletableFuture<Exception> executeTasks(Task<?> parentTask, Collection<Task<?>> tasks) {
|
||||
if (tasks == null || tasks.isEmpty())
|
||||
return CompletableFuture.completedFuture(null);
|
||||
|
||||
@@ -105,7 +108,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
|
||||
return CompletableFuture.allOf(tasks.stream()
|
||||
.map(task -> CompletableFuture.completedFuture(null)
|
||||
.thenComposeAsync(unused2 -> executeTask(task))
|
||||
.thenComposeAsync(unused2 -> executeTask(parentTask, task))
|
||||
).toArray(CompletableFuture<?>[]::new));
|
||||
})
|
||||
.thenApplyAsync(unused -> (Exception) null)
|
||||
@@ -120,11 +123,13 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<?> executeTask(Task<?> task) {
|
||||
private CompletableFuture<?> executeTask(Task<?> parentTask, Task<?> task) {
|
||||
return CompletableFuture.completedFuture(null)
|
||||
.thenComposeAsync(unused -> {
|
||||
task.setCancelled(this::isCancelled);
|
||||
task.setState(Task.TaskState.READY);
|
||||
if (parentTask != null && task.getStage() == null)
|
||||
task.setStage(parentTask.getStage());
|
||||
|
||||
if (task.getSignificance().shouldLog())
|
||||
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
|
||||
@@ -137,7 +142,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
})
|
||||
.thenComposeAsync(unused -> executeTasks(task.getDependents()))
|
||||
.thenComposeAsync(unused -> executeTasks(task, task.getDependents()))
|
||||
.thenComposeAsync(dependentsException -> {
|
||||
boolean isDependentsSucceeded = dependentsException == null;
|
||||
|
||||
@@ -158,7 +163,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
rethrow(throwable);
|
||||
});
|
||||
})
|
||||
.thenComposeAsync(unused -> executeTasks(task.getDependencies()))
|
||||
.thenComposeAsync(unused -> executeTasks(task, task.getDependencies()))
|
||||
.thenComposeAsync(dependenciesException -> {
|
||||
boolean isDependenciesSucceeded = dependenciesException == null;
|
||||
|
||||
|
||||
@@ -23,14 +23,7 @@ import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -47,7 +40,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
public TaskExecutor start() {
|
||||
taskListeners.forEach(TaskListener::onStart);
|
||||
workerQueue.add(Schedulers.schedule(scheduler, wrap(() -> {
|
||||
boolean flag = executeTasks(Collections.singleton(firstTask));
|
||||
boolean flag = executeTasks(null, Collections.singleton(firstTask));
|
||||
taskListeners.forEach(it -> it.onStop(flag, this));
|
||||
})));
|
||||
return this;
|
||||
@@ -58,7 +51,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
taskListeners.forEach(TaskListener::onStart);
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Future<?> future = Schedulers.schedule(scheduler, wrap(() -> {
|
||||
flag.set(executeTasks(Collections.singleton(firstTask)));
|
||||
flag.set(executeTasks(null, Collections.singleton(firstTask)));
|
||||
taskListeners.forEach(it -> it.onStop(flag.get(), this));
|
||||
}));
|
||||
workerQueue.add(future);
|
||||
@@ -82,7 +75,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeTasks(Collection<? extends Task<?>> tasks) throws InterruptedException {
|
||||
private boolean executeTasks(Task<?> parentTask, Collection<? extends Task<?>> tasks) throws InterruptedException {
|
||||
if (tasks.isEmpty())
|
||||
return true;
|
||||
|
||||
@@ -92,7 +85,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
for (Task<?> task : tasks) {
|
||||
if (cancelled.get())
|
||||
return false;
|
||||
Invoker invoker = new Invoker(task, latch, success);
|
||||
Invoker invoker = new Invoker(parentTask, task, latch, success);
|
||||
try {
|
||||
Future<?> future = Schedulers.schedule(scheduler, invoker);
|
||||
workerQueue.add(future);
|
||||
@@ -112,7 +105,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
return success.get() && !cancelled.get();
|
||||
}
|
||||
|
||||
private boolean executeTask(Task<?> task) {
|
||||
private boolean executeTask(Task<?> parentTask, Task<?> task) {
|
||||
task.setCancelled(this::isCancelled);
|
||||
|
||||
if (cancelled.get()) {
|
||||
@@ -122,6 +115,8 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
}
|
||||
|
||||
task.setState(Task.TaskState.READY);
|
||||
if (parentTask != null && task.getStage() == null)
|
||||
task.setStage(parentTask.getStage());
|
||||
|
||||
if (task.getSignificance().shouldLog())
|
||||
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
|
||||
@@ -140,8 +135,12 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
}
|
||||
|
||||
Collection<? extends Task<?>> dependents = task.getDependents();
|
||||
boolean doDependentsSucceeded = executeTasks(dependents);
|
||||
Exception dependentsException = dependents.stream().map(Task::getException).filter(Objects::nonNull).findAny().orElse(null);
|
||||
boolean doDependentsSucceeded = executeTasks(task, dependents);
|
||||
Exception dependentsException = dependents.stream().map(Task::getException)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(x -> !(x instanceof CancellationException))
|
||||
.filter(x -> !(x instanceof InterruptedException))
|
||||
.findAny().orElse(null);
|
||||
if (!doDependentsSucceeded && task.isRelyingOnDependents() || cancelled.get()) {
|
||||
task.setException(dependentsException);
|
||||
throw new CancellationException();
|
||||
@@ -163,8 +162,12 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
}
|
||||
|
||||
Collection<? extends Task<?>> dependencies = task.getDependencies();
|
||||
boolean doDependenciesSucceeded = executeTasks(dependencies);
|
||||
Exception dependenciesException = dependencies.stream().map(Task::getException).filter(Objects::nonNull).findAny().orElse(null);
|
||||
boolean doDependenciesSucceeded = executeTasks(task, dependencies);
|
||||
Exception dependenciesException = dependencies.stream().map(Task::getException)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(x -> !(x instanceof CancellationException))
|
||||
.filter(x -> !(x instanceof InterruptedException))
|
||||
.findAny().orElse(null);
|
||||
|
||||
if (doDependenciesSucceeded)
|
||||
task.setDependenciesSucceeded();
|
||||
@@ -250,11 +253,13 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
|
||||
private class Invoker implements Runnable {
|
||||
|
||||
private final Task<?> parentTask;
|
||||
private final Task<?> task;
|
||||
private final CountDownLatch latch;
|
||||
private final AtomicBoolean success;
|
||||
|
||||
public Invoker(Task<?> task, CountDownLatch latch, AtomicBoolean success) {
|
||||
public Invoker(Task<?> parentTask, Task<?> task, CountDownLatch latch, AtomicBoolean success) {
|
||||
this.parentTask = parentTask;
|
||||
this.task = task;
|
||||
this.latch = latch;
|
||||
this.success = success;
|
||||
@@ -264,7 +269,7 @@ public class CancellableTaskExecutor extends TaskExecutor {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.currentThread().setName(task.getName());
|
||||
if (!executeTask(task))
|
||||
if (!executeTask(parentTask, task))
|
||||
success.set(false);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task;
|
||||
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.util.CacheRepository;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||
import org.jackhuang.hmcl.util.io.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -35,10 +33,8 @@ import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -279,7 +275,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
updateProgress(downloaded, contentLength);
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastTime >= 1000) {
|
||||
updateMessage((downloaded - lastDownloaded) / 1024 + "KB/s");
|
||||
updateDownloadSpeed(downloaded - lastDownloaded);
|
||||
lastDownloaded = downloaded;
|
||||
lastTime = now;
|
||||
}
|
||||
@@ -337,4 +333,39 @@ public class FileDownloadTask extends Task<Void> {
|
||||
throw new DownloadException(urls.get(0), exception);
|
||||
}
|
||||
|
||||
private static final Timer timer = new Timer("DownloadSpeedRecorder", true);
|
||||
private static final AtomicInteger downloadSpeed = new AtomicInteger(0);
|
||||
public static final EventBus speedEvent = new EventBus();
|
||||
|
||||
static {
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
speedEvent.fireEvent(new SpeedEvent(speedEvent, downloadSpeed.getAndSet(0)));
|
||||
}
|
||||
}, 0, 1000);
|
||||
}
|
||||
|
||||
private static void updateDownloadSpeed(int speed) {
|
||||
downloadSpeed.addAndGet(speed);
|
||||
}
|
||||
|
||||
public static class SpeedEvent extends Event {
|
||||
private final int speed;
|
||||
|
||||
public SpeedEvent(Object source, int speed) {
|
||||
super(source);
|
||||
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download speed in byte/sec.
|
||||
* @return
|
||||
*/
|
||||
public int getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,25 @@ public abstract class Task<T> {
|
||||
return cancelled != null ? cancelled.get() : false;
|
||||
}
|
||||
|
||||
// stage
|
||||
private String stage = null;
|
||||
|
||||
/**
|
||||
* Stage of task implies the goal of this task, for grouping tasks.
|
||||
* Stage will inherit from the parent task.
|
||||
*/
|
||||
public String getStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* You must initialize stage in preExecute.
|
||||
* @param stage the stage
|
||||
*/
|
||||
public void setStage(String stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
// state
|
||||
private TaskState state = TaskState.READY;
|
||||
|
||||
@@ -344,18 +363,18 @@ public abstract class Task<T> {
|
||||
}
|
||||
|
||||
public final TaskExecutor cancellableExecutor() {
|
||||
return new CancellableTaskExecutor(this);
|
||||
return new AsyncTaskExecutor(this);
|
||||
}
|
||||
|
||||
public final TaskExecutor cancellableExecutor(boolean start) {
|
||||
TaskExecutor executor = new CancellableTaskExecutor(this);
|
||||
TaskExecutor executor = new AsyncTaskExecutor(this);
|
||||
if (start)
|
||||
executor.start();
|
||||
return executor;
|
||||
}
|
||||
|
||||
public final TaskExecutor cancellableExecutor(TaskListener taskListener) {
|
||||
TaskExecutor executor = new CancellableTaskExecutor(this);
|
||||
TaskExecutor executor = new AsyncTaskExecutor(this);
|
||||
executor.addTaskListener(taskListener);
|
||||
return executor;
|
||||
}
|
||||
@@ -743,6 +762,12 @@ public abstract class Task<T> {
|
||||
return whenComplete(executor, () -> success.accept(getResult()), failure);
|
||||
}
|
||||
|
||||
public Task<T> withStage(String stage) {
|
||||
StageTask<T> task = new StageTask<>(this);
|
||||
task.setStage(stage);
|
||||
return task;
|
||||
}
|
||||
|
||||
public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
|
||||
return runAsync(Schedulers.defaultScheduler(), closure);
|
||||
}
|
||||
@@ -760,6 +785,10 @@ public abstract class Task<T> {
|
||||
}
|
||||
|
||||
public static <T> Task<T> composeAsync(ExceptionalSupplier<Task<T>, ?> fn) {
|
||||
return composeAsync(getCaller(), fn);
|
||||
}
|
||||
|
||||
public static <T> Task<T> composeAsync(String name, ExceptionalSupplier<Task<T>, ?> fn) {
|
||||
return new Task<T>() {
|
||||
Task<T> then;
|
||||
|
||||
@@ -774,7 +803,7 @@ public abstract class Task<T> {
|
||||
public Collection<Task<?>> getDependencies() {
|
||||
return then == null ? Collections.emptySet() : Collections.singleton(then);
|
||||
}
|
||||
};
|
||||
}.setName(name);
|
||||
}
|
||||
|
||||
public static <V> Task<V> supplyAsync(Callable<V> callable) {
|
||||
@@ -960,4 +989,21 @@ public abstract class Task<T> {
|
||||
return relyingOnDependents;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StageTask<T> extends Task<T> {
|
||||
private final Task<T> task;
|
||||
StageTask(Task<T> task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
setResult(task.getResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user