Skip to content

OK-WW专项 由 MAS 自行接管鸣潮游戏更新 - #408

Open
1w1w11w1 wants to merge 3 commits into
devfrom
feat/wuthering-waves-update
Open

OK-WW专项 由 MAS 自行接管鸣潮游戏更新#408
1w1w11w1 wants to merge 3 commits into
devfrom
feat/wuthering-waves-update

Conversation

@1w1w11w1

@1w1w11w1 1w1w11w1 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

变更摘要

  • 本功能属于 OK-WW 专项适配下属的子功能,不是 MAS 通用的游戏更新能力。唯一入口是 AutoProxyTask._mas_launch_game_before_task,仅在 okww 脚本启用「游戏配置」(Game.Enabled)并由 MAS 拉起鸣潮时触发;其他脚本与专项不受影响。
  • 在该入口内由 MAS 拉取官方资源清单完成版本判断、下载、md5 校验与覆写,官方启动器仅用于解析安装目录,全程不被拉起、不做界面识别。
  • 优先使用官方增量补丁包,失败按组回退整文件重下;整文件同步超阈值中止并提示手动处理。

与 dev 基线的差异

dev 上 MAS 完全不参与鸣潮游戏更新:_mas_launch_game_before_task 直接拉起客户端进程,游戏若需更新只能靠 okww 侧日志关键词(游戏更新成功, 游戏即将重启)事后感知并重启任务,MAS 既不知道要更新、也无法判断更新是否完成。

本 PR 中间提交曾尝试「OCR 点击官方启动器更新按钮 + 轮询版本号判完成」(85233b8c),因识别失败即无法更新、且完成状态只能推断,已在 328cff6 弃用,改为现方案。该路线未进入 dev。

更新流程

index.json -> 选 CDN (K1/K2 过滤, P 排序, 全列表保留兜底)
           -> 本地版本在 patchConfig 里?
                是 -> 增量: 下 krpdiff -> hpatchz 应用 -> 校验 dstFiles -> 移入
                否 -> 整文件: 比 md5 找差集 -> 超 10GB 中止报告 -> 下载 -> 移入
           -> 删 deleteFiles -> 最后写版本记录

关键设计

  • 优先官方增量包。实测 3.5.3 -> 3.6.0 增量 23GB,而整文件差分需 88.6GB(改动集中在 26GB 的 pakchunk70 内,整文件粒度吃不到收益)。
  • 只写暂存区,校验通过才移入游戏目录。暂存区置于安装目录内保证同卷,os.replace 为原子改名。版本记录最后写入,中断后下一轮会重新规划续做,不会误判已完成。
  • 单组补丁失败自动回退整文件重下。hpatchz 要求源文件字节精确,用户改动过游戏文件即会失败;远端清单带全量 md5,故总能靠整文件重下自愈,不让一个坏文件搞死整次更新。
  • 整文件同步超 10GB 中止并提示手动处理,避免无人值守时静默耗尽流量与数小时。
  • hpatchz 按需获取。校验固定 sha256 后缓存至 environment/hpatchz/,仓库保持零二进制跟踪。上游 sisong/HDiffPatch 为 MIT,与本项目 AGPL 兼容。
  • 接口不可用放行、更新失败阻断。查不到版本属于「无法判断」,放行启动(旧客户端通常仍能登录);已确认需要更新却失败则抛错阻断,避免拿旧客户端撞登录失败。

自查后收紧的两处

以下两处均为本 PR 内部自查发现并在合入前修正,dev 上不存在对应缺陷(相关代码在 dev 上尚未存在),不作为对已发布行为的修复计入:

  • 中间提交 85233b8_is_newer_version 在本地版本记录缺失时返回 False,会把「读不到版本」当成「已是最新」而静默启动旧客户端。现改为读不到即抛错,且空版本号在比较中判为需更新。
  • 清单 dest 完全来自网络,除拒绝 .. 与绝对路径外还需拒绝目录自身:Path(".")parts 为空元组,放过则解析结果即游戏根目录,后续 os.replace 会冲掉整个目录。
  • 断点续传时若服务端忽略 Range 回 200,必须覆盖写而非追加,否则把新内容接在旧字节后静默产出坏文件。

