Skip to content

fix: 尝试修复实例下载的各种问题#2827

Open
LuLu-ling wants to merge 16 commits into
devfrom
fix/2795
Open

fix: 尝试修复实例下载的各种问题#2827
LuLu-ling wants to merge 16 commits into
devfrom
fix/2795

Conversation

@LuLu-ling
Copy link
Copy Markdown
Member

@LuLu-ling LuLu-ling commented May 14, 2026

close #2795
close #2830

Sourcery 总结

通过使文件写入操作具备原子性、收紧读访问权限,以及添加就绪检查和重试机制,提高实例文件下载和 JSON 处理的可靠性。

错误修复:

  • 在处理之前等待实例 JSON 和相关文件就绪,防止读取部分写入的文件。
  • 通过先写入临时文件再移动到最终路径,确保文本文件写入是原子性的。
  • 在以读模式打开文件时禁止共享写访问,减少并发文件操作之间的干扰。
  • 加强下载器的完成阶段,确保临时下载文件被正确最终化,并在未生成输出文件时失败。
  • 在跨线程加载可能为空或无效的实例 JSON 文件时,统一并强化重试行为。

改进:

  • 新增可复用的文件就绪辅助方法,在使用前轮询文件是否为非空(并可选地校验 JSON 合法性)。
Original summary in English

Summary by Sourcery

通过使文件操作更加健壮和同步,提高实例下载和 JSON 处理的可靠性。

Bug 修复:

  • 通过收紧文件读取共享权限并在访问前添加就绪检查,防止读取到仅部分写入的文件。
  • 通过将临时下载产物重命名为最终路径,并在未生成输出文件时触发失败,确保下载的文件被正确完成。
  • 在跨线程加载可能为空或无效的实例 JSON 文件时,统一并强化重试行为。

增强:

  • 通过先写入临时文件再移动到最终路径,使文本文件写入具备原子性。
  • 引入可复用的辅助工具,在使用前等待文件变为非空(可选地检查为有效 JSON),并将其应用到多个下载和安装器的代码路径中。
Original summary in English

Summary by Sourcery

Improve reliability of instance downloads and JSON handling by making file operations more robust and synchronized.

Bug Fixes:

  • Prevent reading partially written files by tightening file read sharing and adding readiness checks before access.
  • Ensure downloaded files are correctly finalized by renaming temporary download artifacts to their final paths and failing when no output file is produced.
  • Unify and strengthen retry behavior when loading potentially empty or invalid instance JSON files across threads.

Enhancements:

  • Make text file writes atomic by writing to a temporary file and then moving it to the final path.
  • Introduce a reusable helper to wait for files to become non-empty (optionally valid JSON) before use, and apply it in multiple download and installer code paths.

Bug 修复:

  • 通过在处理之前等待 JSON 或其他文件完全写入,防止读取仍在写入中的文件。
  • 通过先写入临时文件再将其移动到目标位置,确保文本文件写入是原子性的。
  • 收紧文件读取时的共享策略,在加载文件时减少并发写入带来的干扰。
  • 加强下载器对已下载文件的收尾和校验,包括重命名临时下载文件,以及在未生成文件时主动失败。

增强:

  • 统一实例 JSON 的重试逻辑,使其在各个线程中对无效或空内容进行一致的重试。
Original summary in English

Sourcery 总结

通过使文件写入操作具备原子性、收紧读访问权限,以及添加就绪检查和重试机制,提高实例文件下载和 JSON 处理的可靠性。

错误修复:

  • 在处理之前等待实例 JSON 和相关文件就绪,防止读取部分写入的文件。
  • 通过先写入临时文件再移动到最终路径,确保文本文件写入是原子性的。
  • 在以读模式打开文件时禁止共享写访问,减少并发文件操作之间的干扰。
  • 加强下载器的完成阶段,确保临时下载文件被正确最终化,并在未生成输出文件时失败。
  • 在跨线程加载可能为空或无效的实例 JSON 文件时,统一并强化重试行为。

