using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; namespace YooAsset.Editor { /// /// 资源依赖数据库 /// public class AssetDependencyDatabase { private const string FILE_VERSION = "1.0"; private class DependencyInfo { /// /// 此哈希函数会聚合了以下内容:源资源路径、源资源、元文件、目标平台以及导入器版本。 /// 如果此哈希值发送变化,则说明导入资源可能已更改,因此应重新搜集依赖关系。 /// public string DependHash; /// /// 直接依赖资源的GUID列表 /// public List DependGUIDs = new List(); } private string _databaseFilePath; private readonly Dictionary _database = new Dictionary(100000); /// /// 创建缓存数据库 /// public void CreateDatabase(bool readCacheDatabaseFile, string databaseFilePath) { _databaseFilePath = databaseFilePath; _database.Clear(); try { if (readCacheDatabaseFile && File.Exists(databaseFilePath)) { // 解析缓存文件 using var stream = File.OpenRead(databaseFilePath); using var reader = new BinaryReader(stream); string fileVersion = reader.ReadString(); if (fileVersion != FILE_VERSION) throw new Exception("The database file version not match !"); var count = reader.ReadInt32(); for (int i = 0; i < count; i++) { var assetPath = reader.ReadString(); var cacheInfo = new DependencyInfo { DependHash = reader.ReadString(), DependGUIDs = ReadStringList(reader), }; _database.Add(assetPath, cacheInfo); } // 移除无效资源 List removeList = new List(10000); foreach (var cacheInfoPair in _database) { var assetPath = cacheInfoPair.Key; var assetGUID = AssetDatabase.AssetPathToGUID(assetPath); if (string.IsNullOrEmpty(assetGUID)) { removeList.Add(assetPath); } } foreach (var assetPath in removeList) { _database.Remove(assetPath); } } } catch (Exception ex) { ClearDatabase(true); Debug.LogError($"Failed to load cache database : {ex.Message}"); } // 查找新增或变动资源 var allAssetPaths = AssetDatabase.GetAllAssetPaths(); foreach (var assetPath in allAssetPaths) { if (_database.TryGetValue(assetPath, out DependencyInfo cacheInfo)) { var dependHash = AssetDatabase.GetAssetDependencyHash(assetPath); if (dependHash.ToString() != cacheInfo.DependHash) { _database[assetPath] = CreateDependencyInfo(assetPath); } } else { var newCacheInfo = CreateDependencyInfo(assetPath); _database.Add(assetPath, newCacheInfo); } } } /// /// 保存缓存数据库 /// public void SaveDatabase() { if (File.Exists(_databaseFilePath)) File.Delete(_databaseFilePath); try { using var stream = File.Create(_databaseFilePath); using var writer = new BinaryWriter(stream); writer.Write(FILE_VERSION); writer.Write(_database.Count); foreach (var assetPair in _database) { string assetPath = assetPair.Key; var assetInfo = assetPair.Value; writer.Write(assetPath); writer.Write(assetInfo.DependHash); WriteStringList(writer, assetInfo.DependGUIDs); } writer.Flush(); } catch (Exception ex) { Debug.LogError($"Failed to save cache database : {ex.Message}"); } } /// /// 清理缓存数据库 /// public void ClearDatabase(bool deleteDatabaseFile) { if (deleteDatabaseFile) { if (File.Exists(_databaseFilePath)) File.Delete(_databaseFilePath); } _database.Clear(); } /// /// 获取资源的依赖列表 /// public string[] GetDependencies(string assetPath, bool recursive) { // 注意:AssetDatabase.GetDependencies()方法返回结果里会踢出丢失文件! // 注意:AssetDatabase.GetDependencies()方法返回结果里会包含主资源路径! // 注意:机制上不允许存在未收录的资源 if (_database.ContainsKey(assetPath) == false) { throw new Exception($"Fatal : can not found cache info : {assetPath}"); } var result = new HashSet { assetPath }; CollectDependencies(assetPath, result, recursive); // 注意:AssetDatabase.GetDependencies保持一致,将主资源添加到依赖列表最前面 return result.ToArray(); } private void CollectDependencies(string assetPath, HashSet result, bool recursive) { if (_database.TryGetValue(assetPath, out var cacheInfo) == false) { throw new Exception($"Fatal : can not found cache info : {assetPath}"); } foreach (var dependGUID in cacheInfo.DependGUIDs) { string dependAssetPath = AssetDatabase.GUIDToAssetPath(dependGUID); if (string.IsNullOrEmpty(dependAssetPath)) continue; // 如果是文件夹资源 if (AssetDatabase.IsValidFolder(dependAssetPath)) continue; // 如果已经收集过 if (result.Contains(dependAssetPath)) continue; result.Add(dependAssetPath); // 递归收集依赖 if (recursive) CollectDependencies(dependAssetPath, result, recursive); } } private List ReadStringList(BinaryReader reader) { var count = reader.ReadInt32(); var values = new List(count); for (int i = 0; i < count; i++) { values.Add(reader.ReadString()); } return values; } private void WriteStringList(BinaryWriter writer, List values) { writer.Write(values.Count); foreach (var value in values) { writer.Write(value); } } private DependencyInfo CreateDependencyInfo(string assetPath) { var dependHash = AssetDatabase.GetAssetDependencyHash(assetPath); var dependAssetPaths = AssetDatabase.GetDependencies(assetPath, false); var dependGUIDs = new List(); foreach (var dependAssetPath in dependAssetPaths) { string guid = AssetDatabase.AssetPathToGUID(dependAssetPath); if (string.IsNullOrEmpty(guid) == false) { dependGUIDs.Add(guid); } } var cacheInfo = new DependencyInfo(); cacheInfo.DependHash = dependHash.ToString(); cacheInfo.DependGUIDs = dependGUIDs; return cacheInfo; } } }