using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
///
/// 战斗网络包查看器
/// 用于查看和搜索 DTCB430_tagSCTurnFightReport 解析的战斗包
/// 快捷键:Alt+8
///
public class BattleReportChecker : EditorWindow
{
#region 静态数据存储
// 存储所有战斗包数据,按 guid 组织
private static Dictionary> allBattlePackages = new Dictionary>();
// 跟踪已执行的包UID(按 guid 组织)
private static Dictionary> executedPackUIDs = new Dictionary>();
// 跟踪当前正在执行的包UID(按 guid 组织)
private static Dictionary currentExecutingPackUID = new Dictionary();
// 包信息类
public class PackageInfo
{
public GameNetPackBasic package;
public string displayName;
public ulong packUID;
public string typeName;
public bool isExpanded;
public Dictionary fields = new Dictionary();
public Dictionary fieldDetails = new Dictionary();
public PackageInfo(GameNetPackBasic pack)
{
package = pack;
packUID = pack.packUID;
typeName = pack.GetType().Name;
displayName = $"[{packUID}] {typeName}";
isExpanded = false;
ExtractFields();
}
private void ExtractFields()
{
if (package == null) return;
var type = package.GetType();
var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (var field in fieldInfos)
{
try
{
var value = field.GetValue(package);
string valueStr = value != null ? value.ToString() : "null";
var detail = new FieldDetail { value = value };
// 特殊处理数组(注意:先判断Array,因为Array不是IList的子类)
if (value != null && value.GetType().IsArray)
{
System.Array array = value as System.Array;
valueStr = $"Array[{array.Length}]";
detail.isArray = true;
detail.arrayValue = array;
}
// 特殊处理列表(IList包括List和其他列表类型)
else if (value is System.Collections.IList list && !(value is string))
{
valueStr = $"List[{list.Count}]";
detail.isList = true;
detail.listValue = list;
}
fields[field.Name] = valueStr;
fieldDetails[field.Name] = detail;
}
catch (System.Exception ex)
{
fields[field.Name] = "读取失败: " + ex.Message;
fieldDetails[field.Name] = new FieldDetail { value = null };
}
}
}
}
// 字段详细信息类
public class FieldDetail
{
public object value;
public bool isArray;
public bool isList;
public bool isExpanded;
public System.Array arrayValue;
public System.Collections.IList listValue;
// 嵌套的子字段详情(用于复杂类型)
public Dictionary subFieldDetails = new Dictionary();
// 数组/列表元素的展开状态
public Dictionary elementDetails = new Dictionary();
}
// 数组/列表元素详细信息类
public class ElementDetail
{
public bool isExpanded;
public Dictionary fieldDetails = new Dictionary();
}
///
/// 供外部调用,添加解析后的包
///
public static void AddBattlePackages(string guid, List packages)
{
if (packages == null || packages.Count == 0) return;
var packInfoList = packages.Select(p => new PackageInfo(p)).ToList();
if (allBattlePackages.ContainsKey(guid))
{
allBattlePackages[guid].AddRange(packInfoList);
}
else
{
allBattlePackages[guid] = packInfoList;
}
Debug.Log($"[BattleReportChecker] 已记录 {packages.Count} 个包,GUID: {guid}");
}
///
/// 清空所有记录
///
public static void ClearAllPackages()
{
allBattlePackages.Clear();
executedPackUIDs.Clear();
currentExecutingPackUID.Clear();
Debug.Log("[BattleReportChecker] 已清空所有包记录");
}
///
/// 标记包开始执行
///
public static void MarkPackageExecuting(string guid, ulong packUID)
{
if (!currentExecutingPackUID.ContainsKey(guid))
{
currentExecutingPackUID[guid] = packUID;
}
else
{
currentExecutingPackUID[guid] = packUID;
}
}
///
/// 标记包执行完成
///
public static void MarkPackageExecuted(string guid, ulong packUID)
{
if (!executedPackUIDs.ContainsKey(guid))
{
executedPackUIDs[guid] = new HashSet();
}
executedPackUIDs[guid].Add(packUID);
// 清除当前执行标记
if (currentExecutingPackUID.ContainsKey(guid) && currentExecutingPackUID[guid] == packUID)
{
currentExecutingPackUID.Remove(guid);
}
}
#endregion
#region 窗口管理
[MenuItem("程序/战斗网络包查看器 &8")] // Alt+8
public static void ShowWindow()
{
var window = GetWindow("战斗包查看器");
window.minSize = new Vector2(600, 400);
window.Show();
}
#endregion
#region UI 状态
private Vector2 scrollPosition;
private Vector2 detailScrollPosition;
private string searchText = "";
private string detailSearchText = ""; // 详情搜索文本
private string lastDetailSearchText = ""; // 上次的详情搜索文本,用于检测变化
private string selectedGuid = "";
private List guidList = new List();
private int selectedGuidIndex = 0;
private List filteredPackages = new List();
private PackageInfo selectedPackage = null;
private PackageInfo lastSelectedPackage = null; // 上次选中的包,用于检测包切换
// 高亮相关
private PackageInfo highlightedPackage = null;
private double highlightStartTime = 0;
private const double highlightDuration = 5.0; // 高亮持续5秒
private int selectedPackageIndex = -1; // 记录选中包在列表中的索引,用于滚动
// 搜索缓存
private HashSet matchedFieldNames = new HashSet(); // 缓存匹配的字段名
private Dictionary fieldHasMatchedChild = new Dictionary(); // 缓存字段是否有匹配的子元素
#endregion
#region Unity 生命周期
private void OnEnable()
{
RefreshGuidList();
// 启用编辑器更新,让颜色实时刷新
EditorApplication.update += OnEditorUpdate;
}
private void OnDisable()
{
// 移除更新回调
EditorApplication.update -= OnEditorUpdate;
}
private void OnEditorUpdate()
{
// 定期刷新UI以更新包的执行状态颜色
Repaint();
}
private void OnGUI()
{
EditorGUILayout.BeginVertical();
DrawToolbar();
DrawGuidSelector();
DrawSearchBar();
EditorGUILayout.BeginHorizontal();
DrawPackageList();
DrawPackageDetails();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
#endregion
#region UI 绘制
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
if (GUILayout.Button("刷新", EditorStyles.toolbarButton, GUILayout.Width(60)))
{
// 刷新前清空搜索缓存,避免卡死
matchedFieldNames.Clear();
fieldHasMatchedChild.Clear();
lastSelectedPackage = null;
lastDetailSearchText = "";
RefreshGuidList();
FilterPackages();
}
if (GUILayout.Button("清空所有", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
if (EditorUtility.DisplayDialog("确认", "确定要清空所有包记录吗?", "确定", "取消"))
{
ClearAllPackages();
RefreshGuidList();
FilterPackages();
}
}
GUILayout.FlexibleSpace();
GUILayout.Label($"总包数: {filteredPackages.Count}", EditorStyles.toolbarButton);
EditorGUILayout.EndHorizontal();
}
private void DrawGuidSelector()
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("战场GUID:", GUILayout.Width(80));
if (guidList.Count == 0)
{
GUILayout.Label("暂无数据", EditorStyles.helpBox);
}
else
{
int newIndex = EditorGUILayout.Popup(selectedGuidIndex, guidList.ToArray());
if (newIndex != selectedGuidIndex)
{
selectedGuidIndex = newIndex;
selectedGuid = guidList[selectedGuidIndex];
FilterPackages();
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(5);
}
private void DrawSearchBar()
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("搜索:", GUILayout.Width(80));
string newSearchText = EditorGUILayout.TextField(searchText);
if (newSearchText != searchText)
{
searchText = newSearchText;
FilterPackages();
// 搜索后自动选中第一个结果并高亮
if (filteredPackages.Count > 0)
{
selectedPackage = filteredPackages[0];
selectedPackageIndex = 0;
StartHighlight(selectedPackage);
ScrollToSelectedPackage();
}
}
if (GUILayout.Button("清空", GUILayout.Width(60)))
{
searchText = "";
FilterPackages();
}
EditorGUILayout.EndHorizontal();
GUILayout.Label("支持搜索:包类型名、UID、字段值", EditorStyles.miniLabel);
EditorGUILayout.Space(5);
}
private void DrawPackageList()
{
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.5f));
GUILayout.Label("包列表", EditorStyles.boldLabel);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUI.skin.box);
if (filteredPackages.Count == 0)
{
GUILayout.Label("没有找到匹配的包", EditorStyles.helpBox);
}
else
{
foreach (var packInfo in filteredPackages)
{
DrawPackageItem(packInfo);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void DrawPackageItem(PackageInfo packInfo)
{
// 判断包的执行状态
bool isExecuting = currentExecutingPackUID.ContainsKey(selectedGuid) &&
currentExecutingPackUID[selectedGuid] == packInfo.packUID;
bool isExecuted = executedPackUIDs.ContainsKey(selectedGuid) &&
executedPackUIDs[selectedGuid].Contains(packInfo.packUID);
// 判断是否是高亮状态
bool isHighlighted = IsPackageHighlighted(packInfo);
// 设置背景颜色
Color originalBgColor = GUI.backgroundColor;
if (isHighlighted)
{
// 高亮状态 - 使用绿色闪烁效果
float pulse = Mathf.PingPong((float)EditorApplication.timeSinceStartup * 2f, 1f);
GUI.backgroundColor = Color.Lerp(new Color(0.3f, 0.8f, 0.3f), new Color(0.5f, 1f, 0.5f), pulse);
}
else if (isExecuting)
{
GUI.backgroundColor = new Color(0.3f, 0.3f, 0.3f); // 深黑色(正在执行)
}
else if (isExecuted)
{
GUI.backgroundColor = new Color(0.6f, 0.6f, 0.6f); // 灰色(已执行)
}
else
{
GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f); // 浅灰色(未执行)
}
EditorGUILayout.BeginVertical(GUI.skin.box);
GUI.backgroundColor = originalBgColor; // 恢复原始颜色
EditorGUILayout.BeginHorizontal();
// 展开/折叠按钮
string foldoutLabel = packInfo.isExpanded ? "▼" : "▶";
if (GUILayout.Button(foldoutLabel, EditorStyles.label, GUILayout.Width(20)))
{
packInfo.isExpanded = !packInfo.isExpanded;
}
// 包名称(可点击选中)
bool isSelected = selectedPackage == packInfo;
// 根据状态设置文本颜色
GUIStyle style;
if (isSelected)
{
style = new GUIStyle(EditorStyles.boldLabel) { normal = { textColor = Color.cyan } };
}
else if (isExecuting)
{
style = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.yellow } }; // 黄色高亮
}
else if (isExecuted)
{
style = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.gray } };
}
else
{
style = EditorStyles.label;
}
if (GUILayout.Button(packInfo.displayName, style))
{
selectedPackage = packInfo;
selectedPackageIndex = filteredPackages.IndexOf(packInfo);
}
EditorGUILayout.EndHorizontal();
// 展开显示简要信息
if (packInfo.isExpanded)
{
EditorGUI.indentLevel++;
// 显示前5个字段
int count = 0;
foreach (var field in packInfo.fields)
{
if (count >= 5) break;
EditorGUILayout.BeginHorizontal();
GUILayout.Label(field.Key + ":", GUILayout.Width(120));
GUILayout.Label(field.Value, EditorStyles.wordWrappedLabel);
EditorGUILayout.EndHorizontal();
count++;
}
if (packInfo.fields.Count > 5)
{
GUILayout.Label($"... 更多字段请在右侧查看", EditorStyles.miniLabel);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndVertical();
}
private void DrawPackageDetails()
{
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.5f - 10));
GUILayout.Label("包详情", EditorStyles.boldLabel);
if (selectedPackage == null)
{
GUILayout.Label("请从左侧列表选择一个包查看详情", EditorStyles.helpBox);
}
else
{
// 详情搜索栏
EditorGUILayout.BeginHorizontal();
GUILayout.Label("搜索字段:", GUILayout.Width(70));
detailSearchText = EditorGUILayout.TextField(detailSearchText);
if (GUILayout.Button("清空", GUILayout.Width(50)))
{
detailSearchText = "";
}
EditorGUILayout.EndHorizontal();
// 检测选中包或搜索文本变化,更新缓存
bool packageChanged = selectedPackage != lastSelectedPackage;
bool searchTextChanged = detailSearchText != lastDetailSearchText;
if (packageChanged || searchTextChanged)
{
lastDetailSearchText = detailSearchText;
lastSelectedPackage = selectedPackage;
UpdateDetailSearchCache();
}
if (string.IsNullOrEmpty(detailSearchText))
{
GUILayout.Label("在此包的字段中搜索(字段名或值)", EditorStyles.miniLabel);
}
else
{
GUILayout.Label($"找到 {matchedFieldNames.Count} 个匹配的字段(已自动展开并高亮)", EditorStyles.miniLabel);
}
EditorGUILayout.Space(5);
detailScrollPosition = EditorGUILayout.BeginScrollView(detailScrollPosition, GUI.skin.box);
// 标题
GUILayout.Label(selectedPackage.displayName, EditorStyles.boldLabel);
EditorGUILayout.Space(5);
// 基本信息 - 支持搜索高亮
string searchLower = detailSearchText.ToLower();
bool typeMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.typeName.ToLower().Contains(searchLower);
bool uidMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.packUID.ToString().Contains(searchLower);
bool cmdMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.package.cmd.ToString().Contains(searchLower);
DrawDetailFieldWithHighlight("包类型", selectedPackage.typeName, typeMatched);
DrawDetailFieldWithHighlight("UID", selectedPackage.packUID.ToString(), uidMatched);
DrawDetailFieldWithHighlight("Cmd", selectedPackage.package.cmd.ToString(), cmdMatched);
EditorGUILayout.Space(10);
// 所有字段
GUILayout.Label("字段列表:", EditorStyles.boldLabel);
// 根据搜索文本过滤字段
foreach (var field in selectedPackage.fields)
{
if (ShouldShowDetailField(field.Key, field.Value, selectedPackage))
{
DrawDetailFieldWithExpand(field.Key, field.Value, selectedPackage);
}
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.EndVertical();
}
private void DrawDetailField(string fieldName, string value)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label(fieldName + ":", GUILayout.Width(150));
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
EditorGUILayout.EndHorizontal();
}
private void DrawDetailFieldWithHighlight(string fieldName, string value, bool isMatched)
{
if (isMatched)
{
Color originalBgColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 淡黄色背景
EditorGUILayout.BeginHorizontal(GUI.skin.box);
GUI.backgroundColor = originalBgColor;
GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(150));
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
EditorGUILayout.EndHorizontal();
}
else
{
DrawDetailField(fieldName, value);
}
}
private void DrawDetailFieldWithExpand(string fieldName, string value, PackageInfo packInfo)
{
if (!packInfo.fieldDetails.ContainsKey(fieldName))
{
DrawDetailField(fieldName, value);
return;
}
var detail = packInfo.fieldDetails[fieldName];
// 使用缓存检查是否匹配
bool isMatched = matchedFieldNames.Contains(fieldName);
// 使用缓存检查子元素是否匹配
bool hasMatchedChild = false;
if (!string.IsNullOrEmpty(detailSearchText))
{
fieldHasMatchedChild.TryGetValue(fieldName, out hasMatchedChild);
}
// 如果自身匹配或子元素匹配,自动展开
if ((isMatched || hasMatchedChild) && !string.IsNullOrEmpty(detailSearchText))
{
detail.isExpanded = true;
}
// 判断是否是复杂对象(非基本类型、非字符串、非枚举)
bool isComplexObject = detail.value != null &&
!detail.value.GetType().IsPrimitive &&
detail.value.GetType() != typeof(string) &&
!detail.value.GetType().IsEnum &&
!detail.isArray &&
!detail.isList;
// 如果是数组、列表或复杂对象,显示展开按钮
if (detail.isArray || detail.isList || isComplexObject)
{
// 如果匹配或有匹配的子元素,添加背景颜色
Color originalBgColor = GUI.backgroundColor;
if (isMatched || hasMatchedChild)
{
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 淡黄色背景
}
EditorGUILayout.BeginHorizontal((isMatched || hasMatchedChild) ? GUI.skin.box : GUIStyle.none);
GUI.backgroundColor = originalBgColor;
// 展开按钮
string foldoutLabel = detail.isExpanded ? "▼" : "▶";
if (GUILayout.Button(foldoutLabel, EditorStyles.label, GUILayout.Width(20)))
{
detail.isExpanded = !detail.isExpanded;
}
// 字段名样式
GUIStyle labelStyle = (isMatched || hasMatchedChild) ? new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold } : GUI.skin.label;
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(130));
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
EditorGUILayout.EndHorizontal();
// 如果展开,显示内容
if (detail.isExpanded)
{
EditorGUI.indentLevel++;
if (detail.isArray && detail.arrayValue != null)
{
DrawArrayElements(detail.arrayValue, detail);
}
else if (detail.isList && detail.listValue != null)
{
DrawListElements(detail.listValue, detail);
}
else if (isComplexObject)
{
// 展开复杂对象的字段
DrawObjectFields(detail.value, detail.subFieldDetails);
}
EditorGUI.indentLevel--;
}
}
else
{
// 简单字段也添加高亮
if (isMatched)
{
Color originalBgColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(1f, 1f, 0.5f);
EditorGUILayout.BeginHorizontal(GUI.skin.box);
GUI.backgroundColor = originalBgColor;
GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(150));
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
EditorGUILayout.EndHorizontal();
}
else
{
DrawDetailField(fieldName, value);
}
}
}
private void DrawArrayElements(System.Array array, FieldDetail parentDetail)
{
if (array == null)
{
EditorGUILayout.LabelField("Array is null");
return;
}
// 显示数组元素数量
EditorGUILayout.LabelField($"总共 {array.Length} 个元素:", EditorStyles.miniLabel);
for (int i = 0; i < array.Length; i++)
{
var element = array.GetValue(i);
DrawArrayElement(i, element, parentDetail);
}
}
private void DrawListElements(System.Collections.IList list, FieldDetail parentDetail)
{
if (list == null)
{
EditorGUILayout.LabelField("List is null");
return;
}
// 显示列表元素数量
EditorGUILayout.LabelField($"总共 {list.Count} 个元素:", EditorStyles.miniLabel);
for (int i = 0; i < list.Count; i++)
{
var element = list[i];
DrawArrayElement(i, element, parentDetail);
}
}
private void DrawArrayElement(int index, object element, FieldDetail parentDetail)
{
if (element == null)
{
EditorGUILayout.LabelField($"[{index}]", "null");
return;
}
var type = element.GetType();
// 检查元素是否匹配搜索
bool isElementMatched = false;
if (!string.IsNullOrEmpty(detailSearchText))
{
HashSet