改进:

  • 新增可复用的文件就绪辅助方法,在使用前轮询文件是否为非空(并可选地校验 JSON 合法性)。
Original summary in English

Summary by Sourcery

通过使文件操作更加健壮和同步,提高实例下载和 JSON 处理的可靠性。

Bug 修复:

  • 通过收紧文件读取共享权限并在访问前添加就绪检查,防止读取到仅部分写入的文件。
  • 通过将临时下载产物重命名为最终路径,并在未生成输出文件时触发失败,确保下载的文件被正确完成。
  • 在跨线程加载可能为空或无效的实例 JSON 文件时,统一并强化重试行为。

增强:

  • 通过先写入临时文件再移动到最终路径,使文本文件写入具备原子性。
  • 引入可复用的辅助工具,在使用前等待文件变为非空(可选地检查为有效 JSON),并将其应用到多个下载和安装器的代码路径中。
Original summary in English

Summary by Sourcery

Improve reliability of instance downloads and JSON handling by making file operations more robust and synchronized.

Bug Fixes:

  • Prevent reading partially written files by tightening file read sharing and adding readiness checks before access.
  • Ensure downloaded files are correctly finalized by renaming temporary download artifacts to their final paths and failing when no output file is produced.
  • Unify and strengthen retry behavior when loading potentially empty or invalid instance JSON files across threads.

Enhancements:

  • Make text file writes atomic by writing to a temporary file and then moving it to the final path.
  • Introduce a reusable helper to wait for files to become non-empty (optionally valid JSON) before use, and apply it in multiple download and installer code paths.

Summary by Sourcery

改进实例下载、文件 I/O 和 JSON 处理的可靠性与健壮性,避免部分写入的文件、竞争条件以及不完整的下载。

Bug 修复:

  • 确保下载器的完成状态被正确尊重,将临时下载文件最终写入其目标路径,并在未生成输出文件时明确失败。
  • 收紧文件读取时的共享策略,禁止在读取期间进行并发写入,降低读取到部分写入文件的概率。
  • 让下载加载器的信号量处理在遇到异常时依然健壮,防止在早期失败或取消时泄漏许可。
  • 统一并强化在多线程环境下加载可能为空或无效的实例 JSON 文件时的重试行为。

增强功能:

  • 通过先写入到临时文件再移动到目标路径的方式,使文本文件写入具备原子性。
  • 添加可复用的辅助工具,在使用文件前等待其变为非空(可选地验证为有效 JSON),并在多个下载和安装器代码路径中复用。
  • 将用于简单文本/JSON 响应的 HTTP 内容获取切换为更高层级的 Requester API,并为每个 URL 提供回退机制与日志记录。
  • 在临时下载和清理流程中改进行为,在出现瞬时 I/O 错误时对文件删除和移动进行有界重试。
  • 通过更健壮地从可视化树中解析关联的文件对象,简化 UI 中“保存”点击的处理逻辑。
Original summary in English

Summary by Sourcery

Improve reliability and robustness of instance downloads, file I/O, and JSON handling to avoid partially written files, race conditions, and incomplete downloads.

Bug Fixes:

  • Ensure downloader completion status is respected, finalize temporary download files into their final paths, and fail when no output file is produced.
  • Tighten file read sharing to disallow concurrent writes while reading, reducing the chance of reading partially written files.
  • Make the download loader’s semaphore handling resilient to exceptions so permits are not leaked on early failures or cancellations.
  • Unify and strengthen retry behavior when loading potentially empty or invalid instance JSON files across threads.

Enhancements:

  • Make text file writes atomic by writing to a temporary file and then moving it to the target path.
  • Add a reusable helper to wait for files to become non-empty (optionally valid JSON) before use, and apply it across multiple download and installer code paths.
  • Switch HTTP content fetching for simple text/JSON responses to a higher-level Requester API with per-URL fallback and logging.
  • Improve temporary download and cleanup behavior with bounded retries on file deletion and moves under transient I/O errors.
  • Simplify UI save-click handling by more robustly resolving the associated file object from the visual tree.

