UE热更新:更新能力与热更安全

UE Hot Update: Updateability and Security

在游戏开发过程中,热更新是非常重要的能力,它能够让我们在不换包的情况下更新功能、修复BUG。但是哪些东西可以热更,哪些不能热更,哪些存在风险、目前没有很详细地文章进行介绍。

本篇文章我会介绍我在开发HotPatcher及项目热更实践过程中,对热更能力和热更安全的一些思考。确保项目上线时,热更的能力和稳定性是有保障的,并且在热更迭代阶段,也能够清晰地知道哪些是可以热更的,这样也能很好地评估更新风险。
以及通过周边能力建设,提前进行风险排查,把热更新流程变成自动化的,只需要版本PM来控制补丁构建和发布时机,完全不需要程序参与到热更流程。

热更流程

补丁构建

基于HotPatcher的热更打包机制:追踪原始工程变动,自动分析所有需要打包的资源,并与上个版本生成差异补丁。

补丁生成后会上传到CDN上,进行后续的测试和发布流程。

基于这套方案的热更构建流程,热更补丁的启动构建、发布均由版本PM控制,无需程序参与,中间流程是全自动化的。

运行时

运行时需要实现补丁的动态下载流程,最好在下载完毕后再统一执行挂载:

热更发布的补丁的优先级是递增的:

  1. 补丁的优先级比安装包内资源及全量下载资源更高
  2. 最新的补丁(x.3)比次新的补丁(x.2)优先级更高,确保最新的补丁能够覆盖旧资源

这样就能确保最新的资源能覆盖旧的,实现更新的目标。

更新能力

可更新内容

资源

游戏内的绝大部分资源都是可以热更的(无法更新的内容在后面有介绍),对资产的主动修改都能够正常地打包,包含修改材质影响到的shader都能够增量追踪到。

并且HotPatcher能够分析引起的被动变更的资源,也同样会打包。

下面的这些情况,就属于被动变更(HotPatcher会自动追踪):

  1. 父蓝图修改,影响到子蓝图
  2. UMG基础控件修改,影响到所有以Instanced方式引用它的控件
  3. 母材质的修改,影响到它的所有材质实例
  4. ….

HotPatcher会根据主动变更的资产,分析它们引起的所有被动变更列表,还会检查被动变更的资源存在于基础版本中(避免带入不必要的新增资源,只影响变化的部分)。

另外,还需要尽可能地减少热更完成前加载的资源(如引擎依赖的关键资源热更界面的资源等),这些无法热更,需要尽可能减少。
为了方便排查和清理非必要的资产,我在HotPatcher中提供了辅助函数,可以在启动时记录所有热更前加载的资产,这样就可以精确地知道哪些资源无法热更,也能够在后续的前置检查中把它们给暴露出来。

文件

游戏中需要用到的绝大部分非资产文件都是可以热更的:

  1. 脚本代码(lua/ts/py etc.)
  2. db
  3. pb
  4. 多语言(game.locres)
  5. 语音(wwise/wem)
  6. PSO
  7. 其他自定义的非资产文件

只要它们能被打包追踪到,并且在热更完成之后才加载(或可重载),都是可以热更的。如本地化等还需要对引擎做一些改造延迟到热更后加载,这个需要根据业务情况自己评估。

ini

我之前的一篇文章有过详细介绍:UE 热更新:Config 的重载与应用

简单地说,通常对Ini的更新支持这三种配置情况:

  1. ConsoleVariables

    1
    2
    [ConsoleVariables]
    launcher.precompile.pso=1
  2. Object Config

    1
    2
    3
    [/Script/UdpMessaging.UdpMessagingSettings]
    EnableTransport=True
    bAutoRepair=True
  3. DevicesProfiles

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [iPhoneXXX DeviceProfile]
    .CVars=sg.ViewDistanceQuality=2
    .CVars=sg.AntiAliasingQuality=2
    .CVars=sg.ShadowQuality=2
    .CVars=sg.PostProcessQuality=2
    .CVars=sg.TextureQuality=2
    .CVars=sg.EffectsQuality=2
    .CVars=sg.FoliageQuality=2
    .CVars=sg.ResolutionQuality=100.0

注:IOS无法动态修改r.MobileContentScaleFactor。Android会出现点击偏移。所以如果想要动态修改DeviceProfiles,不能修改MobileContentScaleFactor的值,需要自己处理延迟到下次启动。