协议要点(均经实测)

  • .krpdiff 是 HDiffPatch 目录差分(HDIFF19&zstd&fadler64),标准 MIT 版 hpatchz v5.1.3 可完整解析,非私有格式。
  • CDN 上是未压缩的原始文件,下载后直接比 md5,无解压步骤。
  • olddir 可直接传游戏根目录:krpdiff 内存储的即游戏根相对路径,hpatchz 按名字匹配、容忍多余文件、且只读被引用的文件(往 olddir 放 1.5GB 未引用文件,耗时 0.061s -> 0.073s)。因此无需为每组构建最小 olddir。
  • 清单中 fromFolder 为逐项下载基址,会覆盖计划级 baseUrl
  • 官方启动器为加壳 stub,无任何可用命令行开关,故「以 CLI 驱动启动器」不可行。

测试

新增 56 个用例(tests/services/test_wuthering_waves_updater.py 38 个、test_wuthering_waves_update.py 18 个),覆盖:

  • 路径穿越(上跳 / 绝对路径 / 盘符 / 目录自身)
  • 续传时服务端忽略 Range 回 200 必须截断而非追加
  • md5 不符需删除残留、坏文件拒绝入库且原文件不动
  • CDN 逐个故障转移、增量与整文件计划选择、超阈值中止
  • 删除项拒绝穿越、本地清单缓存在大小不符时不被盲信

tests/services/ + tests/task/ 共 86 passed;全量 238 passed / 7 failed。7 个失败为 telemetry 与 game_sign 既有问题,涉及文件(app/tools/game_sign_notify.pytests/tools/test_game_sign_notification.pytests/test_main.py)本分支均未改动,其中 game_sign 为 dev 上测试与实现的签名不一致。ruff check 对本分支改动的 5 个文件全部通过(仓库整体存量告警未处理)。

尚未验证

  • 真实 krpdiff 的完整 applyhpatchz -info 对真实包解析全部正确(DirHDiff / fadler64 / zstdoldRefSize 与清单 srcFiles 精确吻合),合成目录差分往返亦已验证,但端到端 apply 需要旧版源文件,而 CDN 仅托管当前版本,只能等真实更新窗口。这也是保留「单组失败回退整文件」的原因。
  • 提权。游戏安装于 C:\Program Files 时写盘需管理员权限,目前会抛 PermissionError 但未做提权引导。
  • 预下载目前仅记录日志,未接管。

Sourcery 摘要

让 MAS 接管鸣潮游戏更新流程,替代官方启动器界面驱动并提升更新可靠性与安全性。

新功能:

  • 由 MAS 直接通过官方资源清单完成鸣潮版本检查、资源下载、校验、增量应用和文件覆写。
  • 支持官方增量补丁、整文件差异同步、CDN 故障转移及增量失败后的整文件回退。

错误修复:

  • 修复本地版本记录缺失时误判为最新版本的问题。
  • 强化网络资源路径校验,防止路径穿越或目录自身被覆盖。
  • 修复断点续传服务端忽略 Range 时错误追加数据的问题,并避免损坏文件进入游戏目录。

改进:

  • 采用同卷暂存与原子替换,并在完成所有文件处理后写入版本记录,提升更新中断恢复和数据安全性。
  • 增加整文件同步体积与磁盘空间保护,并按需获取并校验 hpatchz 工具。

测试:

  • 新增鸣潮更新检查与资源更新流程测试,覆盖版本判断、路径安全、CDN 回退、下载校验、增量计划和整文件同步等场景。
Original summary in English

Summary by Sourcery

让 MAS 接管鸣潮游戏更新流程,替代官方启动器界面驱动并提升更新可靠性与安全性。

New Features:

  • 由 MAS 直接通过官方资源清单完成鸣潮版本检查、资源下载、校验、增量应用和文件覆写。
  • 支持官方增量补丁、整文件差异同步、CDN 故障转移及增量失败后的整文件回退。

