TaskCreateManifest.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. using System;
  2. using System.Linq;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEditor.Build.Pipeline;
  6. using UnityEditor.Build.Pipeline.Interfaces;
  7. namespace YooAsset.Editor
  8. {
  9. public class ManifestContext : IContextObject
  10. {
  11. internal PackageManifest Manifest;
  12. }
  13. [TaskAttribute("创建清单文件")]
  14. public class TaskCreateManifest : IBuildTask
  15. {
  16. void IBuildTask.Run(BuildContext context)
  17. {
  18. CreateManifestFile(context);
  19. }
  20. /// <summary>
  21. /// 创建补丁清单文件到输出目录
  22. /// </summary>
  23. private void CreateManifestFile(BuildContext context)
  24. {
  25. var buildMapContext = context.GetContextObject<BuildMapContext>();
  26. var buildParametersContext = context.GetContextObject<BuildParametersContext>();
  27. var buildParameters = buildParametersContext.Parameters;
  28. string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
  29. // 创建新补丁清单
  30. PackageManifest manifest = new PackageManifest();
  31. manifest.FileVersion = YooAssetSettings.ManifestFileVersion;
  32. manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
  33. manifest.LocationToLower = buildMapContext.Command.LocationToLower;
  34. manifest.IncludeAssetGUID = buildMapContext.Command.IncludeAssetGUID;
  35. manifest.OutputNameStyle = (int)buildParameters.OutputNameStyle;
  36. manifest.PackageName = buildParameters.PackageName;
  37. manifest.PackageVersion = buildParameters.PackageVersion;
  38. // 填充资源包集合
  39. manifest.BundleList = GetAllPackageBundle(context);
  40. CacheBundleIDs(manifest);
  41. // 填充主资源集合
  42. manifest.AssetList = GetAllPackageAsset(context, manifest);
  43. // 更新Unity内置资源包的引用关系
  44. if (buildParameters.BuildPipeline == EBuildPipeline.ScriptableBuildPipeline)
  45. {
  46. if (buildParameters.BuildMode == EBuildMode.IncrementalBuild)
  47. {
  48. var buildResultContext = context.GetContextObject<TaskBuilding_SBP.BuildResultContext>();
  49. UpdateBuiltInBundleReference(manifest, buildResultContext, buildMapContext.Command.ShadersBundleName);
  50. }
  51. }
  52. // 更新资源包之间的引用关系
  53. if (buildParameters.BuildPipeline == EBuildPipeline.ScriptableBuildPipeline)
  54. {
  55. if (buildParameters.BuildMode == EBuildMode.IncrementalBuild)
  56. {
  57. var buildResultContext = context.GetContextObject<TaskBuilding_SBP.BuildResultContext>();
  58. UpdateScriptPipelineReference(manifest, buildResultContext);
  59. }
  60. }
  61. // 更新资源包之间的引用关系
  62. if (buildParameters.BuildPipeline == EBuildPipeline.BuiltinBuildPipeline)
  63. {
  64. if (buildParameters.BuildMode != EBuildMode.SimulateBuild)
  65. {
  66. var buildResultContext = context.GetContextObject<TaskBuilding.BuildResultContext>();
  67. UpdateBuiltinPipelineReference(manifest, buildResultContext);
  68. }
  69. }
  70. // 创建补丁清单文本文件
  71. {
  72. string fileName = YooAssetSettingsData.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion);
  73. string filePath = $"{packageOutputDirectory}/{fileName}";
  74. ManifestTools.SerializeToJson(filePath, manifest);
  75. BuildLogger.Log($"创建补丁清单文件:{filePath}");
  76. }
  77. // 创建补丁清单二进制文件
  78. string packageHash;
  79. {
  80. string fileName = YooAssetSettingsData.GetManifestBinaryFileName(buildParameters.PackageName, buildParameters.PackageVersion);
  81. string filePath = $"{packageOutputDirectory}/{fileName}";
  82. ManifestTools.SerializeToBinary(filePath, manifest);
  83. packageHash = HashUtility.FileMD5(filePath);
  84. BuildLogger.Log($"创建补丁清单文件:{filePath}");
  85. ManifestContext manifestContext = new ManifestContext();
  86. byte[] bytesData = FileUtility.ReadAllBytes(filePath);
  87. manifestContext.Manifest = ManifestTools.DeserializeFromBinary(bytesData);
  88. context.SetContextObject(manifestContext);
  89. }
  90. // 创建补丁清单哈希文件
  91. {
  92. string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
  93. string filePath = $"{packageOutputDirectory}/{fileName}";
  94. FileUtility.WriteAllText(filePath, packageHash);
  95. BuildLogger.Log($"创建补丁清单哈希文件:{filePath}");
  96. }
  97. // 创建补丁清单版本文件
  98. {
  99. string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
  100. string filePath = $"{packageOutputDirectory}/{fileName}";
  101. FileUtility.WriteAllText(filePath, buildParameters.PackageVersion);
  102. BuildLogger.Log($"创建补丁清单版本文件:{filePath}");
  103. }
  104. }
  105. /// <summary>
  106. /// 获取资源包列表
  107. /// </summary>
  108. private List<PackageBundle> GetAllPackageBundle(BuildContext context)
  109. {
  110. var buildMapContext = context.GetContextObject<BuildMapContext>();
  111. List<PackageBundle> result = new List<PackageBundle>(1000);
  112. foreach (var bundleInfo in buildMapContext.Collection)
  113. {
  114. var packageBundle = bundleInfo.CreatePackageBundle();
  115. result.Add(packageBundle);
  116. }
  117. return result;
  118. }
  119. /// <summary>
  120. /// 获取资源列表
  121. /// </summary>
  122. private List<PackageAsset> GetAllPackageAsset(BuildContext context, PackageManifest manifest)
  123. {
  124. var buildMapContext = context.GetContextObject<BuildMapContext>();
  125. List<PackageAsset> result = new List<PackageAsset>(1000);
  126. foreach (var bundleInfo in buildMapContext.Collection)
  127. {
  128. var assetInfos = bundleInfo.GetAllManifestAssetInfos();
  129. foreach (var assetInfo in assetInfos)
  130. {
  131. PackageAsset packageAsset = new PackageAsset();
  132. packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
  133. packageAsset.AssetPath = assetInfo.AssetPath;
  134. packageAsset.AssetGUID = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetGUID : string.Empty;
  135. packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
  136. packageAsset.BundleID = GetCachedBundleID(assetInfo.BundleName);
  137. packageAsset.DependIDs = GetAssetBundleDependIDs(packageAsset.BundleID, assetInfo, manifest);
  138. result.Add(packageAsset);
  139. }
  140. }
  141. return result;
  142. }
  143. private int[] GetAssetBundleDependIDs(int mainBundleID, BuildAssetInfo assetInfo, PackageManifest manifest)
  144. {
  145. HashSet<int> result = new HashSet<int>();
  146. foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
  147. {
  148. if (dependAssetInfo.HasBundleName())
  149. {
  150. int bundleID = GetCachedBundleID(dependAssetInfo.BundleName);
  151. if (mainBundleID != bundleID)
  152. {
  153. if (result.Contains(bundleID) == false)
  154. result.Add(bundleID);
  155. }
  156. }
  157. }
  158. return result.ToArray();
  159. }
  160. /// <summary>
  161. /// 更新Unity内置资源包的引用关系
  162. /// </summary>
  163. private void UpdateBuiltInBundleReference(PackageManifest manifest, TaskBuilding_SBP.BuildResultContext buildResultContext, string shadersBunldeName)
  164. {
  165. // 获取所有依赖着色器资源包的资源包列表
  166. List<string> shaderBundleReferenceList = new List<string>();
  167. foreach (var valuePair in buildResultContext.Results.BundleInfos)
  168. {
  169. if (valuePair.Value.Dependencies.Any(t => t == shadersBunldeName))
  170. shaderBundleReferenceList.Add(valuePair.Key);
  171. }
  172. // 注意:没有任何资源依赖着色器
  173. if (shaderBundleReferenceList.Count == 0)
  174. return;
  175. // 获取着色器资源包索引
  176. Predicate<PackageBundle> predicate = new Predicate<PackageBundle>(s => s.BundleName == shadersBunldeName);
  177. int shaderBundleId = manifest.BundleList.FindIndex(predicate);
  178. if (shaderBundleId == -1)
  179. throw new Exception("没有发现着色器资源包!");
  180. // 检测依赖交集并更新依赖ID
  181. HashSet<string> tagTemps = new HashSet<string>();
  182. foreach (var packageAsset in manifest.AssetList)
  183. {
  184. List<string> dependBundles = GetPackageAssetAllDependBundles(manifest, packageAsset);
  185. List<string> conflictAssetPathList = dependBundles.Intersect(shaderBundleReferenceList).ToList();
  186. if (conflictAssetPathList.Count > 0)
  187. {
  188. HashSet<int> newDependIDs = new HashSet<int>(packageAsset.DependIDs);
  189. if (newDependIDs.Contains(shaderBundleId) == false)
  190. newDependIDs.Add(shaderBundleId);
  191. packageAsset.DependIDs = newDependIDs.ToArray();
  192. foreach (var tag in packageAsset.AssetTags)
  193. {
  194. if (tagTemps.Contains(tag) == false)
  195. tagTemps.Add(tag);
  196. }
  197. }
  198. }
  199. // 更新资源包标签
  200. var packageBundle = manifest.BundleList[shaderBundleId];
  201. HashSet<string> newTags = new HashSet<string>(packageBundle.Tags);
  202. foreach (var tag in tagTemps)
  203. {
  204. if (newTags.Contains(tag) == false)
  205. newTags.Add(tag);
  206. }
  207. packageBundle.Tags = newTags.ToArray();
  208. }
  209. private List<string> GetPackageAssetAllDependBundles(PackageManifest manifest, PackageAsset packageAsset)
  210. {
  211. List<string> result = new List<string>();
  212. string mainBundle = manifest.BundleList[packageAsset.BundleID].BundleName;
  213. result.Add(mainBundle);
  214. foreach (var dependID in packageAsset.DependIDs)
  215. {
  216. string dependBundle = manifest.BundleList[dependID].BundleName;
  217. result.Add(dependBundle);
  218. }
  219. return result;
  220. }
  221. #region 资源包引用关系相关
  222. private readonly Dictionary<string, int> _cachedBundleID = new Dictionary<string, int>(10000);
  223. private readonly Dictionary<string, string[]> _cachedBundleDepends = new Dictionary<string, string[]>(10000);
  224. private void CacheBundleIDs(PackageManifest manifest)
  225. {
  226. UnityEngine.Debug.Assert(manifest.BundleList.Count != 0, "Manifest bundle list is empty !");
  227. for (int index = 0; index < manifest.BundleList.Count; index++)
  228. {
  229. string bundleName = manifest.BundleList[index].BundleName;
  230. _cachedBundleID.Add(bundleName, index);
  231. }
  232. }
  233. private void UpdateScriptPipelineReference(PackageManifest manifest, TaskBuilding_SBP.BuildResultContext buildResultContext)
  234. {
  235. int progressValue;
  236. int totalCount = manifest.BundleList.Count;
  237. // 缓存资源包依赖
  238. _cachedBundleDepends.Clear();
  239. progressValue = 0;
  240. foreach (var packageBundle in manifest.BundleList)
  241. {
  242. if (packageBundle.IsRawFile)
  243. {
  244. _cachedBundleDepends.Add(packageBundle.BundleName, new string[] { });
  245. continue;
  246. }
  247. if (buildResultContext.Results.BundleInfos.ContainsKey(packageBundle.BundleName) == false)
  248. throw new Exception($"Not found bundle in SBP build results : {packageBundle.BundleName}");
  249. var depends = buildResultContext.Results.BundleInfos[packageBundle.BundleName].Dependencies;
  250. _cachedBundleDepends.Add(packageBundle.BundleName, depends);
  251. int pro = ++progressValue;
  252. if (pro % 100 == 0)
  253. {
  254. EditorTools.DisplayProgressBar("缓存资源包依赖列表", pro, totalCount);
  255. }
  256. }
  257. EditorTools.ClearProgressBar();
  258. // 计算资源包引用列表
  259. foreach (var packageBundle in manifest.BundleList)
  260. {
  261. packageBundle.ReferenceIDs = GetBundleRefrenceIDs(manifest, packageBundle);
  262. int pro = ++progressValue;
  263. if (pro % 100 == 0)
  264. {
  265. EditorTools.DisplayProgressBar("计算资源包引用关系", pro, totalCount);
  266. }
  267. }
  268. EditorTools.ClearProgressBar();
  269. }
  270. private void UpdateBuiltinPipelineReference(PackageManifest manifest, TaskBuilding.BuildResultContext buildResultContext)
  271. {
  272. int progressValue;
  273. int totalCount = manifest.BundleList.Count;
  274. // 缓存资源包依赖
  275. _cachedBundleDepends.Clear();
  276. progressValue = 0;
  277. foreach (var packageBundle in manifest.BundleList)
  278. {
  279. if (packageBundle.IsRawFile)
  280. {
  281. _cachedBundleDepends.Add(packageBundle.BundleName, new string[] { });
  282. continue;
  283. }
  284. var depends = buildResultContext.UnityManifest.GetDirectDependencies(packageBundle.BundleName);
  285. _cachedBundleDepends.Add(packageBundle.BundleName, depends);
  286. int pro = ++progressValue;
  287. if (pro % 100 == 0)
  288. {
  289. EditorTools.DisplayProgressBar("缓存资源包依赖列表", pro, totalCount);
  290. }
  291. }
  292. EditorTools.ClearProgressBar();
  293. // 计算资源包引用列表
  294. progressValue = 0;
  295. foreach (var packageBundle in manifest.BundleList)
  296. {
  297. packageBundle.ReferenceIDs = GetBundleRefrenceIDs(manifest, packageBundle);
  298. int pro = ++progressValue;
  299. if (pro % 100 == 0)
  300. {
  301. EditorTools.DisplayProgressBar("计算资源包引用关系", ++progressValue, totalCount);
  302. }
  303. }
  304. EditorTools.ClearProgressBar();
  305. }
  306. private int[] GetBundleRefrenceIDs(PackageManifest manifest, PackageBundle targetBundle)
  307. {
  308. List<string> referenceList = new List<string>();
  309. foreach (var packageBundle in manifest.BundleList)
  310. {
  311. string bundleName = packageBundle.BundleName;
  312. if (bundleName == targetBundle.BundleName)
  313. continue;
  314. string[] dependencies = GetCachedBundleDepends(bundleName);
  315. if (dependencies.Contains(targetBundle.BundleName))
  316. {
  317. referenceList.Add(bundleName);
  318. }
  319. }
  320. HashSet<int> result = new HashSet<int>();
  321. foreach (var bundleName in referenceList)
  322. {
  323. int bundleID = GetCachedBundleID(bundleName);
  324. if (result.Contains(bundleID) == false)
  325. result.Add(bundleID);
  326. }
  327. return result.ToArray();
  328. }
  329. private int GetCachedBundleID(string bundleName)
  330. {
  331. if (_cachedBundleID.TryGetValue(bundleName, out int value) == false)
  332. {
  333. throw new Exception($"Not found cached bundle ID : {bundleName}");
  334. }
  335. return value;
  336. }
  337. private string[] GetCachedBundleDepends(string bundleName)
  338. {
  339. if (_cachedBundleDepends.TryGetValue(bundleName, out string[] value) == false)
  340. {
  341. throw new Exception($"Not found cached bundle depends : {bundleName}");
  342. }
  343. return value;
  344. }
  345. #endregion
  346. }
  347. }