265 lines
8.4 KiB
C#
Raw Normal View History

2025-04-10 20:39:04 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using UnityEditor;
using Object = UnityEngine.Object;
namespace AssetDependencyGraph
{
public interface IDependencyAnalysis
{
void Analyze(string path, Dictionary<AssetIdentify, AssetNode> result);
public (AssetIdentify id, AssetNode node) GetOrCreateFolderNode(string path, Dictionary<AssetIdentify, AssetNode> result)
{
AssetIdentify k = null;
foreach (var item in result.Keys)
{
if (item.Path == path)
{
k = item;
}
}
if (k == null)
{
k = new AssetIdentify()
{
Path = path,
AssetType = "Folder",
Guid = null,
Md5 = null
};
result[k] = new FolderNode()
{
Self = k,
AssetType = "Folder",
};
}
return (k, result[k]);
}
public (AssetIdentify id, AssetNode node) GetOrCreateAssetNode(string path, Dictionary<AssetIdentify, AssetNode> result)
{
AssetIdentify k = null;
foreach (var item in result.Keys)
{
if (item.Path == path)
{
k = item;
}
}
if (k == null)
{
k = new AssetIdentify()
{
Path = path,
Guid = null,
Md5 = Utils.Md5(path)
};
if (path.EndsWith(".prefab") || path.EndsWith(".unity"))
{
result[k] = new PackageNode()
{
Self = k,
};
}
else
{
result[k] = new AssetNode()
{
Self = k,
};
}
}
return (k, result[k]);
}
public bool Exclude(string path) => path.EndsWith(".meta");
public string GetTypeByExtension(string ext)
{
switch (ext.ToLowerInvariant())
{
case ".a":
case ".dll":
case ".so":
case ".exe":
case ".dynlib":
return "Executable";
case ".asmdef":
case ".asmref":
return "UnityAssembly";
case ".cs":
case ".lua":
case ".js":
case ".ts":
case ".java":
case ".h":
case ".cpp":
case ".cxx":
case ".mm":
return "SourceFile";
case ".json":
case ".json5":
return "JsonFile";
case ".xml":
return "XmlFile";
case ".dat":
return "DatFile";
default:
return null;
}
}
}
public class UnityDependencyAnalysis : IDependencyAnalysis
{
HashSet<string> processed = new();
public void Analyze(string path, Dictionary<AssetIdentify, AssetNode> result)
{
if (!processed.Add(path))
{
return;
}
var dependencyAnalysis = (this as IDependencyAnalysis);
var kv = dependencyAnalysis.GetOrCreateAssetNode(path, result);
var relatePath = path.ToUnityRelatePath();
kv.id.Guid = AssetDatabase.AssetPathToGUID(relatePath);
var selfNode = kv.node;
if (dependencyAnalysis.GetTypeByExtension(Path.GetExtension(path)) is string assetType && assetType != null)
{
kv.id.AssetType = assetType;
selfNode.AssetType = assetType;
}
else
{
if (relatePath.EndsWith("Rock_01_Prefab.prefab"))
{
Console.WriteLine();
}
Object mainObject = AssetDatabase.LoadMainAssetAtPath(relatePath);
if (mainObject != null)
{
var prefabType = PrefabUtility.GetPrefabAssetType(mainObject);
if (prefabType != PrefabAssetType.NotAPrefab)
{
selfNode.AssetType = prefabType.ToString();
}
else
{
selfNode.AssetType = mainObject.GetType().Name;
}
kv.id.AssetType = selfNode.AssetType;
string[] dependencies = AssetDatabase.GetDependencies(relatePath, false);
for (int i = 0; i < dependencies.Length; i++)
{
var dep = dependencies[i].ToUnityFullPath();
var depkv = dependencyAnalysis.GetOrCreateAssetNode(dep, result);
depkv.node.Dependent.Add(selfNode.Self);
selfNode.Dependencies.Add(depkv.id);
Analyze(dep, result);
}
}
else
{
UnityEngine.Debug.LogWarning("unknown type:" + path);
}
}
}
}
public class FolderDependencyAnalysis : IDependencyAnalysis
{
public void Analyze(string path, Dictionary<AssetIdentify, AssetNode> result)
{
var dependencyAnalysis = (this as IDependencyAnalysis);
var k = dependencyAnalysis.GetOrCreateFolderNode(path, result);
Utils.TraverseDirectory(path, (p) =>
{
p = p.ToUniversalPath();
if (dependencyAnalysis.Exclude(p))
{
return;
}
var selfNode = result[k.id];
if (Directory.Exists(p))
{
var kv = dependencyAnalysis.GetOrCreateFolderNode(p, result);
kv.node.Dependent.Add(selfNode.Self);
selfNode.Dependencies.Add(kv.id);
}
else if (File.Exists(p))
{
var kv = dependencyAnalysis.GetOrCreateAssetNode(p, result);
kv.node.Dependent.Add(selfNode.Self);
selfNode.Dependencies.Add(kv.id);
}
}, 1);
}
}
internal class DependencyAnalyzer
{
private Dictionary<Predicate<string>, IDependencyAnalysis> dependencyAnalysisDic = new();
private Dictionary<AssetIdentify, AssetNode> assetIdentify2AssetNodeDic = new();
private bool Exclude(string path) => path.EndsWith(".meta");
public DependencyAnalyzer()
{
dependencyAnalysisDic.Add(new Predicate<string>(path => !Directory.Exists(path)), new UnityDependencyAnalysis());
dependencyAnalysisDic.Add(new Predicate<string>(path => Directory.Exists(path)), new FolderDependencyAnalysis());
}
private void Visivt(string path)
{
path = path.ToUniversalPath();
if (Exclude(path))
{
return;
}
foreach (var item in dependencyAnalysisDic)
{
if (item.Key(path))
{
item.Value.Analyze(path, assetIdentify2AssetNodeDic);
}
}
}
public void Analyze(string rootFolder)
{
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
Utils.TraverseDirectory(rootFolder, Visivt, -1);
sw.Stop();
UnityEngine.Debug.Log($"分析引用耗时:{sw.ElapsedMilliseconds / 1000f}s");
AssetDependencyGraphDB db = new AssetDependencyGraphDB("admin", "CCS20190109", "10.225.0.170");
sw.Restart();
db.Clean();
Parallel.ForEach(assetIdentify2AssetNodeDic, item =>
{
db.UpdateOrInsert(item.Value);
});
sw.Stop();
UnityEngine.Debug.Log($"更新数据库:{sw.ElapsedMilliseconds / 1000f}s");
}
}
}