Patch 的挂载和资源加载

这两天在看 UE 打包 Patch 作为热更的方案,UE 打包 Patch 的逻辑是这样的:

  1. 如果在版本 1.0 中,场景 Scene01 中对资源 A 有直接引用(放在场景中),在修改了 A 资源之后,打包 Patch0.1,想要让场景 Scene01 中资源 A 的改动也生效,则需要把 Scene01 也需要打到 Patch 中,因为是直接放置在场景中的,场景记录了该资源 A 的引用信息,这个不会随着资源 A 的更新而更新,加载时会产生 AsyncLoading.cpp 中的报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
[2019.11.13-06.03.27:766][68]LogLinker: Warning: The file '../../../GWorld/Content/PAK/Cube.uasset' contains unrecognizable data, check that it is of the expected type.
[2019.11.13-06.03.27:770][68]LogStreaming: Error: ****DumpDependencies [Dependencies]:
[2019.11.13-06.03.27:771][68]LogStreaming: Error: Export 8 /Game/Map2.Map2:PersistentLevel.Cube_2
[2019.11.13-06.03.27:772][68]LogStreaming: Error: Linker is ../../../GWorld/Content/Map2.umap
[2019.11.13-06.03.27:774][68]LogStreaming: Error: Dep C_BEFORE_S Export 38 /Game/Map2.Map2:PersistentLevel.Cube_2.Cube (class StaticMeshComponent)
[2019.11.13-06.03.27:774][68]LogStreaming: Error: Dep C_BEFORE_S Export 29 /Game/Map2.Map2:PersistentLevel.Cube_2.DefaultSceneRoot (class SceneComponent)
[2019.11.13-06.03.27:775][68]LogStreaming: Error: Dep S_BEFORE_C Import 3 /Game/PAK/Cube.Cube_C
[2019.11.13-06.03.27:776][68]LogStreaming: Error: Dep S_BEFORE_C Import 42 /Game/PAK/Cube.Default__Cube_C
[2019.11.13-06.03.27:776][68]LogStreaming: Error: Dep C_BEFORE_C Export 23 /Game/Map2.Map2:PersistentLevel (class Level)
[2019.11.13-06.03.27:777][68]LogStreaming: Error: Missing Dependency, request for /Game/PAK/Cube.Cube_C but it hasn't been created yet.
[2019.11.13-06.03.27:778][68]LogStreaming: Error: Could not find class Cube_C to create Cube_2
[2019.11.13-06.03.27:778][68]LogStreaming: Error: Could not find outer Cube_2 to create DefaultSceneRoot
[2019.11.13-06.03.27:779][68]LogStreaming: Error: Could not find outer Cube_2 to create Cube

解决这个问题的办法是通过 SoftClassReference 动态加载资源,比如直接 SpawnActorFromClass 直接指定 Class 为具体的类也是会产生上面的错误,但是用 SoftClassReference 可以避免这个错误(因为 SoftClassReference 本质就是存了个路径):

  1. 如果在版本 1.0 中,资源 A 引用了其他资源 B,在 Patch1.0 中将资源 A 中引用的 B 换为了资源 C,则该 Patch 的 pak 中会有资源 A/B/C 三个,因为资源的引用关系变了,但是如果不改动引用关系只是改动 B 中的信息,然后只 Patch 资源 B 是可以的。

直接使用 Unreal.pakCooked的资源打包为 pak 文件需要注意一点:修改 Mount 的路径。
如果说直接使用下列命令:

1
$ UnrealPak.exe New_0_P.pak -create=D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\PAK

则打出来的 pak 的 Mount 路径为:

1
D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\PAK

