技术博客

热更新方案:Pak 挂载优先级与资产补丁

第 86 / 100 篇 · 2026-08-08 · ue5 · 资产管线热更新Pak

手游和活动型项目绕不开热更。UE 的资产热更不神秘:机制就一层——高优先级容器覆盖低优先级容器的同路径文件。难的是把生产流程和兼容红线管住,否则一次热更轻则资源错乱、重则全量崩档。这一篇把机制、流程、红线一次说清。

运行时机制:挂载优先级

FPakPlatformFile(以及 IoStore 的容器挂载)维护一张按优先级排序的挂载表。任何文件路径请求(IFileHandle 层面的 Open)按优先级从高到低问每个容器:"你有 /Game/UI/Icon_A 吗?"第一个答有的赢。于是:

  • 基础包装挂时优先级为 0(默认);
  • 补丁包挂载时给一个更高的 PakOrder,同路径下补丁内容自动生效,游戏代码完全无感知;
  • 同一个包里,补丁没有覆盖的路径继续用基础包的——补丁只需包含变化的资产。

这就是"补丁 = 差量容器 + 高优先级挂载"的全部运行时原理。实现层面注意两点:

  • 挂载要在资产被加载之前完成:启动早期(FCoreDelegates 的 pre-init 时机)挂好补丁容器,否则已经加载进内存的资产不会自动换;
  • 内存里已有的对象不受补丁影响:热更改的是"文件",不是"已加载的 UObject"。要替换运行中的对象,得自己走 Reload 逻辑,或者干脆设计成只影响后续加载。

生产流程:基线 + 补丁两次 Cook

补丁包用 UAT 的补丁模式产出,本质是两次 Cook 的对账:

  1. 基线:发版那次正常 Cook + Stage,保留 Cook 产物和(Zen)快照作为对账基准;
  2. 改内容:正常开发迭代;
  3. 补丁 Cook:UAT 以补丁模式重跑(指向基线的 Cook 元数据),Cook 系统和基线逐包对账,内容哈希变了的包进补丁容器,没变的引用基线数据;
  4. Stage 补丁:补丁容器单独成 pak/utoc,下发。

下发体积就是第 9060 篇的公式:改动触及的 Chunk 容器体积之和。所以补丁小不光靠"改动少",还靠 Chunk 划法把高频改动隔离在小 Chunk 里。

配套基建一般是:版本服务器存每个补丁容器的清单(包名 + 哈希),客户端启动时比对本地清单,差哪些下哪些,下载完按优先级挂载,进游戏。UE 自带的 Chunk 安装(HTTP chunk install)原理相同,只是下载和挂载由引擎代管。

热更两层模型:补丁容器挂高优先级,同路径覆盖基础包 补丁容器(PakOrder=100,热更下发) 只含变化资产:/Game/UI/Icon_A、/Game/Data/Config_B ... 基础容器(PakOrder=0,随安装包发布) 全量资产:/Game/UI/Icon_A(旧版)、其余全部 ... 查找命中高优先级 旧版被覆盖 Open("/Game/UI/Icon_A") → 按 PakOrder 从高到低问 → 补丁答有 → 返回补丁版本 补丁未覆盖的路径 → 继续问到基础容器 → 正常命中

兼容红线:补丁能改什么、不能改什么

补丁容器加载进的是旧版本的可执行文件。新资产要想被旧代码正确反序列化,格式必须兼容。四条红线,按踩坑频率排:

  1. 引擎版本必须一致。FPackageFileVersion、CustomVersions、序列化器实现全是 exe 侧代码,补丁资产用的是补丁 Cook 时的格式,和基线 exe 期望的格式必须对得上。升引擎版本必须走整包更新,没有商量余地;
  2. 不改已发布 UStrUCT/UENUM 的布局。属性按序序列化,结构体中间插入字段、枚举中间插值,都会让旧代码把字节读错位——追加字段放在末尾、枚举新值追加在尾部,才在容错范围内(反射缺失字段有默认值兜底,但错位没有);
  3. 不改已发布资产的包路径。FPackageId 是包名的哈希(第 9020 篇),改名等于对容器系统声明一个全新资产:旧路径指向的旧包还在基础容器里,新路径的补丁包会和它并存,引用关系全乱;
  4. Cook 配置一致。压缩格式、目标平台设置变了,同路径内容哈希全变,补丁退化成全量(第 9040 篇的键前提)。

反过来,补丁适合改的东西:纹理、材质参数、数据表(DataTable/CurveTable)、UI 资产、关卡布置——纯数据、无新类型、无布局变更的内容。逻辑修复要么走脚本层(Lua/Python 插件),要么老实发整包。

验证与灰度

  • 双容器对账:UnrealPak -List 分别列基础容器和补丁容器,确认补丁只含预期包;-fileopenlog 跑一局完整流程,确认运行时加载路径都来自预期容器;
  • 哈希清单校验:下发清单里带每个容器的哈希,客户端挂载前校验,防止下载截断的容器被挂上去——挂载了坏容器的症状是随机缺资源,极难排查;
  • 灰度:补丁按渠道/用户分批发,配合 stat pakfile/日志回传,第一批没问题再全量。热更的故障都是分布式故障,发布纪律比技术细节更能救命。

热更的本质是把"文件系统"当成可叠加的层来用。机制一句话,工程上一半的工作在 Cook 对账和 Chunk 划法(9060/9070 篇),另一半在兼容红线——红线踩一次,修复成本就是一次强更,前面省的都赔回去。