ProductAssetsContext.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. using XGame.Framework.Asset;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using System;
  8. using System.IO;
  9. using XGame.Framework.Asset.Addressable.Data;
  10. using XGame.Editor.Asset;
  11. using XGame.Editor.Build.AssetBundles;
  12. namespace XGame.Editor.Build
  13. {
  14. /// <summary>
  15. /// 该类主要是给提供给编辑器快速查询Addressable/Assetbundle信息用
  16. /// 使用前必须保证AssetbundleMainfest已经存在
  17. /// 该类实例会产生大量的缓存数据,注意释放问题
  18. /// </summary>
  19. public class ProductAssetsContext: IDisposable
  20. {
  21. private Dictionary<string, AssetInfo> _addressableAssetMap;
  22. private Dictionary<string, string> _addressableToGuidMap;
  23. #region Assetbundle
  24. private Dictionary<string, AssetBundleBackupInfo> _abBackupInfoMap;
  25. private Dictionary<uint, string> _bundleIdToNameMap;
  26. private Dictionary<string, string> _assetNameToBundleNameMap;
  27. private Dictionary<string, string[]> _abDependenciesMap;
  28. private Dictionary<string, HashSet<string>> _bundleReferencesMap;
  29. #endregion
  30. /// <summary>
  31. /// 海报资源的标签
  32. /// </summary>
  33. public static readonly string PlacardTag = "/placard/";
  34. /// <summary>
  35. /// 内置资源的标签
  36. /// </summary>
  37. public string[] InnerAssetsTags = new string[] { "/XGame/", "/launch/", PlacardTag };
  38. private IAssetsCustomHandler _customHandler;
  39. private bool IsOnlySplitCustomPackageBundles => _customHandler?.IsOnlySplitCustomPackageBundles ?? false;
  40. public bool Init()
  41. {
  42. var addressableManifest = AddressableHelper.LoadAssetManifest();
  43. if (addressableManifest == null)
  44. {
  45. Debug.LogError($"找不到 AddressableAssetInfoManifest.");
  46. return false;
  47. }
  48. _addressableAssetMap = addressableManifest.GetAssetInfoMapWithGUID();
  49. _addressableToGuidMap = new Dictionary<string, string>();
  50. foreach (var keyValue in _addressableAssetMap)
  51. {
  52. _addressableToGuidMap.Add(keyValue.Value.addressableName, keyValue.Key);
  53. }
  54. if (!InitAssetBundles())
  55. {
  56. return false;
  57. }
  58. InitCustomHandler();
  59. return true;
  60. }
  61. private bool InitAssetBundles()
  62. {
  63. var abManifest = AssetBundleBackupManifest.Load();
  64. if (abManifest == null || abManifest.bundleInfos == null || abManifest.bundleInfos.Length == 0)
  65. return false;
  66. _abBackupInfoMap = new Dictionary<string, AssetBundleBackupInfo>();
  67. _bundleIdToNameMap = new Dictionary<uint, string>();
  68. _assetNameToBundleNameMap = new Dictionary<string, string>();
  69. _abDependenciesMap = new Dictionary<string, string[]>();
  70. _bundleReferencesMap = new Dictionary<string, HashSet<string>>();
  71. foreach (var item in abManifest.bundleInfos)
  72. {
  73. var bundleName = item.FullName;
  74. var bundleId = item.bundleId;
  75. _abBackupInfoMap.Add(bundleName, item);
  76. _bundleIdToNameMap.Add(bundleId, bundleName);
  77. foreach(var asset in item.assets)
  78. {
  79. _assetNameToBundleNameMap.Add(asset, bundleName);
  80. }
  81. }
  82. return true;
  83. }
  84. private void InitCustomHandler()
  85. {
  86. var handlerType = typeof(IAssetsCustomHandler);
  87. var assembly = System.Reflection.Assembly.GetAssembly(handlerType);
  88. var handlerImplType = Array.Find(assembly.GetTypes(), type => type != handlerType && handlerType.IsAssignableFrom(type));
  89. //var editorName = "XGame.Editor";
  90. //var handlerImplType = AppDomain.CurrentDomain.GetAssemblies()
  91. // .First(assembly => assembly.GetName().Name.Equals(editorName))
  92. // .GetTypes().First(type => type != handlerType && handlerType.IsAssignableFrom(type));
  93. if (handlerImplType != null)
  94. {
  95. _customHandler = Activator.CreateInstance(handlerImplType) as IAssetsCustomHandler;
  96. Debug.Log($"AssetsControl: IAssetsCustomHandler {handlerImplType.FullName} if1");
  97. }
  98. else
  99. {
  100. var assemblies = AppDomain.CurrentDomain.GetAssemblies();
  101. foreach (var variable in assemblies)
  102. {
  103. // 仅扫描Editor关键字的程序集
  104. if (!variable.FullName.Contains("Editor"))
  105. {
  106. continue;
  107. }
  108. handlerImplType = Array.Find(variable.GetTypes(), type => type != handlerType && handlerType.IsAssignableFrom(type));
  109. if (handlerImplType != null)
  110. {
  111. _customHandler = Activator.CreateInstance(handlerImplType) as IAssetsCustomHandler;
  112. Debug.Log($"AssetsControl: IAssetsCustomHandler {handlerImplType.FullName} if2");
  113. return;
  114. }
  115. }
  116. Debug.Log("没有找到 IAssetsCustomHandler 的实现类。");
  117. }
  118. }
  119. public string GuidToAddressableName(string guid)
  120. {
  121. if (_addressableAssetMap.ContainsKey(guid))
  122. {
  123. return _addressableAssetMap[guid].addressableName;
  124. }
  125. return string.Empty;
  126. }
  127. public string AddressableNameToGuid(string addressableName)
  128. {
  129. _addressableToGuidMap.TryGetValue(addressableName, out var guid);
  130. return guid;
  131. }
  132. public string AddressableToBundleName(string addressableName)
  133. {
  134. _assetNameToBundleNameMap.TryGetValue(addressableName, out var bundleName);
  135. return bundleName;
  136. }
  137. public HashSet<string> GetBundleReferences(string bundleName)
  138. {
  139. if (_bundleReferencesMap.TryGetValue(bundleName, out var references))
  140. {
  141. return references;
  142. }
  143. references = new HashSet<string>();
  144. if (!_abBackupInfoMap.TryGetValue(bundleName, out var bundle))
  145. {
  146. return references;
  147. }
  148. var bundleId = bundle.bundleId;
  149. foreach(var item in _abBackupInfoMap)
  150. {
  151. var bundleInfo = item.Value;
  152. if (bundleInfo.bundleId == bundleId)
  153. {
  154. continue;
  155. }
  156. if (bundleInfo.DependenciesCount > 0)
  157. {
  158. foreach(var depId in bundleInfo.dependencies)
  159. {
  160. if (depId == bundleId)
  161. {
  162. references.Add(item.Key);
  163. break;
  164. }
  165. }
  166. }
  167. }
  168. _bundleReferencesMap.Add(bundleName, references);
  169. return references;
  170. }
  171. /// <summary>
  172. /// 收集海报的bundle
  173. /// </summary>
  174. /// <returns></returns>
  175. public HashSet<string> CollectPlacardBundles()
  176. {
  177. return CollectInnerBundles(true);
  178. }
  179. /// <summary>
  180. /// 收集所有内置的AssetBundle信息
  181. /// </summary>
  182. /// <param name="customPackageBundles"></param>
  183. /// <returns></returns>
  184. public HashSet<string> CollectInnerBundles(HashSet<string> customPackageBundles = null)
  185. {
  186. return CollectInnerBundles(false, customPackageBundles);
  187. }
  188. public string[] GetDependenciesByBundleName(string assetBundleName)
  189. {
  190. if (string.IsNullOrEmpty(assetBundleName))
  191. {
  192. return null;
  193. }
  194. if (_abDependenciesMap.TryGetValue(assetBundleName, out string[] dependencies))
  195. {
  196. return dependencies;
  197. }
  198. if (_abBackupInfoMap.TryGetValue(assetBundleName, out var bundleInfo))
  199. {
  200. int count = bundleInfo.DependenciesCount;
  201. if (count > 0)
  202. {
  203. dependencies = new string[count];
  204. for (var index = 0; index < count; index++)
  205. {
  206. var depId = bundleInfo.dependencies[index];
  207. dependencies[index] = _bundleIdToNameMap[depId];
  208. }
  209. }
  210. _abDependenciesMap.Add(assetBundleName, dependencies);
  211. }
  212. return dependencies;
  213. }
  214. /// <summary>
  215. /// 收集所有内置的AssetBundle信息
  216. /// </summary>
  217. /// <param name="isPlacard">是否只收集海报资源</param>
  218. /// <param name="customPackageBundles">分包资源的bundleFullName列表</param>
  219. /// <returns></returns>
  220. private HashSet<string> CollectInnerBundles(bool isPlacard, HashSet<string> customPackageBundles = null)
  221. {
  222. //ResStatic下的依赖包至少会被两个AB包依赖
  223. //Regex rawBundleReg = new Regex(@"(^\d+)_(\d+)");
  224. var names = new HashSet<string>();
  225. //manifest
  226. names.Add(Define.MANIFEST_BUNDLE_FULLNAME);
  227. //识别所有bundle是否是内置资源
  228. foreach (var item in _abBackupInfoMap)
  229. {
  230. var bundle = item.Value;
  231. var fullName = item.Key;
  232. if (!names.Contains(fullName))
  233. {
  234. var isInner = false;
  235. if (bundle.bundleType == AssetBundleType.Dependency)
  236. { // 是ResStatic下的依赖包
  237. foreach (var assetPath in bundle.assets)
  238. {
  239. if (IsInnerAsset(assetPath, isPlacard))
  240. {
  241. isInner = true;
  242. break;
  243. }
  244. }
  245. }
  246. else
  247. {
  248. // 只分离分包资源且该ab不在分包的Assetbunle列表,则该ab保留在包内
  249. isInner = isPlacard == false && IsOnlySplitCustomPackageBundles &&
  250. customPackageBundles != null && customPackageBundles.Contains(fullName) == false;
  251. if (!isInner)
  252. {
  253. string assetPath;
  254. if (_addressableToGuidMap.TryGetValue(bundle.assets[0], out var assetGuid))
  255. {
  256. assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
  257. }
  258. else
  259. { // ResStatic的图集资源
  260. assetPath = bundle.assets[0];
  261. }
  262. //其它内置资源,通过文件夹标签识别
  263. if (IsInnerAsset(assetPath, isPlacard))
  264. {
  265. isInner = true;
  266. }
  267. }
  268. }
  269. if (isInner)
  270. {
  271. names.Add(fullName);
  272. Debug.Log($"Inner Bundle:{bundle.nameOrGuid} Name:{fullName}");
  273. if (bundle.DependenciesCount > 0)
  274. {
  275. names.UnionWith(GetDependenciesByBundleName(fullName));
  276. }
  277. }
  278. }
  279. }
  280. var paths = names.ToArray();
  281. var bundleOutputPath = PathDefine.AssetBundleOutputPath;
  282. var result = new HashSet<string>();
  283. for (int i = 0; i < paths.Length; i++)
  284. {
  285. result.Add(Path.Combine(bundleOutputPath, paths[i]).Replace("\\", "/"));
  286. }
  287. return result;
  288. }
  289. /// <summary>
  290. /// 判断一个资源是否是内置资源
  291. /// </summary>
  292. /// <param name="path"></param>
  293. /// <param name="isPlacard"></param>
  294. /// <returns></returns>
  295. private bool IsInnerAsset(string path, bool isPlacard)
  296. {
  297. var lowerPath = path.ToLower();
  298. if (isPlacard)
  299. {
  300. return lowerPath.Contains(PlacardTag);
  301. }
  302. var innerAssetsTags = InnerAssetsTags;
  303. foreach (var tag in innerAssetsTags)
  304. {
  305. if (lowerPath.Contains(tag))
  306. {
  307. return true;
  308. }
  309. }
  310. return _customHandler != null && _customHandler.IsInnerAsset(path);
  311. }
  312. void IDisposable.Dispose()
  313. {
  314. _addressableAssetMap?.Clear();
  315. _addressableAssetMap = null;
  316. _abBackupInfoMap?.Clear();
  317. _bundleIdToNameMap?.Clear();
  318. _assetNameToBundleNameMap?.Clear();
  319. _abDependenciesMap?.Clear();
  320. }
  321. }
  322. }