@pcl-ce-automation pcl-ce-automation Bot added 🚧 正在处理 开发人员正在对该内容进行开发、测试或修复,进展中 size: L PR 大小评估:大型 labels May 14, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 14, 2026

审阅者指南

通过收紧文件访问语义、使写入操作具备原子性,并在关键文件操作周围增加就绪检查与重试逻辑(外加少量重构),提升实例下载和 JSON 处理的健壮性。

新的下载收尾与临时文件清理流程时序图

sequenceDiagram
    participant LoaderDownload as LoaderDownload
    participant FileDownloader as FileDownloader
    participant DownloadService as DownloadService
    participant FileSystem as FileSystem

    LoaderDownload->>FileDownloader: Download(url, localPath, ...)
    activate FileDownloader
    FileDownloader->>DownloadService: new DownloadService(configuration)
    FileDownloader->>DownloadService: DownloadFileTaskAsync(url, localPath, cancellationToken)
    activate DownloadService

    DownloadService-->>FileDownloader: DownloadFileCompleted(args)
    deactivate DownloadService
    FileDownloader->>FileDownloader: tcs.TrySetCanceled/Exception/Result(args)

    FileDownloader->>FileDownloader: await tcs.Task

    FileDownloader->>FileSystem: File.Exists(localPath)
    alt final file missing
        FileDownloader->>FileSystem: File.Exists(tempPath)
        opt temp file exists
            loop retry < 5
                FileDownloader->>FileSystem: File.Move(tempPath, localPath, true)
                FileSystem-->>FileDownloader: IOException?
            end
        end
    end

    FileDownloader->>FileSystem: File.Exists(localPath)
    alt no output file
        FileDownloader->>FileDownloader: throw IOException
    else success
        FileDownloader->>FileDownloader: ModBase.Log("[Download] 下载成功")
    end
    deactivate FileDownloader

    note over FileDownloader,FileSystem: CleanupTempFiles uses TryDeleteFile with retries
Loading

文件级变更

变更 详情 文件
强化文件下载完成与清理语义,确保最终文件一定生成,否则操作会以明确失败结束。
  • 将下载器中 HTTP 客户端的创建切换为通过 CustomHttpClientFactory 使用 NetworkService.GetClient(),并设置额外的下载配置选项,例如 BlockTimeout 与 MinimumSizeOfChunking。
  • 用 using 块包装 DownloadService,并挂载 DownloadFileCompleted 事件以更新跟踪统计信息,同时通过 TaskCompletionSource 将成功、取消或错误状态回传。
  • 在下载任务完成后,验证最终文件是否存在;如果仅存在临时文件,则多次重试将其重命名为最终路径,若最终仍不存在目标文件则抛出异常。
  • 将临时文件清理逻辑改为带重试的 TryDeleteFile 辅助方法,多次尝试删除以容忍瞬时 IO 错误。
Plain Craft Launcher 2/Modules/Network/Downloader/FileDownloader.cs
使文本文件写入具备原子性,并限制读时的共享方式,以避免读取到部分写入的内容。
  • 将 ReadFileBytes 改为以 FileShare.Read 而非 FileShare.ReadWrite 打开文件,从而禁止在读取时存在并发写入。
  • 更新 WriteFile:在非追加模式下,先将文本以字节形式写入唯一的临时路径,然后再以原子方式移动覆盖到目标路径上。
