Files
YooAsset/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateManifest.cs

426 lines
19 KiB
C#
Raw Normal View History

2026-01-12 11:09:27 +08:00
using System;
2023-09-20 16:09:52 +08:00
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
2023-09-20 16:09:52 +08:00
namespace YooAsset.Editor
{
2026-01-12 11:09:27 +08:00
/// <summary>
/// 资源清单上下文,用于在构建流程中传递反序列化后的补丁清单。
/// </summary>
[ContextObject]
public class ManifestContext
2023-12-21 19:29:26 +08:00
{
internal PackageManifest Manifest;
}
2026-01-12 11:09:27 +08:00
/// <summary>
/// 创建补丁清单的任务抽象基类,负责序列化清单并写入输出目录。
/// </summary>
2023-12-21 19:29:26 +08:00
public abstract class TaskCreateManifest
{
2024-12-24 17:35:26 +08:00
private readonly Dictionary<string, int> _cachedBundleIndexIDs = new Dictionary<string, int>(10000);
2023-12-21 19:29:26 +08:00
private readonly Dictionary<int, HashSet<string>> _cacheBundleTags = new Dictionary<int, HashSet<string>>(10000);
/// <summary>
/// 创建补丁清单文件到输出目录
/// </summary>
2026-01-12 11:09:27 +08:00
/// <param name="processBundleDepends">是否处理资源包依赖关系</param>
/// <param name="processBundleTags">是否处理资源包标签</param>
/// <param name="replaceAssetPathWithAddress">是否用可寻址地址替换资源路径</param>
/// <param name="context">构建上下文</param>
2025-10-09 16:14:31 +08:00
protected void CreateManifestFile(bool processBundleDepends, bool processBundleTags, bool replaceAssetPathWithAddress, BuildContext context)
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
_cachedBundleIndexIDs.Clear();
_cacheBundleTags.Clear();
2023-12-21 19:29:26 +08:00
var buildMapContext = context.GetContextObject<BuildMapContext>();
var buildParametersContext = context.GetContextObject<BuildParametersContext>();
var buildParameters = buildParametersContext.Parameters;
string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
2024-12-13 11:00:30 +08:00
// 检测资源包哈希冲突
CheckBundleHashConflict(buildMapContext);
2023-12-21 19:29:26 +08:00
// 创建新补丁清单
PackageManifest manifest = new PackageManifest();
2026-01-12 11:09:27 +08:00
manifest.FileVersion = PackageManifestConsts.FileVersion;
2023-12-21 19:29:26 +08:00
manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
2025-08-28 19:02:08 +08:00
manifest.SupportExtensionless = buildMapContext.Command.SupportExtensionless;
2023-12-21 19:29:26 +08:00
manifest.LocationToLower = buildMapContext.Command.LocationToLower;
2026-01-12 11:09:27 +08:00
manifest.IncludeAssetGuid = buildMapContext.Command.IncludeAssetGUID;
2025-10-09 16:14:31 +08:00
manifest.ReplaceAssetPathWithAddress = replaceAssetPathWithAddress;
2023-12-21 19:29:26 +08:00
manifest.OutputNameStyle = (int)buildParameters.FileNameStyle;
2024-12-24 17:35:26 +08:00
manifest.BuildBundleType = buildParameters.BuildBundleType;
2023-12-21 19:29:26 +08:00
manifest.BuildPipeline = buildParameters.BuildPipeline;
manifest.PackageName = buildParameters.PackageName;
manifest.PackageVersion = buildParameters.PackageVersion;
manifest.PackageNote = buildParameters.PackageNote;
2024-12-24 17:35:26 +08:00
manifest.AssetList = CreatePackageAssetList(buildMapContext);
manifest.BundleList = CreatePackageBundleList(buildMapContext);
// 1. 处理资源清单的资源对象
2026-01-12 11:09:27 +08:00
ProcessPackageAsset(manifest);
2023-12-21 19:29:26 +08:00
// 2. 处理资源包的依赖列表
2024-12-16 16:45:05 +08:00
if (processBundleDepends)
2023-12-21 19:29:26 +08:00
ProcessBundleDepends(context, manifest);
// 3. 处理资源包的标签集合
2024-12-16 16:45:05 +08:00
if (processBundleTags)
2023-12-21 19:29:26 +08:00
ProcessBundleTags(manifest);
2026-01-12 11:09:27 +08:00
// 4. 处理首包资源包
2025-02-21 15:29:42 +08:00
if (processBundleDepends)
2025-10-09 16:14:31 +08:00
{
// 注意:初始化资源清单建立引用关系
manifest.Initialize();
2025-02-21 15:29:42 +08:00
ProcessBuiltinBundleDependency(context, manifest);
2025-10-09 16:14:31 +08:00
}
2025-06-20 11:36:29 +08:00
// 创建资源清单文本文件
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
string fileName = YooAssetConfiguration.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion);
2023-12-21 19:29:26 +08:00
string filePath = $"{packageOutputDirectory}/{fileName}";
2026-01-12 11:09:27 +08:00
PackageManifestHelper.SerializeManifestToJson(filePath, manifest);
BuildLogger.Log($"Create package manifest file: '{filePath}'.");
2023-12-21 19:29:26 +08:00
}
2025-06-20 11:36:29 +08:00
// 创建资源清单二进制文件
2023-12-21 19:29:26 +08:00
string packageHash;
2025-02-21 15:29:42 +08:00
string packagePath;
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(buildParameters.PackageName, buildParameters.PackageVersion);
2025-02-21 15:29:42 +08:00
packagePath = $"{packageOutputDirectory}/{fileName}";
2026-01-12 11:09:27 +08:00
PackageManifestHelper.SerializeManifestToBinary(packagePath, manifest, buildParameters.ManifestEncryptor);
packageHash = HashUtility.ComputeFileCrc32(packagePath);
BuildLogger.Log($"Create package manifest file: '{packagePath}'.");
2023-12-21 19:29:26 +08:00
}
2025-06-20 11:36:29 +08:00
// 创建资源清单哈希文件
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
string fileName = YooAssetConfiguration.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
2023-12-21 19:29:26 +08:00
string filePath = $"{packageOutputDirectory}/{fileName}";
FileUtility.WriteAllText(filePath, packageHash);
2026-01-12 11:09:27 +08:00
BuildLogger.Log($"Create package manifest hash file: '{filePath}'.");
2023-12-21 19:29:26 +08:00
}
2025-06-20 11:36:29 +08:00
// 创建资源清单版本文件
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
string fileName = YooAssetConfiguration.GetPackageVersionFileName(buildParameters.PackageName);
2023-12-21 19:29:26 +08:00
string filePath = $"{packageOutputDirectory}/{fileName}";
FileUtility.WriteAllText(filePath, buildParameters.PackageVersion);
2026-01-12 11:09:27 +08:00
BuildLogger.Log($"Create package manifest version file: '{filePath}'.");
2023-12-21 19:29:26 +08:00
}
2025-02-21 15:29:42 +08:00
// 填充上下文
{
ManifestContext manifestContext = new ManifestContext();
byte[] bytesData = FileUtility.ReadAllBytes(packagePath);
2026-01-12 11:09:27 +08:00
manifestContext.Manifest = PackageManifestHelper.DeserializeManifestFromBinary(bytesData, buildParameters.ManifestDecryptor);
2025-02-21 15:29:42 +08:00
context.SetContextObject(manifestContext);
}
2023-12-21 19:29:26 +08:00
}
2024-12-13 11:00:30 +08:00
/// <summary>
/// 检测资源包哈希冲突
/// </summary>
private void CheckBundleHashConflict(BuildMapContext buildMapContext)
{
// 说明:在特殊情况下,例如某些文件加密算法会导致加密后的文件哈希值冲突!
2024-12-13 11:12:09 +08:00
// 说明:二进制完全相同的原生文件也会冲突!
2024-12-13 11:00:30 +08:00
HashSet<string> guids = new HashSet<string>();
foreach (var bundleInfo in buildMapContext.Collection)
{
if (guids.Contains(bundleInfo.PackageFileHash))
{
2026-01-12 11:09:27 +08:00
string message = BuildLogger.GetErrorMessage(ErrorCode.BundleHashConflict, $"Bundle hash conflict: '{bundleInfo.BundleName}'.");
throw new InvalidOperationException(message);
2024-12-13 11:00:30 +08:00
}
else
{
guids.Add(bundleInfo.PackageFileHash);
}
}
}
2023-12-21 19:29:26 +08:00
/// <summary>
/// 获取资源包的依赖集合
/// </summary>
2026-01-12 11:09:27 +08:00
/// <param name="context">构建上下文</param>
/// <param name="bundleName">资源包名称</param>
/// <returns>依赖的资源包名称数组</returns>
2023-12-21 19:29:26 +08:00
protected abstract string[] GetBundleDepends(BuildContext context, string bundleName);
/// <summary>
2024-12-24 17:35:26 +08:00
/// 创建资源对象列表
2023-12-21 19:29:26 +08:00
/// </summary>
2024-12-24 17:35:26 +08:00
private List<PackageAsset> CreatePackageAssetList(BuildMapContext buildMapContext)
2023-12-21 19:29:26 +08:00
{
List<PackageAsset> result = new List<PackageAsset>(1000);
foreach (var bundleInfo in buildMapContext.Collection)
{
var assetInfos = bundleInfo.GetAllManifestAssetInfos();
foreach (var assetInfo in assetInfos)
{
PackageAsset packageAsset = new PackageAsset();
packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
2023-12-25 14:19:55 +08:00
packageAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
2026-01-12 11:09:27 +08:00
packageAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
2023-12-21 19:29:26 +08:00
packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
2026-01-12 11:09:27 +08:00
packageAsset.EditorUserData = assetInfo;
2023-12-21 19:29:26 +08:00
result.Add(packageAsset);
}
}
2024-12-19 11:15:58 +08:00
// 按照AssetPath排序
result.Sort((a, b) => a.AssetPath.CompareTo(b.AssetPath));
2023-12-21 19:29:26 +08:00
return result;
}
/// <summary>
2024-12-24 17:35:26 +08:00
/// 创建资源包列表
2023-12-21 19:29:26 +08:00
/// </summary>
2024-12-24 17:35:26 +08:00
private List<PackageBundle> CreatePackageBundleList(BuildMapContext buildMapContext)
2023-12-21 19:29:26 +08:00
{
List<PackageBundle> result = new List<PackageBundle>(1000);
foreach (var bundleInfo in buildMapContext.Collection)
{
var packageBundle = bundleInfo.CreatePackageBundle();
result.Add(packageBundle);
}
2024-12-24 17:35:26 +08:00
2024-12-19 11:15:58 +08:00
// 按照BundleName排序
result.Sort((a, b) => a.BundleName.CompareTo(b.BundleName));
2024-12-24 17:35:26 +08:00
return result;
}
2023-12-21 19:29:26 +08:00
2024-12-24 17:35:26 +08:00
/// <summary>
/// 处理资源清单的资源对象列表
2024-12-24 17:35:26 +08:00
/// </summary>
2026-01-12 11:09:27 +08:00
private void ProcessPackageAsset(PackageManifest manifest)
2024-12-24 17:35:26 +08:00
{
// 注意:优先缓存资源包索引
for (int index = 0; index < manifest.BundleList.Count; index++)
2023-12-21 19:29:26 +08:00
{
2024-12-24 17:35:26 +08:00
string bundleName = manifest.BundleList[index].BundleName;
_cachedBundleIndexIDs.Add(bundleName, index);
2023-12-21 19:29:26 +08:00
}
// 记录资源对象所属的资源包ID
2024-12-24 17:35:26 +08:00
foreach (var packageAsset in manifest.AssetList)
{
2026-01-12 11:09:27 +08:00
var assetInfo = packageAsset.EditorUserData as BuildAssetInfo;
packageAsset.BundleID = GetCachedBundleIndexID(assetInfo.BundleName);
2024-12-24 17:35:26 +08:00
}
2025-02-21 15:29:42 +08:00
// 记录资源对象依赖的资源包ID集合
// 注意:依赖关系非引擎构建结果里查询!
foreach (var packageAsset in manifest.AssetList)
{
2026-01-12 11:09:27 +08:00
var mainAssetInfo = packageAsset.EditorUserData as BuildAssetInfo;
packageAsset.DependentBundleIDs = GetAssetDependBundleIDs(mainAssetInfo);
}
2023-12-21 19:29:26 +08:00
}
/// <summary>
/// 处理资源包的依赖集合
/// </summary>
private void ProcessBundleDepends(BuildContext context, PackageManifest manifest)
{
// 查询引擎生成的资源包依赖关系,然后记录到清单
foreach (var packageBundle in manifest.BundleList)
{
2024-12-24 17:35:26 +08:00
int mainBundleID = GetCachedBundleIndexID(packageBundle.BundleName);
2025-02-21 15:29:42 +08:00
string[] dependNames = GetBundleDepends(context, packageBundle.BundleName);
List<int> dependIDs = new List<int>(dependNames.Length);
foreach (var dependName in dependNames)
2023-12-21 19:29:26 +08:00
{
2025-02-21 15:29:42 +08:00
int dependBundleID = GetCachedBundleIndexID(dependName);
if (dependBundleID != mainBundleID)
dependIDs.Add(dependBundleID);
}
2025-02-21 15:29:42 +08:00
// 排序并填充数据
dependIDs.Sort();
2026-01-12 11:09:27 +08:00
packageBundle.DependentBundleIDs = dependIDs.ToArray();
}
2023-12-21 19:29:26 +08:00
}
/// <summary>
/// 处理资源包的标签集合
/// </summary>
private void ProcessBundleTags(PackageManifest manifest)
{
foreach (var packageBundle in manifest.BundleList)
2023-12-21 19:29:26 +08:00
{
packageBundle.Tags = Array.Empty<string>();
}
2023-12-21 19:29:26 +08:00
2025-02-21 15:29:42 +08:00
// 将主资源的标签信息传染给其依赖的资源包集合
foreach (var packageAsset in manifest.AssetList)
2023-12-21 19:29:26 +08:00
{
2025-02-21 15:29:42 +08:00
var assetTags = packageAsset.AssetTags;
int bundleID = packageAsset.BundleID;
CacheBundleTags(bundleID, assetTags);
2026-01-12 11:09:27 +08:00
if (packageAsset.DependentBundleIDs != null)
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
foreach (var dependBundleID in packageAsset.DependentBundleIDs)
{
2025-02-21 15:29:42 +08:00
CacheBundleTags(dependBundleID, assetTags);
}
2023-12-21 19:29:26 +08:00
}
2025-02-20 11:27:44 +08:00
}
2025-02-20 11:27:44 +08:00
// 将缓存的资源标签赋值给资源包
for (int index = 0; index < manifest.BundleList.Count; index++)
{
var packageBundle = manifest.BundleList[index];
if (_cacheBundleTags.TryGetValue(index, out var value))
2023-12-21 19:29:26 +08:00
{
2025-02-20 11:27:44 +08:00
packageBundle.Tags = value.ToArray();
}
else
{
// 注意SBP构建管线会自动剔除一些冗余资源的引用关系导致游离资源包没有被任何主资源包引用。
2026-01-12 11:09:27 +08:00
string warning = BuildLogger.GetErrorMessage(ErrorCode.FoundStrayBundle, $"Found stray bundle. Bundle ID: {index}, bundle name: '{packageBundle.BundleName}'.");
2025-02-20 11:27:44 +08:00
BuildLogger.Warning(warning);
2023-12-21 19:29:26 +08:00
}
}
}
private void CacheBundleTags(int bundleID, string[] assetTags)
{
if (_cacheBundleTags.ContainsKey(bundleID) == false)
_cacheBundleTags.Add(bundleID, new HashSet<string>());
foreach (var assetTag in assetTags)
{
if (_cacheBundleTags[bundleID].Contains(assetTag) == false)
_cacheBundleTags[bundleID].Add(assetTag);
}
}
/// <summary>
2024-12-24 17:35:26 +08:00
/// 获取缓存的资源包的索引ID
2023-12-21 19:29:26 +08:00
/// </summary>
2024-12-24 17:35:26 +08:00
private int GetCachedBundleIndexID(string bundleName)
2023-12-21 19:29:26 +08:00
{
2024-12-24 17:35:26 +08:00
if (_cachedBundleIndexIDs.TryGetValue(bundleName, out int value) == false)
2023-12-21 19:29:26 +08:00
{
2026-01-12 11:09:27 +08:00
throw new YooInternalException($"Should never get here. Bundle index ID not found: '{bundleName}'.");
2023-12-21 19:29:26 +08:00
}
return value;
}
/// <summary>
/// 是否包含该资源包的索引ID
/// </summary>
private bool ContainsCachedBundleIndexID(string bundleName)
{
return _cachedBundleIndexIDs.ContainsKey(bundleName);
}
#region YOOASSET_LEGACY_DEPENDENCY
2025-02-21 15:29:42 +08:00
private void ProcessBuiltinBundleDependency(BuildContext context, PackageManifest manifest)
{
2026-01-12 11:09:27 +08:00
// 注意:如果是可编程构建管线,需要补充首包资源包
// 注意:该步骤依赖前面的操作!
var buildResultContext = context.TryGetContextObject<TaskBuilding_SBP.BuildResultContext>();
if (buildResultContext != null)
{
ProcessBuiltinBundleReference(manifest, buildResultContext.BuiltinShadersBundleName);
ProcessBuiltinBundleReference(manifest, buildResultContext.MonoScriptsBundleName);
var buildParametersContext = context.TryGetContextObject<BuildParametersContext>();
var buildParameters = buildParametersContext.Parameters;
if (buildParameters is ScriptableBuildParameters scriptableBuildParameters)
{
if (scriptableBuildParameters.TrackSpriteAtlasDependencies)
{
// 注意:检测是否开启图集模式
// 说明:需要记录主资源对象对图集的依赖关系!
if (EditorSettings.spritePackerMode != SpritePackerMode.Disabled)
{
var buildMapContext = context.GetContextObject<BuildMapContext>();
foreach (var spriteAtlasAsset in buildMapContext.SpriteAtlasAssetList)
{
string spriteAtlasBundleName = spriteAtlasAsset.BundleName;
ProcessBuiltinBundleReference(manifest, spriteAtlasBundleName);
}
}
}
}
}
}
private void ProcessBuiltinBundleReference(PackageManifest manifest, string builtinBundleName)
{
if (string.IsNullOrEmpty(builtinBundleName))
return;
2026-01-12 11:09:27 +08:00
// 查询首包资源包是否存在
if (ContainsCachedBundleIndexID(builtinBundleName) == false)
return;
2026-01-12 11:09:27 +08:00
// 获取首包资源包
int builtinBundleID = GetCachedBundleIndexID(builtinBundleName);
var builtinPackageBundle = manifest.BundleList[builtinBundleID];
// 更新依赖资源包ID集合
2026-01-12 11:09:27 +08:00
HashSet<int> cacheBundleIDs = new HashSet<int>(builtinPackageBundle.ReferrerBundleIDs);
HashSet<string> tempTags = new HashSet<string>();
foreach (var packageAsset in manifest.AssetList)
{
if (cacheBundleIDs.Contains(packageAsset.BundleID))
{
2026-01-12 11:09:27 +08:00
if (packageAsset.DependentBundleIDs.Contains(builtinBundleID) == false)
{
2026-01-12 11:09:27 +08:00
var tempBundleIDs = new List<int>(packageAsset.DependentBundleIDs);
tempBundleIDs.Add(builtinBundleID);
2026-01-12 11:09:27 +08:00
packageAsset.DependentBundleIDs = tempBundleIDs.ToArray();
}
foreach (var tag in packageAsset.AssetTags)
{
if (tempTags.Contains(tag) == false)
tempTags.Add(tag);
}
}
}
2026-01-12 11:09:27 +08:00
// 更新首包资源包的标签集合
foreach (var tag in builtinPackageBundle.Tags)
{
if (tempTags.Contains(tag) == false)
tempTags.Add(tag);
}
builtinPackageBundle.Tags = tempTags.ToArray();
}
private int[] GetAssetDependBundleIDs(BuildAssetInfo mainAssetInfo)
{
HashSet<int> result = new HashSet<int>();
int mainBundleID = GetCachedBundleIndexID(mainAssetInfo.BundleName);
foreach (var dependAssetInfo in mainAssetInfo.AllDependAssetInfos)
{
if (dependAssetInfo.HasBundleName())
{
int bundleID = GetCachedBundleIndexID(dependAssetInfo.BundleName);
if (mainBundleID != bundleID)
{
if (result.Contains(bundleID) == false)
result.Add(bundleID);
}
}
}
2025-02-21 15:29:42 +08:00
// 排序并返回数据
List<int> listResult = new List<int>(result);
listResult.Sort();
return listResult.ToArray();
}
#endregion
2023-12-21 19:29:26 +08:00
}
2023-09-20 16:09:52 +08:00
}