Bug Fixes:

  • 修复本地版本记录缺失时误判为最新版本的问题。
  • 强化网络资源路径校验,防止路径穿越或目录自身被覆盖。
  • 修复断点续传服务端忽略 Range 时错误追加数据的问题,并避免损坏文件进入游戏目录。

Enhancements:

  • 采用同卷暂存与原子替换,并在完成所有文件处理后写入版本记录,提升更新中断恢复和数据安全性。
  • 增加整文件同步体积与磁盘空间保护,并按需获取并校验 hpatchz 工具。

Tests:

  • 新增鸣潮更新检查与资源更新流程测试,覆盖版本判断、路径安全、CDN 回退、下载校验、增量计划和整文件同步等场景。

@sourcery-ai

sourcery-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown

审查者指南

将《鸣潮》官方启动器版本元数据集成到 OK-WW 自动代理流程中,新增更新/预下载检测、通过 OCR 由启动器驱动的更新编排,以及本地版本轮询,同时继续将实际下载和验证完全交由官方启动器处理。

《鸣潮》官方启动器更新流程时序图

sequenceDiagram
    participant OKWW as OK-WW AutoProxy
    participant API as Official Update API
    participant Launcher as Official Launcher
    participant FS as Local Version Metadata

    OKWW->>API: check_wuthering_waves_update(launcher_path, resource)
    API-->>OKWW: default / predownload version metadata
    OKWW->>Launcher: _start_wuthering_waves_launcher()
    alt Formal update available
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(Update)
        Launcher-->>FS: Download, install, and verify update
        loop Until target version is reached
            OKWW->>FS: _read_local_wuthering_waves_version(launcher_path)
            FS-->>OKWW: current version
        end
        OKWW->>OKWW: wait_wuthering_waves_update(launcher_path, target_version)
    else Predownload available
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(预下载)
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(确定下载)
    end
    OKWW->>OKWW: Start Wuthering Waves client
Loading

文件级变更

变更 详情 文件
重构《鸣潮》启动器路径解码,以公开安装目录解析功能,并添加具有健壮错误处理的本地版本文件解析。
  • 将启动器元数据解码拆分为安装目录辅助函数和进程路径辅助函数,以复用安装目录解析逻辑。
  • 添加 resolve_wuthering_waves_install_dir,用于验证 launcher.exe 是否存在并返回记录的安装目录。
  • 引入 _read_local_wuthering_waves_version,用于读取 launcherDownloadConfig.json、解析 JSON,并提取格式规范的版本字符串,同时提供用户友好的错误消息。
app/services/wuthering_waves.py
基于本地版本元数据,实现由官方启动器驱动的更新版本比较和异步等待逻辑。
  • 添加 _version_key 和 _is_newer_version 辅助函数,通过正则表达式提取并比较类似语义化版本的字符串。
  • 引入 wait_wuthering_waves_update,这是一个异步轮询循环,用于等待本地版本达到或超过目标版本;在临时读取失败时提供超时控制和调试日志。
  • 定义与更新相关的轮询间隔和全局超时常量,以控制等待行为。
app/services/wuthering_waves.py
添加基于异步 HTTP 的更新元数据检查器,以区分不同游戏资源的正式发布和预下载状态,并将其封装在数据类中。
  • 引入 WutheringWavesUpdateInfo 数据类,包含当前版本、正式发布版本和预下载版本字段、更新可用标志,以及用于判断是否应启动启动器的辅助属性。
  • 定义 _OFFICIAL_UPDATE_API,将官服和国际服映射到各自的元数据端点。
  • 使用 httpx.AsyncClient 实现 check_wuthering_waves_update,以获取 JSON、验证结构、读取默认版本和预下载版本信息、解析 predownloadSwitch、计算可用性标志,并记录简洁摘要。
