AddressableInfoManifest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. using XGame.Framework.i18n;
  2. using XGame.Framework.Quality;
  3. using XGame.Framework.Asset.Addressable;
  4. using XGame.Framework.Asset.Addressable.Data;
  5. using System.Collections.Generic;
  6. using UnityEditor;
  7. using UnityEngine;
  8. namespace XGame.Editor.Asset
  9. {
  10. internal class AddressableInfoManifest
  11. {
  12. private AddressableInfosSo manifest;
  13. private List<AssetInfo> assetInfos;
  14. /// <summary>
  15. /// 多语言资源配置,每次收集资源信息都重置
  16. /// </summary>
  17. private Dictionary<long, List<AddressableI18nInfo.I18nData>> i18nInfosMap;
  18. /// <summary>
  19. /// Lod资源配置,每次收集资源信息都重置
  20. /// </summary>
  21. private Dictionary<long, List<AddressableLodInfo.LodData>> lodInfosMap;
  22. /// <summary>
  23. /// 等待二次检测的Asset资源
  24. /// 由于LOD资源在同一目录下,可能出现lod资源比Origin资源更早记录的情况
  25. /// 第一次检测时找不到就先记录下来,等所有资源遍历结束再做一次检测
  26. /// </summary>
  27. private List<AssetInfo> verifingLodAssets;
  28. public AddressableClassify ClassifyOperation
  29. {
  30. get;
  31. private set;
  32. }
  33. public AddressableInfoManifest(AddressableInfosSo manifest, AddressableClassify classify)
  34. {
  35. ClassifyOperation = classify;
  36. this.manifest = manifest;
  37. assetInfos = manifest?.GetAssetInfos();
  38. }
  39. public bool AddAssetInfo(AssetInfo assetInfo)
  40. {
  41. if (manifest == null) return false;
  42. if (assetInfos == null)
  43. {
  44. assetInfos = new List<AssetInfo>();
  45. }
  46. var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(assetInfo.assetGUID) || a.addressableName.Equals(assetInfo.addressableName) || a.addressableId == assetInfo.addressableId);
  47. if (index >= 0)
  48. {
  49. Debug.LogError($"Asset Manifest AddAssetInfo Error. Asset Repeat Last:{assetInfos[index]} Next:{assetInfo}");
  50. return false;
  51. }
  52. assetInfos.Add(assetInfo);
  53. OnAddAssetInfo(assetInfo);
  54. return true;
  55. }
  56. public void RemoveAssetInfoByName(string addressableName)
  57. {
  58. if (assetInfos != null)
  59. {
  60. var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
  61. if (index >= 0)
  62. {
  63. assetInfos.RemoveAt(index);
  64. }
  65. }
  66. }
  67. public bool TryGetAssetInfoByName(string addressableName, out AssetInfo assetInfo)
  68. {
  69. if (assetInfos != null)
  70. {
  71. var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
  72. if (index >= 0)
  73. {
  74. assetInfo = assetInfos[index];
  75. return true;
  76. }
  77. }
  78. assetInfo = default;
  79. return false;
  80. }
  81. public bool TryGetAssetInfoByGUID(string guid, out AssetInfo assetInfo)
  82. {
  83. if (assetInfos != null)
  84. {
  85. var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(guid));
  86. if (index >= 0)
  87. {
  88. assetInfo = assetInfos[index];
  89. return true;
  90. }
  91. }
  92. assetInfo = default;
  93. return false;
  94. }
  95. public bool ContainsName(string addressableName)
  96. {
  97. if (assetInfos != null)
  98. {
  99. var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
  100. return index >= 0;
  101. }
  102. return false;
  103. }
  104. public bool ContainsGUID(string guid)
  105. {
  106. if (assetInfos != null)
  107. {
  108. var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(guid));
  109. return index >= 0;
  110. }
  111. return false;
  112. }
  113. public void Save()
  114. {
  115. if (manifest != null)
  116. {
  117. VerifyLodAssetsSecond();
  118. SaveLodInfos();
  119. SaveI18nInfos();
  120. manifest.SetAssetInfos(assetInfos != null && assetInfos.Count > 0 ? assetInfos.ToArray() : null);
  121. }
  122. }
  123. public void DisposeInvalidAssets(System.Func<AssetInfo, bool> moveAssetEvent)
  124. {
  125. if (assetInfos != null)
  126. {
  127. for (int i = assetInfos.Count - 1; i >= 0; i--)
  128. {
  129. var assetInfo = assetInfos[i];
  130. if (!AddressableHelper.IsValidPath(assetInfo.GetAssetPath(), ClassifyOperation))
  131. {
  132. Debug.LogWarning($"Invalid Asset:{assetInfo}");
  133. if (moveAssetEvent != null && moveAssetEvent.Invoke(assetInfo))
  134. {
  135. assetInfos.RemoveAt(i);
  136. }
  137. }
  138. }
  139. }
  140. }
  141. public void CollectAddressableAssets(ref IDictionary<string, AddressableClassify> addressableNameMap,
  142. ref IDictionary<long, AddressableClassify> addressableIdMap,
  143. ref IDictionary<string, AddressableClassify> guidMap)
  144. {
  145. if (assetInfos != null)
  146. {
  147. for (int i = 0; i < assetInfos.Count; i++)
  148. {
  149. var assetInfo = assetInfos[i];
  150. //刷新relativePath和assetType
  151. bool isInResources = AddressableHelper.IsInResources(assetInfo.GetAssetPath(), out assetInfo.relativePath);
  152. //assetInfo.assetType = isInResources ? AddressableAssetType.Resources : AddressableAssetType.Default;
  153. assetInfos[i] = assetInfo;
  154. //
  155. if (addressableNameMap.ContainsKey(assetInfo.addressableName))
  156. {
  157. Debug.LogError($"Addressable Name Repeat. {assetInfo}");
  158. }
  159. else
  160. {
  161. addressableNameMap.Add(assetInfo.addressableName, ClassifyOperation);
  162. }
  163. if (addressableIdMap.ContainsKey(assetInfo.addressableId))
  164. {
  165. Debug.LogError($"Addressable ID Repeat. {assetInfo}");
  166. }
  167. else
  168. {
  169. addressableIdMap.Add(assetInfo.addressableId, ClassifyOperation);
  170. }
  171. if (guidMap.ContainsKey(assetInfo.assetGUID))
  172. {
  173. Debug.LogError($"Addressable GUID Repeat. {assetInfo}");
  174. }
  175. else
  176. {
  177. guidMap.Add(assetInfo.assetGUID, ClassifyOperation);
  178. }
  179. }
  180. }
  181. }
  182. //private bool IsValidPath(string assetPath)
  183. //{
  184. // if (string.IsNullOrEmpty(assetPath))
  185. // {
  186. // return false;
  187. // }
  188. // switch (ClassifyOperation)
  189. // {
  190. // case AddressableClassifyOperation.BuiltIn:
  191. // if (assetPath.StartsWith(PathDefine.XGamePackageRelative))
  192. // {
  193. // //在Package Framework目录下
  194. // return true;
  195. // }
  196. // break;
  197. // case AddressableClassifyOperation.ResStatic:
  198. // if (assetPath.StartsWith(PathDefine.ResStaticToAddressable))
  199. // {
  200. // //在ResStatic目录下
  201. // return true;
  202. // }
  203. // break;
  204. // default:
  205. // if (assetPath.StartsWith(PathDefine.ResAddressableRelative))
  206. // {
  207. // //在Res/Addressable目录下
  208. // return true;
  209. // }
  210. // if (assetPath.StartsWith(PathDefine.ResourcesRelative))
  211. // {
  212. // //在Resources目录下
  213. // return true;
  214. // }
  215. // break;
  216. // }
  217. // return false;
  218. //}
  219. private void OnAddAssetInfo(AssetInfo assetInfo)
  220. {
  221. if (ClassifyOperation == AddressableClassify.Product)
  222. {
  223. //项目资源才需要检测LOD和多语言
  224. VerifyLodAsset(assetInfo, true);
  225. VerifyI18nAsset(assetInfo);
  226. }
  227. }
  228. public void RefreshAssetInfoByGUID(string assetGUID)
  229. {
  230. if (ClassifyOperation == AddressableClassify.Product)
  231. {
  232. if (TryGetAssetInfoByGUID(assetGUID, out var assetInfo))
  233. {
  234. OnAddAssetInfo(assetInfo);
  235. }
  236. }
  237. }
  238. #region i18n
  239. private void VerifyI18nAsset(AssetInfo assetInfo)
  240. {
  241. var assetPath = assetInfo.GetAssetPath();
  242. var langFlag = AddressableHelper.GetLanguageTypeByAssetPath(assetPath);
  243. if (langFlag != LanguageType.NONE)
  244. { // 是多语言
  245. var originPath = assetPath.Replace($"/{PathDefine.I18nName}/{langFlag.ToName()}", "");
  246. var originGUID = AssetDatabase.AssetPathToGUID(originPath);
  247. if (string.IsNullOrEmpty(originGUID) == false && TryGetAssetInfoByGUID(originGUID, out var originAsset))
  248. {
  249. AddI18nData(originAsset.addressableId, assetInfo.addressableId, langFlag);
  250. }
  251. else
  252. {
  253. Debug.LogError($"多语言资源错误,找不到对应的原始资源. {assetInfo} OriginPath:{originPath}");
  254. }
  255. }
  256. }
  257. private bool AddI18nData(long originId, long i18nId, LanguageType langFlag)
  258. {
  259. if (i18nInfosMap == null)
  260. {
  261. i18nInfosMap = new Dictionary<long, List<AddressableI18nInfo.I18nData>>();
  262. }
  263. if (!i18nInfosMap.TryGetValue(originId, out var lstData))
  264. {
  265. lstData = new List<AddressableI18nInfo.I18nData>();
  266. i18nInfosMap.Add(originId, lstData);
  267. }
  268. var index = lstData.FindIndex((a) => a.langFlag == langFlag);
  269. if (index >= 0)
  270. {
  271. var data = lstData[index];
  272. data.addressableId = i18nId;
  273. lstData[index] = data;
  274. }
  275. else
  276. {
  277. lstData.Add(new AddressableI18nInfo.I18nData()
  278. {
  279. addressableId = i18nId,
  280. langFlag = langFlag,
  281. });
  282. }
  283. return true;
  284. }
  285. private void SaveI18nInfos()
  286. {
  287. if (i18nInfosMap != null && i18nInfosMap.Count > 0)
  288. {
  289. AddressableI18nInfo[] i18nInfos = new AddressableI18nInfo[i18nInfosMap.Count];
  290. var index = 0;
  291. foreach (var pair in i18nInfosMap)
  292. {
  293. var info = new AddressableI18nInfo()
  294. {
  295. addressableId = pair.Key,
  296. i18nDatas = pair.Value.ToArray()
  297. };
  298. i18nInfos[index++] = info;
  299. }
  300. manifest.SetAddressableI18nInfos(i18nInfos, false);
  301. }
  302. else
  303. {
  304. manifest.SetAddressableI18nInfos(null, false);
  305. }
  306. }
  307. #endregion
  308. #region LOD
  309. /// <summary>
  310. /// 二次检验lod资源
  311. /// </summary>
  312. private void VerifyLodAssetsSecond()
  313. {
  314. if (verifingLodAssets != null)
  315. {
  316. foreach (var assetInfo in verifingLodAssets)
  317. {
  318. VerifyLodAsset(assetInfo, false);
  319. }
  320. verifingLodAssets.Clear();
  321. }
  322. }
  323. private void VerifyLodAsset(AssetInfo assetInfo, bool isFirst)
  324. {
  325. var assetPath = assetInfo.GetAssetPath();
  326. if (IsLodAssetPath(assetPath, PathDefine.LodHigh, out var originPath, out var quality) ||
  327. IsLodAssetPath(assetPath, PathDefine.LodLow, out originPath, out quality))
  328. {
  329. Debug.Log($"LOD资源. {assetInfo} OriginPath:{originPath}");
  330. var originGUID = AssetDatabase.AssetPathToGUID(originPath);
  331. if (string.IsNullOrEmpty(originGUID) == false && TryGetAssetInfoByGUID(originGUID, out var originAsset))
  332. {
  333. AddLodData(originAsset.addressableId, assetInfo.addressableId, quality);
  334. }
  335. else if (isFirst)
  336. {
  337. //保存起来,等待二次校验
  338. AddVerifingLodAsset(assetInfo);
  339. }
  340. else
  341. {
  342. Debug.LogError($"LOD资源错误,找不到对应的原始资源. {assetInfo} OriginPath:{originPath}");
  343. }
  344. }
  345. }
  346. private void AddVerifingLodAsset(AssetInfo assetInfo)
  347. {
  348. if (verifingLodAssets == null)
  349. {
  350. verifingLodAssets = new List<AssetInfo>();
  351. }
  352. verifingLodAssets.Add(assetInfo);
  353. }
  354. private bool IsLodAssetPath(string assetPath, string lodTag, out string originPath, out XGame.Framework.Quality.XQualityLevel quality)
  355. {
  356. var fileName = System.IO.Path.GetFileNameWithoutExtension(assetPath);
  357. if (fileName != null && fileName.ToLower().EndsWith(lodTag))
  358. {
  359. var originName = fileName.Substring(0, fileName.Length - lodTag.Length);
  360. originPath = assetPath.Replace(fileName, originName);
  361. quality = LodTagToQualityLevel(lodTag);
  362. return true;
  363. }
  364. originPath = string.Empty;
  365. quality = XGame.Framework.Quality.XQualityLevel.UnKnow;
  366. return false;
  367. }
  368. private XGame.Framework.Quality.XQualityLevel LodTagToQualityLevel(string lodTag)
  369. {
  370. if (lodTag.Equals(PathDefine.LodHigh))
  371. {
  372. return XGame.Framework.Quality.XQualityLevel.High;
  373. }
  374. else if (lodTag.Equals(PathDefine.LodLow))
  375. {
  376. return XGame.Framework.Quality.XQualityLevel.Low;
  377. }
  378. return XGame.Framework.Quality.XQualityLevel.UnKnow;
  379. }
  380. private bool AddLodData(long originId, long lodId, XGame.Framework.Quality.XQualityLevel quality)
  381. {
  382. if (lodInfosMap == null)
  383. {
  384. lodInfosMap = new Dictionary<long, List<AddressableLodInfo.LodData>>();
  385. }
  386. if (!lodInfosMap.TryGetValue(originId, out var lstData))
  387. {
  388. lstData = new List<AddressableLodInfo.LodData>();
  389. lodInfosMap.Add(originId, lstData);
  390. }
  391. var index = lstData.FindIndex(data => data.quality == quality);
  392. if (index >= 0)
  393. {
  394. var data = lstData[index];
  395. data.addressableId = lodId;
  396. lstData[index] = data;
  397. }
  398. else
  399. {
  400. lstData.Add(new AddressableLodInfo.LodData()
  401. {
  402. addressableId = lodId,
  403. quality = quality
  404. });
  405. }
  406. return true;
  407. }
  408. private void SaveLodInfos()
  409. {
  410. if (lodInfosMap != null && lodInfosMap.Count > 0)
  411. {
  412. var lodInfos = new AddressableLodInfo[lodInfosMap.Count];
  413. var index = 0;
  414. foreach (var pair in lodInfosMap)
  415. {
  416. var info = new AddressableLodInfo()
  417. {
  418. addressableId = pair.Key,
  419. lodDatas = pair.Value.ToArray()
  420. };
  421. lodInfos[index++] = info;
  422. }
  423. manifest.SetAddressableLodInfos(lodInfos, false);
  424. }
  425. else
  426. {
  427. manifest.SetAddressableLodInfos(null, false);
  428. }
  429. }
  430. #endregion
  431. }
  432. }