NLDClient-yudde/ProjectNLD/Assets/Editor/Tools/PictorialCharacterIdValidat...

399 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace PictorialEditor
{
/// <summary>
/// 画报角色ID验证工具
/// 扫描 ActivityPicturialData 目录下的所有 JSON 文件,检查角色 ID 是否有效
/// </summary>
public class PictorialCharacterIdValidator : EditorWindow
{
private const string PICTORIAL_DATA_FOLDER = "Assets/Config/Data/ActivityPicturialData";
private const string CHARACTER_CONFIG_PATH = "Assets/Config/Data/charactercfg_characterattri.json";
private Vector2 scrollPosition;
private List<ValidationError> validationErrors = new List<ValidationError>();
private bool isScanning = false;
private string statusMessage = "点击「开始扫描」按钮开始验证";
private HashSet<int> validCharacterIds = new HashSet<int>();
private HashSet<string> validNameIds = new HashSet<string>(); // 角色名称ID如 A00001
[System.Serializable]
private class ValidationError
{
public string fileName;
public string itemName;
public string characterId;
public string errorMessage;
public int lineNumber; // JSON 中的大致行号
}
[MenuItem("Tools/画报工具/角色ID验证工具")]
public static void ShowWindow()
{
var window = GetWindow<PictorialCharacterIdValidator>("画报角色ID验证");
window.minSize = new Vector2(600, 400);
window.Show();
}
private void OnGUI()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("画报角色ID验证工具", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
"此工具会:\n" +
"1. 加载角色配置表 (charactercfg_characterattri.json)\n" +
"2. 扫描 ActivityPicturialData 目录下的所有画报 JSON 文件\n" +
"3. 验证画报中的 characterId 是否有效(格式正确 + 存在于配置表中)",
MessageType.Info
);
EditorGUILayout.Space(10);
// 扫描按钮
EditorGUI.BeginDisabledGroup(isScanning);
if (GUILayout.Button("开始扫描", GUILayout.Height(40)))
{
StartScan();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space(5);
// 状态信息
EditorGUILayout.LabelField("状态:", EditorStyles.boldLabel);
EditorGUILayout.LabelField(statusMessage);
EditorGUILayout.Space(10);
// 显示验证结果
if (validationErrors.Count > 0)
{
EditorGUILayout.LabelField($"发现 {validationErrors.Count} 个问题:", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
foreach (var error in validationErrors)
{
EditorGUILayout.BeginVertical("box");
// 文件名
EditorGUILayout.LabelField("文件:", EditorStyles.boldLabel);
EditorGUILayout.SelectableLabel(error.fileName, GUILayout.Height(18));
// 元素名称
EditorGUILayout.LabelField("元素名称:", EditorStyles.boldLabel);
EditorGUILayout.SelectableLabel(error.itemName, GUILayout.Height(18));
// 角色ID
EditorGUILayout.LabelField("角色ID", EditorStyles.boldLabel);
EditorGUILayout.SelectableLabel(error.characterId, GUILayout.Height(18));
// 错误信息
EditorGUILayout.LabelField("问题:", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(error.errorMessage, MessageType.Error);
// 打开文件按钮
if (GUILayout.Button("打开 JSON 文件", GUILayout.Height(25)))
{
OpenJsonFile(error.fileName);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(5);
}
EditorGUILayout.EndScrollView();
}
else if (!isScanning && validationErrors.Count == 0 && statusMessage.Contains("扫描完成"))
{
EditorGUILayout.HelpBox("✅ 所有画报的角色ID都是有效的", MessageType.Info);
}
}
private void StartScan()
{
isScanning = true;
validationErrors.Clear();
statusMessage = "正在加载角色配置表...";
Repaint();
try
{
// 加载角色配置表
if (!LoadCharacterConfig())
{
statusMessage = "加载角色配置表失败,请检查文件路径";
return;
}
statusMessage = $"角色配置表加载成功,共 {validCharacterIds.Count} 个角色ID{validNameIds.Count} 个名称ID。正在扫描画报文件...";
Repaint();
ScanPictorialJsonFiles();
if (validationErrors.Count > 0)
{
statusMessage = $"扫描完成,发现 {validationErrors.Count} 个问题";
}
else
{
statusMessage = "扫描完成所有角色ID都有效";
}
}
catch (System.Exception e)
{
statusMessage = $"扫描出错: {e.Message}";
Debug.LogError($"[画报角色ID验证] 扫描出错: {e.Message}\n{e.StackTrace}");
}
finally
{
isScanning = false;
Repaint();
}
}
/// <summary>
/// 加载角色配置表获取所有有效的角色ID
/// </summary>
private bool LoadCharacterConfig()
{
validCharacterIds.Clear();
validNameIds.Clear();
if (!File.Exists(CHARACTER_CONFIG_PATH))
{
Debug.LogError($"[画报角色ID验证] 角色配置文件不存在: {CHARACTER_CONFIG_PATH}");
return false;
}
try
{
string jsonContent = File.ReadAllText(CHARACTER_CONFIG_PATH);
JArray jsonArray = JArray.Parse(jsonContent);
if (jsonArray == null)
{
Debug.LogError($"[画报角色ID验证] 无法解析角色配置文件");
return false;
}
// 解析角色配置提取所有角色ID和名称ID
// 配置格式: [{"RoleID": 100001, "NameID": "A00001", ...}, ...]
for (int i = 0; i < jsonArray.Count; i++)
{
JObject item = jsonArray[i] as JObject;
if (item == null)
continue;
// 提取 RoleID (数字ID)
int roleId = item["RoleID"]?.Value<int>() ?? 0;
if (roleId > 0)
{
validCharacterIds.Add(roleId);
}
// 提取 NameID (字符串ID如 A00001)
string nameId = item["NameID"]?.Value<string>();
if (!string.IsNullOrEmpty(nameId))
{
validNameIds.Add(nameId);
}
}
Debug.Log($"[画报角色ID验证] 成功加载 {validCharacterIds.Count} 个角色ID{validNameIds.Count} 个名称ID");
return validCharacterIds.Count > 0 && validNameIds.Count > 0;
}
catch (System.Exception e)
{
Debug.LogError($"[画报角色ID验证] 加载角色配置表出错: {e.Message}\n{e.StackTrace}");
return false;
}
}
private void ScanPictorialJsonFiles()
{
if (!Directory.Exists(PICTORIAL_DATA_FOLDER))
{
statusMessage = $"目录不存在: {PICTORIAL_DATA_FOLDER}";
return;
}
string[] jsonFiles = Directory.GetFiles(PICTORIAL_DATA_FOLDER, "*.json", SearchOption.TopDirectoryOnly);
if (jsonFiles.Length == 0)
{
statusMessage = "未找到任何 JSON 文件";
return;
}
Debug.Log($"[画报角色ID验证] 开始扫描 {jsonFiles.Length} 个文件");
foreach (string jsonFile in jsonFiles)
{
ValidateJsonFile(jsonFile);
}
}
private void ValidateJsonFile(string filePath)
{
string fileName = Path.GetFileName(filePath);
try
{
string jsonContent = File.ReadAllText(filePath);
JObject jsonObj = JObject.Parse(jsonContent);
// 获取 items 数组
JArray items = jsonObj["items"] as JArray;
if (items == null)
{
Debug.LogWarning($"[画报角色ID验证] {fileName} 中没有 items 数组");
return;
}
// 遍历每个元素
for (int i = 0; i < items.Count; i++)
{
JObject item = items[i] as JObject;
if (item == null)
continue;
string itemName = item["itemName"]?.ToString() ?? $"Item_{i}";
string characterId = item["characterId"]?.ToString();
// 如果没有 characterId 或为空,跳过(这是正常的)
if (string.IsNullOrEmpty(characterId))
continue;
// 验证角色ID
string errorMessage = ValidateCharacterId(characterId);
if (!string.IsNullOrEmpty(errorMessage))
{
validationErrors.Add(new ValidationError
{
fileName = fileName,
itemName = itemName,
characterId = characterId,
errorMessage = errorMessage,
lineNumber = EstimateLineNumber(jsonContent, itemName)
});
}
}
}
catch (System.Exception e)
{
Debug.LogError($"[画报角色ID验证] 解析文件失败: {fileName}, 错误: {e.Message}");
}
}
/// <summary>
/// 验证角色ID是否有效
/// </summary>
private string ValidateCharacterId(string characterId)
{
// 检查格式
if (string.IsNullOrWhiteSpace(characterId))
{
return "角色ID为空";
}
// 情况1: NameID 格式(如 "A00001", "A00012"
if (characterId.StartsWith("A") && characterId.Length == 6)
{
// 直接在 validNameIds 中查找
if (validNameIds.Contains(characterId))
{
return null; // 验证通过
}
else
{
return $"角色名称ID不存在于配置表中: {characterId}";
}
}
// 情况2: RoleID 格式(纯数字或 A+数字)
int numericId = 0;
if (characterId.StartsWith("A") && characterId.Length > 6)
{
// "A100001" 格式
string numberPart = characterId.Substring(1);
if (!int.TryParse(numberPart, out numericId))
{
return $"无效的角色ID格式无法解析数字部分: {characterId}";
}
}
else if (!characterId.StartsWith("A"))
{
// 纯数字格式
if (!int.TryParse(characterId, out numericId))
{
return $"无效的角色ID格式不是数字也不是A开头: {characterId}";
}
// 如果小于 100000补齐
if (numericId < 100000)
numericId += 100000;
}
else
{
return $"无法识别的角色ID格式: {characterId}";
}
// 检查角色ID范围
if (numericId < 100000 || numericId > 999999)
{
return $"角色ID超出有效范围 (100000-999999): {characterId} (解析后: {numericId})";
}
// 检查角色ID是否存在于配置表中
if (!validCharacterIds.Contains(numericId))
{
return $"角色ID不存在于配置表中: {characterId} (解析后: {numericId})";
}
return null; // 验证通过
}
/// <summary>
/// 估算元素在 JSON 文件中的行号(用于定位)
/// </summary>
private int EstimateLineNumber(string jsonContent, string itemName)
{
string[] lines = jsonContent.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains($"\"itemName\": \"{itemName}\""))
{
return i + 1; // 行号从1开始
}
}
return -1;
}
/// <summary>
/// 在外部编辑器中打开 JSON 文件
/// </summary>
private void OpenJsonFile(string fileName)
{
string fullPath = Path.Combine(PICTORIAL_DATA_FOLDER, fileName);
if (File.Exists(fullPath))
{
System.Diagnostics.Process.Start(fullPath);
}
else
{
Debug.LogError($"[画报角色ID验证] 文件不存在: {fullPath}");
}
}
}
}