最近项目里需要一个生成协议脚本的工具,因为有空闲时间就写了一个,协议文件是html格式,我们用requests库请求然后解析,然后生成cs代码,以下是代码:
# -*- coding: utf8 -*-
import wx
import os
import requests
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
h2Start = "<h2>"
h2End = "</h2>"
C2SS4 = "C2SS4"
SS2C4 = "SS2C4"
C2BS4 = "C2BS4"
BS2C4 = "BS2C4"
extends = "</font> extends"
h3pre = "</h3><pre>"
NoParams = "无参数"
pre = "</pre>"
class ValueAttribute:
def __init__(self):
self.name = ''
self.structType = ''
self.normalType = ''
self.annotations = ''
self.stringLen = 0
self.sturctName = ''
handlerScriptName = "Test"
config_name = 'NetHandlerConfig.txt'
bufferConfigPath = os.getcwd() + "/" + config_name
class App(wx.App):
def OnInit(self):
frame = wx.Frame(parent=None, title='协议代码生成工具1.0', pos=(1000, 200), size=(650, 130))
scriptNameTitle = wx.StaticText(frame, label="脚本名称", pos=(5, 10), size=(200, 30))
scriptNameInput = wx.TextCtrl(frame, pos=(110, 8), size=(400, 24))
netHandlerNumbersTitle = wx.StaticText(frame, label="协议号(逗号分割)", pos=(5, 35), size=(150, 30))
netHandlerNumbersInput = wx.TextCtrl(frame, pos=(110, 33), size=(400, 24))
assetsPathTitle = wx.StaticText(frame, label="Assets文件目录", pos=(5, 60), size=(150, 30))
assetsInput = wx.TextCtrl(frame, pos=(110, 58), size=(400, 24))
file = open(bufferConfigPath, encoding='utf-8')
fileLineArr = file.readlines()
for line in fileLineArr:
# print(line)
strArr = line.replace('\n', '').split(':')
if strArr[0] == "ScriptName":
scriptNameInput.SetValue(strArr[1])
elif strArr[0] == "NetHandlerNumbers":
netHandlerNumbersInput.SetValue(strArr[1])
elif strArr[0] == "AssetsPath":
assetsInput.SetValue(strArr[1] + ":" + strArr[2])
checkButton = wx.Button(frame, label="生成代码", pos=(515, 35), size=(100, 24))
def onClickCheckButton(event):
handlerScriptName = scriptNameInput.GetValue()
netHandlerNumbersStr = netHandlerNumbersInput.GetValue()
assetsPath = assetsInput.GetValue().replace('\\', '/')
netHandlerNumbersArr = netHandlerNumbersStr.split(',')
netHandlerNumberList = []
for numberStr in netHandlerNumbersArr:
netHandlerNumberList.append(int(numberStr))
writeStr = ""
writeCount = 0
for netHandlerNumber in netHandlerNumberList:
url = "http://dungeon.admin.indra.vip:90/{0}.html".format(netHandlerNumber)
# print(url)
response = requests.request("GET", url, stream=True, data=None, headers=None)
response.encoding = 'utf-8'
# print(response.text.replace('/h3>', '/h3>\n\n\t').replace('/>', '/>\n\t').replace('br>', 'br>\n')
# .replace('BR>', 'BR>\n\t\t').replace('<pre>', '<pre>\n\t\t').replace('</pre>', '</pre>\n\n')
# .replace('\t</pre>', '</pre>'))
titleIndex = 0
title = ""
cssIndex = 0
cbsIndex = 0
reqIndex = 0
sscIndex = 0
bscIndex = 0
repIndex = 0
reqStr = "Req"
reqType = "NormalMessage"
reqValueStr = ""
reqValueList = []
repStr = "Rep"
repValueStr = ""
repValueList = []
subStructStr = ""
subStructList = []
subStructDict = {}
isOne = False
isTwo = False
isThree = False
isFour = False
for c in response.text:
# 解析标题
if titleIndex < len(h2Start):
if c == h2Start[titleIndex]:
titleIndex += 1
if titleIndex >= len(h2Start) and titleIndex - len(h2Start) < len(h2End):
if c == h2End[titleIndex - len(h2Start)]:
titleIndex += 1
if titleIndex == len(h2Start):
title += c
# 解析C2SS
if cssIndex >= len(C2SS4) and cssIndex - len(C2SS4) < len(extends):
if c == extends[cssIndex - len(C2SS4)]:
cssIndex += 1
if cssIndex == len(C2SS4) and not isTwo:
reqType = "NormalMessage"
reqStr += c
isOne = True
if cssIndex < len(C2SS4):
if c == C2SS4[cssIndex]:
cssIndex += 1
else:
cssIndex = 0
# print(cssIndex, c, bscIndex)
# 解析C2BS
if cbsIndex >= len(C2BS4) and cbsIndex - len(C2BS4) < len(extends):
if c == extends[cbsIndex - len(C2BS4)]:
cbsIndex += 1
if cbsIndex == len(C2BS4) and not isOne:
reqType = "PvpMessage"
reqStr += c
isTwo = True
if cbsIndex < len(C2BS4):
if c == C2BS4[cbsIndex]:
cbsIndex += 1
else:
cbsIndex = 0
# 解析请求字段
if reqIndex >= len(h3pre) and reqIndex - len(h3pre) < len(pre):
if c == pre[reqIndex - len(h3pre)]:
reqIndex += 1
else:
reqIndex = len(h3pre)
if reqIndex == len(h3pre):
reqValueStr += c
if reqIndex < len(h3pre):
if c == h3pre[reqIndex]:
reqIndex += 1
# 解析SS2C
if sscIndex >= len(SS2C4) and sscIndex - len(SS2C4) < len(extends):
if c == extends[sscIndex - len(SS2C4)]:
sscIndex += 1
if sscIndex == len(SS2C4) and not isFour:
reqType = "NormalMessage"
repStr += c
isThree = True
if sscIndex < len(SS2C4):
if c == SS2C4[sscIndex]:
sscIndex += 1
else:
sscIndex = 0
# 解析BS2C
if bscIndex >= len(BS2C4) and bscIndex - len(BS2C4) < len(extends):
if c == extends[bscIndex - len(BS2C4)]:
bscIndex += 1
if bscIndex == len(BS2C4) and not isThree:
reqType = "PvpMessage"
repStr += c
isFour = True
if bscIndex < len(BS2C4):
if c == BS2C4[bscIndex]:
bscIndex += 1
else:
bscIndex = 0
# 解析响应字段(SS2C)
if (sscIndex >= len(SS2C4) or bscIndex > len(BS2C4)) and repIndex >= len(h3pre) and repIndex - len(
h3pre) < len(pre):
if c == pre[repIndex - len(h3pre)]:
repIndex += 1
else:
repIndex = len(h3pre)
if (sscIndex >= len(SS2C4) or bscIndex > len(BS2C4)) and repIndex == len(h3pre):
repValueStr += c
if (sscIndex >= len(SS2C4) or bscIndex > len(BS2C4)) and repIndex < len(h3pre):
if c == h3pre[repIndex]:
repIndex += 1
title = title[6:-1]
title = title[:6] + " " + title[6:]
reqArr = reqValueStr.replace('=> ', '').split('BR>')
# 请求字段加入列表
for reqVal in reqArr:
reqValArr = reqVal.split(' ')
if len(reqValArr) < 3 or NoParams in reqVal:
continue
val = ValueAttribute()
val.name = reqValArr[1]
val.structType = "Int32"
val.normalType = "int"
# 判断字段类型
if "Signed8" in reqValArr[0]:
val.structType = "byte"
val.normalType = "byte"
elif "Signed32" in reqValArr[0]:
if "list" in reqValArr[1]:
val.structType = "fixed Int32 list[100]"
val.normalType = "List<int>"
else:
val.structType = "Int32"
val.normalType = "int"
elif "Signed64" in reqValArr[0]:
if "list" in reqValArr[1]:
val.structType = "fixed Int64 list[100]"
val.normalType = "List<long>"
else:
val.structType = "Int64"
val.normalType = "long"
elif "UTF8String" in reqValArr[0]:
stringLen = reqValArr[0].replace("\t", "")[11:-1]
val.stringLen = stringLen
val.structType = "fixed byte " + val.name + "[" + stringLen + "]"
val.normalType = "string"
else:
prevStruct = subStructStr
subStructStr = reqValArr[0].replace("[]", "")
val.structType = "fixed byte " + val.name + "[100]"
val.normalType = "struct"
val.sturctName = subStructStr.replace("\t", "")
if subStructStr not in subStructDict.keys():
subStructDict[subStructStr] = []
if "\t" in subStructStr:
subStructDict[prevStruct].append(val)
val.annotation = ""
for i in range(len(reqValArr)):
if i > 1:
val.annotation += " " + reqValArr[i]
if "\t" in reqVal: # 二级结构体字段
subStructList.append(val)
subStructDict[subStructStr].append(val)
else: # 普通字段
reqValueList.append(val)
repArr = repValueStr.replace('=> ', '').split('BR>')
# 响应字段加入列表
for repVal in repArr:
repValArr = repVal.split(' ')
if len(repValArr) < 3:
continue
val = ValueAttribute()
val.name = repValArr[1]
# 判断字段类型
structName = ""
if "Signed8" in repValArr[0]:
val.structType = "byte"
val.normalType = "byte"
elif "Signed32" in repValArr[0]:
if "list" in repValArr[1]:
val.structType = "fixed Int32 list[100]"
val.normalType = "List<int>"
else:
val.structType = "Int32"
val.normalType = "int"
elif "Signed64" in repValArr[0]:
if "list" in repValArr[1]:
val.structType = "fixed Int64 list[100]"
val.normalType = "List<long>"
else:
val.structType = "Int64"
val.normalType = "long"
elif "UTF8String" in repValArr[0]:
stringLen = repValArr[0].replace("\t", "")[11:-1]
val.stringLen = stringLen
val.structType = "fixed byte " + val.name + "[" + stringLen + "]"
val.normalType = "string"
else:
prevStruct = subStructStr
subStructStr = repValArr[0].replace("[]", "")
val.structType = "fixed byte " + val.name + "[100]"
val.normalType = "struct"
val.sturctName = subStructStr.replace("\t", "")
if subStructStr not in subStructDict.keys():
subStructDict[subStructStr] = []
if "\t" in subStructStr:
subStructDict[prevStruct].append(val)
val.annotation = ""
for i in range(len(repValArr)):
if i > 1:
val.annotation += " " + repValArr[i]
# print(repVal)
if "\t" in repVal: # 二级结构体字段
subStructList.append(val)
subStructDict[subStructStr].append(val)
else: # 普通字段
repValueList.append(val)
# for k in subStructDict:
# print(k)
# for va in subStructDict[k]:
# print("\t", va.name, va.normalType)
if writeCount == 0:
writeStr += "//----------------------------------------------------------------------\n" \
"// Generate By NetHandlerGenerator.exe, Author: OuYangYiPeng, 2021/5/29\n" \
"//----------------------------------------------------------------------\n\n" \
"using UnityEngine;\nusing System.Collections;\nusing System;\nusing System.Configuration;\n" \
"using System.Collections.Generic;\nusing System.Runtime;\nusing System.Runtime.InteropServices;\n" \
"using System.Runtime.CompilerServices;\nusing System.Net;\n\npublic struct " + \
handlerScriptName + "NetMessage\n{\n"
tempStr = ""
# 写入请求结构体
if len(reqValueList) > 0 or NoParams in reqValueStr:
tempStr += "\t//" + title + "(请求)\n\t[StructLayout(LayoutKind.Sequential , Pack = 1)]\n\tpublic unsafe struct " + reqStr + \
" : NetHandlerInterface\n\t{\n\t\tpublic GameNetMessage.PacketBase baseHeader;\n\t\t" \
"public void InitNetBasePacket(GameNetMessage.PacketBase pb) { baseHeader = pb; }\n"
if NoParams not in reqValueStr:
for val in reqValueList:
# print(val.name, val.structType, val.normalType, val.annotation)
if val.normalType == "string" or val.normalType == "struct" or \
val.normalType == "List<int>" or val.normalType == "List<long>":
tempStr += "\t\tpublic " + val.structType + ";\t//" + val.annotation + "\n"
else:
tempStr += "\t\tpublic " + val.structType + " " + val.name + ";\t//" + val.annotation + "\n"
tempStr += "\t}\n\n"
# 写入响应结构体
if len(repValueList) > 0:
tempStr += "\t//" + title + "(响应)\n\t[StructLayout(LayoutKind.Sequential , Pack = 1)]\n\tpublic unsafe struct " + repStr + \
"\n\t{\n\t\tpublic GameNetMessage.PacketBase baseHeader;\n"
if NoParams not in repValueStr:
for val in repValueList:
# print(val.name, val.structType, val.normalType, val.annotation)
if val.normalType == "string" or val.normalType == "struct" \
or val.normalType == "List<int>" or val.normalType == "List<long>":
tempStr += "\t\tpublic " + val.structType + ";\t//" + val.annotation + "\n"
else:
tempStr += "\t\tpublic " + val.structType + " " + val.name + ";\t//" + val.annotation + "\n"
tempStr += "\t}\n\n"
# 写入二级结构体
if len(subStructDict) > 0:
for k in subStructDict:
if "\t" in k:
continue
valList = subStructDict[k]
tempStr += "\t//" + k + "(结构体)\n\tpublic unsafe struct " + k + "\n\t{\n"
for val in valList:
# print(val.name, val.structType, val.normalType, val.annotation)
if val.normalType == "string" or val.normalType == "struct":
tempStr += "\t\tpublic " + val.structType + ";\t//" + val.annotation + "\n"
else:
tempStr += "\t\tpublic " + val.structType + " " + val.name + ";\t//" + val.annotation + "\n"
tempStr += "\t}\n\n"
tempStr += "{%s1}"
if writeCount == 0:
writeStr += tempStr
writeStr += "\n}\n\n"
else:
writeStr = writeStr.replace("{%s1}", tempStr)
tempStr = ""
# 协议单例类
if writeCount == 0:
writeStr += "public unsafe class {0}NetHandler : SingletonEntity<{0}NetHandler>".format(
handlerScriptName)
writeStr += "\n{\n"
# InitHandler
writeStr += "\tpublic void InitHandler()\n\t{\n"
if reqType == "NormalMessage":
tempStr += "\t\tGameSocketManager.Instance.regeditMsg(GameNetMessage.MsgType." + reqStr + ", " + repStr + ");\n"
tempStr += "{%s2}\n"
if writeCount == 0:
writeStr += tempStr;
writeStr += "\t}\n\n"
else:
writeStr = writeStr.replace("{%s2}", tempStr)
tempStr = ""
# InitPvpHandler
if writeCount == 0:
writeStr += "\tpublic void InitPvpHandler()\n\t{\n"
if reqType == "PvpMessage":
tempStr += "\t\tPVPSocketManager.Instance.regeditMsg(GameNetMessage.MsgType." + reqStr + ", " + repStr + ");\n"
tempStr += "{%s3}\n"
if writeCount == 0:
writeStr += tempStr;
writeStr += "\t}\n\n"
else:
writeStr = writeStr.replace("{%s3}", tempStr)
tempStr = ""
# 发送方法
tempStr += "\t//" + title + "(请求)\n\tpublic void " + reqStr + "("
if len(reqValueList) > 0:
for val in reqValueList:
if val.name == "num":
continue
if val.normalType == "struct":
tempStr += "List<" + val.sturctName + "> _" + val.name + ", "
else:
tempStr += val.normalType + " _" + val.name + ", "
tempStr = tempStr[:-2]
tempStr += ")\n\t{\n\t\t" + handlerScriptName + "NetMessage." + reqStr + " vData = \n\t\t\tNetHandlerTool.Instance." \
"InitReqData<" + handlerScriptName + "NetMessage." + reqStr + ">(GameNetMessage.MsgType." + reqStr + ");\n"
isWriteStruct = False
if len(reqValueList) > 0:
for val in reqValueList:
tempStr += "\t\t"
if val.normalType == "int" or val.normalType == "long":
tempStr += "vData.{0} = IPAddress.HostToNetworkOrder(_{0});".format(val.name)
elif val.normalType == "string":
tempStr += "GameDefine.stringToByte_cn(vData.{0}, _{0}, {1});".format(val.name,
val.stringLen)
elif val.normalType == "struct":
isWriteStruct = True
tempStr += "int itemSize = Marshal.SizeOf(typeof({0}NetMessage.{1}));\n". \
format(handlerScriptName, val.sturctName)
tempStr += "\t\tint vCurMax = 1024 / itemSize;\n"
tempStr += "\t\tint iCount = 0;\n\t\tfor (iCount = 0; iCount < costList.Count && iCount < vCurMax;" \
" iCount++)\n\t\t{\n\t\t\t"
tempStr += "{0}NetMessage.{1}* vItem = \n\t\t\t({0}NetMessage.{1}*)" \
"(vData.{2} + iCount * itemSize);\n". \
format(handlerScriptName, val.sturctName, val.name)
for val2 in subStructDict[val.sturctName]:
if val2.normalType == "int" or val2.normalType == "long":
tempStr += "\t\t\tvItem->{0} = NetHandlerTool.Instance.HostToNetworkOrder(_{1}[iCount].{0});\n". \
format(val2.name, val.name)
elif val.normalType == "string":
tempStr += "\t\t\tGameDefine.stringToByte_cn(vItem->{0}, _{0}, {1});\n". \
format(val2.name, val2.stringLen)
tempStr += "\t\t}\n\t\tvData.num = NetHandlerTool.Instance.HostToNetworkOrder(iCount);\n"
tempStr += "\t\tint delLen = 1024 - iCount * itemSize;"
tempStr += "\n"
if reqType == "NormalMessage":
if isWriteStruct:
tempStr += "\t\tNetHandlerTool.Instance.SendMessage(ref vData, delLen);\n\t}\n\n"
else:
tempStr += "\t\tNetHandlerTool.Instance.SendMessage(vData);\n\t}\n\n"
else:
if isWriteStruct:
tempStr += "\t\tNetHandlerTool.Instance.SendPVPMessage(ref vData, delLen);\n\t}\n\n"
else:
tempStr += "\t\tNetHandlerTool.Instance.SendPVPMessage(vData);\n\t}\n\n"
# 响应方法
tempStr += "\t//" + title + "(响应)\n\tpublic void " + repStr + "(System.IntPtr head)\n\t{\n\t\t" + handlerScriptName + \
"NetMessage." + repStr + "* msg = (" + handlerScriptName + "NetMessage." + repStr + "*)head;\n"
if len(repValueList) > 0:
for val in repValueList:
tempStr += "\t\t"
if val.normalType == "int" or val.normalType == "long" and val.name != "num":
tempStr += "{0} {1} = IPAddress.NetworkToHostOrder(msg->{1});".format(val.normalType,
val.name)
elif val.normalType == "string":
tempStr += "{0} {1} = GameDefine.byteToString(msg->{1}, {2});". \
format(val.normalType, val.name, val.stringLen)
elif val.normalType == "byte":
tempStr += "{0} {1} = msg->{1};".format(val.normalType, val.name)
elif val.normalType == "List<int>" or val.normalType == "List<long>":
childType = "int"
if val.normalType == "List<long>":
childType = "long"
tempStr += "{0}* item = ({0}*)msg->{1};".format(childType, val.name)
tempStr += "\n\t\tint num = IPAddress.NetworkToHostOrder(msg->num);\n" \
"\t\tfor (int i = 0; i < num; i++)\n\t\t{\n\t\t\tint id = IPAddress.NetworkToHostOrder" \
"(item[i]);\n\t\t}"
elif val.normalType == "struct":
tempStr += "{0}NetMessage.{1}* item = ({0}NetMessage.{1}*)msg->{2};". \
format(handlerScriptName, subStructStr, val.name)
tempStr += "\n\t\tint num = IPAddress.NetworkToHostOrder(msg->num);\n" \
"\t\tfor (int i = 0; i < num; i++)\n\t\t{\n"
if len(subStructList) > 0:
for val2 in subStructList:
tempStr += "\t\t\t"
if val2.normalType == "int" or val2.normalType == "long":
tempStr += "{0} {1} = IPAddress.NetworkToHostOrder(item[i].{1});". \
format(val2.normalType, val2.name)
elif val2.normalType == "string":
tempStr += "{0} {1} = GameDefine.byteToString(item[i].{1}, {2});". \
format(val2.normalType, val2.name, val2.stringLen)
elif val2.normalType == "byte":
tempStr += "{0} {1} = item[i].{1};". \
format(val2.normalType, val2.name)
tempStr += "\n"
tempStr += "\t\t}"
tempStr += "\n"
tempStr += "\t\tFSMService.Instance.UpdateFSMTopUI((int)GameNetMessage.MsgType." + reqStr + ");\n\t}\n\n"
tempStr += "{%s4}"
if writeCount == 0:
writeStr += tempStr;
writeStr += "\n}"
else:
writeStr = writeStr.replace("{%s4}", tempStr)
# 写入GameNetMessage
gameNetMessagePath = assetsPath + '/Scripts/NetHandler/GameNetMessage.cs'
f = open(gameNetMessagePath, encoding='utf-8')
fileArr = f.readlines()
lineIndex = 0
flag1 = 101
ws1 = ""
for line in fileArr:
# print(line)
lineIndex += 1
ws1 += line
# 插入一行
if lineIndex == flag1:
ws1 += "\t\t" + reqStr + ' = ' + str(netHandlerNumber) + ',\t//' + title.replace(str(netHandlerNumber), "") + '\n'
f = open(gameNetMessagePath, "w+", encoding='utf-8')
f.write(ws1)
f.close()
writeCount += 1
writeStr = writeStr.replace("{%s1}", "").replace("{%s2}", "").replace("{%s3}", "").replace("{%s4}", "") \
.replace("public void InitHandler()\n\t{\n\t}", "").replace("public void InitPvpHandler()\n\t{\n\t}","")\
.replace("\n\n\n\t}", "\t}").replace("\n\n\t}", "\t}").replace("\n}", "}")
#写入脚本
netHandlerScriptPath = assetsPath + '/Scripts/NetHandler/' + handlerScriptName + "NetHandler.cs"
file = open(netHandlerScriptPath, "w+", encoding='utf-8')
file.write(writeStr)
file.close()
msgWindow = wx.MessageDialog(None, "代码生成完毕!", "提示", wx.YES_DEFAULT | wx.ICON_QUESTION)
if msgWindow.ShowModal() == wx.ID_YES: # 如果点击了提示框的确定按钮
msgWindow.Destroy() # 则关闭提示框
# print(writeStr)
checkButton.Bind(wx.EVT_BUTTON, onClickCheckButton)
frame.Show()
return True
app = App()
app.MainLoop()
需要配合一个配置文件使用
NetHandlerConfig.txt
ScriptName:Test
NetHandlerNumbers:110011,110101,112507
AssetsPath:E:\Work\Trunk\Assets