Plain Craft Launcher 2/Modules/Base/ModBase.cs
引入并使用可复用的辅助方法,在使用文件之前等待其就绪(可选地验证 JSON)。
  • 新增 WaitForFileReady 辅助方法,在给定超时时间内轮询文件是否存在且内容非空,可选地验证类似 JSON 的内容,在处理 IOExceptions 时于重试间隙进行休眠。
  • 用 WaitForFileReady 替换先前在读取实例 JSON 和安装器文件前基于 Thread.Sleep 的等待逻辑,覆盖多个下载与安装路径,包括原版库、资源、LabyMod 以及类 Forge 安装器的处理。
Plain Craft Launcher 2/Modules/Base/ModBase.cs
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.cs
调整网络代码路径,对于简单文本响应改用带每 URL 重试的高层 Fetch,而非基于临时文件的下载。
  • 修改接受多个 URL 的 NetGetCodeByLoader 重载,使其遍历各个 URL,使用带可配置超时与 User-Agent 的 Requester.Fetch,按需求返回 JSON 解析结果或原始内容。
  • 对每个失败的 URL 以调试级别记录日志;如果所有 URL 均失败,则抛出包装最后一个错误的异常。
Plain Craft Launcher 2/Modules/Network/Facade/ModNet.cs
在加载可能无效或不完整的实例 JSON 时统一并强化重试行为。
  • 当实例 JSON 为空或无效时,总是先短暂休眠(UI 线程上时间更短,后台线程上更长),然后重新读取文件;如果快速检查仍失败,则强制让 JSON 解析抛出异常,而不是在 UI 线程上不重试就直接放弃。
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.cs
提升下载器协调逻辑以及少量 UI/文件选择逻辑的并发安全性和健壮性。
  • 确保 LoaderDownload.Run 中的信号量仅在成功获取后才会被释放,防止在 WaitAsync 完成前发生取消时的过度释放。
  • 微调 PageDownloadCompDetail 中的 Save_Click,通过向上查找最多两个 FrameworkElement 父元素的方式,更稳健地定位关联的 ModComp.CompFile。
  • 在从 CurseForge 整合包读取模组加载器 ID 时显式将 Id 声明为 string,以进行小幅类型安全改进。
Plain Craft Launcher 2/Modules/Network/Loaders/LoaderDownload.cs
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.cs
Plain Craft Launcher 2/Modules/Minecraft/ModModpack.cs

与关联 Issue 的对照评估

Issue 目标 是否解决 说明
#2795 确保在 C# 版本中下载游戏/加载器文件(包括 Fabric/Forge/NeoForge)时,可靠地产生完整的本地文件,而不是只生成部分/临时文件并导致失败。
#2795 确保后续安装步骤(如读取实例 JSON 和安装器归档文件)不再因与文件写入存在竞态、或在游戏安装过程中读取到空/无效 JSON 而失败。
#2830 减少或消除在下载大量文件时严重的 UI 卡顿(确保下载过程不会阻塞 UI 线程,或在批量下载期间造成明显卡顿)。 本 PR 重点在于提升实例/文件下载与 JSON 处理的可靠性与正确性(原子写入、文件就绪性检查、更严格的文件共享策略、更明确的完成/失败行为以及重试逻辑)。它并未改变 UI 线程的使用方式,也没有将同步操作迁移到非 UI 线程上,亦未引入显式机制来防止在大量文件下载时阻塞 UI。整体变更偏向后端与 I/O 可靠性增强,而不是针对性地优化 UI 响应性,因此对于批量下载时 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

Improves robustness of instance downloads and JSON handling by tightening file access semantics, making writes atomic, and adding readiness and retry logic around key file operations, plus a few small refactors.

Sequence diagram for the new download finalization and temp file cleanup