app/services/wuthering_waves.py
将官方启动器更新逻辑集成到 OK-WW 自动代理流程中,使用 OCR 点击启动器界面按钮,并在启动游戏前协调更新检查。
  • 在 AutoProxy 状态中跟踪 launcher_path 和专用的 launcher_process_manager,并在加载配置期间完成初始化。
  • 添加 _click_wuthering_waves_launcher_text,使用 OCRTool.click_txt 配合特定资源的窗口标题和简单重试逻辑,点击本地化的按钮文本。
  • 实现 _start_wuthering_waves_launcher:当 launcher.exe 尚未运行时启动它,并通过 ProcessManager 进行管理。
  • 实现 _trigger_wuthering_waves_launcher_update,通过 OCR 点击更新或预下载按钮及确认按钮来驱动启动器界面,记录相关操作,并在开始正式发布更新时调用 wait_wuthering_waves_update。
  • 添加 _check_and_trigger_wuthering_waves_update,用于调用 check_wuthering_waves_update、优雅地处理失败、评估 should_start_launcher,并在需要时触发由界面驱动的更新。
  • 扩展 _mas_launch_game_before_task,在处理游戏客户端进程生命周期之前执行官方更新检查/触发步骤。
app/task/Okww/AutoProxy.py
更新 OK-WW 日志和版本元数据记录。
  • 调整 set_okww 中的日志消息,改用不包含 f-string 插值的静态字符串。
  • 更新 res/version.json,以记录与扩展后的《鸣潮》更新流程相关的新版本。
app/task/Okww/AutoProxy.py
res/version.json

提示和命令

与 Sourcery 交互

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

自定义使用体验

访问你的控制面板

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

获取帮助

Original review guide in English

Reviewer's Guide

Integrates Wuthering Waves official launcher version metadata into the OK-WW auto-proxy flow, adding update/predownload detection, launcher-driven update orchestration via OCR, and local version polling, while keeping actual download and verification fully delegated to the official launcher.

Sequence diagram for the Wuthering Waves official launcher update flow

sequenceDiagram
    participant OKWW as OK-WW AutoProxy
    participant API as Official Update API
    participant Launcher as Official Launcher
    participant FS as Local Version Metadata

    OKWW->>API: check_wuthering_waves_update(launcher_path, resource)
    API-->>OKWW: default / predownload version metadata
    OKWW->>Launcher: _start_wuthering_waves_launcher()
    alt Formal update available
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(Update)
        Launcher-->>FS: Download, install, and verify update
        loop Until target version is reached
            OKWW->>FS: _read_local_wuthering_waves_version(launcher_path)
            FS-->>OKWW: current version
        end
        OKWW->>OKWW: wait_wuthering_waves_update(launcher_path, target_version)
    else Predownload available
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(预下载)
        OKWW->>Launcher: _click_wuthering_waves_launcher_text(确定下载)
    end
    OKWW->>OKWW: Start Wuthering Waves client
Loading

File-Level Changes

Change Details Files
Refactor Wuthering Waves launcher path decoding to expose install directory resolution and add local version file parsing with robust error handling.
  • Split launcher metadata decoding into an install-dir helper and a process-path helper to reuse install directory resolution.
  • Add resolve_wuthering_waves_install_dir to validate launcher.exe existence and return the recorded install directory.
  • Introduce _read_local_wuthering_waves_version to read launcherDownloadConfig.json, parse JSON, and extract a clean version string with user-friendly error messages.
app/services/wuthering_waves.py
Implement version comparison and async waiting logic for official launcher-driven updates based on local version metadata.
  • Add _version_key and _is_newer_version helpers to compare semantic-like version strings extracted via regex.
  • Introduce wait_wuthering_waves_update, an async polling loop that waits for the local version to reach or exceed the target, with timeout and debug logging on transient read failures.
  • Define update-related constants for polling interval and global timeout to control waiting behavior.
app/services/wuthering_waves.py
Add an async HTTP-based update metadata checker that distinguishes release and predownload states for different game resources, encapsulated in a dataclass.
  • Introduce WutheringWavesUpdateInfo dataclass with fields for current, release, and predownload versions, flags for update availability, and a helper property to decide whether to start the launcher.
  • Define _OFFICIAL_UPDATE_API mapping for 官服 and 国际服 to their respective metadata endpoints.
  • Implement check_wuthering_waves_update using httpx.AsyncClient to fetch JSON, validate structure, read default and predownload version info, interpret predownloadSwitch, compute availability flags, and log a concise summary.
