655 lines
16 KiB
C#
655 lines
16 KiB
C#
using System.Linq;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System;
|
|
using System.IO;
|
|
using BF;
|
|
|
|
namespace BFEditor
|
|
{
|
|
public class ProtoEnum
|
|
{
|
|
public string enumName = "";
|
|
public int enumNum = 0;
|
|
public int moduleId = 0;
|
|
}
|
|
|
|
public class ProtoModule
|
|
{
|
|
public string moduleDesc = "";
|
|
public int moduleNum = 0;
|
|
}
|
|
|
|
public class ProtoMessage
|
|
{
|
|
public string commentStr = "";
|
|
public string messageName = "";
|
|
public List<ProtoField> fields = new List<ProtoField>();
|
|
}
|
|
|
|
public class ProtoStruct
|
|
{
|
|
public string typeName = "";
|
|
public List<ProtoField> fields = new List<ProtoField>();
|
|
}
|
|
|
|
public class ProtoField
|
|
{
|
|
public string commentStr = "";
|
|
public string fieldName = "";
|
|
public string typeName = "";
|
|
public bool isRepeated = false;
|
|
|
|
public bool isMap = false;
|
|
public string keyTypeName = "";
|
|
public string valueTypeName = "";
|
|
}
|
|
|
|
public class Proto2MarkDown
|
|
{
|
|
const string COMMENT_CONST = "comment = ";
|
|
const string MESSAGE_NAME_CONST = "message_name = ";
|
|
const string MARKDOWN_FILE_NAME = "/bf_proto.md";
|
|
|
|
static string[] TYPE_KEYWORDS = new string[] { "uint32", "int32", "uint64", "int64", "string", "bool" };
|
|
|
|
static string MarkdownPath { get { return LocalData.GetProtoPath(); } }
|
|
|
|
static string MsgIdsProtoPath { get { return LocalData.GetProtoPath() + "/bf_msgids.proto"; } }
|
|
static string CommonProtoPath { get { return LocalData.GetProtoPath() + "/bf_comm.proto"; } }
|
|
static string LogicProtoPath { get { return LocalData.GetProtoPath() + "/bf_logic.proto"; } }
|
|
static string BattleProtoPath { get { return LocalData.GetProtoPath() + "/bf_battle.proto"; } }
|
|
static string protoMessageCommentStr = COMMENT_CONST;
|
|
static string protoStructTypeNameStr = "";
|
|
|
|
static StringBuilder sb = new StringBuilder();
|
|
|
|
static bool readingProtoEnum = false;
|
|
static bool readingProtoMessage = false;
|
|
static bool readingProtoStruct = false;
|
|
static List<ProtoEnum> protoEnums = new List<ProtoEnum>();
|
|
static List<ProtoModule> protoModules = new List<ProtoModule>();
|
|
static List<ProtoMessage> protoMessages = new List<ProtoMessage>();
|
|
static List<ProtoStruct> protoStructs = new List<ProtoStruct>();
|
|
static List<int> moduleIds = new List<int>();
|
|
|
|
public static bool Pb2Markdown()
|
|
{
|
|
protoEnums.Clear();
|
|
protoModules.Clear();
|
|
protoMessages.Clear();
|
|
protoStructs.Clear();
|
|
moduleIds.Clear();
|
|
|
|
if (string.IsNullOrEmpty(MsgIdsProtoPath) || string.IsNullOrEmpty(CommonProtoPath) ||
|
|
string.IsNullOrEmpty(LogicProtoPath) || string.IsNullOrEmpty(MarkdownPath) || string.IsNullOrEmpty(BattleProtoPath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ParsingEnum();
|
|
ParsingLogic();
|
|
ParsingCommon();
|
|
ParsingBattle();
|
|
SortEnum();
|
|
Export2Markdown();
|
|
return true;
|
|
}
|
|
|
|
static void ParsingEnum()
|
|
{
|
|
var reader = new StreamReader(new FileStream(MsgIdsProtoPath, FileMode.Open));
|
|
var logicLineStr = "";
|
|
while ((logicLineStr = reader.ReadLine()) != null)
|
|
{
|
|
if (logicLineStr == "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (logicLineStr == "enum MsgId")
|
|
{
|
|
readingProtoEnum = true;
|
|
continue;
|
|
}
|
|
|
|
if (readingProtoEnum)
|
|
{
|
|
if (logicLineStr == "{")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (logicLineStr == "}")
|
|
{
|
|
readingProtoEnum = false;
|
|
continue;
|
|
}
|
|
|
|
ReadProtoEnum(logicLineStr);
|
|
}
|
|
}
|
|
|
|
reader.Close();
|
|
reader.Dispose();
|
|
}
|
|
|
|
static void ParsingLogic()
|
|
{
|
|
var reader = new StreamReader(new FileStream(LogicProtoPath, FileMode.Open));
|
|
var logicLineStr = "";
|
|
while ((logicLineStr = reader.ReadLine()) != null)
|
|
{
|
|
if (logicLineStr == "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//") && logicLineStr.Contains("========"))
|
|
{
|
|
ReadProtoModule(logicLineStr);
|
|
continue;
|
|
}
|
|
|
|
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//"))
|
|
{
|
|
protoMessageCommentStr = protoMessageCommentStr + logicLineStr.Replace("//", " ");
|
|
continue;
|
|
}
|
|
|
|
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
|
|
{
|
|
readingProtoMessage = true;
|
|
|
|
sb.AppendLine(protoMessageCommentStr);
|
|
protoMessageCommentStr = COMMENT_CONST;
|
|
string messageName = MESSAGE_NAME_CONST + logicLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
|
|
sb.AppendLine(messageName);
|
|
continue;
|
|
}
|
|
|
|
if (readingProtoMessage)
|
|
{
|
|
if (logicLineStr.Replace(" ", "").Replace("\t", "") == "}")
|
|
{
|
|
ReadProtoMessage(sb);
|
|
readingProtoMessage = false;
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(logicLineStr);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
reader.Close();
|
|
reader.Dispose();
|
|
}
|
|
|
|
static void ReadProtoEnum(string enumStr)
|
|
{
|
|
enumStr = enumStr.Replace(" ", "").Replace("\t", "");
|
|
enumStr = enumStr.Replace(";", "");
|
|
var index = enumStr.IndexOf("//");
|
|
if (index > 0)
|
|
{
|
|
enumStr = enumStr.Remove(index, enumStr.Length - index);
|
|
}
|
|
|
|
var equalIndex = enumStr.IndexOf("=");
|
|
var name = enumStr.Substring(0, equalIndex);
|
|
var num = Int32.Parse(enumStr.Substring(equalIndex + 1, enumStr.Length - equalIndex - 1));
|
|
|
|
var pe = new ProtoEnum();
|
|
pe.enumName = name;
|
|
pe.enumNum = num;
|
|
var moduleKey = (num - (num % 1000)) / 1000;
|
|
pe.moduleId = moduleKey;
|
|
|
|
protoEnums.Add(pe);
|
|
}
|
|
|
|
static void ReadProtoModule(string moduleStr)
|
|
{
|
|
moduleStr = moduleStr.Replace(" ", "").Replace("\t", "");
|
|
moduleStr = moduleStr.Replace("=", "");
|
|
moduleStr = moduleStr.Replace("//", "");
|
|
|
|
var pm = new ProtoModule();
|
|
pm.moduleDesc = moduleStr;
|
|
var num = Int32.Parse(System.Text.RegularExpressions.Regex.Replace(moduleStr, @"[^0-9]+", ""));
|
|
pm.moduleNum = num;
|
|
moduleIds.Add(num);
|
|
protoModules.Add(pm);
|
|
}
|
|
|
|
static void ReadProtoMessage(StringBuilder sb)
|
|
{
|
|
var message = sb.ToString();
|
|
var lines = message.Split(new char[] { '\n' });
|
|
var pm = new ProtoMessage();
|
|
foreach (var line in lines)
|
|
{
|
|
if (line == "")
|
|
{
|
|
continue;
|
|
}
|
|
if (line.Contains(COMMENT_CONST))
|
|
{
|
|
pm.commentStr = line.Replace(COMMENT_CONST, "");
|
|
continue;
|
|
}
|
|
if (line.Contains(MESSAGE_NAME_CONST))
|
|
{
|
|
pm.messageName = line.Replace(MESSAGE_NAME_CONST, "");
|
|
continue;
|
|
}
|
|
|
|
var pf = GetProtoField(line);
|
|
pm.fields.Add(pf);
|
|
}
|
|
|
|
protoMessages.Add(pm);
|
|
sb.Clear();
|
|
}
|
|
|
|
static void SortEnum()
|
|
{
|
|
protoEnums.Sort((x, y) => { return x.enumNum - y.enumNum; });
|
|
}
|
|
|
|
static void ParsingCommon()
|
|
{
|
|
var reader = new StreamReader(new FileStream(CommonProtoPath, FileMode.Open));
|
|
var commonLineStr = "";
|
|
while ((commonLineStr = reader.ReadLine()) != null)
|
|
{
|
|
if (commonLineStr == "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (commonLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
|
|
{
|
|
protoStructTypeNameStr = commonLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
|
|
readingProtoStruct = true;
|
|
continue;
|
|
}
|
|
|
|
if (readingProtoStruct)
|
|
{
|
|
if (commonLineStr.Replace(" ", "").Replace("\t", "") == "}")
|
|
{
|
|
ReadProtoStruct(sb, protoStructTypeNameStr);
|
|
protoStructTypeNameStr = "";
|
|
readingProtoStruct = false;
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(commonLineStr);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
reader.Close();
|
|
reader.Dispose();
|
|
}
|
|
|
|
static void ParsingBattle()
|
|
{
|
|
var reader = new StreamReader(new FileStream(BattleProtoPath, FileMode.Open));
|
|
var lineStr = "";
|
|
while ((lineStr = reader.ReadLine()) != null)
|
|
{
|
|
if (lineStr == "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (lineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
|
|
{
|
|
protoStructTypeNameStr = lineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
|
|
readingProtoStruct = true;
|
|
continue;
|
|
}
|
|
|
|
if (readingProtoStruct)
|
|
{
|
|
if (lineStr.Replace(" ", "").Replace("\t", "") == "}")
|
|
{
|
|
ReadProtoStruct(sb, protoStructTypeNameStr);
|
|
protoStructTypeNameStr = "";
|
|
readingProtoStruct = false;
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(lineStr);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
reader.Close();
|
|
reader.Dispose();
|
|
}
|
|
|
|
static void ReadProtoStruct(StringBuilder sb, string structName)
|
|
{
|
|
var ps = new ProtoStruct();
|
|
ps.typeName = structName;
|
|
var message = sb.ToString();
|
|
var lines = message.Split(new char[] { '\n' });
|
|
foreach (var line in lines)
|
|
{
|
|
if (line == "")
|
|
{
|
|
continue;
|
|
}
|
|
ProtoField pf = GetProtoField(line);
|
|
ps.fields.Add(pf);
|
|
}
|
|
sb.Clear();
|
|
protoStructs.Add(ps);
|
|
}
|
|
|
|
static ProtoField GetProtoField(string fieldLine)
|
|
{
|
|
var pf = new ProtoField(); // 解析filed
|
|
if (fieldLine.Replace(" ", "").Replace("\t", "").StartsWith("repeated"))
|
|
{
|
|
pf.isRepeated = true;
|
|
}
|
|
|
|
if (fieldLine.Contains("//"))
|
|
{
|
|
var index1 = fieldLine.IndexOf("//");
|
|
var comment = fieldLine.Substring(index1, fieldLine.Length - index1);
|
|
pf.commentStr = comment;
|
|
}
|
|
|
|
var index2 = fieldLine.IndexOf("=");
|
|
var fieldStr = fieldLine.Remove(index2).Replace("repeated", "");
|
|
if (fieldStr.Replace(" ", "").Replace("\t", "").StartsWith("map<")) // map结构
|
|
{
|
|
pf.isMap = true;
|
|
|
|
var index3 = fieldStr.IndexOf(">") + 1;
|
|
var fieldName = fieldStr.Substring(index3, fieldStr.Length - index3).Replace(" ", "").Replace("\t", "");
|
|
pf.fieldName = fieldName;
|
|
|
|
var keyValueStr = fieldStr.Replace("map<", "").Replace(">", "").Replace(fieldName, "").Replace(" ", "").Replace("\t", "");
|
|
var index4 = keyValueStr.IndexOf(",");
|
|
var keyName = keyValueStr.Substring(0, index4);
|
|
pf.keyTypeName = keyName;
|
|
|
|
var vauleName = keyValueStr.Substring(index4 + 1, keyValueStr.Length - index4 - 1);
|
|
pf.valueTypeName = vauleName.Replace(" ", "").Replace("\t", "");
|
|
}
|
|
else
|
|
{
|
|
while (fieldStr.Contains(" ")) // 这个处理不好
|
|
{
|
|
fieldStr = fieldStr.Replace(" ", " ");
|
|
}
|
|
|
|
var typeFiledNameStr = fieldStr.Split(new char[] { ' ' });
|
|
foreach (var item in typeFiledNameStr)
|
|
{
|
|
if (item == "")
|
|
{
|
|
continue;
|
|
}
|
|
if (pf.typeName == "")
|
|
{
|
|
pf.typeName = item.Replace(" ", "").Replace("\t", "");
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
pf.fieldName = item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pf;
|
|
}
|
|
|
|
static void Export2Markdown()
|
|
{
|
|
var sw = new StreamWriter(MarkdownPath + MARKDOWN_FILE_NAME, false);
|
|
WriteTitle(sw);
|
|
WriteProtoModule(sw);
|
|
WriteProtoEnum(sw);
|
|
WriteProtoMessage(sw);
|
|
WriteProtoStructs(sw);
|
|
sw.Close();
|
|
sw.Dispose();
|
|
}
|
|
|
|
static void WriteTitle(StreamWriter sw)
|
|
{
|
|
sw.WriteLine("# *BF协议文档*");
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("<I><B>Auto Generated By: </B></I>");
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("bf_logic.proto (协议描述文件)\n");
|
|
sw.WriteLine("bf_comm.proto、bf_battle.proto (结构体描述文件)\n");
|
|
sw.WriteLine("bf_msgids.proto(协议枚举描述文件)\n");
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("<I><B>文档分为四部分:</B></I>\n");
|
|
sw.WriteLine("[功能模块](#一)、[协议枚举](#二)、[消息内容](#三)、[自定义结构体](#四)");
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("req: 前端请求, rsp: 服务器应答");
|
|
}
|
|
|
|
static void WriteProtoModule(StreamWriter sw)
|
|
{
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("## " + GetHtmlLink("top") + GetHtmlLink("一") + "第一部分 模块");
|
|
foreach (var item in protoModules)
|
|
{
|
|
sw.WriteLine(" - [" + "模块id: " + item.moduleNum + " // " + item.moduleDesc + "]" + "(#" + item.moduleNum + ")");
|
|
}
|
|
}
|
|
|
|
static void WriteProtoEnum(StreamWriter sw)
|
|
{
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("## " + GetHtmlLink("二") + "第二部分 ProtoEnum");
|
|
|
|
var moduleId = protoEnums[0].moduleId;
|
|
sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId));
|
|
|
|
foreach (var item in protoEnums)
|
|
{
|
|
if (item.moduleId != moduleId)
|
|
{
|
|
moduleId = item.moduleId;
|
|
sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId));
|
|
}
|
|
sw.WriteLine("> - [" + item.enumName + " = " + item.enumNum + "]" + "(#" + item.enumName + ")");
|
|
}
|
|
}
|
|
|
|
static string GetModuleDesc(int key)
|
|
{
|
|
foreach (var item in protoModules)
|
|
{
|
|
if (item.moduleNum == key)
|
|
{
|
|
return item.moduleDesc;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static void WriteProtoMessage(StreamWriter sw)
|
|
{
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("## " + GetHtmlLink("三") + "第三部分 ProtoMessage");
|
|
|
|
foreach (var item in protoEnums)
|
|
{
|
|
var rspPM = GetRspMessage(item.enumName);
|
|
var reqPM = GetReqMessage(item.enumName);
|
|
if (rspPM == null && reqPM == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string comment = "";
|
|
if (reqPM != null && reqPM.commentStr != "")
|
|
{
|
|
comment = reqPM.commentStr;
|
|
}
|
|
else
|
|
{
|
|
comment = rspPM.commentStr;
|
|
}
|
|
|
|
sw.WriteLine("## " + "__" + GetHtmlLink(item.enumName) + "消息 " + item.enumName + " //" + comment + "__");
|
|
|
|
WriteProtoMessage(sw, reqPM, "req : ");
|
|
sw.WriteLine(" ");
|
|
|
|
WriteProtoMessage(sw, rspPM, "rsp : ");
|
|
sw.WriteLine(" ");
|
|
|
|
DrawTopBtn(sw);
|
|
|
|
sw.WriteLine("<br>");
|
|
sw.WriteLine("<br>");
|
|
sw.WriteLine(" ");
|
|
}
|
|
}
|
|
|
|
static ProtoMessage GetRspMessage(string enumName)
|
|
{
|
|
const string RSP = "rsp";
|
|
foreach (var item in protoMessages)
|
|
{
|
|
if (item.messageName == RSP + enumName)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static ProtoMessage GetReqMessage(string enumName)
|
|
{
|
|
const string REQ = "req";
|
|
foreach (var item in protoMessages)
|
|
{
|
|
if (item.messageName == REQ + enumName)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static void WriteProtoMessage(StreamWriter sw, ProtoMessage pm, string title)
|
|
{
|
|
if (pm == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sw.WriteLine("### " + title);
|
|
sw.WriteLine(" ");
|
|
|
|
WriteTableTitle(sw);
|
|
|
|
foreach (var item in pm.fields)
|
|
{
|
|
WriteField(sw, item);
|
|
}
|
|
}
|
|
|
|
static void WriteTableTitle(StreamWriter sw)
|
|
{
|
|
sw.WriteLine("| repeated | 类型 | 字段名 | 注释 |");
|
|
sw.WriteLine("| :------------ | :------------ | :------------ | :------------ |");
|
|
}
|
|
|
|
static void WriteField(StreamWriter sw, ProtoField pf)
|
|
{
|
|
var str = "| ";
|
|
if (pf.isRepeated)
|
|
{
|
|
str = str + "repeated |";
|
|
}
|
|
else
|
|
{
|
|
str = str + " |";
|
|
}
|
|
|
|
if (pf.isMap)
|
|
{
|
|
str = str + "map< " + GetTypeMarkdownStr(pf.keyTypeName) + " , " + GetTypeMarkdownStr(pf.valueTypeName) + "> |";
|
|
}
|
|
else
|
|
{
|
|
str = str + GetTypeMarkdownStr(pf.typeName) + " |";
|
|
}
|
|
|
|
str = str + pf.fieldName + " |";
|
|
str = str + pf.commentStr + " |";
|
|
sw.WriteLine(str);
|
|
}
|
|
|
|
static string GetTypeMarkdownStr(string typeName)
|
|
{
|
|
if (TYPE_KEYWORDS.Contains(typeName))
|
|
{
|
|
return typeName;
|
|
}
|
|
else
|
|
{
|
|
return "[" + typeName + "]" + "(#TYPE_" + typeName + ")";
|
|
}
|
|
}
|
|
|
|
static void WriteProtoStructs(StreamWriter sw)
|
|
{
|
|
sw.WriteLine(" ");
|
|
sw.WriteLine("## " + GetHtmlLink("四") + "第四部分 结构体");
|
|
|
|
foreach (var item in protoStructs)
|
|
{
|
|
WriteStruct(sw, item);
|
|
sw.WriteLine(" ");
|
|
|
|
DrawTopBtn(sw);
|
|
|
|
sw.WriteLine("<br>");
|
|
sw.WriteLine(" ");
|
|
}
|
|
}
|
|
|
|
static void WriteStruct(StreamWriter sw, ProtoStruct ps)
|
|
{
|
|
sw.WriteLine("### " + "__" + GetHtmlLink("TYPE_" + ps.typeName) + "结构体 " + ps.typeName + "__");
|
|
WriteTableTitle(sw);
|
|
|
|
foreach (var item in ps.fields)
|
|
{
|
|
WriteField(sw, item);
|
|
}
|
|
}
|
|
|
|
static string GetHtmlLink(string name)
|
|
{
|
|
return "<a name = \"" + name + "\" />";
|
|
}
|
|
|
|
static void DrawTopBtn(StreamWriter sw)
|
|
{
|
|
sw.WriteLine("<p align=\"right\"><a href=\"#top\"><I><U>回到顶部</U></I></a></p>");
|
|
}
|
|
}
|
|
}
|