From bcefa87e2705ebe8636fa9a7a9df41d3e3402fae Mon Sep 17 00:00:00 2001 From: Layue13 Date: Wed, 11 Nov 2026 12:36:56 +0800 Subject: [PATCH] fix: base home sync progress on announced download total The percentage used finished/seen task counts, which hit the 99% cap as soon as the first tasks finished and then stayed there while new tasks were still being discovered. Use the "total" property announced by the sync task as the denominator and count finished file downloads; stay indeterminate until known. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01TiBPbVGBHNHdzqfq1L5tch --- .../hmcl/ui/instances/ClientSyncProgress.java | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ClientSyncProgress.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ClientSyncProgress.java index 156ee2b5b2..635587faf8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ClientSyncProgress.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ClientSyncProgress.java @@ -4,6 +4,7 @@ package org.jackhuang.hmcl.ui.instances; import javafx.beans.property.*; import org.jackhuang.hmcl.task.FetchTask; +import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskListener; @@ -19,8 +20,7 @@ import java.util.function.Consumer; /// /// The model is only a view of the task executor started by [Instances#ensureClientAndJoin(ClientSyncProgress)]: /// it never changes what is downloaded. All properties must be read and written on the JavaFX thread. -/// Overall percentage is estimated from the number of finished tasks, so it never moves backwards but -/// may advance unevenly while new tasks are discovered. +/// Overall percentage is estimated from finished file downloads against the total announced by the sync task, and stays indeterminate until that total is known. @NotNullByDefault public final class ClientSyncProgress { @@ -53,8 +53,12 @@ public final class ClientSyncProgress { private @Nullable TaskExecutor executor; private long downloadedBytes; - private int seenTasks; - private int finishedTasks; + /// Total number of downloads announced by tasks so far. + private int expectedFiles; + /// Number of finished file downloads. + private int finishedFiles; + /// Tasks whose `total` has already been added to [#expectedFiles]. + private final java.util.Set> announcedTasks = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>()); private boolean cancelled; /// Keeps the weakly registered speed listener alive. @@ -101,8 +105,9 @@ public final class ClientSyncProgress { executor = null; cancelled = false; downloadedBytes = 0; - seenTasks = 0; - finishedTasks = 0; + expectedFiles = 0; + finishedFiles = 0; + announcedTasks.clear(); progress.set(-1); detail.set(""); error.set(""); @@ -149,6 +154,15 @@ public final class ClientSyncProgress { return cancelled; } + /// Recomputes the fraction from finished downloads; stays indeterminate until a total is known. + private void refreshProgress(TaskExecutor current) { + if (executor != current || expectedFiles <= 0) + return; + double value = Math.min(0.99, (double) finishedFiles / expectedFiles); + if (value > progress.get()) + progress.set(value); + } + /// Submits `task` to the serial task queue and mirrors its task count into the progress. /// /// @param task the task to run @@ -156,21 +170,28 @@ public final class ClientSyncProgress { TaskExecutor taskExecutor = task.executor(); executor = taskExecutor; taskExecutor.addTaskListener(new TaskListener() { + /// Tasks announce how many downloads they will start through the `total` property + /// (set once the file list is known), so the denominator never grows with discovered tasks. @Override - public void onReady(Task ready) { - FXUtils.runInFX(() -> seenTasks++); + public void onPropertiesUpdate(Task updated) { + Object total = updated.getProperties().get("total"); + if (total instanceof Integer count && count > 0) { + FXUtils.runInFX(() -> { + if (announcedTasks.add(updated)) + expectedFiles += count; + refreshProgress(taskExecutor); + }); + } } @Override public void onFinished(Task finishedTask) { - FXUtils.runInFX(() -> { - finishedTasks++; - if (executor != taskExecutor || seenTasks <= 0) - return; - double value = Math.min(0.99, (double) finishedTasks / seenTasks); - if (value > progress.get()) - progress.set(value); - }); + if (finishedTask instanceof FileDownloadTask) { + FXUtils.runInFX(() -> { + finishedFiles++; + refreshProgress(taskExecutor); + }); + } } }); TaskManager.getInstance().submit(taskExecutor, null);