除此之外,在引擎启动时加载的ini配置,时机太早无法通过热更下发,需要通过重新出包进行修改(虽然把补丁放到自动挂载目录也可以,但这是一个危险行为,不建议这么做,后面热更安全部分会具体介绍)。

建议:线上版本的ini中的DeviceProfiles中的配置,非必要不改。如必须要修改,需在发热更时实际修改的内容具体评估能否热更以及风险。

PSO

虽然UE提供了在打包时就把PSO打进安装包的机制,但是我们并未采用,因为限定构建安装包之前采集PSO的流程我觉得不够灵活。

我们目前的PSO流程,是与构建安装包无关的:

  1. 构建安装包时,没有任何PSO相关的东西参与
  2. 安装包打出后,自动化采集PSO
  3. 采集完毕,自动化生成stable.upipelinecache并可以作为热更发布

这样就把PSO作为一个独立的流程拆分出来了,并不是一个前置要求了。而且也能够实现直接实现PSO的热更新(后续有时间写一篇PSO热更的内容)。可以在发布了几个热更版本之后重新采集PSO,并热更发布。

删除文件

绝大部分情况下,我们只需要关注新增和修改的部分。但是有人会问,如果我把资产或文件删除了,能够通过热更下发到这个删除的行为吗?

可以的!但是对于UFS层面的删除而言,要实现的并不是真实地把文件从用户磁盘上物理消除。因为文件是被打包到PAK中的,如果想要从PAK完全删除,需要重新生成PAK,这在本地执行有些风险。

而是通过让UFS找不到真实的文件,从而起到“删除”的作用。

分析引擎的代码能够知道,可以通过UnrealPak的response file文件,传递-delete参数:

1
"../../../Blank425/Blank425.uproject" "../../../Blank425/Blank425.uproject" -delete

将它打包为Pak时,其实是添加了一个空的Entry,并且被标记为了Deleted:

通过UnrealPak的-list查看,它也像普通的文件一样,不过比较特殊,没有大小、offset为0,SHA1也为0:

i

在运行时从UFS已挂载的PAK里查找文件时,如果标记了delete的Pak优先级比较高,就会触发FoundDeleted:

还可以添加多个删除标记:

1
2
3
4
5
"../../../Blank245/Content/StarterContent/Maps/StarterMap.umap" "../../../Blank245/Content/StarterContent/Maps/StarterMap.umap" -delete
"../../../Blank245/Content/StarterContent/Maps/StarterMap.uexp" "../../../Blank245/Content/StarterContent/Maps/StarterMap.uexp" -delete
"../../../Blank425/Content/DefaultMap.umap" "../../../Blank425/Content/DefaultMap.umap" -delete
"../../../Blank425/Content/DefaultMap.uexp" "../../../Blank425/Content/DefaultMap.uexp" -delete
"../../../Blank425/Blank425.uproject" "../../../Blank425/Blank425.uproject" -delete

删除的文件列表同样会参与MountPoint的计算:

其实,如果想要标记一个资源被删除,真实的物理文件都不需要存在,因为它不会被真的打包到pak里去。只是通过这个MountPath的路径,创建出一个空的PakEntry。

让UFS查找文件时优先找到这个空的PakEntry,加载不到真实的资源,就是我们要实现的“删除”需求了。

文件标记删除,PAK的创建过程:

  1. 从PakList中读取所有行
  2. 检查是否有-delete的token
  3. 若有,则把Entrie标记为bIsDeleted
  4. CollectFilesToAdd中,对于标记bIsDeleted的文件,会直接跳过,不会去查找实际的磁盘文件
  5. CreatePakFile中,会检查FilesToAdd的每个元素的bIsDeleted,并会给对应的PakEntry添加Flag_Deleted的标记,用于标识被删除。并且不会把该元素指向的本地文件给打包到Pak内(这也是随便指定一个路径也可以标记为删除的原因)

这样就实现了从paklist.txt -> UnrealPak -> Deleted.pak -> 运行时加载PakEntry这样的一套流程了。

HotPatcher已经自动化了这一过程,对删除的资源和文件,如果Patch的配置中,使用了bIgnoreDeletedFiles=false,则会在打出的补丁中,将它们标记删除。


这样就能够实现通过热更下发“删除文件”的行为了。

无法热更的内容

无法热更的内容,是指无法通过在游戏内动态下载更新给玩家,只能重新打完整包发布。

