265 lines
8.4 KiB
C#
265 lines
8.4 KiB
C#
|
|
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");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|