222 lines
7.2 KiB
C#
222 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.EditorTools
|
|
{
|
|
public enum HandlerEntryKind
|
|
{
|
|
DirectorHandler = 0,
|
|
AnimatorHandler = 1
|
|
}
|
|
|
|
[Serializable]
|
|
public class HandlerNameCacheEntry
|
|
{
|
|
public string guid;
|
|
public string assetPath;
|
|
public string fileHash;
|
|
public string lastModified;
|
|
public string registeredName;
|
|
public HandlerEntryKind kind;
|
|
public string gameObjectPath;
|
|
public bool isInScene;
|
|
public int lineNumber;
|
|
}
|
|
|
|
[Serializable]
|
|
public class HandlerNameCache
|
|
{
|
|
public const int CurrentVersion = 2;
|
|
|
|
public int version = CurrentVersion;
|
|
public string lastFullScanTime;
|
|
public List<HandlerNameCacheEntry> entries = new List<HandlerNameCacheEntry>();
|
|
|
|
private static readonly string CacheFileName = "handler_name_cache.json";
|
|
|
|
public static string CacheFolderFullPath =>
|
|
Path.Combine(Application.dataPath, "Editor/HandlerNameCollector/Cache");
|
|
|
|
public static string CacheFileFullPath =>
|
|
Path.Combine(CacheFolderFullPath, CacheFileName);
|
|
|
|
/// <summary>迁移用:旧版 TimelineNameCollector 缓存路径。</summary>
|
|
private static string LegacyTimelineCacheFileFullPath =>
|
|
Path.Combine(Application.dataPath, "Editor/TimelineNameCollector/Cache/timeline_name_cache.json");
|
|
|
|
public static HandlerNameCache Load()
|
|
{
|
|
try
|
|
{
|
|
string json = null;
|
|
if (File.Exists(CacheFileFullPath))
|
|
json = File.ReadAllText(CacheFileFullPath, Encoding.UTF8);
|
|
else if (File.Exists(LegacyTimelineCacheFileFullPath))
|
|
json = File.ReadAllText(LegacyTimelineCacheFileFullPath, Encoding.UTF8);
|
|
|
|
if (string.IsNullOrEmpty(json))
|
|
{
|
|
return NewEmpty();
|
|
}
|
|
|
|
json = MigrateLegacyCacheJson(json);
|
|
var cache = JsonConvert.DeserializeObject<HandlerNameCache>(json);
|
|
if (cache == null)
|
|
cache = NewEmpty();
|
|
|
|
cache.version = CurrentVersion;
|
|
cache.entries ??= new List<HandlerNameCacheEntry>();
|
|
|
|
return cache;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[HandlerNameCache] Failed to load cache: {e}");
|
|
return NewEmpty();
|
|
}
|
|
}
|
|
|
|
private static HandlerNameCache NewEmpty()
|
|
{
|
|
return new HandlerNameCache
|
|
{
|
|
version = CurrentVersion,
|
|
lastFullScanTime = null,
|
|
entries = new List<HandlerNameCacheEntry>()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将旧版 timelineName 字段、缺省 kind 等合并为当前 JSON 结构。
|
|
/// </summary>
|
|
private static string MigrateLegacyCacheJson(string json)
|
|
{
|
|
try
|
|
{
|
|
var jo = JObject.Parse(json);
|
|
if (jo["entries"] is JArray arr)
|
|
{
|
|
foreach (var token in arr)
|
|
{
|
|
if (token is not JObject item)
|
|
continue;
|
|
|
|
var reg = item["registeredName"]?.ToString();
|
|
if (string.IsNullOrWhiteSpace(reg))
|
|
{
|
|
var legacy = item["timelineName"]?.ToString();
|
|
if (!string.IsNullOrEmpty(legacy))
|
|
item["registeredName"] = legacy;
|
|
}
|
|
|
|
item.Remove("timelineName");
|
|
|
|
if (item["kind"] == null)
|
|
item["kind"] = (int)HandlerEntryKind.DirectorHandler;
|
|
}
|
|
}
|
|
|
|
jo["version"] = CurrentVersion;
|
|
return jo.ToString(Formatting.None);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogWarning($"[HandlerNameCache] JSON migration fallback: {e}");
|
|
return json;
|
|
}
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
try
|
|
{
|
|
if (!Directory.Exists(CacheFolderFullPath))
|
|
Directory.CreateDirectory(CacheFolderFullPath);
|
|
|
|
entries ??= new List<HandlerNameCacheEntry>();
|
|
|
|
var settings = new JsonSerializerSettings
|
|
{
|
|
Formatting = Formatting.Indented,
|
|
StringEscapeHandling = StringEscapeHandling.Default
|
|
};
|
|
var json = JsonConvert.SerializeObject(this, settings);
|
|
File.WriteAllText(CacheFileFullPath, json, Encoding.UTF8);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[HandlerNameCache] Failed to save cache: {e}");
|
|
}
|
|
}
|
|
|
|
public void SetLastFullScanNow()
|
|
{
|
|
lastFullScanTime = DateTime.UtcNow.ToString("o");
|
|
}
|
|
|
|
public static string ComputeFileHash(string fullPath)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(fullPath))
|
|
return null;
|
|
|
|
using (var stream = File.OpenRead(fullPath))
|
|
using (var md5 = MD5.Create())
|
|
{
|
|
var hash = md5.ComputeHash(stream);
|
|
var sb = new StringBuilder(hash.Length * 2);
|
|
foreach (var b in hash)
|
|
sb.Append(b.ToString("x2"));
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[HandlerNameCache] Failed to compute file hash for '{fullPath}': {e}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static string GetAssetFullPath(string assetPath)
|
|
{
|
|
if (string.IsNullOrEmpty(assetPath))
|
|
return null;
|
|
|
|
if (!assetPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) &&
|
|
!assetPath.Equals("Assets", StringComparison.OrdinalIgnoreCase))
|
|
return null;
|
|
|
|
var relative = assetPath.Substring("Assets".Length)
|
|
.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
return Path.Combine(Application.dataPath, relative);
|
|
}
|
|
|
|
public void RemoveEntriesForAsset(string assetPath)
|
|
{
|
|
if (entries == null || string.IsNullOrEmpty(assetPath))
|
|
return;
|
|
entries.RemoveAll(e => e != null && e.assetPath == assetPath);
|
|
}
|
|
|
|
public void CleanupDeletedAssets()
|
|
{
|
|
if (entries == null)
|
|
return;
|
|
entries.RemoveAll(e =>
|
|
{
|
|
if (e == null || string.IsNullOrEmpty(e.assetPath))
|
|
return true;
|
|
return AssetDatabase.LoadMainAssetAtPath(e.assetPath) == null;
|
|
});
|
|
}
|
|
}
|
|
}
|