Skip to content

fix: Download manager button breaks when one of multiple tasks completes - #3573

Open
sensen0025 wants to merge 1 commit into
PCL-Community:devfrom
sensen0025:fix-multiple-download-manager-bug
Open

fix: Download manager button breaks when one of multiple tasks completes#3573
sensen0025 wants to merge 1 commit into
PCL-Community:devfrom
sensen0025:fix-multiple-download-manager-bug

Conversation

@sensen0025

@sensen0025 sensen0025 commented Aug 28, 2026

Copy link
Copy Markdown

Fixes #3567.

Summary

When multiple download/update tasks are running and one finishes, the original condition \loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading)\ prevented finished tasks from being removed from \loaderTaskbar.
This caused the taskbar manager UI (\PageSpeedLeft) to repeatedly re-create and dispose the UI card on every 300ms Watcher tick, resulting in UI glitches and a broken download manager button.

This fix removes tasks from \loaderTaskbar\ as soon as they are no longer \Loading.

Sourcery 摘要

错误修复:

  • 修复多个下载或更新任务中的某个任务完成时下载管理器按钮和任务栏 UI 的显示问题,及时移除所有非加载中的任务。
Original summary in English

Summary by Sourcery

Bug Fixes:

  • Fix the download manager button and taskbar UI glitches when one of multiple download or update tasks completes by removing every non-loading task promptly.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: XS PR 大小评估:微型 labels Aug 28, 2026
@sourcery-ai

sourcery-ai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor
审查者指南(小型 PR 中折叠显示)

审查者指南

更新任务栏进度刷新逻辑,使每个非加载中的任务在完成或退出后立即被移除,从而避免速度面板 UI 在监视器更新期间反复创建和销毁下载管理器卡片。

进度刷新期间任务栏清理的时序图

sequenceDiagram
    participant Watcher
    participant ModLoader
    participant PageSpeedLeft
    participant Taskbar

    loop Every 300ms watcher tick
        Watcher->>ModLoader: LoaderTaskbarProgressRefresh()
        ModLoader->>Taskbar: Inspect task State
        alt Task.State != Loading
            ModLoader->>PageSpeedLeft: TaskRefresh(Task)
            ModLoader->>Taskbar: Remove(Task)
        end
    end
Loading

文件级变更

变更 详情 文件
任务离开 Loading 状态后,立即将其从任务栏跟踪集合中移除。
  • 将针对整个集合的完成检查替换为针对单个任务的状态检查。
  • 移除任务前,继续刷新对应的速度面板任务 UI。
  • 统一将 Waiting、Aborted 和已完成的任务状态视为可移除状态。
  • 通过移除 UTF-8 BOM 标记来规范化文件头。
Plain Craft Launcher 2/Modules/Base/ModLoader.cs

针对关联 Issue 的评估

Issue 目标 已解决 说明
#3567 修复同时更新多个分类资源时,一个下载任务完成后下载管理按钮或下载管理页面失效、无法正常显示进度的问题。
#3567 在下载任务完成、等待或中止后及时将其从下载任务栏状态列表中移除,避免任务栏管理界面反复重建或销毁导致 UI 异常。

可能关联的 Issue


提示和命令

与 Sourcery 交互

  • 触发新的审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论生成 GitHub Issue: 回复审查评论,请 Sourcery 根据该评论创建 Issue。你也可以使用 @sourcery-ai issue 回复审查评论,以根据该评论创建 Issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title,以随时生成或重新生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary,以随时生成或重新生成摘要。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时生成或重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这一功能会很有用。
  • 忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这一功能尤其有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义使用体验

访问你的仪表板以:

  • 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates taskbar progress refresh to remove each non-loading task as soon as it completes or exits, preventing the speed-panel UI from repeatedly recreating and disposing the download-manager card during watcher updates.

Sequence diagram for taskbar cleanup during progress refresh