app/services/wuthering_waves.py
Integrate official launcher update logic into the OK-WW AutoProxy pipeline, using OCR to press launcher UI buttons and coordinating update checks before game launch.
  • Track launcher_path and a dedicated launcher_process_manager in AutoProxy state, initialized during configuration loading.
  • Add _click_wuthering_waves_launcher_text to use OCRTool.click_txt with resource-specific window titles and simple retry behavior to click localized button texts.
  • Implement _start_wuthering_waves_launcher to launch launcher.exe when not already running, managed via ProcessManager.
  • Implement _trigger_wuthering_waves_launcher_update to drive the launcher UI: clicking update or predownload and confirmation buttons via OCR, logging actions, and invoking wait_wuthering_waves_update when a release update is started.
  • Add _check_and_trigger_wuthering_waves_update to call check_wuthering_waves_update, gracefully handle failures, evaluate should_start_launcher, and trigger UI-driven update as needed.
  • Extend _mas_launch_game_before_task to run the official update check/trigger step before handling the game client process lifecycle.
app/task/Okww/AutoProxy.py
Update OK-WW logging and version metadata records.
  • Adjust a log message in set_okww to use a static string without f-string interpolation.
  • Update res/version.json to record the new version associated with the extended Wuthering Waves update flow.
app/task/Okww/AutoProxy.py
res/version.json

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

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="app/services/wuthering_waves.py" line_range="151-152" />
<code_context>
+
+
+def _is_newer_version(candidate: str | None, current: str | None) -> bool:
+    if not candidate or not current:
+        return False
+    return _version_key(candidate) > _version_key(current)
+
+
</code_context>
<issue_to_address>
**问题 (bug_risk):**`launcherDownloadConfig.json` 缺失或不包含可用的本地版本时,`_is_newer_version` 始终返回 `False`,因此正式更新和预下载都会被报告为不可用,并且永远不会启动官方启动器。

**触发条件:** 本地启动器版本记录不存在、为空,或在之前的安装/更新过程中被删除时。

**建议修复:** 将未知的本地版本视为需要启动启动器,或者明确使检查失败,而不是报告没有更新。

```suggestion
    if not current:
        return True
```
</issue_to_address>

