using System; using System.Linq; using System.Collections; using System.Collections.Generic; using UnityEditor.Build.Pipeline; using UnityEditor.Build.Pipeline.Interfaces; namespace YooAsset.Editor { public class ManifestContext : IContextObject { internal PackageManifest Manifest; } [TaskAttribute(ETaskPipeline.AllPipeline, 800, "创建清单文件")] public class TaskCreateManifest : IBuildTask { void IBuildTask.Run(BuildContext context) { CreateManifestFile(context); } /// /// 创建补丁清单文件到输出目录 /// private void CreateManifestFile(BuildContext context) { var buildMapContext = context.GetContextObject(); var buildParametersContext = context.GetContextObject(); var buildParameters = buildParametersContext.Parameters; string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory(); // 创建新补丁清单 PackageManifest manifest = new PackageManifest(); manifest.FileVersion = YooAssetSettings.ManifestFileVersion; manifest.EnableAddressable = buildMapContext.Command.EnableAddressable; manifest.LocationToLower = buildMapContext.Command.LocationToLower; manifest.IncludeAssetGUID = buildMapContext.Command.IncludeAssetGUID; manifest.OutputNameStyle = (int)buildParameters.OutputNameStyle; manifest.PackageName = buildParameters.PackageName; manifest.PackageVersion = buildParameters.PackageVersion; manifest.BundleList = GetAllPackageBundle(context); manifest.AssetList = GetAllPackageAsset(context, manifest); // 更新Unity内置资源包的引用关系 if (buildParameters.BuildPipeline == EBuildPipeline.ScriptableBuildPipeline) { if (buildParameters.BuildMode == EBuildMode.IncrementalBuild) { var buildResultContext = context.GetContextObject(); UpdateBuiltInBundleReference(manifest, buildResultContext, buildMapContext.Command.ShadersBundleName); } } // 更新资源包之间的引用关系 if (buildParameters.BuildPipeline == EBuildPipeline.ScriptableBuildPipeline) { if (buildParameters.BuildMode == EBuildMode.IncrementalBuild) { var buildResultContext = context.GetContextObject(); UpdateScriptPipelineReference(manifest, buildResultContext); } } // 更新资源包之间的引用关系 if (buildParameters.BuildPipeline == EBuildPipeline.BuiltinBuildPipeline) { if (buildParameters.BuildMode != EBuildMode.SimulateBuild) { var buildResultContext = context.GetContextObject(); UpdateBuiltinPipelineReference(manifest, buildResultContext); } } // 创建补丁清单文本文件 { string fileName = YooAssetSettingsData.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion); string filePath = $"{packageOutputDirectory}/{fileName}"; ManifestTools.SerializeToJson(filePath, manifest); BuildLogger.Log($"创建补丁清单文件:{filePath}"); } // 创建补丁清单二进制文件 string packageHash; { string fileName = YooAssetSettingsData.GetManifestBinaryFileName(buildParameters.PackageName, buildParameters.PackageVersion); string filePath = $"{packageOutputDirectory}/{fileName}"; ManifestTools.SerializeToBinary(filePath, manifest); packageHash = HashUtility.FileMD5(filePath); BuildLogger.Log($"创建补丁清单文件:{filePath}"); ManifestContext manifestContext = new ManifestContext(); byte[] bytesData = FileUtility.ReadAllBytes(filePath); manifestContext.Manifest = ManifestTools.DeserializeFromBinary(bytesData); context.SetContextObject(manifestContext); } // 创建补丁清单哈希文件 { string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion); string filePath = $"{packageOutputDirectory}/{fileName}"; FileUtility.WriteAllText(filePath, packageHash); BuildLogger.Log($"创建补丁清单哈希文件:{filePath}"); } // 创建补丁清单版本文件 { string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName); string filePath = $"{packageOutputDirectory}/{fileName}"; FileUtility.WriteAllText(filePath, buildParameters.PackageVersion); BuildLogger.Log($"创建补丁清单版本文件:{filePath}"); } } /// /// 获取资源包列表 /// private List GetAllPackageBundle(BuildContext context) { var buildMapContext = context.GetContextObject(); List result = new List(1000); foreach (var bundleInfo in buildMapContext.Collection) { var packageBundle = bundleInfo.CreatePackageBundle(); result.Add(packageBundle); } return result; } /// /// 获取资源列表 /// private List GetAllPackageAsset(BuildContext context, PackageManifest manifest) { var buildMapContext = context.GetContextObject(); List result = new List(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; packageAsset.AssetPath = assetInfo.AssetPath; packageAsset.AssetGUID = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetGUID : string.Empty; packageAsset.AssetTags = assetInfo.AssetTags.ToArray(); packageAsset.BundleID = GetAssetBundleID(assetInfo.BundleName, manifest); packageAsset.DependIDs = GetAssetBundleDependIDs(packageAsset.BundleID, assetInfo, manifest); result.Add(packageAsset); } } return result; } private int[] GetAssetBundleDependIDs(int mainBundleID, BuildAssetInfo assetInfo, PackageManifest manifest) { List result = new List(); foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos) { if (dependAssetInfo.HasBundleName()) { int bundleID = GetAssetBundleID(dependAssetInfo.BundleName, manifest); if (mainBundleID != bundleID) { if (result.Contains(bundleID) == false) result.Add(bundleID); } } } return result.ToArray(); } private int GetAssetBundleID(string bundleName, PackageManifest manifest) { for (int index = 0; index < manifest.BundleList.Count; index++) { if (manifest.BundleList[index].BundleName == bundleName) return index; } throw new Exception($"Not found bundle name : {bundleName}"); } /// /// 更新Unity内置资源包的引用关系 /// private void UpdateBuiltInBundleReference(PackageManifest manifest, TaskBuilding_SBP.BuildResultContext buildResultContext, string shadersBunldeName) { // 获取所有依赖着色器资源包的资源包列表 List shaderBundleReferenceList = new List(); foreach (var valuePair in buildResultContext.Results.BundleInfos) { if (valuePair.Value.Dependencies.Any(t => t == shadersBunldeName)) shaderBundleReferenceList.Add(valuePair.Key); } // 注意:没有任何资源依赖着色器 if (shaderBundleReferenceList.Count == 0) return; // 获取着色器资源包索引 Predicate predicate = new Predicate(s => s.BundleName == shadersBunldeName); int shaderBundleId = manifest.BundleList.FindIndex(predicate); if (shaderBundleId == -1) throw new Exception("没有发现着色器资源包!"); // 检测依赖交集并更新依赖ID HashSet tagTemps = new HashSet(); foreach (var packageAsset in manifest.AssetList) { List dependBundles = GetPackageAssetAllDependBundles(manifest, packageAsset); List conflictAssetPathList = dependBundles.Intersect(shaderBundleReferenceList).ToList(); if (conflictAssetPathList.Count > 0) { List newDependIDs = new List(packageAsset.DependIDs); if (newDependIDs.Contains(shaderBundleId) == false) newDependIDs.Add(shaderBundleId); packageAsset.DependIDs = newDependIDs.ToArray(); foreach (var tag in packageAsset.AssetTags) { if (tagTemps.Contains(tag) == false) tagTemps.Add(tag); } } } // 更新资源包标签 var packageBundle = manifest.BundleList[shaderBundleId]; List newTags = new List(packageBundle.Tags); foreach (var tag in tagTemps) { if (newTags.Contains(tag) == false) newTags.Add(tag); } packageBundle.Tags = newTags.ToArray(); } private List GetPackageAssetAllDependBundles(PackageManifest manifest, PackageAsset packageAsset) { List result = new List(); string mainBundle = manifest.BundleList[packageAsset.BundleID].BundleName; result.Add(mainBundle); foreach (var dependID in packageAsset.DependIDs) { string dependBundle = manifest.BundleList[dependID].BundleName; result.Add(dependBundle); } return result; } #region 资源包引用关系相关 private readonly Dictionary _cachedBundleID = new Dictionary(10000); private readonly Dictionary _cachedBundleDepends = new Dictionary(10000); private void UpdateScriptPipelineReference(PackageManifest manifest, TaskBuilding_SBP.BuildResultContext buildResultContext) { int progressValue; int totalCount = manifest.BundleList.Count; // 缓存资源包ID _cachedBundleID.Clear(); progressValue = 0; foreach (var packageBundle in manifest.BundleList) { int bundleID = GetAssetBundleID(packageBundle.BundleName, manifest); _cachedBundleID.Add(packageBundle.BundleName, bundleID); EditorTools.DisplayProgressBar("缓存资源包索引", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); // 缓存资源包依赖 _cachedBundleDepends.Clear(); progressValue = 0; foreach (var packageBundle in manifest.BundleList) { if (packageBundle.IsRawFile) { _cachedBundleDepends.Add(packageBundle.BundleName, new string[] { }); continue; } if (buildResultContext.Results.BundleInfos.ContainsKey(packageBundle.BundleName) == false) throw new Exception($"Not found bundle in SBP build results : {packageBundle.BundleName}"); var depends = buildResultContext.Results.BundleInfos[packageBundle.BundleName].Dependencies; _cachedBundleDepends.Add(packageBundle.BundleName, depends); EditorTools.DisplayProgressBar("缓存资源包依赖列表", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); // 计算资源包引用列表 foreach (var packageBundle in manifest.BundleList) { packageBundle.ReferenceIDs = GetBundleRefrenceIDs(manifest, packageBundle); EditorTools.DisplayProgressBar("计算资源包引用关系", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); } private void UpdateBuiltinPipelineReference(PackageManifest manifest, TaskBuilding.BuildResultContext buildResultContext) { int progressValue; int totalCount = manifest.BundleList.Count; // 缓存资源包ID _cachedBundleID.Clear(); progressValue = 0; foreach (var packageBundle in manifest.BundleList) { int bundleID = GetAssetBundleID(packageBundle.BundleName, manifest); _cachedBundleID.Add(packageBundle.BundleName, bundleID); EditorTools.DisplayProgressBar("缓存资源包索引", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); // 缓存资源包依赖 _cachedBundleDepends.Clear(); progressValue = 0; foreach (var packageBundle in manifest.BundleList) { if (packageBundle.IsRawFile) { _cachedBundleDepends.Add(packageBundle.BundleName, new string[] { }); continue; } var depends = buildResultContext.UnityManifest.GetDirectDependencies(packageBundle.BundleName); _cachedBundleDepends.Add(packageBundle.BundleName, depends); EditorTools.DisplayProgressBar("缓存资源包依赖列表", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); // 计算资源包引用列表 progressValue = 0; foreach (var packageBundle in manifest.BundleList) { packageBundle.ReferenceIDs = GetBundleRefrenceIDs(manifest, packageBundle); EditorTools.DisplayProgressBar("计算资源包引用关系", ++progressValue, totalCount); } EditorTools.ClearProgressBar(); } private int[] GetBundleRefrenceIDs(PackageManifest manifest, PackageBundle targetBundle) { List referenceList = new List(); foreach (var packageBundle in manifest.BundleList) { string bundleName = packageBundle.BundleName; if (bundleName == targetBundle.BundleName) continue; string[] dependencies = GetCachedBundleDepends(bundleName); if (dependencies.Contains(targetBundle.BundleName)) { referenceList.Add(bundleName); } } List result = new List(); foreach (var bundleName in referenceList) { int bundleID = GetCachedBundleID(bundleName); if (result.Contains(bundleID) == false) result.Add(bundleID); } return result.ToArray(); } private int GetCachedBundleID(string bundleName) { if (_cachedBundleID.TryGetValue(bundleName, out int value) == false) { throw new Exception($"Not found cached bundle ID : {bundleName}"); } return value; } private string[] GetCachedBundleDepends(string bundleName) { if (_cachedBundleDepends.TryGetValue(bundleName, out string[] value) == false) { throw new Exception($"Not found cached bundle depends : {bundleName}"); } return value; } #endregion } }