sequenceDiagram
    participant Watcher
    participant ModLoader
    participant PageSpeedLeft
    participant Taskbar

    loop Every 300ms watcher tick
        Watcher->>ModLoader: LoaderTaskbarProgressRefresh()
        ModLoader->>Taskbar: Inspect task State
        alt Task.State != Loading
            ModLoader->>PageSpeedLeft: TaskRefresh(Task)
            ModLoader->>Taskbar: Remove(Task)
        end
    end
Loading

File-Level Changes

Change Details Files
Remove every task from the taskbar tracking collection immediately after it leaves the Loading state.
  • Replace the collection-wide completion check with a per-task state check.
  • Continue refreshing the corresponding speed-panel task UI before removal.
  • Treat Waiting, Aborted, and completed task states uniformly as removable.
  • Normalize the file header by removing the UTF-8 BOM marker.
Plain Craft Launcher 2/Modules/Base/ModLoader.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#3567 修复同时更新多个分类资源时,一个下载任务完成后下载管理按钮或下载管理页面失效、无法正常显示进度的问题。
#3567 在下载任务完成、等待或中止后及时将其从下载任务栏状态列表中移除,避免任务栏管理界面反复重建或销毁导致 UI 异常。

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

您好——我发现了 2 个问题

AI Agent 提示词
请处理本次代码审查中的评论:

## 单独评论

### 评论 1
<location path="Plain Craft Launcher 2/Modules/Base/ModLoader.cs" line_range="44" />
<code_context>
             var newProgress = LoaderTaskbarProgressGet();
             // 若单个任务已中止,或全部任务已完成,则刷新并移除
             foreach (var Task in loaderTaskbar)
-                if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
</code_context>
<issue_to_address>
**nitpick:** 该注释表示只有在某个任务中止或所有任务都完成时才会移除任务,但新条件会移除任何不是 `Loading` 状态的单个任务,包括其他任务仍处于活动状态时的 `Waiting``Failed``Finished` 任务。该注释现在错误地描述了方法的行为。

**建议修复:** 更新注释,说明每个非 `Loading` 状态的任务都会被刷新并移除。
</issue_to_address>

### 评论 2
<location path="Plain Craft Launcher 2/Modules/Base/ModLoader.cs" line_range="43-49" />
<code_context>
             var newProgress = LoaderTaskbarProgressGet();
             // 若单个任务已中止,或全部任务已完成,则刷新并移除
             foreach (var Task in loaderTaskbar)
