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 visited = new HashSet(); isElementMatched = RecursiveSearchInObject(element, detailSearchText.ToLower(), visited); } // 检查是否是 GameNetPackBasic 类型,并判断是否处于执行状态 bool isPackageElement = element is GameNetPackBasic; bool isHighlightedPackage = false; bool isExecutingPackage = false; bool isExecutedPackage = false; if (isPackageElement) { GameNetPackBasic pack = element as GameNetPackBasic; ulong packUID = pack.packUID; // 检查高亮状态 if (highlightedPackage != null && highlightedPackage.packUID == packUID) { double elapsed = EditorApplication.timeSinceStartup - highlightStartTime; isHighlightedPackage = elapsed < highlightDuration; } // 检查执行状态 isExecutingPackage = currentExecutingPackUID.ContainsKey(selectedGuid) && currentExecutingPackUID[selectedGuid] == packUID; isExecutedPackage = executedPackUIDs.ContainsKey(selectedGuid) && executedPackUIDs[selectedGuid].Contains(packUID); } // 如果是基本类型,直接显示 if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { // 如果匹配,添加高亮 if (isElementMatched) { Color originalBgColor = GUI.backgroundColor; GUI.backgroundColor = new Color(1f, 1f, 0.5f); EditorGUILayout.BeginHorizontal(GUI.skin.box); GUI.backgroundColor = originalBgColor; GUIStyle labelStyle = new GUIStyle(EditorStyles.label) { fontStyle = FontStyle.Bold }; EditorGUILayout.PrefixLabel($"[{index}]", labelStyle, labelStyle); EditorGUILayout.SelectableLabel(element.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel($"[{index}]"); EditorGUILayout.SelectableLabel(element.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } } else { // 复杂类型,需要展开 // 确保有ElementDetail if (parentDetail != null && !parentDetail.elementDetails.ContainsKey(index)) { parentDetail.elementDetails[index] = new ElementDetail(); } var elemDetail = parentDetail?.elementDetails[index]; // 如果元素匹配搜索,自动展开 if (isElementMatched && elemDetail != null) { elemDetail.isExpanded = true; } // 为包元素设置背景颜色 Color originalBgColor = GUI.backgroundColor; if (isPackageElement) { if (isHighlightedPackage) { // 高亮状态 - 使用绿色闪烁效果 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 (isExecutingPackage) { GUI.backgroundColor = new Color(0.3f, 0.3f, 0.3f); // 深黑色(正在执行) } else if (isExecutedPackage) { GUI.backgroundColor = new Color(0.6f, 0.6f, 0.6f); // 灰色(已执行) } else if (isElementMatched) { GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 黄色(搜索匹配) } } else if (isElementMatched) { GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 黄色(搜索匹配) } EditorGUILayout.BeginVertical(isPackageElement || isElementMatched ? GUI.skin.box : GUIStyle.none); GUI.backgroundColor = originalBgColor; // 展开按钮 + 类型名 GUIStyle labelStyle = EditorStyles.foldout; if (isPackageElement) { if (isHighlightedPackage) { labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.green }, fontStyle = FontStyle.Bold }; } else if (isExecutingPackage) { labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.yellow }, fontStyle = FontStyle.Bold }; } else if (isExecutedPackage) { labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.gray } }; } else if (isElementMatched) { labelStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold }; } } else if (isElementMatched) { labelStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold }; } if (elemDetail != null) { GameNetPackBasic pack = element as GameNetPackBasic; string displayText = isPackageElement ? $"[{index}] [{pack.packUID}] {type.Name}" : $"[{index}] {type.Name}"; // 特殊处理:HB427 技能包显示释放者和技能名 if (element is HB427_tagSCUseSkill skillPack) { string casterName = GetObjectName(skillPack.ObjID); string skillName = GetSkillName(skillPack.SkillID); displayText += $" - {casterName} 使用 {skillName}"; } // 特殊处理:CustomHB426CombinePack 显示是否需要等待 else if (element is CustomHB426CombinePack combinePack) { bool needWaiting = combinePack.NeedWaiting(); displayText += needWaiting ? " [需要等待]" : " [无需等待]"; } elemDetail.isExpanded = EditorGUILayout.Foldout(elemDetail.isExpanded, displayText, true, labelStyle); } else { EditorGUILayout.LabelField($"[{index}] {type.Name}"); } // 如果展开,显示字段 if (elemDetail != null && elemDetail.isExpanded) { EditorGUI.indentLevel++; DrawObjectFields(element, elemDetail.fieldDetails); EditorGUI.indentLevel--; } if (isPackageElement || isElementMatched) { EditorGUILayout.EndVertical(); } } } // 绘制对象的字段(支持递归展开) private void DrawObjectFields(object obj, Dictionary fieldDetailsDict) { if (obj == null) return; var type = obj.GetType(); var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var field in fields) { try { var value = field.GetValue(obj); // 确保有FieldDetail if (!fieldDetailsDict.ContainsKey(field.Name)) { var detail = new FieldDetail { value = value }; if (value != null && value.GetType().IsArray) { detail.isArray = true; detail.arrayValue = value as System.Array; } else if (value is System.Collections.IList list && !(value is string)) { detail.isList = true; detail.listValue = list; } fieldDetailsDict[field.Name] = detail; } var fieldDetail = fieldDetailsDict[field.Name]; // 绘制字段 DrawFieldWithRecursiveExpand(field.Name, value, fieldDetail); } catch { EditorGUILayout.LabelField(field.Name, "读取失败"); } } } // 递归地绘制字段(支持多层展开) private void DrawFieldWithRecursiveExpand(string fieldName, object value, FieldDetail fieldDetail) { if (value == null) { EditorGUILayout.LabelField(fieldName + ":", "null"); return; } var type = value.GetType(); // 检查字段值是否匹配搜索 bool isValueMatched = false; if (!string.IsNullOrEmpty(detailSearchText)) { string searchLower = detailSearchText.ToLower(); // 字段名匹配 if (fieldName.ToLower().Contains(searchLower)) isValueMatched = true; // 字段值匹配 else if (value.ToString().Contains(searchLower)) isValueMatched = true; } // 基本类型直接显示 if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { // 获取显示值,如果是ObjID则追加对象名称 string displayValue = value.ToString(); if (fieldName == "ObjID" && uint.TryParse(displayValue, out uint objID)) { string objName = GetObjectName(objID); displayValue = $"{displayValue} ({objName})"; } if (isValueMatched) { // 高亮显示匹配的字段 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.Space(EditorGUI.indentLevel * 15); // 手动缩进 GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(120)); EditorGUILayout.SelectableLabel(displayValue, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(); GUILayout.Space(EditorGUI.indentLevel * 15); // 手动缩进 GUILayout.Label(fieldName + ":", GUILayout.Width(120)); EditorGUILayout.SelectableLabel(displayValue, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } } // 数组类型 else if (type.IsArray && fieldDetail.arrayValue != null) { fieldDetail.isExpanded = EditorGUILayout.Foldout(fieldDetail.isExpanded, $"{fieldName}: Array[{fieldDetail.arrayValue.Length}]", true); if (fieldDetail.isExpanded) { EditorGUI.indentLevel++; for (int i = 0; i < fieldDetail.arrayValue.Length; i++) { var element = fieldDetail.arrayValue.GetValue(i); DrawArrayElement(i, element, fieldDetail); } EditorGUI.indentLevel--; } } // 列表类型 else if (fieldDetail.isList && fieldDetail.listValue != null) { fieldDetail.isExpanded = EditorGUILayout.Foldout(fieldDetail.isExpanded, $"{fieldName}: List[{fieldDetail.listValue.Count}]", true); if (fieldDetail.isExpanded) { EditorGUI.indentLevel++; for (int i = 0; i < fieldDetail.listValue.Count; i++) { var element = fieldDetail.listValue[i]; DrawArrayElement(i, element, fieldDetail); } EditorGUI.indentLevel--; } } // 其他复杂类型 else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(fieldName); EditorGUILayout.SelectableLabel(value.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); EditorGUILayout.EndHorizontal(); } } #endregion #region 详情搜索 /// /// 更新详情搜索缓存(仅在搜索文本改变时调用) /// private void UpdateDetailSearchCache() { matchedFieldNames.Clear(); fieldHasMatchedChild.Clear(); if (selectedPackage == null || string.IsNullOrEmpty(detailSearchText)) return; string searchLower = detailSearchText.ToLower(); // 检查基本信息是否匹配(包类型、UID、Cmd) bool basicInfoMatched = selectedPackage.typeName.ToLower().Contains(searchLower) || selectedPackage.packUID.ToString().Contains(searchLower) || selectedPackage.package.cmd.ToString().Contains(searchLower); foreach (var field in selectedPackage.fields) { string fieldName = field.Key; string fieldValue = field.Value; // 检查字段名或字段值是否匹配 bool fieldMatched = fieldName.ToLower().Contains(searchLower) || fieldValue.ToLower().Contains(searchLower); if (fieldMatched) { matchedFieldNames.Add(fieldName); } // 检查数组/列表的子元素是否匹配 if (selectedPackage.fieldDetails.ContainsKey(fieldName)) { var detail = selectedPackage.fieldDetails[fieldName]; if (detail.isArray || detail.isList) { bool hasMatch = CheckChildElementsMatch(detail, searchLower); if (hasMatch) { fieldHasMatchedChild[fieldName] = true; if (!fieldMatched) { matchedFieldNames.Add(fieldName); // 也算作匹配字段 } } } } } // 如果基本信息匹配但没有字段匹配,至少显示一个字段让用户知道有匹配 if (basicInfoMatched && matchedFieldNames.Count == 0 && selectedPackage.fields.Count > 0) { // 添加第一个字段,这样至少会显示一些内容 var firstField = selectedPackage.fields.First(); matchedFieldNames.Add(firstField.Key); } } /// /// 检查字段是否匹配搜索(用于高亮显示) /// private bool IsFieldMatched(string fieldName, string fieldValue, FieldDetail detail) { if (string.IsNullOrEmpty(detailSearchText)) return false; string searchLower = detailSearchText.ToLower(); // 字段名匹配 if (fieldName.ToLower().Contains(searchLower)) return true; // 字段值匹配 if (fieldValue.ToLower().Contains(searchLower)) return true; return false; } /// /// 检查数组/列表中是否有匹配的子元素(用于缓存计算) /// private bool CheckChildElementsMatch(FieldDetail detail, string searchLower) { HashSet visited = new HashSet(); // 检查数组 if (detail.isArray && detail.arrayValue != null) { foreach (var element in detail.arrayValue) { if (element != null) { // 特殊处理:如果是 GameNetPackBasic,优先检查 packUID if (element is GameNetPackBasic pack && pack.packUID.ToString().Contains(searchLower)) return true; if (RecursiveSearchInObject(element, searchLower, visited)) return true; } } } // 检查列表 if (detail.isList && detail.listValue != null) { foreach (var element in detail.listValue) { if (element != null) { // 特殊处理:如果是 GameNetPackBasic,优先检查 packUID if (element is GameNetPackBasic pack && pack.packUID.ToString().Contains(searchLower)) return true; if (RecursiveSearchInObject(element, searchLower, visited)) return true; } } } return false; } /// /// 判断字段是否应该显示(根据详情搜索文本) /// private bool ShouldShowDetailField(string fieldName, string fieldValue, PackageInfo packInfo) { if (string.IsNullOrEmpty(detailSearchText)) return true; string searchLower = detailSearchText.ToLower(); // 搜索字段名 if (fieldName.ToLower().Contains(searchLower)) return true; // 搜索字段值 if (fieldValue.ToLower().Contains(searchLower)) return true; // 如果有详细信息,递归搜索 if (packInfo.fieldDetails.ContainsKey(fieldName)) { var detail = packInfo.fieldDetails[fieldName]; HashSet visited = new HashSet(); if (RecursiveSearchInFieldDetail(detail, searchLower, visited)) return true; } return false; } /// /// 递归搜索字段详情 /// private bool RecursiveSearchInFieldDetail(FieldDetail detail, string searchLower, HashSet visited) { if (detail.value != null) { // 防止循环引用 if (!detail.value.GetType().IsPrimitive && detail.value.GetType() != typeof(string) && !detail.value.GetType().IsEnum) { if (visited.Contains(detail.value)) return false; visited.Add(detail.value); } // 搜索值本身 if (detail.value.ToString().ToLower().Contains(searchLower)) return true; // 如果是数组 if (detail.isArray && detail.arrayValue != null) { foreach (var element in detail.arrayValue) { if (element != null && RecursiveSearchInObject(element, searchLower, visited)) return true; } } // 如果是列表 if (detail.isList && detail.listValue != null) { foreach (var element in detail.listValue) { if (element != null && RecursiveSearchInObject(element, searchLower, visited)) return true; } } // 搜索子字段 foreach (var subField in detail.subFieldDetails.Values) { if (RecursiveSearchInFieldDetail(subField, searchLower, visited)) return true; } } return false; } /// /// 递归搜索对象内容 /// private bool RecursiveSearchInObject(object obj, string searchLower, HashSet visited) { if (obj == null) return false; var type = obj.GetType(); // 特殊处理:如果是 GameNetPackBasic,优先检查 packUID if (obj is GameNetPackBasic pack) { if (pack.packUID.ToString().Contains(searchLower)) return true; // 还要检查类型名 if (pack.GetType().Name.ToLower().Contains(searchLower)) return true; } // 基本类型直接比较(包括所有数字类型) if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { string valueStr = obj.ToString(); // 对于数字,直接比较字符串(不转小写,因为数字没有大小写) if (type.IsPrimitive && !type.IsEnum) { return valueStr.Contains(searchLower); } // 对于字符串和枚举,转小写比较 return valueStr.ToLower().Contains(searchLower); } // 防止循环引用 if (visited.Contains(obj)) return false; visited.Add(obj); // 限制递归深度,防止过深的嵌套 if (visited.Count > 1000) return false; // 特殊处理数组 if (type.IsArray) { var array = obj as System.Array; if (array != null) { foreach (var element in array) { if (element != null && RecursiveSearchInObject(element, searchLower, visited)) return true; } } return false; } // 特殊处理列表 if (obj is System.Collections.IList list && !(obj is string)) { foreach (var element in list) { if (element != null && RecursiveSearchInObject(element, searchLower, visited)) return true; } return false; } // 复杂对象,搜索所有字段 var fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (var field in fields) { try { // 搜索字段名 if (field.Name.ToLower().Contains(searchLower)) return true; var value = field.GetValue(obj); if (value != null && RecursiveSearchInObject(value, searchLower, visited)) return true; } catch { } } return false; } #endregion #region 辅助方法 /// /// 根据对象ID获取对象名称 /// private string GetObjectName(uint objID) { try { var battleField = BattleManager.Instance?.GetBattleField(selectedGuid); if (battleField != null && battleField.battleObjMgr != null) { var battleObj = battleField.battleObjMgr.GetBattleObject((int)objID); if (battleObj is HeroBattleObject heroBattleObject && heroBattleObject.teamHero != null) { return heroBattleObject.teamHero.name; } } } catch { } return $"对象{objID}"; } /// /// 根据技能ID获取技能名称 /// private string GetSkillName(uint skillID) { try { var skillConfig = SkillConfig.Get((int)skillID); if (skillConfig != null && !string.IsNullOrEmpty(skillConfig.SkillName)) { return skillConfig.SkillName; } } catch { } return $"技能{skillID}"; } #endregion #region 高亮和滚动 /// /// 开始高亮一个包 /// private void StartHighlight(PackageInfo package) { highlightedPackage = package; highlightStartTime = EditorApplication.timeSinceStartup; } /// /// 判断包是否处于高亮状态 /// private bool IsPackageHighlighted(PackageInfo package) { if (highlightedPackage == null || highlightedPackage != package) return false; double elapsed = EditorApplication.timeSinceStartup - highlightStartTime; return elapsed < highlightDuration; } /// /// 滚动到选中的包位置 /// private void ScrollToSelectedPackage() { if (selectedPackageIndex < 0 || filteredPackages.Count == 0) return; // 计算目标滚动位置 // 每个包条目的大概高度(可根据实际情况调整) float itemHeight = 60f; float targetY = selectedPackageIndex * itemHeight; // 滚动到目标位置 scrollPosition.y = targetY; Repaint(); } #endregion #region 数据处理 private void RefreshGuidList() { guidList = allBattlePackages.Keys.ToList(); if (guidList.Count > 0) { if (string.IsNullOrEmpty(selectedGuid) || !guidList.Contains(selectedGuid)) { selectedGuidIndex = 0; selectedGuid = guidList[0]; } else { selectedGuidIndex = guidList.IndexOf(selectedGuid); } FilterPackages(); } else { selectedGuid = ""; selectedGuidIndex = 0; filteredPackages.Clear(); } } private void FilterPackages() { if (string.IsNullOrEmpty(selectedGuid) || !allBattlePackages.ContainsKey(selectedGuid)) { filteredPackages.Clear(); return; } var allPackages = allBattlePackages[selectedGuid]; if (string.IsNullOrEmpty(searchText)) { filteredPackages = new List(allPackages); } else { string searchLower = searchText.ToLower(); filteredPackages = allPackages.Where(p => { // 搜索包类型名 if (p.typeName.ToLower().Contains(searchLower)) return true; // 搜索 UID if (p.packUID.ToString().Contains(searchLower)) return true; // 递归搜索所有字段(包括嵌套内容) if (RecursiveSearchInPackage(p.package, searchLower)) return true; return false; }).ToList(); } // 按照 packUID 从小到大排序 filteredPackages.Sort((a, b) => a.packUID.CompareTo(b.packUID)); Repaint(); } /// /// 递归搜索对象的所有字段(包括嵌套数组、列表等) /// private bool RecursiveSearchInPackage(object obj, string searchLower) { if (obj == null) return false; var type = obj.GetType(); // 基本类型直接比较 if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { return obj.ToString().ToLower().Contains(searchLower); } // 数组类型 if (obj is System.Array array) { foreach (var element in array) { if (RecursiveSearchInPackage(element, searchLower)) return true; } return false; } // 列表类型 if (obj is System.Collections.IList list) { foreach (var element in list) { if (RecursiveSearchInPackage(element, searchLower)) return true; } return false; } // 复杂对象,递归搜索所有字段 var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var field in fields) { try { // 搜索字段名 if (field.Name.ToLower().Contains(searchLower)) return true; var value = field.GetValue(obj); if (value == null) continue; // 递归搜索字段值 if (RecursiveSearchInPackage(value, searchLower)) return true; } catch { // 忽略读取失败的字段 } } return false; } #endregion }