sequenceDiagram
    participant LoaderDownload as LoaderDownload
    participant FileDownloader as FileDownloader
    participant DownloadService as DownloadService
    participant FileSystem as FileSystem

    LoaderDownload->>FileDownloader: Download(url, localPath, ...)
    activate FileDownloader
    FileDownloader->>DownloadService: new DownloadService(configuration)
    FileDownloader->>DownloadService: DownloadFileTaskAsync(url, localPath, cancellationToken)
    activate DownloadService

    DownloadService-->>FileDownloader: DownloadFileCompleted(args)
    deactivate DownloadService
    FileDownloader->>FileDownloader: tcs.TrySetCanceled/Exception/Result(args)

    FileDownloader->>FileDownloader: await tcs.Task

    FileDownloader->>FileSystem: File.Exists(localPath)
    alt final file missing
        FileDownloader->>FileSystem: File.Exists(tempPath)
        opt temp file exists
            loop retry < 5
                FileDownloader->>FileSystem: File.Move(tempPath, localPath, true)
                FileSystem-->>FileDownloader: IOException?
            end
        end
    end

    FileDownloader->>FileSystem: File.Exists(localPath)
    alt no output file
        FileDownloader->>FileDownloader: throw IOException
    else success
        FileDownloader->>FileDownloader: ModBase.Log("[Download] 下载成功")
    end
    deactivate FileDownloader

    note over FileDownloader,FileSystem: CleanupTempFiles uses TryDeleteFile with retries
Loading

File-Level Changes

Change Details Files
Harden file download completion and cleanup semantics to ensure final files are produced or the operation fails explicitly.
  • Switch downloader HTTP client creation to use NetworkService.GetClient() via CustomHttpClientFactory and set additional download configuration options like BlockTimeout and MinimumSizeOfChunking.
  • Wrap DownloadService in a using block and hook DownloadFileCompleted to update tracking stats and complete a TaskCompletionSource with success, cancellation, or error.
  • After the download task completes, verify the final file exists; if only the temp file exists, retry renaming it to the final path several times, then throw if no final file is present.
  • Change temporary file cleanup to a retrying TryDeleteFile helper that attempts deletion multiple times to tolerate transient IO errors.
Plain Craft Launcher 2/Modules/Network/Downloader/FileDownloader.cs
Make text file writes atomic and restrict read sharing to avoid reading partially written content.
  • Change ReadFileBytes to open files with FileShare.Read instead of FileShare.ReadWrite to disallow concurrent writers while reading.
  • Update WriteFile to write non-append text as bytes to a unique temporary path and then move it over the target path, replacing existing files atomically.
Plain Craft Launcher 2/Modules/Base/ModBase.cs
Introduce and use a reusable helper to wait for files—optionally JSON—to become ready before use.
  • Add WaitForFileReady helper that polls for file existence and non-empty content within a timeout, with an option to validate JSON-like content, handling IOExceptions and sleeping between attempts.
  • Replace prior Thread.Sleep-based waits before reading instance JSON and installer files with WaitForFileReady in multiple download and installer code paths, including vanilla libs, assets, LabyMod, and Forgelike installer handling.
Plain Craft Launcher 2/Modules/Base/ModBase.cs
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.cs
Adjust network code paths to use higher-level fetching with per-URL retries instead of temp-file-based downloads for simple text responses.
  • Change NetGetCodeByLoader overload that takes multiple URLs to iterate over them, using Requester.Fetch with configurable timeout and user-agent, returning JSON-parsed or raw content as requested.
  • Log failures per URL at debug level and, if all URLs fail, throw an exception that wraps the last error.
Plain Craft Launcher 2/Modules/Network/Facade/ModNet.cs
Unify and strengthen retry behavior when loading potentially invalid or incomplete instance JSON.
  • When instance JSON is empty or invalid, always sleep briefly (shorter on UI thread, longer on background thread), then re-read the file and, if the fast check still fails, force JSON parsing to throw, instead of bailing without retry on UI thread.
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.cs
Improve concurrency safety and robustness in downloader coordination and minor UI/file selection logic.
  • Ensure the semaphore in LoaderDownload.Run is only released if it was successfully acquired, preventing over-release when cancellation occurs before WaitAsync completes.
  • Refine Save_Click in PageDownloadCompDetail to more robustly locate the associated ModComp.CompFile by walking up to two FrameworkElement parents.
  • Make a small type-safety improvement when reading mod loader IDs from CurseForge packs by explicitly declaring Id as string.
