| | |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.IO; |
| | | using System.Reflection; |
| | | |
| | | public class GMQuickPlayingEditor : EditorWindow |
| | | { |
| | |
| | | private List<string> gmCommandHistory = new List<string>(); // 存储历史GM命令 |
| | | private const int maxGmMessages = 50; // 最多保存50条消息 |
| | | private const int maxGmHistory = 30; // 最多保存30条历史命令 |
| | | |
| | | // 模拟发包工具相关 |
| | | private bool showPacketSimulator = false; // 显示模拟发包工具 |
| | | private Vector2 packetScrollPosition; // 包字段滚动位置 |
| | | private string packetClassName = ""; // 输入的包类名 |
| | | private System.Type currentPacketType = null; // 当前包类型 |
| | | private object currentPacketInstance = null; // 当前包实例 |
| | | private Dictionary<FieldInfo, object> fieldValues = new Dictionary<FieldInfo, object>(); // 存储字段值 |
| | | |
| | | // 战报相关 |
| | | private string reportFilePath = ""; |
| | |
| | | // 显示GM面板开关 |
| | | GUILayout.Label("显示GM", GUILayout.Width(100)); |
| | | showGmPanel = EditorGUILayout.Toggle(showGmPanel, GUILayout.Width(20)); |
| | | |
| | | GUILayout.Space(20); |
| | | |
| | | // 显示模拟发包工具开关 |
| | | GUILayout.Label("模拟发包工具", GUILayout.Width(100)); |
| | | showPacketSimulator = EditorGUILayout.Toggle(showPacketSimulator, GUILayout.Width(20)); |
| | | |
| | | GUILayout.FlexibleSpace(); |
| | | GUILayout.EndHorizontal(); |
| | |
| | | EditorGUILayout.LabelField("⚠ 添加物品最好跟服务器确认过物品的存在后再添加", warningStyle); |
| | | |
| | | GUILayout.EndVertical(); |
| | | } |
| | | |
| | | // ========== 中间面板 - 模拟发包工具 ========== |
| | | if (showPacketSimulator) |
| | | { |
| | | // 分割线 |
| | | if (showGmPanel) |
| | | { |
| | | GUILayout.Box("", GUILayout.Width(2), GUILayout.ExpandHeight(true)); |
| | | } |
| | | |
| | | float packetWidth = showBattleInfo ? position.width * 0.35f : (showGmPanel ? position.width * 0.4f : position.width - 20); |
| | | GUILayout.BeginVertical("box", GUILayout.Width(packetWidth)); |
| | | |
| | | DrawPacketSimulator(); |
| | | |
| | | GUILayout.EndVertical(); |
| | | } |
| | | |
| | | // ========== 右侧面板 - 战场信息 ========== |
| | |
| | | battleField.ForceFinish(); |
| | | |
| | | } |
| | | |
| | | // ========== 模拟发包工具相关方法 ========== |
| | | |
| | | private void DrawPacketSimulator() |
| | | { |
| | | GUILayout.Label("📦 模拟发包工具", EditorStyles.boldLabel); |
| | | |
| | | GUILayout.Space(5); |
| | | |
| | | // 包名输入区域 |
| | | GUILayout.BeginVertical("box"); |
| | | GUILayout.Label("包类名(例如:HB301_tagCSLoginReq)", EditorStyles.miniBoldLabel); |
| | | |
| | | GUILayout.BeginHorizontal(); |
| | | string newPacketClassName = EditorGUILayout.TextField(packetClassName); |
| | | |
| | | // 检测包名是否改变 |
| | | if (newPacketClassName != packetClassName) |
| | | { |
| | | packetClassName = newPacketClassName; |
| | | // 清空当前包信息 |
| | | currentPacketType = null; |
| | | currentPacketInstance = null; |
| | | fieldValues.Clear(); |
| | | } |
| | | |
| | | if (GUILayout.Button("加载包", GUILayout.Width(80))) |
| | | { |
| | | LoadPacketType(); |
| | | } |
| | | GUILayout.EndHorizontal(); |
| | | |
| | | GUILayout.EndVertical(); |
| | | |
| | | GUILayout.Space(10); |
| | | |
| | | // 包字段显示和编辑区域 |
| | | if (currentPacketType != null && currentPacketInstance != null) |
| | | { |
| | | GUILayout.BeginVertical("box"); |
| | | GUILayout.Label($"📋 包字段 - {currentPacketType.Name}", EditorStyles.boldLabel); |
| | | |
| | | packetScrollPosition = GUILayout.BeginScrollView(packetScrollPosition, GUILayout.ExpandHeight(true)); |
| | | |
| | | DrawPacketFields(); |
| | | |
| | | GUILayout.EndScrollView(); |
| | | GUILayout.EndVertical(); |
| | | |
| | | GUILayout.Space(10); |
| | | |
| | | // 操作按钮 |
| | | GUILayout.BeginHorizontal(); |
| | | |
| | | // 发送按钮(绿色) |
| | | GUI.backgroundColor = new Color(0.5f, 1f, 0.5f); |
| | | if (GUILayout.Button("✉ 发送包", GUILayout.Height(35))) |
| | | { |
| | | SendPacket(); |
| | | } |
| | | GUI.backgroundColor = Color.white; |
| | | |
| | | GUILayout.Space(10); |
| | | |
| | | // 清空按钮(橙色) |
| | | GUI.backgroundColor = new Color(1f, 0.8f, 0.4f); |
| | | if (GUILayout.Button("🗑 清空字段", GUILayout.Height(35))) |
| | | { |
| | | ClearPacketFields(); |
| | | } |
| | | GUI.backgroundColor = Color.white; |
| | | |
| | | GUILayout.EndHorizontal(); |
| | | } |
| | | else |
| | | { |
| | | GUILayout.FlexibleSpace(); |
| | | |
| | | GUIStyle hintStyle = new GUIStyle(EditorStyles.helpBox); |
| | | hintStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f); |
| | | hintStyle.fontSize = 12; |
| | | hintStyle.alignment = TextAnchor.MiddleCenter; |
| | | hintStyle.wordWrap = true; |
| | | |
| | | GUILayout.Label("💡 输入包类名后点击\"加载包\"按钮\n即可自动识别并编辑包的所有字段", hintStyle, GUILayout.Height(80)); |
| | | |
| | | GUILayout.FlexibleSpace(); |
| | | } |
| | | } |
| | | |
| | | private void LoadPacketType() |
| | | { |
| | | if (string.IsNullOrEmpty(packetClassName)) |
| | | { |
| | | Debug.LogWarning("包类名不能为空"); |
| | | return; |
| | | } |
| | | |
| | | try |
| | | { |
| | | // 尝试在所有已加载的程序集中查找类型 |
| | | System.Type foundType = null; |
| | | foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) |
| | | { |
| | | foundType = assembly.GetType(packetClassName); |
| | | if (foundType != null) |
| | | break; |
| | | } |
| | | |
| | | if (foundType == null) |
| | | { |
| | | Debug.LogError($"未找到类型: {packetClassName},请检查类名是否正确(区分大小写)"); |
| | | return; |
| | | } |
| | | |
| | | // 检查是否继承自 GameNetPackBasic |
| | | if (!typeof(GameNetPackBasic).IsAssignableFrom(foundType)) |
| | | { |
| | | Debug.LogError($"类型 {packetClassName} 不是 GameNetPackBasic 的子类"); |
| | | return; |
| | | } |
| | | |
| | | currentPacketType = foundType; |
| | | currentPacketInstance = System.Activator.CreateInstance(foundType); |
| | | fieldValues.Clear(); |
| | | |
| | | // 初始化字段值 |
| | | var fields = GetAllFields(foundType); |
| | | foreach (var field in fields) |
| | | { |
| | | object defaultValue = field.GetValue(currentPacketInstance); |
| | | fieldValues[field] = defaultValue; |
| | | } |
| | | |
| | | Debug.Log($"成功加载包: {foundType.Name},共 {fields.Count} 个字段"); |
| | | } |
| | | catch (System.Exception e) |
| | | { |
| | | Debug.LogError($"加载包类型失败: {e.Message}"); |
| | | currentPacketType = null; |
| | | currentPacketInstance = null; |
| | | fieldValues.Clear(); |
| | | } |
| | | } |
| | | |
| | | private List<FieldInfo> GetAllFields(System.Type type) |
| | | { |
| | | List<FieldInfo> fields = new List<FieldInfo>(); |
| | | |
| | | // 只获取当前类声明的字段,不包括父类字段 |
| | | FieldInfo[] typeFields = type.GetFields( |
| | | BindingFlags.Public | |
| | | BindingFlags.NonPublic | |
| | | BindingFlags.Instance | |
| | | BindingFlags.DeclaredOnly); |
| | | |
| | | foreach (var field in typeFields) |
| | | { |
| | | // 排除编译器生成的字段 |
| | | if (!field.Name.Contains("<") && !field.Name.Contains(">")) |
| | | { |
| | | fields.Add(field); |
| | | } |
| | | } |
| | | |
| | | return fields; |
| | | } |
| | | |
| | | private void DrawPacketFields() |
| | | { |
| | | var fields = GetAllFields(currentPacketType); |
| | | |
| | | foreach (var field in fields) |
| | | { |
| | | GUILayout.BeginVertical("box"); |
| | | |
| | | // 字段名称和类型 |
| | | GUILayout.BeginHorizontal(); |
| | | GUILayout.Label(field.Name, EditorStyles.boldLabel, GUILayout.Width(150)); |
| | | GUILayout.Label($"({field.FieldType.Name})", EditorStyles.miniLabel); |
| | | GUILayout.EndHorizontal(); |
| | | |
| | | // 字段值编辑 |
| | | if (!fieldValues.ContainsKey(field)) |
| | | { |
| | | fieldValues[field] = field.GetValue(currentPacketInstance); |
| | | } |
| | | |
| | | object newValue = DrawFieldValue(field, fieldValues[field]); |
| | | if (newValue != null || field.FieldType.IsClass) |
| | | { |
| | | fieldValues[field] = newValue; |
| | | field.SetValue(currentPacketInstance, newValue); |
| | | } |
| | | |
| | | GUILayout.EndVertical(); |
| | | GUILayout.Space(3); |
| | | } |
| | | } |
| | | |
| | | private object DrawFieldValue(FieldInfo field, object currentValue) |
| | | { |
| | | System.Type fieldType = field.FieldType; |
| | | |
| | | try |
| | | { |
| | | // 基本类型 |
| | | if (fieldType == typeof(int)) |
| | | { |
| | | return EditorGUILayout.IntField((int)(currentValue ?? 0)); |
| | | } |
| | | else if (fieldType == typeof(uint)) |
| | | { |
| | | return (uint)EditorGUILayout.LongField((uint)(currentValue ?? 0)); |
| | | } |
| | | else if (fieldType == typeof(long)) |
| | | { |
| | | return EditorGUILayout.LongField((long)(currentValue ?? 0L)); |
| | | } |
| | | else if (fieldType == typeof(ulong)) |
| | | { |
| | | long val = EditorGUILayout.LongField((long)(ulong)(currentValue ?? 0UL)); |
| | | return (ulong)System.Math.Max(0, val); |
| | | } |
| | | else if (fieldType == typeof(short)) |
| | | { |
| | | return (short)EditorGUILayout.IntField((short)(currentValue ?? (short)0)); |
| | | } |
| | | else if (fieldType == typeof(ushort)) |
| | | { |
| | | return (ushort)EditorGUILayout.IntField((ushort)(currentValue ?? (ushort)0)); |
| | | } |
| | | else if (fieldType == typeof(byte)) |
| | | { |
| | | return (byte)EditorGUILayout.IntField((byte)(currentValue ?? (byte)0)); |
| | | } |
| | | else if (fieldType == typeof(sbyte)) |
| | | { |
| | | return (sbyte)EditorGUILayout.IntField((sbyte)(currentValue ?? (sbyte)0)); |
| | | } |
| | | else if (fieldType == typeof(float)) |
| | | { |
| | | return EditorGUILayout.FloatField((float)(currentValue ?? 0f)); |
| | | } |
| | | else if (fieldType == typeof(double)) |
| | | { |
| | | return EditorGUILayout.DoubleField((double)(currentValue ?? 0.0)); |
| | | } |
| | | else if (fieldType == typeof(bool)) |
| | | { |
| | | return EditorGUILayout.Toggle((bool)(currentValue ?? false)); |
| | | } |
| | | else if (fieldType == typeof(string)) |
| | | { |
| | | return EditorGUILayout.TextField((string)(currentValue ?? "")); |
| | | } |
| | | // List 类型 |
| | | else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>)) |
| | | { |
| | | System.Type elementType = fieldType.GetGenericArguments()[0]; |
| | | var list = currentValue as System.Collections.IList; |
| | | |
| | | if (list == null) |
| | | { |
| | | list = (System.Collections.IList)System.Activator.CreateInstance(fieldType); |
| | | } |
| | | |
| | | GUILayout.BeginHorizontal(); |
| | | GUILayout.Label($"Count: {list.Count}", GUILayout.Width(80)); |
| | | |
| | | if (GUILayout.Button("➕", GUILayout.Width(30))) |
| | | { |
| | | object newElement = GetDefaultValue(elementType); |
| | | list.Add(newElement); |
| | | } |
| | | |
| | | if (list.Count > 0 && GUILayout.Button("➖", GUILayout.Width(30))) |
| | | { |
| | | list.RemoveAt(list.Count - 1); |
| | | } |
| | | GUILayout.EndHorizontal(); |
| | | |
| | | return list; |
| | | } |
| | | // 数组类型 |
| | | else if (fieldType.IsArray) |
| | | { |
| | | var array = currentValue as System.Array; |
| | | int length = array != null ? array.Length : 0; |
| | | GUILayout.Label($"Array Length: {length}", EditorStyles.miniLabel); |
| | | return currentValue; |
| | | } |
| | | // 枚举类型 |
| | | else if (fieldType.IsEnum) |
| | | { |
| | | return EditorGUILayout.EnumPopup((System.Enum)(currentValue ?? System.Enum.GetValues(fieldType).GetValue(0))); |
| | | } |
| | | // 其他复杂类型 |
| | | else |
| | | { |
| | | GUILayout.Label("(复杂类型,请在代码中设置)", EditorStyles.miniLabel); |
| | | return currentValue; |
| | | } |
| | | } |
| | | catch (System.Exception e) |
| | | { |
| | | GUILayout.Label($"(编辑错误: {e.Message})", EditorStyles.miniLabel); |
| | | return currentValue; |
| | | } |
| | | } |
| | | |
| | | private object GetDefaultValue(System.Type type) |
| | | { |
| | | if (type.IsValueType) |
| | | { |
| | | return System.Activator.CreateInstance(type); |
| | | } |
| | | else if (type == typeof(string)) |
| | | { |
| | | return ""; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | return System.Activator.CreateInstance(type); |
| | | } |
| | | catch |
| | | { |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void SendPacket() |
| | | { |
| | | if (currentPacketInstance == null) |
| | | { |
| | | Debug.LogWarning("没有可发送的包实例"); |
| | | return; |
| | | } |
| | | |
| | | if (!Application.isPlaying) |
| | | { |
| | | Debug.LogError("只能在运行时发送包"); |
| | | EditorUtility.DisplayDialog("错误", "只能在运行时发送网络包!", "确定"); |
| | | return; |
| | | } |
| | | |
| | | try |
| | | { |
| | | var packet = currentPacketInstance as GameNetPackBasic; |
| | | if (packet != null) |
| | | { |
| | | GameNetSystem.Instance.SendInfo(packet); |
| | | Debug.Log($"✅ 成功发送包: {currentPacketType.Name}"); |
| | | |
| | | // 显示发送的详细信息 |
| | | var fields = GetAllFields(currentPacketType); |
| | | string fieldInfo = "字段值:\n"; |
| | | foreach (var field in fields) |
| | | { |
| | | object value = field.GetValue(packet); |
| | | fieldInfo += $" {field.Name} = {value}\n"; |
| | | } |
| | | Debug.Log(fieldInfo); |
| | | } |
| | | else |
| | | { |
| | | Debug.LogError("包实例不是 GameNetPackBasic 类型"); |
| | | } |
| | | } |
| | | catch (System.Exception e) |
| | | { |
| | | Debug.LogError($"发送包失败: {e.Message}\n{e.StackTrace}"); |
| | | EditorUtility.DisplayDialog("发送失败", $"发送包时出错:\n{e.Message}", "确定"); |
| | | } |
| | | } |
| | | |
| | | private void ClearPacketFields() |
| | | { |
| | | if (currentPacketInstance == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | try |
| | | { |
| | | // 重新创建实例 |
| | | currentPacketInstance = System.Activator.CreateInstance(currentPacketType); |
| | | fieldValues.Clear(); |
| | | |
| | | // 重新初始化字段值 |
| | | var fields = GetAllFields(currentPacketType); |
| | | foreach (var field in fields) |
| | | { |
| | | object defaultValue = field.GetValue(currentPacketInstance); |
| | | fieldValues[field] = defaultValue; |
| | | } |
| | | |
| | | Debug.Log("已清空所有字段"); |
| | | } |
| | | catch (System.Exception e) |
| | | { |
| | | Debug.LogError($"清空字段失败: {e.Message}"); |
| | | } |
| | | } |
| | | } |