-                if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
-                    Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
+                if (Task.State != ModBase.LoadState.Loading)
                 {
                     ModMain.frmSpeedLeft?.TaskRefresh(Task);
</code_context>
<issue_to_address>
**issue (bug_risk):** `newProgress` 在已完成任务被移除之前根据 `loaderTaskbar` 计算,因此进度更新会包含那些已经不再被跟踪的终止状态任务。当一个已完成任务的进度为 1,而剩余任务的进度较低时,任务栏进度会在本次刷新时被高估,并且平滑状态会根据错误的任务集合进行更新。

**触发条件:** 当一个任务完成时,其他任务栏任务仍在加载且它们的进度不同时。

**建议修复:** 在计算 `LoaderTaskbarProgressGet()` 之前移除非加载状态的任务,或者在移除循环之后重新计算 `newProgress````suggestion
            // 若单个任务已中止,或全部任务已完成,则刷新并移除
            foreach (var Task in loaderTaskbar)
                if (Task.State != ModBase.LoadState.Loading)
                {
                    ModMain.frmSpeedLeft?.TaskRefresh(Task);
                    loaderTaskbar.Remove(Task);
                    ModBase.Log($"[Taskbar] {Task.name} 已移出任务列表");
                }
            var newProgress = LoaderTaskbarProgressGet();
```
</issue_to_address>

Sourcery 对开源项目免费——如果您喜欢我们的审查结果,请考虑分享它们 ✨
帮助我变得更有用!请在每条评论上点击 👍 或 👎,我会利用反馈来改进审查结果。
Original comment in English

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="Plain Craft Launcher 2/Modules/Base/ModLoader.cs" line_range="44" />
<code_context>
             var newProgress = LoaderTaskbarProgressGet();
             // 若单个任务已中止,或全部任务已完成,则刷新并移除
             foreach (var Task in loaderTaskbar)
-                if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
</code_context>
<issue_to_address>
**nitpick:** The comment says tasks are removed only when one task is aborted or all tasks are complete, but the new condition removes any individual task that is not `Loading`, including `Waiting`, `Failed`, and `Finished` tasks while other tasks remain active. The comment now gives an incorrect description of the method's behavior.

**Suggested fix:** Update the comment to state that every non-`Loading` task is refreshed and removed.
</issue_to_address>

### Comment 2
<location path="Plain Craft Launcher 2/Modules/Base/ModLoader.cs" line_range="43-49" />
<code_context>
             var newProgress = LoaderTaskbarProgressGet();
             // 若单个任务已中止,或全部任务已完成,则刷新并移除
             foreach (var Task in loaderTaskbar)
-                if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
-                    Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
+                if (Task.State != ModBase.LoadState.Loading)
                 {
                     ModMain.frmSpeedLeft?.TaskRefresh(Task);
</code_context>
<issue_to_address>
**issue (bug_risk):** `newProgress` is calculated from `loaderTaskbar` before completed tasks are removed, so the progress update includes terminal tasks that are no longer tracked. When a completed task has progress 1 and a remaining task has lower progress, the taskbar progress is overstated for the refresh tick and the smoothing state is updated from the wrong task set.

**Triggers:** When one task completes while other taskbar tasks are still loading and their progress differs.

**Suggested fix:** Remove non-loading tasks before calculating `LoaderTaskbarProgressGet()`, or recalculate `newProgress` after the removal loop.

```suggestion
            // 若单个任务已中止,或全部任务已完成,则刷新并移除
            foreach (var Task in loaderTaskbar)
                if (Task.State != ModBase.LoadState.Loading)
                {
                    ModMain.frmSpeedLeft?.TaskRefresh(Task);
                    loaderTaskbar.Remove(Task);
                    ModBase.Log($"[Taskbar] {Task.name} 已移出任务列表");
                }
            var newProgress = LoaderTaskbarProgressGet();
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -43,8 +43,7 @@ public static void LoaderTaskbarProgressRefresh()
var newProgress = LoaderTaskbarProgressGet();
// 若单个任务已中止,或全部任务已完成,则刷新并移除

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: 该注释表示只有在某个任务中止或所有任务都完成时才会移除任务,但新条件会移除任何不是 Loading 状态的单个任务,包括其他任务仍处于活动状态时的 WaitingFailedFinished 任务。该注释现在错误地描述了方法的行为。

建议修复: 更新注释,说明每个非 Loading 状态的任务都会被刷新并移除。

Original comment in English

nitpick: The comment says tasks are removed only when one task is aborted or all tasks are complete, but the new condition removes any individual task that is not Loading, including Waiting, Failed, and Finished tasks while other tasks remain active. The comment now gives an incorrect description of the method's behavior.

Suggested fix: Update the comment to state that every non-Loading task is refreshed and removed.

Comment on lines 43 to 49
var newProgress = LoaderTaskbarProgressGet();
// 若单个任务已中止,或全部任务已完成,则刷新并移除
foreach (var Task in loaderTaskbar)
if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
if (Task.State != ModBase.LoadState.Loading)
{
ModMain.frmSpeedLeft?.TaskRefresh(Task);
loaderTaskbar.Remove(Task);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): newProgress 在已完成任务被移除之前根据 loaderTaskbar 计算,因此进度更新会包含那些已经不再被跟踪的终止状态任务。当一个已完成任务的进度为 1,而剩余任务的进度较低时,任务栏进度会在本次刷新时被高估,并且平滑状态会根据错误的任务集合进行更新。

触发条件: 当一个任务完成时,其他任务栏任务仍在加载且它们的进度不同时。

建议修复: 在计算 LoaderTaskbarProgressGet() 之前移除非加载状态的任务,或者在移除循环之后重新计算 newProgress

Suggested change
var newProgress = LoaderTaskbarProgressGet();
// 若单个任务已中止,或全部任务已完成,则刷新并移除
foreach (var Task in loaderTaskbar)
if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
if (Task.State != ModBase.LoadState.Loading)
{
ModMain.frmSpeedLeft?.TaskRefresh(Task);
loaderTaskbar.Remove(Task);
// 若单个任务已中止,或全部任务已完成,则刷新并移除
foreach (var Task in loaderTaskbar)
if (Task.State != ModBase.LoadState.Loading)
{
ModMain.frmSpeedLeft?.TaskRefresh(Task);
loaderTaskbar.Remove(Task);
ModBase.Log($"[Taskbar] {Task.name} 已移出任务列表");
}
var newProgress = LoaderTaskbarProgressGet();
Original comment in English

issue (bug_risk): newProgress is calculated from loaderTaskbar before completed tasks are removed, so the progress update includes terminal tasks that are no longer tracked. When a completed task has progress 1 and a remaining task has lower progress, the taskbar progress is overstated for the refresh tick and the smoothing state is updated from the wrong task set.

Triggers: When one task completes while other taskbar tasks are still loading and their progress differs.

Suggested fix: Remove non-loading tasks before calculating LoaderTaskbarProgressGet(), or recalculate newProgress after the removal loop.

Suggested change
var newProgress = LoaderTaskbarProgressGet();
// 若单个任务已中止,或全部任务已完成,则刷新并移除
foreach (var Task in loaderTaskbar)
if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
if (Task.State != ModBase.LoadState.Loading)
{
ModMain.frmSpeedLeft?.TaskRefresh(Task);
loaderTaskbar.Remove(Task);
// 若单个任务已中止,或全部任务已完成,则刷新并移除
foreach (var Task in loaderTaskbar)
if (Task.State != ModBase.LoadState.Loading)
{
ModMain.frmSpeedLeft?.TaskRefresh(Task);
loaderTaskbar.Remove(Task);
ModBase.Log($"[Taskbar] {Task.name} 已移出任务列表");
}
var newProgress = LoaderTaskbarProgressGet();

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c169238ca

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

foreach (var Task in loaderTaskbar)
if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
if (Task.State != ModBase.LoadState.Loading)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve failed tasks while other tasks are running

When one task fails while another remains loading and the task-manager page has not yet been opened, this condition removes the failed task immediately and frmSpeedLeft?.TaskRefresh(Task) is a no-op because that page is created lazily. Opening the manager through the still-visible download button then shows only the remaining task, so the failed task's error details cannot be inspected or copied. Previously, failed tasks remained in loaderTaskbar until no task was loading; restrict the new early-removal behavior to successfully finished tasks while retaining the existing handling for failures.

Useful? React with 👍 / 👎.

@LuLu-ling LuLu-ling left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请严格遵守 技术规范 重新修整你的 PR:

  1. PR 中的所有 Commit 必须要附带有效的、已验证的签名。参考 提交信息规范-提交签名要求 章节与 GPG 签名配置指南
  2. 若原文件使用不同换行符或编码,不应为了统一格式而产生大量无关 diff,以免与其他分支中的修改产生冲突。

@LuLu-ling LuLu-ling changed the title Fix Issue #3567: Download manager button breaks when one of multiple tasks completes fix: Download manager button breaks when one of multiple tasks completes Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XS PR 大小评估:微型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

同时更新两个分类的资源会导致下载打不开

2 participants