AddressableBuildTask.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. using XGame.Framework.i18n;
  2. using XGame.Framework.Asset.Addressable;
  3. using XGame.Framework.Asset.Addressable.Data;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using System.Text;
  9. namespace XGame.Editor.Asset
  10. {
  11. public class AddressableBuildTask
  12. {
  13. /// <summary>
  14. /// 方便其它模块使用
  15. /// </summary>
  16. public static void GenManifest()
  17. {
  18. var task = new AddressableBuildTask();
  19. task.Run();
  20. }
  21. private IDictionary<AddressableClassify, AddressableInfoManifest> manifests;
  22. private AddressableInfoManifest backupManifest;
  23. private IDictionary<string, AddressableClassify> addressableNameMap;
  24. private IDictionary<long, AddressableClassify> addressableIdMap;
  25. private IDictionary<string, AddressableClassify> guidMap;
  26. private AddressableClassify[] queryOrder;
  27. public AddressableBuildTask()
  28. {
  29. manifests = new Dictionary<AddressableClassify, AddressableInfoManifest>();
  30. addressableNameMap = new Dictionary<string, AddressableClassify>();
  31. addressableIdMap = new Dictionary<long, AddressableClassify>();
  32. guidMap = new Dictionary<string, AddressableClassify>();
  33. queryOrder = new AddressableClassify[] { AddressableClassify.Product, AddressableClassify.BuiltIn, AddressableClassify.Tests };
  34. }
  35. private void Init()
  36. {
  37. LoadManifest();
  38. DisposeInvalidAssets();
  39. CollectAddressableAssets();
  40. }
  41. public void Run()
  42. {
  43. Init();
  44. Collect();
  45. Save();
  46. GenCustomSearch();
  47. }
  48. private void Collect()
  49. {
  50. AddressableClassify[] classifys = { AddressableClassify.BuiltIn, AddressableClassify.Product, AddressableClassify.Tests };
  51. for (int i = 0; i < classifys.Length; i++)
  52. {
  53. var classify = classifys[i];
  54. string[] assetsRoots = AddressableHelper.GetAssetsRoots(classify);
  55. foreach (var assetsRoot in assetsRoots)
  56. {
  57. var i18nName = string.Empty;
  58. var langFlag = LanguageType.NONE;
  59. if (classify == AddressableClassify.Product)
  60. {
  61. langFlag = AddressableHelper.GetLanguageTypeByAssetPath(assetsRoot);
  62. if (langFlag != LanguageType.NONE)
  63. {
  64. i18nName = langFlag.ToString().ToLower();
  65. }
  66. }
  67. FileUtil.FindFiles(assetsRoot, files =>
  68. {
  69. foreach (var file in files)
  70. {
  71. var path = FileUtil.ToRelativePath(file);// file.Substring(file.IndexOf("Assets/", StringComparison.Ordinal));
  72. var guid = AssetDatabase.AssetPathToGUID(path);
  73. if (ContainsGUID(guid))
  74. {
  75. //Debug.Log($"GUID Repeat. Path:{path}");
  76. if (langFlag != LanguageType.NONE || AddressableHelper.IsLodAssetPath(path))
  77. { //多语言或者LOD资源
  78. RefreshAssetInfoByGUID(guid, classify);
  79. }
  80. continue;
  81. }
  82. string fileName;
  83. //取备份信息
  84. if (TryGetBackupAssetInfoByGUID(guid, out var backupInfo))
  85. {
  86. fileName = backupInfo.addressableName;
  87. }
  88. else
  89. {
  90. fileName = AddressableHelper.GetFileName(path);
  91. if (langFlag != LanguageType.NONE)
  92. {
  93. fileName = $"{i18nName}_{fileName}";
  94. }
  95. }
  96. if (ContainsName(fileName))
  97. {
  98. //二次校验
  99. TryGetAssetInfoByName(fileName, out var lastAssetInfo);
  100. var lastPath = lastAssetInfo.GetAssetPath();
  101. string temp = string.Empty;
  102. if (string.IsNullOrEmpty(lastPath))
  103. {
  104. Debug.LogError($"Two assets have the same name. But one of the files is missing. Last {lastAssetInfo}. Next Path:{path}");
  105. }
  106. else if (path.Equals(lastPath))
  107. {
  108. Debug.LogError($"Two assets have the same path. But the GUID is different.Last {lastAssetInfo}. Next Path:{path}");
  109. }
  110. else
  111. {
  112. var ext = Path.GetExtension(path);
  113. if (!string.IsNullOrEmpty(ext) && !ext.Equals(Path.GetExtension(lastPath)))
  114. {
  115. //后缀不一样
  116. temp = ($"{fileName}{ext.Replace('.', '_')}").ToLower();
  117. //if (langFlag != LangFlag.NONE)
  118. //{
  119. // temp = $"{i18nName}_{temp}";
  120. //}
  121. if (ContainsName(temp))
  122. {
  123. temp = string.Empty;
  124. }
  125. }
  126. }
  127. if (string.IsNullOrEmpty(temp))
  128. {
  129. temp = ($"{fileName}_{guid.Substring(0, 8)}").ToLower();
  130. //if (langFlag != LangFlag.NONE)
  131. //{
  132. // temp = $"{i18nName}_{temp}";
  133. //}
  134. if (ContainsName(temp))
  135. {
  136. temp = ($"{fileName}_{guid}").ToLower();
  137. //if (langFlag != LangFlag.NONE)
  138. //{
  139. // temp = $"{i18nName}_{temp}";
  140. //}
  141. }
  142. }
  143. fileName = temp;
  144. }
  145. //addressableId使用guid+path,预防相同路径的情况
  146. var isInResources = AddressableHelper.IsInResources(path, out var relativePath);
  147. //var assetType = isInResources ? AddressableAssetType.Resources : AddressableAssetType.Default;
  148. var assetInfo = new AssetInfo()
  149. {
  150. addressableId = Crc32.GetCrc32($"{guid}+{path}"),
  151. addressableName = fileName,
  152. assetGUID = guid,
  153. //assetType = assetType,
  154. relativePath = relativePath,
  155. };
  156. if (AddAssetInfo(assetInfo))
  157. {
  158. Debug.Log($"guid:{guid} name:{fileName} path:{path}");
  159. }
  160. }
  161. });
  162. }
  163. }
  164. }
  165. private void Save()
  166. {
  167. for (int i = 0; i < queryOrder.Length; i++)
  168. {
  169. if (manifests.TryGetValue(queryOrder[i], out var manifest))
  170. {
  171. manifest.Save();
  172. }
  173. }
  174. backupManifest.Save();
  175. }
  176. public bool TryGetAssetInfoByName(string addressableName, out AssetInfo assetInfo)
  177. {
  178. if (addressableNameMap.TryGetValue(addressableName, out var classify))
  179. {
  180. if (manifests[classify].TryGetAssetInfoByName(addressableName, out var tempAsset))
  181. {
  182. assetInfo = tempAsset;
  183. return true;
  184. }
  185. }
  186. assetInfo = default;
  187. return false;
  188. }
  189. public bool TryGetBackupAssetInfoByGUID(string guid, out AssetInfo assetInfo)
  190. {
  191. if (backupManifest.TryGetAssetInfoByGUID(guid, out var tempAsset))
  192. {
  193. assetInfo = tempAsset;
  194. return true;
  195. }
  196. assetInfo = default;
  197. return false;
  198. }
  199. private bool ContainsName(string addressableName)
  200. {
  201. if (addressableNameMap.ContainsKey(addressableName))
  202. {
  203. return true;
  204. }
  205. //for (int i = 0; i < queryOrder.Length; i++)
  206. //{
  207. // if (manifests.TryGetValue(queryOrder[i], out var manifest))
  208. // {
  209. // if (manifest.ContainsName(addressableName))
  210. // {
  211. // return true;
  212. // }
  213. // }
  214. //}
  215. return false;
  216. }
  217. private bool ContainsGUID(string guid)
  218. {
  219. if (guidMap.ContainsKey(guid))
  220. {
  221. return true;
  222. }
  223. //for (int i = 0; i < queryOrder.Length; i++)
  224. //{
  225. // if (manifests.TryGetValue(queryOrder[i], out var manifest))
  226. // {
  227. // if (manifest.ContainsGUID(guid))
  228. // {
  229. // return true;
  230. // }
  231. // }
  232. //}
  233. return false;
  234. }
  235. private void RefreshAssetInfoByGUID(string guid, AddressableClassify classify)
  236. {
  237. if (manifests.TryGetValue(classify, out var manifest))
  238. {
  239. manifest.RefreshAssetInfoByGUID(guid);
  240. }
  241. }
  242. private bool AddAssetInfo(AssetInfo assetInfo)
  243. {
  244. var assetPath = assetInfo.GetAssetPath();
  245. if (AddressableHelper.TryAssetPathToClassify(assetPath, out var classify))
  246. {
  247. if (manifests.TryGetValue(classify, out var manifest))
  248. {
  249. if (manifest.AddAssetInfo(assetInfo))
  250. {
  251. AddAddressableName(assetInfo.addressableName, classify);
  252. AddAddressableId(assetInfo.addressableId, classify);
  253. AddAssetGUID(assetInfo.assetGUID, classify);
  254. return true;
  255. }
  256. }
  257. }
  258. return false;
  259. }
  260. private void LoadManifest()
  261. {
  262. for (int i = 0; i < queryOrder.Length; i++)
  263. {
  264. var classify = queryOrder[i];
  265. var manifest = CreateManifest(classify);
  266. if (manifest != null)
  267. manifests.Add(classify, manifest);
  268. }
  269. backupManifest = CreateManifest(AddressableClassify.Backup);
  270. }
  271. private AddressableInfoManifest CreateManifest(AddressableClassify classify)
  272. {
  273. var manifest = AddressableHelper.LoadOrCreateManifest(classify);
  274. if (manifest != null)
  275. {
  276. var adapter = new AddressableInfoManifest(manifest, classify);
  277. return adapter;
  278. }
  279. return null;
  280. }
  281. private void DisposeInvalidAssets()
  282. {
  283. for (int i = 0; i < queryOrder.Length; i++)
  284. {
  285. if (manifests.TryGetValue(queryOrder[i], out var manifest))
  286. {
  287. manifest.DisposeInvalidAssets(AddAssetInfo);
  288. }
  289. }
  290. }
  291. private void CollectAddressableAssets()
  292. {
  293. addressableNameMap.Clear();
  294. addressableIdMap.Clear();
  295. guidMap.Clear();
  296. for (int i = 0; i < queryOrder.Length; i++)
  297. {
  298. if (manifests.TryGetValue(queryOrder[i], out var manifest))
  299. {
  300. manifest.CollectAddressableAssets(ref addressableNameMap, ref addressableIdMap, ref guidMap);
  301. }
  302. }
  303. }
  304. private void AddAddressableName(string addressableName, AddressableClassify classify)
  305. {
  306. if (addressableNameMap.ContainsKey(addressableName))
  307. {
  308. addressableNameMap[addressableName] = classify;
  309. }
  310. else
  311. {
  312. addressableNameMap.Add(addressableName, classify);
  313. }
  314. }
  315. private void AddAssetGUID(string guid, AddressableClassify classify)
  316. {
  317. if (guidMap.ContainsKey(guid))
  318. {
  319. guidMap[guid] = classify;
  320. }
  321. else
  322. {
  323. guidMap.Add(guid, classify);
  324. }
  325. }
  326. private void AddAddressableId(long id, AddressableClassify classify)
  327. {
  328. if (addressableIdMap.ContainsKey(id))
  329. {
  330. addressableIdMap[id] = classify;
  331. }
  332. else
  333. {
  334. addressableIdMap.Add(id, classify);
  335. }
  336. }
  337. #region 资源搜索和引用标签 https://docs.unity.cn/cn/tuanjiemanual/1.2/Manual/AssetsReferences.html
  338. /// <summary>
  339. /// 生成CustomSearch.txt
  340. /// </summary>
  341. private void GenCustomSearch()
  342. {
  343. var manifest = AddressableHelper.LoadAssetManifest(AddressableClassify.Product);
  344. var assetInfos = manifest.assetInfos;
  345. var assetPaths = new List<string>();
  346. for(var i = 0; i < assetInfos.Length; i++)
  347. {
  348. var assetPath = assetInfos[i].GetAssetPath();
  349. if (string.IsNullOrEmpty(assetPath))
  350. {
  351. continue;
  352. }
  353. assetPaths.Add(assetPath);
  354. }
  355. var sb = new StringBuilder();
  356. sb.AppendLine((assetPaths.Count + 4).ToString());
  357. for(var i = 0;i < assetPaths.Count; i++)
  358. {
  359. sb.AppendLine(assetPaths[i]);
  360. }
  361. sb.AppendLine(PathDefine.BundleManifestPath);
  362. sb.AppendLine(PathDefine.SceneAssetBundleManifestPath);
  363. sb.AppendLine(PathDefine.ReferenceManifestPath);
  364. sb.AppendLine(PathDefine.ProductAssetManifestPath);
  365. var filePath = PathDefine.AddressableDataRelative + "CustomSearch.txt";
  366. File.WriteAllText(filePath, sb.ToString());
  367. AssetDatabase.Refresh();
  368. }
  369. #endregion
  370. }
  371. }