diff --git a/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs b/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs new file mode 100644 index 00000000..829a1be5 --- /dev/null +++ b/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs @@ -0,0 +1,448 @@ +using System; +using UnityEditor; +using UnityEngine; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using TEngine; +using TEngine.Editor; +using Debug = UnityEngine.Debug; + +class GenNetScriptWindow : EditorWindow +{ + private static GenNetScriptWindow _window = null; + + //绑定通知协议 + private StringBuilder _strBindNotify; + + //枚举对应协议号 + private readonly Dictionary _dicName2ID = new Dictionary(); + + //xxx.xml下的协议枚举list + private readonly Dictionary> _dicPath2Name = new Dictionary>(); + + //协议号有没有被勾选 + private readonly Dictionary _dicID2Select = new Dictionary(); + + //记录回包协议号 + private readonly Dictionary _dicID2ID = new Dictionary(); + + private Vector2 _scrollPos; + private Vector2 _fileListScrollPos; + + private string _path = @"G:\github\TEngine\Luban\Proto\pb_schemas\"; + private readonly List _filePathList = new List(); + private string _curSelectFile = string.Empty; + + private string _filterFileName = string.Empty; + private string _filterProName = string.Empty; + + [MenuItem("TEngine/协议生成工具|Protobuf Tools")] + static void OpenGenNetScriptWindow() + { + if (!_window) + { + _window = ScriptableObject.CreateInstance(); + _window.maxSize = new Vector2(1000, 800); + _window.minSize = _window.maxSize / 2; + _window.LoadLastPath(); + } + + _window.ShowUtility(); + } + + void OnGUI() + { + EditorGUILayout.PrefixLabel("protoPath"); + _path = EditorGUILayout.TextField(_path); + var r = EditorGUILayout.BeginHorizontal("Button"); + if (GUI.Button(r, GUIContent.none)) + { + ReadPath(); + } + GUILayout.Label("Search"); + EditorGUILayout.EndHorizontal(); + + + //加个文件筛选 + //EditorGUILayout.BeginHorizontal(); + GUILayout.Label("filter file:"); + _filterFileName = GUILayout.TextField(_filterFileName); + //EditorGUILayout.EndHorizontal(); + //显示文件名部分 + if (_filePathList.Count > 0) + { + _fileListScrollPos = EditorGUILayout.BeginScrollView(_fileListScrollPos, GUILayout.Width(r.width), GUILayout.Height(200)); + for (int i = 0; i < _filePathList.Count; ++i) + { + var fileName = Path.GetFileNameWithoutExtension(_filePathList[i]); + if (!string.IsNullOrEmpty(_filterFileName) && fileName.IndexOf(_filterFileName, StringComparison.Ordinal) == -1) + { + continue; + } + if (GUILayout.Button(fileName)) + { + _curSelectFile = _filePathList[i]; + LoadSelectFile(); + } + } + EditorGUILayout.EndScrollView(); + } + GUILayout.Label("-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); + + if (!string.IsNullOrEmpty(_curSelectFile)) + { + //加个协议筛选 + GUILayout.Label("filter proto:"); + _filterProName = GUILayout.TextField(_filterProName); + + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, GUILayout.Width(r.width), GUILayout.Height(200)); + var fileName2 = Path.GetFileNameWithoutExtension(_curSelectFile); + List list; + if (_dicPath2Name.TryGetValue(fileName2, out list)) + { + EditorGUI.indentLevel++; + for (int i = 0; i < list.Count; ++i) + { + var cmdName = list[i]; + //筛选一下,忽略大小写 + if (!string.IsNullOrEmpty(_filterProName) && cmdName.ToLower().IndexOf(_filterProName.ToLower()) == -1) + continue; + var cmdID = _dicName2ID[cmdName]; + EditorGUILayout.BeginHorizontal(GUIStyle.none); + //协议名 + EditorGUILayout.LabelField(cmdName); + //toggle + if (!_dicID2Select.ContainsKey(cmdID)) + _dicID2Select[cmdID] = false; + _dicID2Select[cmdID] = EditorGUILayout.Toggle(cmdID.ToString(),_dicID2Select[cmdID]); + //回包协议号 + if (!_dicID2ID.ContainsKey(cmdID)) + { + if (cmdName.EndsWith("REQ")) + _dicID2ID[cmdID] = cmdID + 1; + else + _dicID2ID[cmdID] = 0; + } + _dicID2ID[cmdID] = EditorGUILayout.IntField(_dicID2ID[cmdID]); + EditorGUILayout.EndHorizontal(); + } + EditorGUI.indentLevel--; + } + + EditorGUILayout.EndScrollView(); + } + + if (!string.IsNullOrEmpty(_curSelectFile)) + { + if (GUILayout.Button("GenSelect")) + { + OnClickGenBtn(false); + } + if (GUILayout.Button("GenAll")) + { + OnClickGenBtn(true); + } + } + + if (GUILayout.Button("导出Proto To Csharp|Export Proto To Csharp")) + { + ExportProto(); + } + } + + #region 加载 + private void LoadLastPath() + { + if (PlayerPrefs.HasKey("GenNetScriptWindow.Path")) + { + _path = PlayerPrefs.GetString("GenNetScriptWindow.Path"); + } + ReadPath(); + } + + private void ReadPath() + { + PlayerPrefs.SetString("GenNetScriptWindow.Path", _path); + _filePathList.Clear(); + _curSelectFile = String.Empty; + JustLoadFileList(_filePathList, _path); + } + + private void JustLoadFileList(List exportList, string folderPath, bool deep = false) + { + if (!LoadFoldChildFileList(exportList, folderPath, deep)) + { + EditorUtility.DisplayDialog("folder not exist", "folder not exist:"+_path, "ok"); + } + } + + private void LoadSelectFile() + { + _dicID2Select.Clear(); + _dicID2ID.Clear(); + _dicName2ID.Clear(); + + var xmlFilePath = _curSelectFile; + var fileName = Path.GetFileNameWithoutExtension(xmlFilePath); + var protocolNameList = new List(); + //读xml + if (fileName.StartsWith("proto_cs")) + { + Debug.Log("load xml.name:" + fileName); + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(xmlFilePath); + GenProtocolNameList(xmlDoc.ChildNodes, protocolNameList); + _dicPath2Name[fileName] = protocolNameList; + Debug.Log(fileName + " success."); + } + } + + private bool LoadFoldChildFileList(List exportList, string folderPath, bool deep = false) + { + if (!Directory.Exists(folderPath)) + { + Log.Error("folder not exist: {0}", folderPath); + return false; + } + + string[] subFile = Directory.GetFiles(folderPath); + foreach (string fileName in subFile) + { + //有些筛选条件,直接写死这里了。方便 + var name = Path.GetFileNameWithoutExtension(fileName); + if (name.StartsWith("proto_cs")) + exportList.Add(fileName); + } + + if (deep) + { + string[] subFolders = Directory.GetDirectories(folderPath); + foreach (string folderName in subFolders) + { + LoadFoldChildFileList(exportList, folderName); + } + } + + return true; + } + + //把macro里的协议号和协议枚举名对应起来 + private void GenProtocolNameList(XmlNodeList nodeList, List nameList) + { + foreach (XmlNode node in nodeList) + { + if (node.Attributes == null) + continue; + if (node.Name == "macro") + { + var name = node.Attributes.GetNamedItem("name").Value; + if (name.StartsWith("CS_CMD") || name.StartsWith("CS_NOTIFY")) + { + var id = Convert.ToInt32(node.Attributes.GetNamedItem("value").Value); + _dicName2ID[name] = id; + if (nameList != null) + nameList.Add(name); + } + } + GenProtocolNameList(node.ChildNodes, nameList); + } + } + #endregion + + #region 点击处理 + private void OnClickGenBtn(bool genAll) + { + _strBindNotify = new StringBuilder(); + bool needRegCmdHandle = false; + StringBuilder sb = new StringBuilder(); + List listGenId = new List(); + var iter = _dicID2Select.GetEnumerator(); + while (iter.MoveNext()) + { + if (iter.Current.Value || genAll) + { + int resId = iter.Current.Key; + int resID; + if (_dicID2ID.TryGetValue(resId, out resID)) + { + if (resID != 0) + { + var oneStr = GenOneReq(resId, resID); + sb.Append(oneStr); + sb.Append("\n\n"); + listGenId.Add(resId); + listGenId.Add(resID); + } + else if (!listGenId.Contains(resId)) + { + needRegCmdHandle = true; + var oneStr = GenOneReq(0, resId); + sb.Append(oneStr); + sb.Append("\n\n"); + listGenId.Add(resId); + } + } + } + } + + if (needRegCmdHandle) + { + sb.Append("public void RegCmdHandle()\n"); + sb.Append("{\n"); + sb.Append(_strBindNotify); + sb.Append("}\n"); + } + + TextEditor te = new TextEditor(); + te.text = sb.ToString(); + te.SelectAll(); + te.Copy(); + } + + private static readonly Dictionary TempParamDic = new Dictionary(); + private static string _reqClassName = string.Empty; + private static string _resClassName = string.Empty; + private static string _reqEnumName = string.Empty; + private static string _resEnumName = string.Empty; + private static string _reqDesc = string.Empty; + private static bool _resResult; + + private string GenOneReq(int reqId, int resId) + { + TempParamDic.Clear(); + _reqClassName = string.Empty; + _resClassName = string.Empty; + _reqEnumName = string.Empty; + _resEnumName = string.Empty; + _resResult = false; + + var xmlFilePath = _curSelectFile; + var fileName = Path.GetFileNameWithoutExtension(xmlFilePath); + //读xml + if (fileName.StartsWith("proto_cs")) + { + Debug.Log("load xml.name:" + fileName); + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(xmlFilePath); + _GenOneReq(xmlDoc.ChildNodes, reqId,resId); + Debug.Log(fileName + " success."); + + var sb = new StringBuilder(); + sb.Append("#region "); + sb.Append(_reqDesc); + sb.Append(string.Format("\n//{0}\n", _reqDesc)); + if (reqId != 0) + { + sb.Append("public void "); + sb.Append(_reqClassName.Substring(2)); + sb.Append("("); + foreach (var item in TempParamDic) + { + sb.Append(string.Format("{0} {1}, ", item.Value, item.Key)); + } + sb.Remove(sb.Length - 2, 2);//把多余的逗号和空格删了 + sb.Append(")\n{\n"); + sb.Append(string.Format("\tCSPkg reqPkg = ProtoUtil.BuildCSMsg(netMacros.{0});\n", _reqEnumName)); + sb.Append(string.Format("\t{0} reqData = reqPkg.Body.{1};\n", _reqClassName, _reqClassName.Substring(2))); + foreach (var item in TempParamDic) + { + sb.Append(string.Format("\treqData.{0} = {0};\n", item.Key)); + } + + sb.Append("\n"); + sb.Append(string.Format("\tGameClient.Instance.SendCSMsg(reqPkg, netMacros.{0}, {1});\n", _resEnumName, + _resClassName.Substring(2))); + sb.Append("}\n\n"); + } + else + { + _strBindNotify.Append(string.Format("\t\tGameClient.Instance.RegCmdHandle(netMacros.{0}, {1});\n", _resEnumName, _resClassName.Substring(2))); + } + + //回包 + sb.Append(string.Format("private void {0}(CSMsgResult result, CSPkg msg)\n", _resClassName.Substring(2))); + sb.Append("{\n"); + sb.Append(string.Format("\tif (DodUtil.CheckHaveError(result, msg, typeof({0})))\n", _resClassName)); + sb.Append("\t\treturn;\n\n"); + sb.Append(string.Format("\t{0} resData = msg.Body.{1};\n", _resClassName, _resClassName.Substring(2))); + if (_resResult) + { + sb.Append("\tif (resData.Result.Ret != 0)\n"); + sb.Append("\t{\n"); + sb.Append("\t\tUISys.Mgr.ShowTipMsg(resData.Result);\n"); + sb.Append("\t\treturn;\n"); + sb.Append("\t}\n"); + } + + sb.Append("\t//todo\n"); + sb.Append("}\n#endregion"); + return sb.ToString(); + } + + return null; + } + + private void _GenOneReq(XmlNodeList nodeList, int reqId,int resId) + { + foreach (XmlNode node in nodeList) + { + if (node.Attributes == null) + continue; + if (node.Name.Equals("macro")) + { + var name = node.Attributes.GetNamedItem("name").Value; + if (name.StartsWith("CS_CMD") || name.StartsWith("CS_NOTIFY")) + { + var id = Convert.ToInt32(node.Attributes.GetNamedItem("value").Value); + if (id == reqId) + _reqEnumName = name; + if (id == resId) + _resEnumName = name; + } + } + + if (node.Name.Equals("struct")) + { + if (node.Attributes.GetNamedItem("id") == null) + continue; + var enumName = node.Attributes.GetNamedItem("id").Value; + if (enumName.Equals(_reqEnumName)) + { + var name = node.Attributes.GetNamedItem("name").Value; + _reqClassName = name; + if (node.Attributes.GetNamedItem("name") != null) + { + _reqDesc = node.Attributes.GetNamedItem("desc").Value; + } + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode != null && childNode.Name.Equals("entry")) + { + var paramName = childNode.Attributes.GetNamedItem("name").Value; + var paramType = childNode.Attributes.GetNamedItem("type").Value; + TempParamDic.Add(paramName, paramType); + } + } + } + + if (enumName.Equals(_resEnumName)) + { + var className = node.Attributes.GetNamedItem("name").Value; + _resClassName = className; + } + } + _GenOneReq(node.ChildNodes, reqId, resId); + } + } + #endregion + + #region 导出协议ToCsharp + + private void ExportProto() + { + ProtoGenTools.Export(); + } + #endregion +} \ No newline at end of file diff --git a/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs.meta b/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs.meta new file mode 100644 index 00000000..a62c3ae5 --- /dev/null +++ b/Assets/TEngine/Editor/ProtoGenTools/ProtoScriptTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 989d4686a599e7047bba6f0cbb1bb5e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Luban/Proto/pb_schemas/proto_cs_base.proto b/Luban/Proto/pb_schemas/proto_cs_base.proto new file mode 100644 index 00000000..55a40694 --- /dev/null +++ b/Luban/Proto/pb_schemas/proto_cs_base.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package GameProto; +// 这个文件只放协议,和协议头 + +// 消息协议 +message CSPkg +{ + CSPkgHead Head = 1; //消息协议头 + CSPkgBody Body = 2; //消息协议体 +} + +// 消息协议头 +message CSPkgHead +{ + uint32 MsgId = 1; //协议号 + uint32 MsgLength = 2; //协议长度 + uint32 MsgVersion = 3; //协议版本 + uint32 Echo = 4; //回带字段 + uint32 SvrTime = 5; //服务器时间 +} + +// 消息协议体 +message CSPkgBody +{ + +} + + +// 协议ID +enum CSMsgID +{ + CS_START = 0; + CS_HeartBeat = 10001; + CS_END = 10000; +}