而查看使用引擎打包的 Pak 和 patch 的 pak 都是相对路径的,这个性格对路径就是 UE 的工程里资源的路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
D:\GWorldPackage\WindowsNoEditor\GWorld\Content\Paks>UnrealPak.exe GWorld-WindowsNoEditor.pak -list
LogPaths: Warning: No paths for game localization data were specifed in the game configuration.
LogPakFile: Display: Using command line for crypto configuration
LogPakFile: Display: Added 0 entries to add to pak file.
LogPakFile: Display: Mount point ../../../
LogPakFile: Display: "Engine/Content/BasicShapes/BasicShapeMaterial.uasset" offset: 0, size: 749 bytes, sha1: E028879C8856192DC648EA4C422C145E38951DA9, compression: Zlib.
LogPakFile: Display: "Engine/Content/EditorMaterials/PreviewShadowIndicator.uasset" offset: 819, size: 496 bytes, sha1: 80BCDD2FF2674FED35763CD6A8C93C5A0EC27442, compression: Zlib.
// ....
LogPakFile: Display: "GWorld/AssetRegistry.bin" offset: 16928768, size: 2759 bytes, sha1: 35E6A5C7667C23FDDFD1F3BDC5535FA4E3BFF24F, compression: Zlib.
LogPakFile: Display: "GWorld/Config/DefaultEngine.ini" offset: 16932864, size: 2378 bytes, sha1: 7E33CA6CC805CC1A6E2FF84EF3010CEACBF99E04, compression: Zlib.
LogPakFile: Display: "GWorld/Config/DefaultGame.ini" offset: 16935312, size: 523 bytes, sha1: DDD788C5E2C9446AB11E2C8BC7D83EA24CB85AA9, compression: Zlib.
LogPakFile: Display: "GWorld/Config/DefaultInput.ini" offset: 16935905, size: 753 bytes, sha1: 6C03DCDAE9605BEB9CB2EB250631D6AA2C2A4336, compression: Zlib.
LogPakFile: Display: "GWorld/Content/Map2.uexp" offset: 16936960, size: 339962 bytes, sha1: 6605D83E8355CC2B4D20DE31C3D6182324BA556C, compression: Zlib.
LogPakFile: Display: "GWorld/Content/Map2.umap" offset: 17278976, size: 5541 bytes, sha1: A400A39FD85529E832750CB481D9F845E4C4A74B, compression: Zlib.
LogPakFile: Display: "GWorld/Content/Map2_BuiltData.uasset" offset: 17285120, size: 795 bytes, sha1: 1388AE2CCFA55587DD0A3120CA9679AAF8FC9336, compression: Zlib.
LogPakFile: Display: "GWorld/Content/Map2_BuiltData.ubulk" offset: 17287168, size: 8637 bytes, sha1: 7D8E7AE0D100415553DF6F222B9C35EBACF4BE28, compression: Zlib.
LogPakFile: Display: "GWorld/Content/Map2_BuiltData.uexp" offset: 17297408, size: 586603 bytes, sha1: 3B776E46005F6A4BEECA505674ED7FB0B8C432D6, compression: Zlib.
// ...
LogPakFile: Display: "GWorld/Content/ShaderArchive-GWorld-PCD3D_SM5.ushaderbytecode" offset: 23377920, size: 1207165 bytes, sha1: 44474138CD033FEF89EDFA86A480E569615A8850, compression: None.
LogPakFile: Display: "GWorld/GWorld.uproject" offset: 24585135, size: 323 bytes, sha1: D2E183A541A10DEEC5C87533400FFC0E89CA3B83, compression: Zlib.
LogPakFile: Display: "GWorld/Plugins/GWorld.upluginmanifest" offset: 24586240, size: 5022 bytes, sha1: CFDB721323DABBA1C7C7ABE6CB967852EA2AC23B, compression: Zlib.
LogPakFile: Display: "GWorld/Plugins/LowEntryExtStdLib/LowEntryExtStdLib.uplugin" offset: 24591332, size: 434 bytes, sha1: 5CD75BB5212731BEA8E0A552C99B69B259FA489C, compression: Zlib.
LogPakFile: Display: "GWorld/Plugins/UIFramework/UIFramework.uplugin" offset: 24591836, size: 236 bytes, sha1: C9426AD575AA6ADECEF5C1AE79CF4A49304C3350, compression: Zlib.
LogPakFile: Display: "GWorld/Plugins/VaRestPlugin/VaRestPlugin.uplugin" offset: 24592384, size: 393 bytes, sha1: 28B9C2F3CEB767F4A8D5D466CB8C6EEF772CDA43, compression: Zlib.
LogPakFile: Display: 1257 files (24050765 bytes), (0 filtered bytes).
LogPakFile: Display: Unreal pak executed in 1.746391 seconds

解决方案 是可以通过 UnrealPak.exe-create来指定一个 txt 文件来指定某个资源的绝对路径换算为相对路径:

1
2
"D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\PAK\BasicShapeMaterial_3.uasset" "../../../GWorld/Content/PAK/BasicShapeMaterial_3.uasset" -compress
"D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\PAK\BasicShapeMaterial_3.uexp" "../../../GWorld/Content/PAK/BasicShapeMaterial_3.uexp" -compress

可以看作有数列的表,第一列是资源的 绝对路径 ,第二列是 该绝对路径资源对应的相对路径 ,后面是 参数 (可以是-compress/-encrypt),生成的代码在Source/Programs/AutomationTool/Script/CopyBuildToStagingDirectory.Automation.cs 中。

1
2
3
4
5
6
7
8
9
10
11
12
D:\>UnrealPak.exe D:\NEW_6_P.pak -create="D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\New.txt"
LogPaths: Warning: No paths for game localization data were specifed in the game configuration.
LogPakFile: Display: Using command line for crypto configuration
LogPakFile: Display: Loading response file D:\GWorldClient\Saved\Cooked\WindowsNoEditor\GWorld\Content\New.txt
LogPakFile: Display: Added 2 entries to add to pak file.
LogPakFile: Display: Collecting files to add to pak file...
LogPakFile: Display: Collected 2 files in 0.00s.
LogPakFile: Display: Encrypting using embedded key
LogPakFile: Display: Added 2 files, 8549 bytes total, time 0.00s.
LogPakFile: Display: Compression summary: 13.27% of original size. Compressed Size 7981 bytes, Original Size 60159 bytes.
LogPakFile: Display: Encryption - DISABLED
LogPakFile: Display: Unreal pak executed in 0.010672 seconds

检查打包出来的New_6_P.pak

1
2
3
4
5
6
7
8
9
D:\>UnrealPak.exe NEW_6_P.pak -list
LogPaths: Warning: No paths for game localization data were specifed in the game configuration.
LogPakFile: Display: Using command line for crypto configuration
LogPakFile: Display: Added 0 entries to add to pak file.
LogPakFile: Display: Mount point ../../../GWorld/Content/PAK/
LogPakFile: Display: "BasicShapeMaterial_3.uasset" offset: 0, size: 753 bytes, sha1: 63E00757694E91070403390CFB808829E13D01FF, compression: Zlib.
LogPakFile: Display: "BasicShapeMaterial_3.uexp" offset: 823, size: 7228 bytes, sha1: BD3C0B302FC49790327CF804F2B644F01A14EFCD, compression: Zlib.
LogPakFile: Display: 2 files (7981 bytes), (0 filtered bytes).
LogPakFile: Display: Unreal pak executed in 0.001987 seconds

可以看到变成了相对路径了。