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.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class TaskListPane extends StackPane { public final class TaskListPane extends StackPane {
@ -172,12 +173,24 @@ public final class TaskListPane extends StackPane {
node.setThrowable(throwable); 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 String stage;
private final Label title = new Label(); private final Label title = new Label();
private final String message;
private boolean started = false; private boolean started = false;
public StageNode(String stage) { public StageNode(String stage) {
@ -185,7 +198,6 @@ public final class TaskListPane extends StackPane {
String stageKey = StringUtils.substringBefore(stage, ':'); String stageKey = StringUtils.substringBefore(stage, ':');
String stageValue = StringUtils.substringAfter(stage, ':'); String stageValue = StringUtils.substringAfter(stage, ':');
String message;
// @formatter:off // @formatter:off
switch (stageKey) { switch (stageKey) {
@ -222,6 +234,13 @@ public final class TaskListPane extends StackPane {
public void succeed() { public void succeed() {
setLeft(FXUtils.limitingSize(SVG.check(Theme.blackFillBinding(), 14, 14), 14, 14)); 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 { private class ProgressListNode extends BorderPane {

View File

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

View File

@ -123,13 +123,17 @@ public final class GameAssetDownloadTask extends Task<Void> {
.setCacheRepository(dependencyManager.getCacheRepository()) .setCacheRepository(dependencyManager.getCacheRepository())
.setCaching(true) .setCaching(true)
.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory() .setCandidate(dependencyManager.getCacheRepository().getCommonDirectory()
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()))); .resolve("assets").resolve("objects").resolve(assetObject.getLocation())).withCounter());
} else { } else {
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash()); dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
} }
updateProgress(++progress, index.getObjects().size()); updateProgress(++progress, index.getObjects().size());
} }
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
}
} }
public static final boolean DOWNLOAD_INDEX_FORCIBLY = true; 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())) { if (!modManager.hasSimpleMod(file.getFileName())) {
dependencies.add(new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile()) dependencies.add(new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile())
.setCacheRepository(dependency.getCacheRepository()) .setCacheRepository(dependency.getCacheRepository())
.setCaching(true)); .setCaching(true).withCounter());
} }
} }
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
}
} }
@Override @Override

View File

@ -20,11 +20,10 @@ package org.jackhuang.hmcl.task;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.function.ExceptionalRunnable; import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
import java.util.Collection; import java.util.*;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import java.util.logging.Level; import java.util.logging.Level;
public class CancellableTaskExecutor extends TaskExecutor { public class CancellableTaskExecutor extends TaskExecutor {
@ -105,6 +104,19 @@ public class CancellableTaskExecutor extends TaskExecutor {
return success.get() && !cancelled.get(); 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) { private boolean executeTask(Task<?> parentTask, Task<?> task) {
task.setCancelled(this::isCancelled); task.setCancelled(this::isCancelled);
@ -161,6 +173,10 @@ public class CancellableTaskExecutor extends TaskExecutor {
task.setState(Task.TaskState.EXECUTED); task.setState(Task.TaskState.EXECUTED);
} }
if (task.properties != null) {
updateStageProperties(task.getStage(), task.properties);
}
Collection<? extends Task<?>> dependencies = task.getDependencies(); Collection<? extends Task<?>> dependencies = task.getDependencies();
boolean doDependenciesSucceeded = executeTasks(task, dependencies); boolean doDependenciesSucceeded = executeTasks(task, dependencies);
Exception dependenciesException = dependencies.stream().map(Task::getException) 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()); 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)); task.onDone().fireEvent(new TaskEvent(this, task, false));
taskListeners.forEach(it -> it.onFinished(task)); taskListeners.forEach(it -> it.onFinished(task));
} catch (RejectedExecutionException e) { } 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.ExceptionalRunnable;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.util.Arrays; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -107,6 +105,13 @@ public abstract class Task<T> {
return getStage() == null ? Collections.emptyList() : Collections.singletonList(getStage()); 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 // state
private TaskState state = TaskState.READY; 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) { public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
return runAsync(Schedulers.defaultScheduler(), closure); return runAsync(Schedulers.defaultScheduler(), closure);
} }
@ -1063,4 +1072,37 @@ public abstract class Task<T> {
return Lang.merge(Task.this.getStages(), super.getStages()); 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 org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -31,6 +33,7 @@ public abstract class TaskExecutor {
protected final AtomicBoolean cancelled = new AtomicBoolean(false); protected final AtomicBoolean cancelled = new AtomicBoolean(false);
protected Exception exception; protected Exception exception;
private final List<String> stages; private final List<String> stages;
protected final Map<String, Map<String, Object>> stageProperties = new HashMap<>();
public TaskExecutor(Task<?> task) { public TaskExecutor(Task<?> task) {
this.firstTask = task; this.firstTask = task;

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.task; package org.jackhuang.hmcl.task;
import java.util.EventListener; 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 onStop(boolean success, TaskExecutor executor) {
} }
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
}
} }