背景
某次调整入包地图时,发现某张地图没有成功打包。于是调查并做此记录。
操作过程:
第一次构建时,配置DefaultGame.ini如下:
+MapsToCook=(FilePath="M的路径")
第二次构建时,配置DefaultGame.ini如下:
; (注释掉的意思)+MapsToCook=(FilePath="M的路径")
+DirectoriesToNeverCookWildcard=M的路径
第三次构建时,配置DefaultGame.ini如下:
+MapsToCook=(FilePath="M的路径")
; +DirectoriesToNeverCookWildcard=M的路径
表现:第一次构建时,包体里有M;第二次构建时,包体里没有M;第三次构建时,包体里依然没有M,预期有M,因此属于错误情况。
接下来我们看为何M在第三次时没能成功Cook。
结论先行
1、根据 《IgnoreIniSettingsOutOfDate开关的介绍》 ,由于我所在的项目里,打开了 IgnoreIniSettingsOutOfDate ,因此不会在 "项目的打包配置(尤其是Cook设置)发生重大变更",走UE4自带的 "清理整个 Saved目录" 的逻辑。
2、DirectoriesToNeverCookWildcard 的反注释,属于"重大变更" ,因此需要清理Saved目录。
3、解决方法:
● 以后都关闭 IgnoreIniSettingsOutOfDate ,遇到这类问题就不用担心出问题。
● 保持 IgnoreIniSettingsOutOfDate ,当 DirectoriesToNeverCookWildcard 反注释时,主动清理 Saved目录(搞清楚所有原理后,我们能更加精确地得知,只需要清理 Saved/Cooked 即可)。
接下来我们看看原理细节。
IdenticalUncookedPackages
在真正开始Cook前,UE4会根据上一次的 原始资源状态(记录在 D:\我的工程\Saved\Cooked\平台名LinuxServer\我的工程名\Metadata\DevelopmentAssetRegistry.bin )(代码见 PreviousPackageData),和当前的资源状态,做比较,其中一种情形如下,当原始资源哈希没有变化,且曾经没有真正产出到 Saved/Cooked 目录中 (PreviousPackageData->DiskSize < 0)时,该资源归属于 IdenticalUncookedPackages 。
// 位于void FAssetRegistryGenerator::ComputePackageDifferences(......) EngineSource\Engine\Source\Editor\UnrealEd\Private\Commandlets\AssetRegistryGenerator.cpp
......
else if (CurrentPackageData->PackageGuid == PreviousPackageData->PackageGuid)
{
if (PreviousPackageData->DiskSize < 0)
{
IdenticalUncookedPackages.Add(PackageName);
}
else
{
IdenticalCookedPackages.Add(PackageName);
}
}
......
而从下面的代码可以看出IdenticalUncookedPackages 的含义------此资源会直接跳过cook,直接加入到 CookedPackages队列(已完成)、KeptPackages队列(未变化的资源队列 | 增量跳过队列)中,且++NumMarkedFailedSaveKept 也预示着,这个资源此次未能保存成功。
直白来说:"一旦因某种合理的原因没有Cook该资源",且Cook全流程完成了,那么后续构建遇到此资源,也会直接跳过,不会考虑Cook该资源。直到此原始资源MD5发生变更。
// 位于 void UCookOnTheFlyServer::PopulateCookedPackagesFromDisk(......) EngineSource\Engine\Source\Editor\UnrealEd\Private\CookOnTheFlyServer.cpp
// Register identical uncooked packages from previous run
for (FName UncookedPackage : IdenticalUncookedPackages) // 直接认为 kept(也就是不用cook,直接放入到 cooked中)
{
const FName UncookedFilename = PackageNameCache->GetCachedStandardPackageFileFName(UncookedPackage);
TArray<FName> PlatformNames;
PlatformNames.Add(PlatformFName);
ensure(PackageTracker->CookedPackages.Exists(UncookedFilename, PlatformNames, false) == false);
PackageTracker->CookedPackages.Add(FFilePlatformCookedPackage(UncookedFilename, MoveTemp(PlatformNames)));
KeptPackages.Add(UncookedPackage);
++NumMarkedFailedSaveKept;
}
代入到背景case中则是:
● "某种合理的原因"------背景介绍里第二次构建的配置,添加了 DirectoriesToNeverCookWildcard。
● "Cook全流程完成了"------背景介绍里第二次构建是成功的构建,并不属于异常。
● "后续构建"------第三次构建。
因此解决方法是删除 Saved/Cooked ,目的是让所有Cooked产物都删除掉,且让 PreviousPackageData 也删除掉(失效掉),因为PreviousPackageData 即 D:\我的工程\Saved\Cooked\平台名LinuxServer\我的工程名\Metadata\DevelopmentAssetRegistry.bin 也在 Saved/Cooked 中。
最佳实践
当你的工程很大,打开了 IgnoreIniSettingsOutOfDate 时,并且修改了 "进包配置",例如调整了 DirectoriesToNeverCookWildcard,那么需要删除掉 Saved/Cooked 。这个过程最好是自动化。