chore: initial commit
This commit is contained in:
164
Assets/Scripts/Editor/AddressKeyValidator.cs
Normal file
164
Assets/Scripts/Editor/AddressKeyValidator.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
#endif
|
||||
|
||||
namespace BaseGames.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor 工具:验证 <see cref="BaseGames.Core.Assets.AddressKeys"/> 中所有常量
|
||||
/// 是否与 Addressable 分组中实际存在的地址同步(架构 13_AssetPoolModule §10)。
|
||||
///
|
||||
/// 菜单:BaseGames → Tools → Validate Address Keys
|
||||
/// </summary>
|
||||
public static class AddressKeyValidator
|
||||
{
|
||||
[MenuItem("BaseGames/Tools/Validate Address Keys")]
|
||||
public static void ValidateAll()
|
||||
{
|
||||
var results = RunValidation();
|
||||
LogResults(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行验证,返回每个 key 的验证结果。供 Build Pre-process 或测试调用。
|
||||
/// </summary>
|
||||
public static List<ValidationResult> RunValidation()
|
||||
{
|
||||
var results = new List<ValidationResult>();
|
||||
var registeredAddresses = GetAllAddressableAddresses();
|
||||
|
||||
// 通过反射取出 AddressKeys 中所有 public const string 字段
|
||||
var keyType = typeof(BaseGames.Core.Assets.AddressKeys);
|
||||
var fields = keyType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
|
||||
.Where(f => f.IsLiteral && !f.IsInitOnly && f.FieldType == typeof(string));
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var value = (string)field.GetRawConstantValue();
|
||||
var exists = registeredAddresses.Contains(value);
|
||||
results.Add(new ValidationResult(field.Name, value, exists));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ── Internal ──────────────────────────────────────────────────────────
|
||||
|
||||
private static HashSet<string> GetAllAddressableAddresses()
|
||||
{
|
||||
var addresses = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("[AddressKeyValidator] Addressable Settings 未找到,请先初始化 Addressables。");
|
||||
return addresses;
|
||||
}
|
||||
|
||||
foreach (var group in settings.groups)
|
||||
{
|
||||
if (group == null) continue;
|
||||
foreach (var entry in group.entries)
|
||||
{
|
||||
if (entry != null)
|
||||
addresses.Add(entry.address);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private static void LogResults(List<ValidationResult> results)
|
||||
{
|
||||
int missing = 0;
|
||||
foreach (var r in results)
|
||||
{
|
||||
if (!r.ExistsInAddressables)
|
||||
{
|
||||
Debug.LogWarning($"[AddressKeyValidator] ⚠ 孤儿 Key:AddressKeys.{r.FieldName} = \"{r.Value}\" — 未在 Addressable 分组中找到对应地址。");
|
||||
missing++;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing == 0)
|
||||
Debug.Log($"[AddressKeyValidator] ✓ 所有 {results.Count} 个 AddressKeys 常量均在 Addressable 分组中存在。");
|
||||
else
|
||||
Debug.LogWarning($"[AddressKeyValidator] 共 {results.Count} 个常量,发现 {missing} 个孤儿 Key,请检查 Addressable 分组配置。");
|
||||
}
|
||||
|
||||
// ── 结果结构 ──────────────────────────────────────────────────────────
|
||||
|
||||
public readonly struct ValidationResult
|
||||
{
|
||||
public readonly string FieldName;
|
||||
public readonly string Value;
|
||||
public readonly bool ExistsInAddressables;
|
||||
|
||||
public ValidationResult(string fieldName, string value, bool exists)
|
||||
{
|
||||
FieldName = fieldName;
|
||||
Value = value;
|
||||
ExistsInAddressables = exists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资产导入后自动触发 AddressKey 验证(架构 13_AssetPoolModule §10)。
|
||||
/// 仅在 Addressable Group 资产发生变更时触发,避免每次导入都验证。
|
||||
/// </summary>
|
||||
public class AddressKeyImportWatcher : AssetPostprocessor
|
||||
{
|
||||
private const string AddressableGroupAssetExt = ".asset";
|
||||
private const string AddressableGroupFolder = "Assets/AddressableAssetsData";
|
||||
|
||||
private static void OnPostprocessAllAssets(
|
||||
string[] importedAssets,
|
||||
string[] deletedAssets,
|
||||
string[] movedAssets,
|
||||
string[] movedFromAssetPaths)
|
||||
{
|
||||
bool addressablesChanged = false;
|
||||
|
||||
foreach (var path in importedAssets)
|
||||
{
|
||||
if (path.StartsWith(AddressableGroupFolder, StringComparison.OrdinalIgnoreCase)
|
||||
&& path.EndsWith(AddressableGroupAssetExt, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
addressablesChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addressablesChanged)
|
||||
{
|
||||
foreach (var path in deletedAssets)
|
||||
{
|
||||
if (path.StartsWith(AddressableGroupFolder, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
addressablesChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addressablesChanged)
|
||||
{
|
||||
// 延迟一帧执行,等待 AssetDatabase 完全刷新
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
Debug.Log("[AddressKeyImportWatcher] 检测到 Addressable 分组变更,自动触发 Key 验证...");
|
||||
AddressKeyValidator.ValidateAll();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user