add: show asset and mod download progress

This commit is contained in:
huanghongxun 2020-03-03 00:59:15 +08:00
parent 4f51ab00a1
commit 3efec78732
8 changed files with 109 additions and 12 deletions

View File

@ -57,6 +57,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class TaskListPane extends StackPane {
@ -172,12 +173,24 @@ public final class TaskListPane extends StackPane {
node.setThrowable(throwable);
});
}
@Override
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
stageProperties.forEach((stage, properties) -> {
int count = tryCast(properties.get("count"), Integer.class).orElse(0),
total = tryCast(properties.get("total"), Integer.class).orElse(0);
if (total > 0)
Platform.runLater(() ->
stageNodes.stream().filter(x -> x.stage.equals(stage)).findAny().ifPresent(stageNode -> stageNode.updateCounter(count, total)));
});
}
});
}
private class StageNode extends BorderPane {
private static class StageNode extends BorderPane {
private final String stage;
private final Label title = new Label();
private final String message;
private boolean started = false;
public StageNode(String stage) {
@ -185,7 +198,6 @@ public final class TaskListPane extends StackPane {
String stageKey = StringUtils.substringBefore(stage, ':');
String stageValue = StringUtils.substringAfter(stage, ':');
String message;
// @formatter:off
switch (stageKey) {
@ -222,6 +234,13 @@ public final class TaskListPane extends StackPane {
public void succeed() {
setLeft(FXUtils.limitingSize(SVG.check(Theme.blackFillBinding(), 14, 14), 14, 14));
}
public void updateCounter(int count, int total) {
if (total > 0)
title.setText(String.format("%s - %d/%d", message, count, total));
else
title.setText(message);
}
}
private class ProgressListNode extends BorderPane {

View File

@ -78,6 +78,7 @@ public class VersionPage extends Control implements DecoratorPage {
Profiles.registerVersionsListener(this::loadVersions);
listView.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
if (newValue != null)
loadVersion(newValue, profile);
});

View File

@ -123,13 +123,17 @@ public final class GameAssetDownloadTask extends Task<Void> {
.setCacheRepository(dependencyManager.getCacheRepository())
.setCaching(true)
.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory()
.resolve("assets").resolve("objects").resolve(assetObject.getLocation())));
.resolve("assets").resolve("objects").resolve(assetObject.getLocation())).withCounter());
} else {
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
}
updateProgress(++progress, index.getObjects().size());
}
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
}
}
public static final boolean DOWNLOAD_INDEX_FORCIBLY = true;

View File

@ -151,9 +151,13 @@ public final class CurseCompletionTask extends Task<Void> {
if (!modManager.hasSimpleMod(file.getFileName())) {
dependencies.add(new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile())
.setCacheRepository(dependency.getCacheRepository())
.setCaching(true));
.setCaching(true).withCounter());
}
}
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
}
}
@Override

View File

@ -20,11 +20,10 @@ package org.jackhuang.hmcl.task;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import java.util.logging.Level;
public class CancellableTaskExecutor extends TaskExecutor {
@ -105,6 +104,19 @@ public class CancellableTaskExecutor extends TaskExecutor {
return success.get() && !cancelled.get();
}
private synchronized void updateStageProperties(String stage, Map<String, Object> taskProperties) {
stageProperties.putIfAbsent(stage, new HashMap<>());
Map<String, Object> prop = stageProperties.get(stage);
for (Map.Entry<String, Object> entry : taskProperties.entrySet()) {
if (entry.getValue() instanceof UnaryOperator) {
prop.put(entry.getKey(), ((UnaryOperator) entry.getValue()).apply(prop.get(entry.getKey())));
} else {
prop.put(entry.getKey(), entry.getValue());
}
}
taskListeners.forEach(taskListener -> taskListener.onPropertiesUpdate(stageProperties));
}
private boolean executeTask(Task<?> parentTask, Task<?> task) {
task.setCancelled(this::isCancelled);
@ -161,6 +173,10 @@ public class CancellableTaskExecutor extends TaskExecutor {
task.setState(Task.TaskState.EXECUTED);
}
if (task.properties != null) {
updateStageProperties(task.getStage(), task.properties);
}
Collection<? extends Task<?>> dependencies = task.getDependencies();
boolean doDependenciesSucceeded = executeTasks(task, dependencies);
Exception dependenciesException = dependencies.stream().map(Task::getException)
@ -191,6 +207,10 @@ public class CancellableTaskExecutor extends TaskExecutor {
Logging.LOG.log(Level.FINER, "Task finished: " + task.getName());
}
if (task.properties != null) {
updateStageProperties(task.getStage(), task.properties);
}
task.onDone().fireEvent(new TaskEvent(this, task, false));
taskListeners.forEach(it -> it.onFinished(task));
} catch (RejectedExecutionException e) {

View File

@ -32,15 +32,13 @@ import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -107,6 +105,13 @@ public abstract class Task<T> {
return getStage() == null ? Collections.emptyList() : Collections.singletonList(getStage());
}
Map<String, Object> properties;
protected Map<String, Object> getProperties() {
if (properties == null) properties = new HashMap<>();
return properties;
}
// state
private TaskState state = TaskState.READY;
@ -802,6 +807,10 @@ public abstract class Task<T> {
};
}
public Task<T> withCounter() {
return new CountTask();
}
public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
return runAsync(Schedulers.defaultScheduler(), closure);
}
@ -1063,4 +1072,37 @@ public abstract class Task<T> {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
private class CountTask extends Task<T> {
private final UnaryOperator<Integer> COUNTER = a -> {
int result = 0;
if (a != null) result += a;
return result + 1;
};
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public void execute() throws Exception {
setResult(Task.this.getResult());
}
@Override
public boolean doPostExecute() {
return true;
}
@Override
public void postExecute() {
getProperties().put("count", COUNTER);
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
}

View File

@ -19,8 +19,10 @@ package org.jackhuang.hmcl.task;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -31,6 +33,7 @@ public abstract class TaskExecutor {
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
protected Exception exception;
private final List<String> stages;
protected final Map<String, Map<String, Object>> stageProperties = new HashMap<>();
public TaskExecutor(Task<?> task) {
this.firstTask = task;

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.task;
import java.util.EventListener;
import java.util.Map;
/**
*
@ -78,4 +79,7 @@ public abstract class TaskListener implements EventListener {
*/
public void onStop(boolean success, TaskExecutor executor) {
}
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
}
}