前言
引擎版本 UE 5.32源码
需求:打包时将非uasset资产(如Lua脚本)单独打入指定chunk包
遇到过的问题:
- PakFileRules文件被排除
- 设置了指定的chunkId但无效
流程
修改DefaultGame.ini,添加允许的配置文件目录
对AutomationTool\AutomationUtils\DeploymentContext.cs进行修改
对Scripts/CopyBuildToStagingDirectory.Automation.cs 进行修改
private struct PakFileRules
{
// Name of config section
public string Name;
// Combined filter from all +Files= entries
public FileFilter Filter;
// An alternative and efficient way to match files
public HashSet<string> ExactFileMatches = new();
// To use exact file match or file filter
public bool bUseExactFilePathMatch = false;
// Rather to exclude entirely from paks
public bool bExcludeFromPaks = false;
// Rather to allow overriding the chunk manifest, if false will only modify loose files
public bool bOverrideChunkManifest = false;
// Whether pak file rule is disabled
public bool bDisabled = false;
// List of pak files to use instead of manifest
public List<string> OverridePaks = new();
// Whether this rule is defined for content-on-demand
public bool bOnDemand = false;
public bool bStageLoose = false;
// Encryption key
public string EncryptionKeyGuid = "";
public PakFileRules()
{
Name = "";
Filter = new FileFilter();
}
public static bool IsMatch(PakFileRules PakRules, KeyValuePair<string, string> StagingFile)
{
bool bMatched = !PakRules.bDisabled &&
((!PakRules.bUseExactFilePathMatch && PakRules.Filter != null && PakRules.Filter.Matches(StagingFile.Key)) ||
(PakRules.bUseExactFilePathMatch && PakRules.ExactFileMatches != null && PakRules.ExactFileMatches.Contains(StagingFile.Value)));
return bMatched;
}
};
/// <summary>
/// Reads Default/BasePakFileRules.ini and returns all PakFileRules objects that apply for this deployment
/// </summary>
/// <param name="Params"></param>
/// <param name="SC"></param>
private static List<PakFileRules> GetPakFileRules(ProjectParams Params, DeploymentContext SC)
{
// if we want to ignore the rules while staging, just don't read any rules in!
bool bWarnedAboutMultipleTargets = false;
bool bFoundConfig = false;
List<ConfigFile> ConfigFiles = new List<ConfigFile>();
FileReference BaseConfigFileReference = FileReference.Combine(SC.EngineRoot, "Config", "BasePakFileRules.ini");
if (FileReference.Exists(BaseConfigFileReference))
{
ConfigFiles.Add(new ConfigFile(BaseConfigFileReference));
bFoundConfig = true;
}
FileReference ProjectConfigFileReference = FileReference.Combine(SC.ProjectRoot, "Config", "DefaultPakFileRules.ini");
if (FileReference.Exists(ProjectConfigFileReference))
{
ConfigFiles.Add(new ConfigFile(ProjectConfigFileReference));
bFoundConfig = true;
}
if (!bFoundConfig)
{
return null;
}
bool bChunkedBuild = SC.PlatformUsesChunkManifests && DoesChunkPakManifestExist(Params, SC);
ConfigHierarchy PakRulesConfig = new ConfigHierarchy(ConfigFiles);
List<PakFileRules> RulesList = new List<PakFileRules>();
foreach (string SectionName in PakRulesConfig.SectionNames)
{
Logger.LogInformation("Building PakFileRules for Section {0}", SectionName);
bool bOnlyChunkedBuilds = false;
if (!PakRulesConfig.TryGetValue(SectionName, "bOnlyChunkedBuilds", out bOnlyChunkedBuilds))
{
bOnlyChunkedBuilds = false;
}
bool bOnlyNonChunkedBuilds = false;
if (!PakRulesConfig.TryGetValue(SectionName, "bOnlyNonChunkedBuilds", out bOnlyNonChunkedBuilds))
{
bOnlyNonChunkedBuilds = false;
}
Logger.LogInformation("GetPakFileRules bChunkedBuild: {bChunkedBuild} bOnlyNonChunkedBuilds:{bOnlyNonChunkedBuilds}", bChunkedBuild, bOnlyNonChunkedBuilds);
if (bChunkedBuild && bOnlyNonChunkedBuilds)
{
continue;
}
if (!bChunkedBuild && bOnlyChunkedBuilds)
{
continue;
}
string PlatformString;
if (PakRulesConfig.TryGetValue(SectionName, "Platforms", out PlatformString))
{
string[] PlatformStrings = PlatformString.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
bool bMatches = false;
// Check platform string
foreach (string Platform in PlatformStrings)
{
if (SC.StageTargetPlatform.PlatformType.ToString().Equals(Platform, StringComparison.OrdinalIgnoreCase))
{
bMatches = true;
break;
}
else if (SC.StageTargetPlatform.IniPlatformType.ToString().Equals(Platform, StringComparison.OrdinalIgnoreCase))
{
bMatches = true;
break;
}
}
if (!bMatches)
{
Logger.LogInformation("No matching platform for PakFileRules for Section {0} : {1}, {2}", SectionName, SC.StageTargetPlatform.PlatformType.ToString(), SC.StageTargetPlatform.IniPlatformType.ToString());
continue;
}
}
string TargetString;
if (PakRulesConfig.TryGetValue(SectionName, "Targets", out TargetString))
{
string[] TargetStrings = TargetString.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
bool bMatches = false;
// Check target string
foreach (string Target in TargetStrings)
{
foreach (UnrealTargetConfiguration C in SC.StageTargetConfigurations)
{
if (C.ToString() == Target)
{
bMatches = true;
break;
}
}
}
if (!bMatches)
{
continue;
}
else if (SC.StageTargetConfigurations.Count > 0 && !bWarnedAboutMultipleTargets)
{
bWarnedAboutMultipleTargets = true;
Logger.LogInformation("Staging with more than one target, PakFileRules may apply too many rules!");
}
}
PakFileRules PakRules = new PakFileRules();
PakRules.Name = SectionName;
PakRulesConfig.TryGetValue(SectionName, "bExcludeFromPaks", out PakRules.bExcludeFromPaks);
PakRulesConfig.TryGetValue(SectionName, "bOverrideChunkManifest", out PakRules.bOverrideChunkManifest);
PakRulesConfig.TryGetValue(SectionName, "bDisabled", out PakRules.bDisabled);
string PakString;
PakRulesConfig.TryGetValue(SectionName, "OverridePaks", out PakString);
if (PakString != null && PakString.Length > 0)
{
PakRules.OverridePaks = PakString.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
}
else
{
PakRules.OverridePaks = null;
}
if (PakRules.bExcludeFromPaks && PakRules.OverridePaks != null)
{
Logger.LogWarning("Error in PakFileRules {0}, set to exclude but also sets override!", PakRules.Name);
continue;
}
else if (!PakRules.bExcludeFromPaks && PakRules.OverridePaks == null)
{
Logger.LogWarning("Error in PakFileRules {0}, set to include but did not specify paks!", PakRules.Name);
continue;
}
IReadOnlyList<string> FilesEnumberable;
if (PakRulesConfig.TryGetValues(SectionName, "Files", out FilesEnumberable))
{
// Only add if we have actual files, we can end up with none due to config overriding
PakRules.Filter = new FileFilter();
foreach (string FileFilter in FilesEnumberable)
{
Logger.LogInformation("Adding to PakFileRules for Section {0} : {1}", SectionName, FileFilter);
PakRules.Filter.AddRule(FileFilter);
}
Logger.LogInformation("GetPakFileRules PakRules: {PakRules}", PakRules);
RulesList.Add(PakRules);
}
}
return RulesList;
}
在 ProjectName/Config/下创建DefaultPakFileRules.ini文件
; 这些规则按顺序应用,对于每个文件,第一个匹配的规则被采纳,后续规则不再评估。
; [SectionName] ; 匹配规则名称
; bOverrideChunkManifest=false ; 如果为true,则允许覆盖来自cooker的分块清单分配。
; bExcludeFromPaks=false ; 如果为true,则完全从.pak文件中排除此文件,不能与OverridePaks同时使用。
; OverridePaks="pakchunk1" ; 如果设置,表示要将文件存放入哪个Pakchunk。
; Platforms="iOS,Android" ; 如果设置,此规则仅适用于这些平台。
; Targets="Shipping,Test" ; 如果设置,此规则仅适用于这些构建配置。
; bOnlyChunkedBuilds=true ; 如果设置,此规则仅适用于分块构建。
; bOnlyNonChunkedBuilds=true ; 如果设置,此规则仅适用于非分块构建。
; +Files=".../*FileMask*.*" ; 应用于的文件列表,使用C# FileFilter类的文件掩码。
[ExcludeContentForMobile]
; 在移动平台上排除特定的大型纹理文件,此规则原先位于CopyBuildToStagingDirectory.cs中。
; 可以通过在游戏的DefaultPakFileRules.ini中使用相同的段落,并添加新的段落来增加规则。
; 要移除这条规则,可以使用!Files清除文件路径列表。
Platforms="Android,iOS,tvOS" ; 此规则仅适用于Android、iOS和tvOS平台。
bExcludeFromPaks=true ; 将这些文件完全排除在.pak文件之外。
bOverrideChunkManifest=true ; 允许覆盖从cooker获取的分块清单分配。
+Files=".../Engine/Content/EngineMaterials/DefaultBloomKernel.*" ; 排除DefaultBloomKernel相关的所有文件。
+Files=".../Engine/Content/EngineMaterials/DefaultCalibrationColor.*" ; 排除DefaultCalibrationColor相关的所有文件。
+Files=".../Engine/Content/EngineMaterials/DefaultCalibrationGrayscale.*" ; 排除DefaultCalibrationGrayscale相关的所有文件。
+Files=".../Engine/Content/EngineMaterials/PPM_DefaultCalibrationColor.*" ; 排除PPM_DefaultCalibrationColor相关的所有文件。
+Files=".../Engine/Content/EngineMaterials/PPM_DefaultCalibrationGrayscale.*" ; 排除PPM_DefaultCalibrationGrayscale相关的所有文件。其中
其中overridePaks 不能设置为已经存在的chunkID ,如果设置了 会在CreatePaksUsingChunkManifests中被判断返回掉,导致加入基础pak包
判断chunkdeinitions中是否有对应的chunkName,如果有就continue
CreatePaksUsingChunkManifests中处理了添加资源到chunk的逻辑,
通过PakfileRules添加的打包规则,会通过ApplyPakFileRules函数找到对应的chunk包添加进去。读取所有的chunk匹配规则,IsMatch函数确定文件是否符合筛选标准
使用projectLauncher进行打包
打基础包这样设置
打热更包这样设置
成功把lua脚本文件打入指定的chunk包啦
UE持续学习中,欢迎大佬指正错误之处。
个人踩坑记录,未经同意谢绝转载谢谢!
参考
https://zhuanlan.zhihu.com/p/523509674
https://zhuanlan.zhihu.com/p/401132377
https://zhuanlan.zhihu.com/p/682342893
https://link.zhihu.com/?target=https%3A//dev.epicgames.com/documentation/zh-cn/unreal-engine/how-to-create-a-patch-in-unreal-engine
https://link.zhihu.com/?target=https%3A//ue5wiki.com/wiki/61ac95b3/