### 评论 2
<location path="app/services/wuthering_waves.py" line_range="234" />
<code_context>
+    if not isinstance(payload, dict):
+        raise ValueError("鸣潮官方更新接口返回格式错误")
+
+    default_info = payload.get("default")
+    predownload_info = payload.get("predownload")
+    if not isinstance(default_info, dict):
+        raise ValueError("鸣潮官方更新接口缺少 default 版本信息")
+
+    release_version = str(default_info.get("version") or "").strip() or None
+    predownload_version = (
+        str(predownload_info.get("version") or "").strip()
+        if isinstance(predownload_info, dict)
</code_context>
<issue_to_address>
**问题 (bug_risk):** 当 API 响应包含 `default` 对象但没有有效的 `version` 时,该响应仍会被接受,导致 `release_version=None`,并且更新检测会被静默禁用,而不是拒绝格式错误的响应。

**触发条件:** 官方更新 API 返回不完整或暂时格式错误的 `default` 条目时。

**建议修复:** 验证 `default_info["version"]` 是非空字符串;当其缺失或无效时,抛出 `ValueError````suggestion
    release_version = default_info.get("version")
    if not isinstance(release_version, str) or not release_version.strip():
        raise ValueError("鸣潮官方更新接口缺少有效的 default version")
    release_version = release_version.strip()
```
</issue_to_address>

Sourcery 评估

需要人工审查。 需要先处理 2 个发现,并且该变更会根据远程版本接口和 OCR 操作官方启动器,自动下载、解压并覆盖本地游戏安装文件;如果版本判断或按钮识别错误,影响会在代码回滚后仍留在本地安装中。影响范围通常局限于单个游戏安装,可通过官方启动器重新更新或重装修复,但不能仅靠回滚完全撤销。

阻塞性发现:app/services/wuthering_waves.py:152app/services/wuthering_waves.py:234


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="app/services/wuthering_waves.py" line_range="151-152" />
<code_context>
+
+
+def _is_newer_version(candidate: str | None, current: str | None) -> bool:
+    if not candidate or not current:
+        return False
+    return _version_key(candidate) > _version_key(current)
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** When `launcherDownloadConfig.json` is missing or contains no usable local version, `_is_newer_version` always returns `False`, so both formal updates and predownloads are reported as unavailable and the official launcher is never started.

**Triggers:** When the local launcher version record is absent, empty, or has been removed during a prior installation/update.

**Suggested fix:** Treat an unknown local version as requiring the launcher to start, or explicitly fail the check instead of reporting no update.

```suggestion
    if not current:
        return True
```
</issue_to_address>

### Comment 2
<location path="app/services/wuthering_waves.py" line_range="234" />
<code_context>
+    if not isinstance(payload, dict):
+        raise ValueError("鸣潮官方更新接口返回格式错误")
+
+    default_info = payload.get("default")
+    predownload_info = payload.get("predownload")
+    if not isinstance(default_info, dict):
+        raise ValueError("鸣潮官方更新接口缺少 default 版本信息")
+
+    release_version = str(default_info.get("version") or "").strip() or None
+    predownload_version = (
+        str(predownload_info.get("version") or "").strip()
+        if isinstance(predownload_info, dict)
</code_context>
<issue_to_address>
**issue (bug_risk):** An API response with a `default` object but without a valid `version` is accepted, producing `release_version=None` and silently disabling update detection instead of rejecting the malformed response.

**Triggers:** When the official update API returns an incomplete or temporarily malformed `default` entry.

**Suggested fix:** Validate that `default_info["version"]` is a non-empty string and raise `ValueError` when it is missing or invalid.

```suggestion
    release_version = default_info.get("version")
    if not isinstance(release_version, str) or not release_version.strip():
        raise ValueError("鸣潮官方更新接口缺少有效的 default version")
    release_version = release_version.strip()
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and 该变更会根据远程版本接口和 OCR 操作官方启动器,自动下载、解压并覆盖本地游戏安装文件;如果版本判断或按钮识别错误,影响会在代码回滚后仍留在本地安装中。影响范围通常局限于单个游戏安装,可通过官方启动器重新更新或重装修复,但不能仅靠回滚完全撤销。.

Blocking findings: app/services/wuthering_waves.py:152, app/services/wuthering_waves.py:234


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.

Comment thread app/services/wuthering_waves.py Outdated
Comment thread app/services/wuthering_waves.py Outdated
弃用 OCR 与官方启动器,改由 MAS 拉取官方资源清单完成下载、校验与覆写。
启动器仅用于解析安装目录,不再被拉起。

- 优先使用官方增量补丁包(3.5.3->3.6.0 实测 23GB,整文件同步需 88GB),
  按需获取并校验 hpatchz 后应用;单组应用失败自动回退为整文件重下
- 多 CDN 断点续传,逐文件 md5 校验;仅写暂存区,校验通过才移入游戏目录,
  版本记录最后写入,中断后可重新规划续做
- 整文件同步超 10GB 时中止并提示手动处理,避免无人值守时静默耗尽流量
- 清单 dest 来自网络,落盘前校验路径穿越;修复 "." 会解析为游戏根目录、
  进而被 os.replace 冲掉整个目录的问题
- 修复本地版本记录缺失时被误判为已是最新,从而静默启动旧版客户端的问题
@1w1w11w1 1w1w11w1 changed the title 完善鸣潮官方更新等待流程 OK-WW专项 由 MAS 自行接管鸣潮游戏更新 Aug 25, 2026
_is_newer_version 的空值分支在当前调用链下已不可达(本地版本缺失时
read_wuthering_waves_local_state 会抛错),但静默返回 False 会把
"查不到版本"误当成"已是最新",是危险方向。去掉后 _version_key("")
为空元组,本地版本未知时任何有效版本都判为需更新,方向安全。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant