AssetDependencyDatabase.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using UnityEditor;
  7. using UnityEngine;
  8. namespace YooAsset.Editor
  9. {
  10. /// <summary>
  11. /// 资源依赖数据库
  12. /// </summary>
  13. public class AssetDependencyDatabase
  14. {
  15. private const string FILE_VERSION = "1.0";
  16. private class DependencyInfo
  17. {
  18. /// <summary>
  19. /// 此哈希函数会聚合了以下内容:源资源路径、源资源、元文件、目标平台以及导入器版本。
  20. /// 如果此哈希值发送变化,则说明导入资源可能已更改,因此应重新搜集依赖关系。
  21. /// </summary>
  22. public string DependHash;
  23. /// <summary>
  24. /// 直接依赖资源的GUID列表
  25. /// </summary>
  26. public List<string> DependGUIDs = new List<string>();
  27. }
  28. private string _databaseFilePath;
  29. private readonly Dictionary<string, DependencyInfo> _database = new Dictionary<string, DependencyInfo>(100000);
  30. /// <summary>
  31. /// 创建缓存数据库
  32. /// </summary>
  33. public void CreateDatabase(bool readCacheDatabaseFile, string databaseFilePath)
  34. {
  35. _databaseFilePath = databaseFilePath;
  36. _database.Clear();
  37. FileStream stream = null;
  38. BinaryReader reader = null;
  39. try
  40. {
  41. if (readCacheDatabaseFile && File.Exists(databaseFilePath))
  42. {
  43. // 解析缓存文件
  44. stream = File.OpenRead(databaseFilePath);
  45. reader = new BinaryReader(stream);
  46. string fileVersion = reader.ReadString();
  47. if (fileVersion != FILE_VERSION)
  48. throw new Exception("The database file version not match !");
  49. var count = reader.ReadInt32();
  50. for (int i = 0; i < count; i++)
  51. {
  52. var assetPath = reader.ReadString();
  53. var cacheInfo = new DependencyInfo
  54. {
  55. DependHash = reader.ReadString(),
  56. DependGUIDs = ReadStringList(reader),
  57. };
  58. _database.Add(assetPath, cacheInfo);
  59. }
  60. // 移除无效资源
  61. List<string> removeList = new List<string>(10000);
  62. foreach (var cacheInfoPair in _database)
  63. {
  64. var assetPath = cacheInfoPair.Key;
  65. var assetGUID = AssetDatabase.AssetPathToGUID(assetPath);
  66. if (string.IsNullOrEmpty(assetGUID))
  67. {
  68. removeList.Add(assetPath);
  69. }
  70. }
  71. foreach (var assetPath in removeList)
  72. {
  73. _database.Remove(assetPath);
  74. }
  75. }
  76. }
  77. catch (Exception ex)
  78. {
  79. ClearDatabase(true);
  80. Debug.LogError($"Failed to load cache database : {ex.Message}");
  81. }
  82. finally
  83. {
  84. if (reader != null)
  85. reader.Close();
  86. if (stream != null)
  87. stream.Close();
  88. }
  89. // 查找新增或变动资源
  90. var allAssetPaths = AssetDatabase.GetAllAssetPaths();
  91. foreach (var assetPath in allAssetPaths)
  92. {
  93. if (_database.TryGetValue(assetPath, out DependencyInfo cacheInfo))
  94. {
  95. var dependHash = AssetDatabase.GetAssetDependencyHash(assetPath);
  96. if (dependHash.ToString() != cacheInfo.DependHash)
  97. {
  98. _database[assetPath] = CreateDependencyInfo(assetPath);
  99. }
  100. }
  101. else
  102. {
  103. var newCacheInfo = CreateDependencyInfo(assetPath);
  104. _database.Add(assetPath, newCacheInfo);
  105. }
  106. }
  107. }
  108. /// <summary>
  109. /// 保存缓存数据库
  110. /// </summary>
  111. public void SaveDatabase()
  112. {
  113. if (File.Exists(_databaseFilePath))
  114. File.Delete(_databaseFilePath);
  115. FileStream stream = null;
  116. BinaryWriter writer = null;
  117. try
  118. {
  119. stream = File.Create(_databaseFilePath);
  120. writer = new BinaryWriter(stream);
  121. writer.Write(FILE_VERSION);
  122. writer.Write(_database.Count);
  123. foreach (var assetPair in _database)
  124. {
  125. string assetPath = assetPair.Key;
  126. var assetInfo = assetPair.Value;
  127. writer.Write(assetPath);
  128. writer.Write(assetInfo.DependHash);
  129. WriteStringList(writer, assetInfo.DependGUIDs);
  130. }
  131. writer.Flush();
  132. }
  133. catch (Exception ex)
  134. {
  135. Debug.LogError($"Failed to save cache database : {ex.Message}");
  136. }
  137. finally
  138. {
  139. if (writer != null)
  140. writer.Close();
  141. if (stream != null)
  142. stream.Close();
  143. }
  144. }
  145. /// <summary>
  146. /// 清理缓存数据库
  147. /// </summary>
  148. public void ClearDatabase(bool deleteDatabaseFile)
  149. {
  150. if (deleteDatabaseFile)
  151. {
  152. if (File.Exists(_databaseFilePath))
  153. File.Delete(_databaseFilePath);
  154. }
  155. _database.Clear();
  156. }
  157. /// <summary>
  158. /// 获取资源的依赖列表
  159. /// </summary>
  160. public string[] GetDependencies(string assetPath, bool recursive)
  161. {
  162. // 注意:AssetDatabase.GetDependencies()方法返回结果里会踢出丢失文件!
  163. // 注意:AssetDatabase.GetDependencies()方法返回结果里会包含主资源路径!
  164. // 注意:机制上不允许存在未收录的资源
  165. if (_database.ContainsKey(assetPath) == false)
  166. {
  167. throw new Exception($"Fatal : can not found cache info : {assetPath}");
  168. }
  169. var result = new HashSet<string> { assetPath };
  170. CollectDependencies(assetPath, result, recursive);
  171. // 注意:AssetDatabase.GetDependencies保持一致,将主资源添加到依赖列表最前面
  172. return result.ToArray();
  173. }
  174. private void CollectDependencies(string assetPath, HashSet<string> result, bool recursive)
  175. {
  176. if (_database.TryGetValue(assetPath, out var cacheInfo) == false)
  177. {
  178. throw new Exception($"Fatal : can not found cache info : {assetPath}");
  179. }
  180. foreach (var dependGUID in cacheInfo.DependGUIDs)
  181. {
  182. string dependAssetPath = AssetDatabase.GUIDToAssetPath(dependGUID);
  183. if (string.IsNullOrEmpty(dependAssetPath))
  184. continue;
  185. // 如果是文件夹资源
  186. if (AssetDatabase.IsValidFolder(dependAssetPath))
  187. continue;
  188. // 如果已经收集过
  189. if (result.Contains(dependAssetPath))
  190. continue;
  191. result.Add(dependAssetPath);
  192. // 递归收集依赖
  193. if (recursive)
  194. CollectDependencies(dependAssetPath, result, recursive);
  195. }
  196. }
  197. private List<string> ReadStringList(BinaryReader reader)
  198. {
  199. var count = reader.ReadInt32();
  200. var values = new List<string>(count);
  201. for (int i = 0; i < count; i++)
  202. {
  203. values.Add(reader.ReadString());
  204. }
  205. return values;
  206. }
  207. private void WriteStringList(BinaryWriter writer, List<string> values)
  208. {
  209. writer.Write(values.Count);
  210. foreach (var value in values)
  211. {
  212. writer.Write(value);
  213. }
  214. }
  215. private DependencyInfo CreateDependencyInfo(string assetPath)
  216. {
  217. var dependHash = AssetDatabase.GetAssetDependencyHash(assetPath);
  218. var dependAssetPaths = AssetDatabase.GetDependencies(assetPath, false);
  219. var dependGUIDs = new List<string>();
  220. foreach (var dependAssetPath in dependAssetPaths)
  221. {
  222. string guid = AssetDatabase.AssetPathToGUID(dependAssetPath);
  223. if (string.IsNullOrEmpty(guid) == false)
  224. {
  225. dependGUIDs.Add(guid);
  226. }
  227. }
  228. var cacheInfo = new DependencyInfo();
  229. cacheInfo.DependHash = dependHash.ToString();
  230. cacheInfo.DependGUIDs = dependGUIDs;
  231. return cacheInfo;
  232. }
  233. }
  234. }