TaskCreatePatchManifest.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. [TaskAttribute("创建补丁清单文件")]
  10. public class TaskCreatePatchManifest : IBuildTask
  11. {
  12. void IBuildTask.Run(BuildContext context)
  13. {
  14. CreatePatchManifestFile(context);
  15. }
  16. /// <summary>
  17. /// 创建补丁清单文件到输出目录
  18. /// </summary>
  19. private void CreatePatchManifestFile(BuildContext context)
  20. {
  21. var buildParameters = context.GetContextObject<BuildParametersContext>();
  22. int resourceVersion = buildParameters.Parameters.BuildVersion;
  23. // 创建新补丁清单
  24. PatchManifest patchManifest = new PatchManifest();
  25. patchManifest.FileVersion = YooAssetSettings.PatchManifestFileVersion;
  26. patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion;
  27. patchManifest.EnableAddressable = buildParameters.Parameters.EnableAddressable;
  28. patchManifest.OutputNameStyle = (int)buildParameters.Parameters.OutputNameStyle;
  29. patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags;
  30. patchManifest.BundleList = GetAllPatchBundle(context);
  31. patchManifest.AssetList = GetAllPatchAsset(context, patchManifest);
  32. // 更新Unity内置资源包的引用关系
  33. if (buildParameters.Parameters.BuildPipeline == EBuildPipeline.ScriptableBuildPipeline)
  34. {
  35. if(buildParameters.Parameters.BuildMode == EBuildMode.IncrementalBuild)
  36. {
  37. var buildResultContext = context.GetContextObject<TaskBuilding_SBP.BuildResultContext>();
  38. UpdateBuiltInBundleReference(patchManifest, buildResultContext.Results);
  39. }
  40. }
  41. // 创建补丁清单文件
  42. string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettingsData.GetPatchManifestFileName(resourceVersion)}";
  43. BuildRunner.Log($"创建补丁清单文件:{manifestFilePath}");
  44. PatchManifest.Serialize(manifestFilePath, patchManifest);
  45. // 创建补丁清单哈希文件
  46. string manifestHashFilePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettingsData.GetPatchManifestHashFileName(resourceVersion)}";
  47. string manifestHash = HashUtility.FileMD5(manifestFilePath);
  48. BuildRunner.Log($"创建补丁清单哈希文件:{manifestHashFilePath}");
  49. FileUtility.CreateFile(manifestHashFilePath, manifestHash);
  50. // 创建静态版本文件
  51. string staticVersionFilePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettings.VersionFileName}";
  52. string staticVersion = resourceVersion.ToString();
  53. BuildRunner.Log($"创建静态版本文件:{staticVersionFilePath}");
  54. FileUtility.CreateFile(staticVersionFilePath, staticVersion);
  55. }
  56. /// <summary>
  57. /// 获取资源包列表
  58. /// </summary>
  59. private List<PatchBundle> GetAllPatchBundle(BuildContext context)
  60. {
  61. var buildParameters = context.GetContextObject<BuildParametersContext>();
  62. var buildMapContext = context.GetContextObject<BuildMapContext>();
  63. var encryptionContext = context.GetContextObject<TaskEncryption.EncryptionContext>();
  64. List<PatchBundle> result = new List<PatchBundle>(1000);
  65. List<string> buildinTags = buildParameters.Parameters.GetBuildinTags();
  66. foreach (var bundleInfo in buildMapContext.BundleInfos)
  67. {
  68. var bundleName = bundleInfo.BundleName;
  69. string fileHash = GetBundleFileHash(bundleInfo, buildParameters);
  70. string fileCRC = GetBundleFileCRC(bundleInfo, buildParameters);
  71. long fileSize = GetBundleFileSize(bundleInfo, buildParameters);
  72. string[] tags = buildMapContext.GetBundleTags(bundleName);
  73. bool isEncrypted = encryptionContext.IsEncryptFile(bundleName);
  74. bool isBuildin = IsBuildinBundle(tags, buildinTags);
  75. bool isRawFile = bundleInfo.IsRawFile;
  76. PatchBundle patchBundle = new PatchBundle(bundleName, fileHash, fileCRC, fileSize, tags);
  77. patchBundle.SetFlagsValue(isEncrypted, isBuildin, isRawFile);
  78. result.Add(patchBundle);
  79. }
  80. return result;
  81. }
  82. private bool IsBuildinBundle(string[] bundleTags, List<string> buildinTags)
  83. {
  84. // 注意:没有任何分类标签的Bundle文件默认为内置文件
  85. if (bundleTags.Length == 0)
  86. return true;
  87. foreach (var tag in bundleTags)
  88. {
  89. if (buildinTags.Contains(tag))
  90. return true;
  91. }
  92. return false;
  93. }
  94. private string GetBundleFileHash(BuildBundleInfo bundleInfo, BuildParametersContext buildParametersContext)
  95. {
  96. var buildMode = buildParametersContext.Parameters.BuildMode;
  97. if (buildMode == EBuildMode.DryRunBuild || buildMode == EBuildMode.SimulateBuild)
  98. return "00000000000000000000000000000000"; //32位
  99. string filePath = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}";
  100. return HashUtility.FileMD5(filePath);
  101. }
  102. private string GetBundleFileCRC(BuildBundleInfo bundleInfo, BuildParametersContext buildParametersContext)
  103. {
  104. var buildMode = buildParametersContext.Parameters.BuildMode;
  105. if (buildMode == EBuildMode.DryRunBuild || buildMode == EBuildMode.SimulateBuild)
  106. return "00000000"; //8位
  107. string filePath = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}";
  108. return HashUtility.FileCRC32(filePath);
  109. }
  110. private long GetBundleFileSize(BuildBundleInfo bundleInfo, BuildParametersContext buildParametersContext)
  111. {
  112. var buildMode = buildParametersContext.Parameters.BuildMode;
  113. if (buildMode == EBuildMode.DryRunBuild || buildMode == EBuildMode.SimulateBuild)
  114. return 0;
  115. string filePath = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}";
  116. return FileUtility.GetFileSize(filePath);
  117. }
  118. /// <summary>
  119. /// 获取资源列表
  120. /// </summary>
  121. private List<PatchAsset> GetAllPatchAsset(BuildContext context, PatchManifest patchManifest)
  122. {
  123. var buildParameters = context.GetContextObject<BuildParametersContext>();
  124. var buildMapContext = context.GetContextObject<BuildMapContext>();
  125. List<PatchAsset> result = new List<PatchAsset>(1000);
  126. foreach (var bundleInfo in buildMapContext.BundleInfos)
  127. {
  128. var assetInfos = bundleInfo.GetAllPatchAssetInfos();
  129. foreach (var assetInfo in assetInfos)
  130. {
  131. PatchAsset patchAsset = new PatchAsset();
  132. if (buildParameters.Parameters.EnableAddressable)
  133. patchAsset.Address = assetInfo.Address;
  134. else
  135. patchAsset.Address = string.Empty;
  136. patchAsset.AssetPath = assetInfo.AssetPath;
  137. patchAsset.AssetTags = assetInfo.AssetTags.ToArray();
  138. patchAsset.BundleID = GetAssetBundleID(assetInfo.GetBundleName(), patchManifest);
  139. patchAsset.DependIDs = GetAssetBundleDependIDs(patchAsset.BundleID, assetInfo, patchManifest);
  140. result.Add(patchAsset);
  141. }
  142. }
  143. return result;
  144. }
  145. private int[] GetAssetBundleDependIDs(int mainBundleID, BuildAssetInfo assetInfo, PatchManifest patchManifest)
  146. {
  147. List<int> result = new List<int>();
  148. foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
  149. {
  150. if (dependAssetInfo.HasBundleName())
  151. {
  152. int bundleID = GetAssetBundleID(dependAssetInfo.GetBundleName(), patchManifest);
  153. if (mainBundleID != bundleID)
  154. {
  155. if (result.Contains(bundleID) == false)
  156. result.Add(bundleID);
  157. }
  158. }
  159. }
  160. return result.ToArray();
  161. }
  162. private int GetAssetBundleID(string bundleName, PatchManifest patchManifest)
  163. {
  164. for (int index = 0; index < patchManifest.BundleList.Count; index++)
  165. {
  166. if (patchManifest.BundleList[index].BundleName == bundleName)
  167. return index;
  168. }
  169. throw new Exception($"Not found bundle name : {bundleName}");
  170. }
  171. /// <summary>
  172. /// 更新Unity内置资源包的引用关系
  173. /// </summary>
  174. private void UpdateBuiltInBundleReference(PatchManifest patchManifest, IBundleBuildResults buildResults)
  175. {
  176. // 获取所有依赖着色器资源包的资源包列表
  177. string shadersBunldeName = YooAssetSettingsData.GetUnityShadersBundleFullName();
  178. List<string> shaderBundleReferenceList = new List<string>();
  179. foreach (var valuePair in buildResults.BundleInfos)
  180. {
  181. if (valuePair.Value.Dependencies.Any(t => t == shadersBunldeName))
  182. shaderBundleReferenceList.Add(valuePair.Key);
  183. }
  184. // 获取着色器资源包索引
  185. Predicate<PatchBundle> predicate = new Predicate<PatchBundle>(s => s.BundleName == shadersBunldeName);
  186. int shaderBundleId = patchManifest.BundleList.FindIndex(predicate);
  187. if (shaderBundleId == -1)
  188. throw new Exception("没有发现着色器资源包!");
  189. // 检测依赖交集并更新依赖ID
  190. foreach (var patchAsset in patchManifest.AssetList)
  191. {
  192. List<string> dependBundles = GetPatchAssetAllDependBundles(patchManifest, patchAsset);
  193. List<string> conflictAssetPathList = dependBundles.Intersect(shaderBundleReferenceList).ToList();
  194. if (conflictAssetPathList.Count > 0)
  195. {
  196. List<int> newDependIDs = new List<int>(patchAsset.DependIDs);
  197. if (newDependIDs.Contains(shaderBundleId) == false)
  198. newDependIDs.Add(shaderBundleId);
  199. patchAsset.DependIDs = newDependIDs.ToArray();
  200. }
  201. }
  202. }
  203. private List<string> GetPatchAssetAllDependBundles(PatchManifest patchManifest, PatchAsset patchAsset)
  204. {
  205. List<string> result = new List<string>();
  206. string mainBundle = patchManifest.BundleList[patchAsset.BundleID].BundleName;
  207. result.Add(mainBundle);
  208. foreach (var dependID in patchAsset.DependIDs)
  209. {
  210. string dependBundle = patchManifest.BundleList[dependID].BundleName;
  211. result.Add(dependBundle);
  212. }
  213. return result;
  214. }
  215. }
  216. }