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