# TDD-02 客户端热更新技术方案 > **文档类型**:技术设计方案(Technical Design Document) > **版本**:v1.0 > **日期**:2026-06-30 > **关联文档**:TDD-00《挂机手游技术栈方案.md》、TDD-04《数据库表结构设计》、PRD-03《热更新与活动系统需求文档》(待创建)、GDD-06《经济系统设计》、GDD-08《大陆地图与区域开放系统》、GDD-18《世界地图副本遗迹生成引擎》、GDD-22《开放世界随机事件与玩家可交互内容层》 --- ## 1. 设计目标 《洪荒大陆》以“概率/机遇驱动 + 玩家自发事件轮换”替代传统赛季重置,意味着活动玩法、事件池、战报文案、地图生成参数必须能高频调整,且尽量绕过应用商店审核。本方案的目标: 1. **脚本与资源可热更**:活动 UI、逻辑脚本、配置、音效、图集等通过 CDN 差量下发,无需整包发版。 2. **运行时参数可热更**:数值平衡、事件概率、经济参数等通过 Nacos 配置中心实时下发,客户端无需重启。 3. **可灰度、可回滚**:支持按设备/账号/服务器百分比灰度,失败时自动或手动回滚到旧版本。 4. **安全可信**:manifest 签名校验、资源加密、防二次打包篡改。 > 核心约束来源:TDD-00 §2.3、TDD-00 §3.5(Nacos 配置中心)、GDD-22 §5(事件池动态调参)。 --- ## 2. 技术选型 | 层级 | 选型 | 说明 | |------|------|------| | 客户端引擎 | Cocos Creator 3.x | 官方支持 Asset Bundle 热更,覆盖 Android / iOS / 鸿蒙 NEXT | | 热更协议 | HTTP(S) + CDN | 差量 Bundle 通过 CDN 边缘节点分发,支持断点续传 | | 版本清单 | 自定义 manifest(JSON) | 记录版本号、文件列表、MD5、依赖关系、diff 包地址 | | 差量生成 | 服务端 diff 工具 | 基于文件级 MD5 对比生成差异包;大文件使用 bsdiff 二进制差分 | | 运行时配置 | Nacos 2.x | 经济、战斗、事件、地图参数实时下发,无需客户端重启 | | 灰度控制 | 服务端版本接口 + Nacos | 按 device_id / account_id / server_id / 百分比控制可见版本 | | 回滚 | 本地缓存旧 Bundle + 服务端回切版本号 | 自动检测到异常后回退;玩家也可在登录界面手动选择“修复” | > 不采用 Cocos 官方 `cc.AssetManager` 内置热更整包(project.manifest),而是基于 **Asset Bundle 远程加载 + 自定义 manifest** 的轻量方案,避免全量 manifest 体积过大,并支持更灵活的灰度策略。 --- ## 3. 热更范围 ### 3.1 可热更资源(无需商店发版) | 资源类型 | 存放位置 | 示例 | 说明 | |----------|----------|------|------| | TypeScript/JavaScript 脚本 | `bundles/game-core/scripts/` | 活动玩法逻辑、战斗结算、随机事件处理 | 脚本以 Bundle 形式发布,运行时被 `import()` | | JSON/Excel 导出配置 | `bundles/configs/` | 掉落表、技能参数、地图事件池、经济参数(运行时仍优先 Nacos) | 作为本地兜底与离线缓存 | | UI 预制体 | `bundles/ui/` | 活动面板、战报详情、交易行界面 | 支持新增/替换 Prefab | | 图集与纹理 | `bundles/atlases/` | 活动图标、种族头像、地图地块 | 按功能分包,减少下载量 | | 音效与音乐 | `bundles/audio/` | 事件音效、背景音乐 | 可按平台选择格式 | | 战报文案 | `bundles/i18n/` | 战斗日志模板、事件描述 | 支持多语言热更 | ### 3.2 必须整包更新的资源(需商店发版) | 资源类型 | 原因 | |----------|------| | 引擎版本升级 | Cocos Creator 引擎核心库变更,无法通过 Bundle 替换 | | 原生插件 / Native Bridge | 支付、推送、登录 SDK 等原生代码变更 | | 启动场景(首包) | 启动器本身不能依赖待下载资源 | | 应用图标 / 启动图 / 权限声明 | 商店包元数据变更 | | 重大协议变更 | protobuf 结构体不兼容旧客户端,必须强更 | > 原则:**能热更就不发版**。每次发版前由技术负责人确认是否属于“必须整包更新”范围。 --- ## 4. 版本号与清单 ### 4.1 版本号规则 采用四级版本号:`主版本.次版本.热更版本.资源版本` | 位 | 名称 | 变更时机 | 示例 | |----|------|----------|------| | 第 1 位 | 主版本 | 大版本迭代、引擎升级、重大架构重构 | `2.0.0.0` | | 第 2 位 | 次版本 | 新增系统、玩法里程碑、商店强更 | `1.5.0.0` | | 第 3 位 | 热更版本 | 通过热更发布的逻辑/配置更新 | `1.5.3.0` | | 第 4 位 | 资源版本 | 仅资源替换(图集、音效、文案),无脚本变更 | `1.5.3.12` | - 兼容性规则:**主.次** 不一致视为不兼容,必须整包更新;**热更.资源** 不一致可通过热更修复。 - 版本比较:按位逐次比较,高位优先。 ### 4.2 Manifest 格式 ```json { "version": "1.5.3.12", "min_compatible_version": "1.5.0.0", "bundle_name": "game-core", "release_note": "修复战报文案;新增端午节活动", "grayscale_group": "all", "grayscale_ratio": 1.0, "files": [ { "path": "scripts/activity/dragon-boat.js", "size": 12480, "md5": "a1b2c3d4e5f6...", "compressed": true, "download_url": "https://cdn.example.com/bundles/game-core/1.5.3.12/scripts/activity/dragon-boat.js.lz4" }, { "path": "configs/event-pool.json", "size": 8192, "md5": "f6e5d4c3b2a1...", "compressed": true, "download_url": "https://cdn.example.com/bundles/game-core/1.5.3.12/configs/event-pool.json.lz4" } ], "deleted": ["scripts/activity/spring-festival.js"], "signature": "SHA256-RSA-SIGNATURE-HERE" } ``` ### 4.3 Diff 算法 1. **文件级 Diff**:以文件 MD5 为粒度, manifest 中只列出新增/变更/删除的文件,客户端只下载变更文件。 2. **二进制 Diff(可选)**:对超过 1MB 的资源文件(如大图集、音效包)使用 bsdiff/xdelta 生成补丁包,进一步减少流量。 3. **Bundle 级 Diff**:每个 Asset Bundle 独立生成 manifest;不同 Bundle 之间无依赖时允许并行下载。 --- ## 5. 热更流程 ### 5.1 文字流程图 ``` 客户端启动 │ ▼ 读取本地 manifest(各 Bundle 当前版本) │ ▼ 请求服务端版本接口 /hotfix/version 携带:当前主.次版本、各 Bundle 热更/资源版本、device_id、account_id、server_id、渠道号 │ ▼ 服务端返回目标版本 + 灰度标识 + manifest_url │ ▼ 版本兼容性检查 ├─ 主.次版本 < min_compatible_version ──→ 弹强更提示,跳转商店下载 │ └─ 主.次版本兼容 ──→ 继续 │ ▼ 灰度判定(服务端已按设备/账号/百分比计算) ├─ 未命中灰度 ──→ 使用 CDN 上旧版本 manifest_url,跳过更新 │ └─ 命中灰度 ──→ 使用新版本 manifest_url │ ▼ 下载新 manifest │ ▼ 比对本地与新 manifest 文件列表 │ ▼ 生成待下载文件列表(新增 + MD5 变更) │ ▼ 并行下载差分包(支持断点续传、失败重试 3 次) │ ▼ 逐文件校验 MD5 与数字签名 ├─ 校验失败 ──→ 标记该文件失败,重试;连续失败则回滚到旧版本 │ └─ 校验通过 ──→ 写入临时目录 │ ▼ 所有文件就绪后原子移动到正式 Bundle 目录 │ ▼ 更新本地 manifest │ ▼ 若涉及脚本/配置变更: ├─ 可在运行时重载的 ──→ 即时生效(如 Nacos 配置、战报文案) ├─ 需要重新加载 Bundle 的 ──→ 触发 Bundle 重载,回到登录界面 └─ 涉及启动逻辑的 ──→ 提示“重启游戏生效” │ ▼ 进入游戏 ``` ### 5.2 关键时序说明 | 阶段 | 责任方 | 关键动作 | |------|--------|----------| | 版本决策 | 服务端 /hotfix/version | 根据客户端版本、灰度策略、渠道包返回目标版本 | | 清单分发 | CDN | manifest 与差分包均走 CDN,源站故障时不影响已缓存资源 | | 下载校验 | 客户端 | MD5 + 签名双重校验,失败文件不入正式目录 | | 原子应用 | 客户端 | 先写入 `{bundle}/_temp/`,全部成功后再 `rename` 到 `{bundle}/` | | 生效方式 | 客户端 + 服务端 | 运行时配置走 Nacos;Bundle 级变更走重载/重启 | --- ## 6. 灰度与回滚 ### 6.1 灰度策略 | 维度 | 说明 | 优先级 | |------|------|--------| | 设备分组 | 按 device_id 尾号 / UUID hash 分桶 | 高 | | 账号分组 | 按 account_id 尾号 / 白名单 | 高 | | 服务器分组 | 仅对指定 server_id 开放 | 中 | | 渠道分组 | Android / iOS / 鸿蒙 / 特定渠道包 | 中 | | 百分比 | 0% → 5% → 20% → 50% → 100% 阶梯放量 | 低(兜底) | - 灰度控制存储于 Nacos `hotfix.grayscale` 配置项,可在不重启服务端的情况下调整。 - 客户端请求 `/hotfix/version` 时,服务端实时查询 Nacos 灰度配置并计算是否命中。 - 灰度命中后,服务端返回新版本 manifest_url;未命中则返回当前稳定版本。 ### 6.2 自动回滚 ``` 客户端检测到以下任一情况 │ ├─ 新 Bundle 加载后连续崩溃 3 次 ├─ 关键接口(登录/拉取角色)失败率 > 30% ├─ 校验失败文件数 > 阈值 └─ 启动阶段热更流程异常中断 │ ▼ 自动将本地 manifest 回退到上一稳定版本 删除/隔离异常 Bundle 文件 │ ▼ 下次启动时向服务端上报 rollback_event 服务端根据上报动态降低该版本灰度比例或关闭灰度 ``` ### 6.3 用户可选手动回滚 1. 登录界面提供“修复/清除缓存”入口。 2. 玩家点击后,客户端删除所有热更 Bundle,只保留首包资源。 3. 重新走热更流程,拉取当前稳定版本。 4. 手动回滚事件上报服务端,用于排查问题版本。 --- ## 7. 异常处理 | 异常场景 | 处理策略 | 用户体验 | |----------|----------|----------| | 下载失败 | 单文件重试 3 次;失败文件加入重试队列;全部失败则回滚 | 显示“资源更新失败,正在恢复” | | 校验失败(MD5/签名) | 删除该文件,重新下载;连续 3 次失败则标记版本异常 | 提示“资源校验失败,尝试重新下载” | | 存储不足 | 下载前预估所需空间;空间不足时提示清理缓存或只下载必要 Bundle | 提示“存储空间不足,请清理后重试” | | 网络中断 | 支持断点续传;网络恢复后继续下载 | 显示“等待网络连接” | | 版本不兼容 | 主.次版本低于 min_compatible_version 时强更 | 弹窗“请前往商店下载最新版本” | | CDN 节点异常 | 客户端回退到备用 CDN 域名或源站 | 无感切换(最多 1 次重试可见) | | 灰度配置冲突 | 服务端返回明确的目标版本,客户端不自行决策 | 以服务端返回为准 | > 所有异常均需上报埋点,字段包括:错误码、版本号、设备型号、网络类型、失败文件、重试次数。 --- ## 8. 安全 ### 8.1 Manifest 签名 - manifest 文件由 CI/CD 流水线使用服务端 RSA 私钥签名,客户端使用内置公钥验签。 - 签名覆盖 manifest 全部字段(version、files、deleted、grayscale 等),防止中间人篡改下载地址或版本号。 - 公钥随首包硬编码,不通过热更下发。 ### 8.2 资源加密 | 资源类型 | 加密方式 | 说明 | |----------|----------|------| | 脚本(JS/TS 编译后) | XXTEA / AES-128-CBC | 下载后解密再加载,防止直接读取源码 | | 配置文件(JSON) | 同上 | 策划配置不外泄 | | 图集/纹理 | 可选轻度混淆 | 美术资源优先级较低,视情况加密 | | 音效 | 不加密或简单 XOR | 降低运行时解密开销 | - 密钥派生:由客户端内置主密钥 + 当前版本号派生,每个版本密钥不同。 - 加密密钥不直接出现在客户端源码中,采用 Native 层安全存储或代码混淆。 ### 8.3 防篡改 1. **运行时校验**:关键脚本加载前再次校验 MD5。 2. **文件完整性校验**:每次启动时抽查部分 Bundle 文件 MD5。 3. **非法修改检测**:检测到本地文件被改动且无法通过签名验证时,触发“修复”流程。 4. **反二次打包**:Native 层校验 APK/IPA 签名与包名;签名不一致则拒绝启动。 --- ## 9. 与 Nacos 的协同 ### 9.1 运行时配置热更(无需客户端重启) TDD-02 处理的“客户端热更”主要指 Bundle 资源/脚本更新;而**数值参数、事件概率、经济参数**等应优先通过 Nacos 实时配置下发,无需下载 Bundle 也无需重启客户端。 | 配置命名空间 | 用途 | 示例 | 关联 GDD | |--------------|------|------|----------| | `economy` | 经济参数 | 掉落倍率、交易行税率、货币兑换比例 | GDD-06 | | `combat` | 战斗参数 | 技能伤害系数、ATB 速度公式、渡劫难度 | GDD-03 / GDD-12 | | `event` | 事件参数 | 随机事件触发权重、事件链分支概率 | GDD-22 | | `map_gen` | 地图生成 | 副本/遗迹刷新间隔、资源点密度 | GDD-08 / GDD-18 | | `drop` | 掉落配置 | 怪物掉落表、宝箱掉落池 | GDD-19 / GDD-21 | | `hotfix` | 热更控制 | 版本目标、灰度比例、强制更新开关 | TDD-02 | ### 9.2 客户端 Nacos 长轮询流程 ``` 客户端登录成功后 │ ▼ 建立 Nacos 长轮询 / 监听指定 dataId + group │ ▼ Nacos 配置变更时推送通知 │ ▼ 客户端拉取最新配置 │ ▼ 配置合法性校验(JSON Schema / 范围检查) ├─ 不合法 ──→ 丢弃并告警 │ └─ 合法 ──→ 应用到本地配置缓存 │ ▼ 触发相关模块重载(战斗计算器、经济计算器等) ``` ### 9.3 与数据库 dynamic_configs 的关系 - **运行时主配置源**:Nacos。 - **本地快照/审计**:`dynamic_configs` 表(见 TDD-04 §5.13)仅用于记录配置历史、灰度记录、回滚审计。 - 服务端逻辑直接从 Nacos 读取;`dynamic_configs` 不用于实时决策。 --- ## 10. CDN 与热更服务架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ CI/CD 流水线 │ │ 构建 Bundle → 生成 manifest → diff → 签名 → 上传 CDN 源站 │ └───────────────────────────┬─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ CDN 边缘节点 │ │ manifest.json / diff bundles / 全量 bundles │ └───────────────────────────┬─────────────────────────────────┘ │ HTTPS ▼ ┌─────────────────────────────────────────────────────────────┐ │ 客户端(Cocos Creator 3.x) │ │ 启动器 → /hotfix/version → 下载 manifest → diff → 校验 → 应用 │ │ 登录后 → Nacos 长轮询 → 运行时配置热更 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 服务端(Nakama / Gin) │ │ /hotfix/version:版本决策 + 灰度计算 │ │ Nacos 配置中心:运行时参数 + 灰度策略 │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 11. 验收标准 | 编号 | 验收项 | 可测试标准 | |------|--------|------------| | AC-01 | 版本号管理 | 修改脚本后热更版本号正确递增(如 `1.5.3.0` → `1.5.4.0`),仅替换图集时资源版本号递增(`1.5.3.0` → `1.5.3.1`) | | AC-02 | 差量下载 | 两次热更之间仅下载变更文件;删除文件在本地被正确移除 | | AC-03 | 校验机制 | 篡改 manifest 签名或文件 MD5 后,客户端拒绝应用并触发回滚 | | AC-04 | 灰度放量 | 配置 5% 灰度后,约 5% 设备命中新版本;调整至 100% 后全部命中 | | AC-05 | 自动回滚 | 模拟新版本脚本崩溃 3 次,客户端自动回退到上一稳定版本并能正常启动 | | AC-06 | 存储不足 | 在存储不足设备上触发更新,客户端给出明确提示并不进入死循环 | | AC-07 | Nacos 运行时配置 | 修改 Nacos `economy.drop_rate` 后,客户端在 30 秒内生效且无需重启 | | AC-08 | 跨平台一致 | Android / iOS / 鸿蒙 NEXT 三端使用同一 manifest 与 CDN 地址,热更行为一致 | --- ## 12. 与现有文档的冲突与说明 | 编号 | 检查项 | 结论 | |------|--------|------| | C01 | 与 TDD-00 热更方案是否一致 | ✅ 一致。TDD-02 在 TDD-00 基础上细化 manifest、diff、灰度、安全、Nacos 协同。 | | C02 | 与 TDD-04 dynamic_configs 表是否一致 | ✅ 一致。TDD-02 明确 Nacos 为主配置源,`dynamic_configs` 仅做审计快照。 | | C03 | 与 PRD-03 活动/事件热更需求是否对齐 | ⚠️ PRD-03 尚未创建。TDD-02 已预留接口:事件池配置既可通过 Nacos 实时调参,也可通过 Bundle 热更下发完整事件脚本。 | | C04 | 与 GDD-22 随机事件系统是否冲突 | ✅ 无冲突。GDD-22 要求事件池动态维护,TDD-02 提供 Bundle 热更 + Nacos 调参两种手段。 | | C05 | 版本号规则是否覆盖 GDD-21 数值版本 | ✅ GDD-21 为数值设计文档,不定义版本号;TDD-02 四级版本号专门用于客户端/资源版本管理。 | --- ## 13. 版本记录 | 版本 | 日期 | 修改人 | 变更说明 | |------|------|--------|----------| | v1.0 | 2026-06-30 | - | 初稿:完成技术选型、热更范围、版本号与 manifest、热更流程、灰度回滚、异常处理、安全、Nacos 协同及验收标准。 | --- *文档结束*