Plain Craft Launcher 2/Modules/Network/Loaders/LoaderDownload.cs
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.cs
Plain Craft Launcher 2/Modules/Minecraft/ModModpack.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#2795 Ensure that downloading game/loader files (including Fabric/Forge/NeoForge) in the C# version reliably produces complete local files instead of failing with partial/temporary artifacts.
#2795 Ensure that subsequent installation steps (e.g., reading instance JSON and installer archives) no longer fail due to racing with file writes or reading empty/invalid JSON during game installation.
#2830 Reduce or eliminate severe UI freezing when downloading a large number of files (ensure the download process does not block the UI thread or cause noticeable stutter during bulk downloads). The PR focuses on improving reliability and correctness of instance/file downloads and JSON handling (atomic writes, file-read readiness checks, tighter file sharing, better completion/failure behavior, and retry logic). It does not change UI-thread usage or move synchronous operations off the UI, nor does it introduce explicit mechanisms to prevent UI blocking when many files are downloaded. The changes are backend and I/O robustness improvements rather than targeted UI responsiveness fixes, so the specific issue of UI freezing during bulk downloads is not clearly addressed.

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

@LuLu-ling LuLu-ling marked this pull request as ready for review May 15, 2026 07:43
@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 and removed 🚧 正在处理 开发人员正在对该内容进行开发、测试或修复,进展中 labels May 15, 2026
@LuLu-ling LuLu-ling requested a review from a team May 15, 2026 07:43
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - 我在这里给出一些高层次的反馈:

  • 新的 WaitForFileReady 辅助函数在调用 ReadFile 时没有用 try/catch 进行保护,因此当另一个写入方仍然打开文件时,任何共享冲突或瞬时 IO 错误都会跳出轮询循环,从而使等待失去意义;建议在读取 JSON 的路径上捕获 IOException(以及可能的 UnauthorizedAccessException)。
  • WriteFile 中的临时文件命名方式(FilePath + ".pcltmp." + Environment.TickCount.ToString("X8"))在同一路径上多次写入且间隔小于计时器分辨率时有发生冲突的风险;使用 GUID 或随机后缀(或 Path.GetRandomFileName())可以让原子写入更加稳健。
  • 现在有多个调用点使用 WaitForFileReady,并在调用线程上硬编码 Thread.Sleep 轮询(包括 UI 相关的代码路径);建议提供一个可异步/可取消的变体,或至少让等待过程能够与现有的取消令牌协作,以避免在较长时间的 IO 阻塞期间不必要地阻塞线程。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- The new `WaitForFileReady` helper calls `ReadFile` without guarding it in a `try/catch`, so any sharing violation or transient IO error while another writer still has the file open will escape the polling loop and defeat the purpose of the wait; consider catching `IOException` (and possibly `UnauthorizedAccessException`) around the JSON-read path as well.
- The temp file naming in `WriteFile` (`FilePath + ".pcltmp." + Environment.TickCount.ToString("X8")`) risks collisions when multiple writes to the same path happen within the timer resolution; using a GUID or random suffix (or `Path.GetRandomFileName()`) would make the atomic write more robust.
- Several call sites now use `WaitForFileReady` with hardcoded `Thread.Sleep` polling on the calling thread (including UI-related code paths); consider exposing an async/cancellable variant or at least making the wait cooperative with existing cancellation tokens to avoid unnecessarily blocking threads during longer IO stalls.

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've left some high level feedback:

  • The new WaitForFileReady helper calls ReadFile without guarding it in a try/catch, so any sharing violation or transient IO error while another writer still has the file open will escape the polling loop and defeat the purpose of the wait; consider catching IOException (and possibly UnauthorizedAccessException) around the JSON-read path as well.
  • The temp file naming in WriteFile (FilePath + ".pcltmp." + Environment.TickCount.ToString("X8")) risks collisions when multiple writes to the same path happen within the timer resolution; using a GUID or random suffix (or Path.GetRandomFileName()) would make the atomic write more robust.
  • Several call sites now use WaitForFileReady with hardcoded Thread.Sleep polling on the calling thread (including UI-related code paths); consider exposing an async/cancellable variant or at least making the wait cooperative with existing cancellation tokens to avoid unnecessarily blocking threads during longer IO stalls.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `WaitForFileReady` helper calls `ReadFile` without guarding it in a `try/catch`, so any sharing violation or transient IO error while another writer still has the file open will escape the polling loop and defeat the purpose of the wait; consider catching `IOException` (and possibly `UnauthorizedAccessException`) around the JSON-read path as well.