无法热更新的部分:

  1. 原生/C++代码(影响Runtime的代码,可转发到VM执行的不在此列)
  2. 第三方库
  3. 包的签名、Entitlements
  4. 包内的文件(如GCloudVoice的模型文件)
  5. Android的Manifest、IOS的PLIST等

不建议热更新的部分:

  1. uproject、uplugin
  2. ush/usf/global shader
  3. WorldGridMaterial等基础材质的修改(如/Engine/EngineMaterils,pak的挂载与ShaderLibrary加载时机不同可能会导致crash)
  4. 热更完成前,已加载的资源(引擎依赖的关键资源热更界面的资源等,这些无法热更,需要尽可能减少,保持依赖最小化)

除了完全无法热更新的之外,剩下的这些非要更新也不是不可以,只需要下载后放入自动挂载目录,设定一个比安装包内资产更高的优先级,然后重启生效。
因为UFS本身的机制,就是去加载当前时机下优先级最高的PAK中的文件,所以只要我们能保证引擎一启动,补丁的优先级就最高,那么UFS内的所有文件都是可以生效的。

但为了安全考虑,我依然把他们列入无法热更的列表中(机制可以提供这样的能力,但不能轻易使用)。因为商业化产品的稳定与安全性是更重要的因素,我们需要在更新能力和安全做一些取舍,下一小结会具体介绍。

热更安全

因为热更新非常重要,它是进入游戏的入口,也是版本迭代的基石,所以如何让热更本身具有比较强的健壮性是非常重要的。
有些项目可能踩过类似的坑,一个热更补丁下发下去,导致游戏无法正常启动了,只能重新换包。所以我把热更安全提到了一个非常重要的高度,哪怕牺牲一些更新能力也在所不惜。

根据我的实践经验,对于热更安全的关注应该包含下面几个方面:

  1. 更新的内容应该是稳定无害的
  2. 更新的内容是可以撤销的
  3. 更新的内容不会影响到下次程序启动
  4. 更新的内容不会影响到其他热更能力

所以,为了做到这一点,我做了下面的这些工作:

  1. 完全不采用自动挂载机制,所有的补丁都由业务逻辑控制挂载,完全杜绝自动挂载行为。因为自动挂载时机太早了,如果出现问题,就是致命的。
  2. 在1的基础上,拒绝强依赖重启生效的行为,所有的更新内容都必须实时生效(可根据项目情况评估,如对r.MobileContentScaleFactor等非资产属性的处理)
  3. 统一PAK与ShaderLibrary的挂载/加载顺序以及加载时机,避免Shader和Pak内资产对不上的情况
  4. 完全按照优先级来控制优先级
  5. 每次初始启动游戏,直至热更完成,都跟新安装的情况一致,不做特殊逻辑。这样能够确保不管我们更新下去任何内容,都能正常走到热更流程,这样如果补丁有问题还有补救的空间。

除了热更本身之外,资源管理也需要严格把控:

  1. 确保每一次热更都是可以精确追溯的,精确记录每一个变更的资源、文件的信息,以及它们的cooked的信息,以及记录当前版本的仓库信息、生成的补丁信息。
  2. 检查每一次热更的资产规范,避免资产修改引起的异常带入现网。

对于资源管理而言,我们在每一次热更补丁构建之后,都充分记录了当前版本的信息:

以及归档了补丁和Metadatas:

这样可以随时基于这些数据去回溯每个热更版本的变动情况,补丁生成后也可以进行检查是否符合预期。

另外,对于参与热更的资产,我们会对所有参与进包的进行资产检查,如果有触发规则,则会同步归档并群提醒:


这样就能够清晰地知道当前补丁内有哪些潜在风险了,方便评估补丁的负面影响。
关于资源检查的部分我之前也写了好几篇内容了,感兴趣的话,可以参考资源管理分类里的几篇文章。

结语

博客内的相关文章:热更新系列资源管理系列,本文提及的一些内容,会在这些文章中有更详细的介绍,感兴趣的可以前往查看。

本篇文章介绍了我在UE热更新的更新能力和更新安全方面的一些思考,根据实际项目上线的情况热更机制是零故障的,可以作为一种实践参考。

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:UE热更新:更新能力与热更安全
文章作者:查利鹏
发布时间:2025/07/17 14:03
本文字数:4.5k 字
原始链接:https://imzlp.com/posts/66804/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!