- The temp file naming in `WriteFile` (`FilePath + ".pcltmp." + Environment.TickCount.ToString("X8")`) risks collisions when multiple writes to the same path happen within the timer resolution; using a GUID or random suffix (or `Path.GetRandomFileName()`) would make the atomic write more robust.
- Several call sites now use `WaitForFileReady` with hardcoded `Thread.Sleep` polling on the calling thread (including UI-related code paths); consider exposing an async/cancellable variant or at least making the wait cooperative with existing cancellation tokens to avoid unnecessarily blocking threads during longer IO stalls.

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.

@whitecat346
Copy link
Copy Markdown
Member

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - 我在这里给出了一些整体性的反馈:

  • WaitForFileReady(..., requireJson: true) 中,当前的就绪检查只测试 Trim().StartsWith("{"),这会漏掉合法的 JSON 数组或其他结构;建议使用一次轻量级解析(在 try/catch 中调用 GetJson),或者至少同时接受以 [ 开头,以避免误报为未就绪。
  • 新的原子写入路径使用了 File.Move(tempPath, FilePath, true);请再次确认目标框架,并为不支持 bool overwrite 重载的方法的运行环境添加回退实现,以避免构建/运行时问题。
面向 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
-`WaitForFileReady(..., requireJson: true)` 中,当前的就绪检查只测试 `Trim().StartsWith("{")`,这会漏掉合法的 JSON 数组或其他结构;建议使用一次轻量级解析(在 try/catch 中调用 `GetJson`),或者至少同时接受以 `[` 开头,以避免误报为未就绪。
- 新的原子写入路径使用了 `File.Move(tempPath, FilePath, true)`;请再次确认目标框架,并为不支持 `bool overwrite` 重载的方法的运行环境添加回退实现,以避免构建/运行时问题。

Sourcery 对开源项目是免费的——如果你觉得我们的点评有帮助,欢迎分享给更多人 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've left some high level feedback:

  • In WaitForFileReady(..., requireJson: true) the readiness check only tests Trim().StartsWith("{"), which will miss valid JSON arrays or other structures; consider using a lightweight parse (GetJson in a try/catch) or at least accepting [ as well to avoid false negatives.
  • The new atomic write path uses File.Move(tempPath, FilePath, true); please double-check the target frameworks and add a fallback implementation for environments where the bool overwrite overload is not available to avoid build/runtime issues.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `WaitForFileReady(..., requireJson: true)` the readiness check only tests `Trim().StartsWith("{")`, which will miss valid JSON arrays or other structures; consider using a lightweight parse (`GetJson` in a try/catch) or at least accepting `[` as well to avoid false negatives.
- The new atomic write path uses `File.Move(tempPath, FilePath, true)`; please double-check the target frameworks and add a fallback implementation for environments where the `bool overwrite` overload is not available to avoid build/runtime issues.

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.

@SALTWOOD
Copy link
Copy Markdown
Member

我总感觉文件写入不完全出现在 Network 部分或者是 Downloader 库本身的问题🤔

如果是后者,这样子去擦屁股是不是不大好

@whitecat346
Copy link
Copy Markdown
Member

我总感觉文件写入不完全出现在 Network 部分或者是 Downloader 库本身的问题🤔

如果是后者,这样子去擦屁股是不是不大好

不要,我这边有个跑的涉及到那边了,会有冲突

@LuLu-ling

This comment was marked as resolved.

@Big-Cake-jpg
Copy link
Copy Markdown
Member

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - 我给出了一些总体反馈:

  • 新的 WaitForFileReady 帮助方法使用 Environment.TickCount 和 Thread.Sleep 进行自旋等待,没有任何取消机制或超时日志,这可能导致等待无响应;建议接受一个 CancellationToken,使用单调计时器,并在超时时返回布尔值或抛出异常,让调用方显式处理失败情况。
  • 带有 requireJson 的 WaitForFileReady 仅检查去除空白后的内容是否以 “{” 开头,这是非常薄弱的 JSON 有效性检查;如果这是为了在后续解析前做预检,建议使用轻量级的解析/校验,或者至少同时检查对象/数组两种形式,并显式捕获解析错误。
  • 在 ModBase.WriteFile 中,你现在先写入临时文件,然后使用 File.Move 并设置 overwrite=true,但没有处理异常,也没有保证在失败时清理临时文件;可以将移动操作包在 try/catch 中,并在出错时删除临时文件,以避免留下多余的临时文件和部分写入的结果。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- The new WaitForFileReady helper spin-waits using Environment.TickCount and Thread.Sleep without any cancellation or logging on timeout, which can cause unresponsive waits; consider accepting a CancellationToken, using a monotonic timer, and returning a boolean/throwing on timeout to make callers explicitly handle failure.
- WaitForFileReady with requireJson only checks that trimmed content starts with '{', which is a very weak JSON validity check; if this is meant to gate later parsing, consider using a lightweight parse/validation or at least checking for both object/array and catching parse errors explicitly.
- In ModBase.WriteFile you now write to a temp file and then File.Move with overwrite=true but don't handle exceptions or ensure the temp file is cleaned up on failure; wrapping the move in a try/catch and deleting the temp file on error would avoid leaving stray temp files and partially applied writes.

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've left some high level feedback:

  • The new WaitForFileReady helper spin-waits using Environment.TickCount and Thread.Sleep without any cancellation or logging on timeout, which can cause unresponsive waits; consider accepting a CancellationToken, using a monotonic timer, and returning a boolean/throwing on timeout to make callers explicitly handle failure.
  • WaitForFileReady with requireJson only checks that trimmed content starts with '{', which is a very weak JSON validity check; if this is meant to gate later parsing, consider using a lightweight parse/validation or at least checking for both object/array and catching parse errors explicitly.
  • In ModBase.WriteFile you now write to a temp file and then File.Move with overwrite=true but don't handle exceptions or ensure the temp file is cleaned up on failure; wrapping the move in a try/catch and deleting the temp file on error would avoid leaving stray temp files and partially applied writes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new WaitForFileReady helper spin-waits using Environment.TickCount and Thread.Sleep without any cancellation or logging on timeout, which can cause unresponsive waits; consider accepting a CancellationToken, using a monotonic timer, and returning a boolean/throwing on timeout to make callers explicitly handle failure.
- WaitForFileReady with requireJson only checks that trimmed content starts with '{', which is a very weak JSON validity check; if this is meant to gate later parsing, consider using a lightweight parse/validation or at least checking for both object/array and catching parse errors explicitly.
- In ModBase.WriteFile you now write to a temp file and then File.Move with overwrite=true but don't handle exceptions or ensure the temp file is cleaned up on failure; wrapping the move in a try/catch and deleting the temp file on error would avoid leaving stray temp files and partially applied writes.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[C#] 下载大量文件时 UI 严重卡顿 [BUG] C# 版本中游戏下载安装存在问题

4 participants