fatiao 9c39586fbb 为fruit增加一个服务消息
message SSPlayerOnlineNtf{  //project social|fruit
message SSPlayerOfflineNtf{ //project social|battleboss|fruit
buildproto.bat
2025-06-09 11:19:43 +08:00

635 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#coding=utf-8
import sys,os,re,getopt
repattern = re.compile(r'.*[\.]{1}([^\.]+)')
packagekey = re.compile(r'package\s+([\S]+)\s*;')
messageidkey = re.compile(r'\s+([\S]+)\s+=\s+([\d]+)\s*;\s*//\s*([^\r\n]*)')
messagedefRulekey = re.compile(r'\s*message\s+([\S]+).*//\s*project\s+([\S]+)\s*//\s*RouteRule\s+([\S]+)')
messagedefConfirmKey = re.compile(r'\s*message\s+([\S]+).*//\s*project\s+([\S]+)\s*//\s*RouteRule\s+([\S]+)\s*//\s*confirm.*')
messagedefkey = re.compile(r'\s*message\s+([\S]+).*//\s*project\s+([\S]+)')
messagekey = re.compile(r'\s*message\s+([\S]+)[^\r\n]*')
def useage():
print("Usage %s: [-h|-c] [--help|--config] args ..." % sys.argv[0])
#获得msgidconfig.cfg中定义的消息ID区间段
#例如:client-login.proto 1000-1999
def getMsgSection(fileName):
msgSection = {}
file = open(fileName, 'r', encoding='utf-8')
#file = open(fileName, 'r')
if file:
for line in file.readlines():
line = line.strip()
if len(line) <= 1 or line[0] == '#':
continue
keys = line.split(':')
if keys == None or len(keys) < 2:
continue
protoname = keys[0].strip()
if protoname == None or len(protoname) <= 0:
continue
msgSection[protoname] = []
for i in range(1, len(keys)):
values = keys[i].strip().split('-')
if values == None or len(values) < 2:
continue
msgSection[protoname].append({"begin":int(values[0]), "end":int(values[1])})
file.close()
return msgSection
#生成消息对应的ID映射
#messages[msgkey] = {"id":int(msgid), "desc":msgdesc}
#已经存在的消息映射不会改变
def analysisMessageDef(fileName):
messages = {}
file = open(fileName, 'r', encoding='utf-8')
#file = open(fileName, 'r')
if file:
for line in file.readlines():
match = messageidkey.search(line)
if match:
groups = match.groups() #返回匹配结果组
if groups and len(groups) >= 3:
msgkey,msgid,msgdesc = groups[0].strip(),groups[1].strip(),groups[2].strip()
#print(msgkey, msgid, msgdesc)
messages[msgkey] = {"id":int(msgid), "desc":msgdesc}
file.close()
return messages
#获取路径下的指定类型文件列表
def getFileList(path, fileList):
for parent, dirnames, filenames in os.walk(path):
for filename in filenames:
match = repattern.match(filename)
if match:
filetype = match.groups()[0]
if filetype in ['proto']:
fileList.append(os.path.join(parent, filename))
#def data2UTF8(data):
# if data == None or len(data) <=0:
# return data
# charset = chardet.detect(data)
# try:
# if charset != None and charset['encoding'] and charset['encoding'] in ('utf-8'):
# data = data.decode('utf-8', 'replace').encode('GB2312')
# except BaseException:
# print(charst)
# finally:
# return data
def saveFile(filename, data):
file = open(filename, 'w', encoding='utf-8')
if file:
#data = data2UTF8(data)
file.write(data)
file.close()
#根据proto中定义的结构名称生成对应规则的枚举名称
#例如LoginReq -> LOGIN_REQ
def messageIdGen(name, packageName):
str = name[:2]
up = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
nameLen = len(name)
for i in range(2, nameLen - 1):
if name[i] in up:
if i == 2 or name[i-1] not in up or name[i+1] not in up:
str += '_'
str += name[i]
str += name[nameLen - 1]
return str.upper()
def saveMessageDef(fileName, messageFileClient, messageDef):
sortMsg = {}
for key, data in messageDef.items():
sproject = ''
if 'project' in data:
sproject = ','.join(data['project'])
#协议被删除后的处理
if 'name' in data:
sortMsg[int(data['id'])] = {'key':key,
'desc':data['desc'],
'name':data['name'],
'file':data['file'],
'project':sproject
}
if 'confirm' in data:
sortMsg[int(data['id'])]['confirm'] = 1
#print(data)
messageText = ''
messageTextClient = ''
for id in sorted(sortMsg.keys()):
data = sortMsg[id]
if 'confirm' in data:
messageText += '\t%-*s = %d;\t\t//\t%s **%s **%s **%s [%s][confirm]\n' % (32, data['key'], id, data['desc'], data['name'], data['file'], data['project'],data['name'])
else:
messageText += '\t%-*s = %d;\t\t//\t%s **%s **%s **%s [%s]\n' % (32, data['key'], id, data['desc'], data['name'], data['file'], data['project'],data['name'])
match1 = re.search('CS_', data['key'])
match2 = re.search('SC_', data['key'])
if (match1 and match1.start(0) == 0) or (match2 and match2.start(0) == 0):
if 'confirm' in data:
messageTextClient += '\t%-*s = %d;\t\t//\t%s **%s **%s **%s [%s][confirm]\n' % (32, data['key'], id, data['desc'], data['name'], data['file'], data['project'],data['name'])
else:
messageTextClient += '\t%-*s = %d;\t\t//\t%s **%s **%s **%s [%s]\n' % (32, data['key'], id, data['desc'], data['name'], data['file'], data['project'],data['name'])
saveFile(fileName,
'''syntax = "proto3";
package serverproto;
enum protoMsgId{
MSG_BEGIN = 0;
%s
}
''' %messageText)
saveFile(messageFileClient,
'''syntax = "proto3";
package serverproto;
enum protoMsgId{
MSG_BEGIN = 0;
%s
}
''' %messageTextClient)
#保存pbbind_gen.go文件
def savePBbindGo(fileName, messageDef, projectList):
#print('fileName:',fileName, projectList)
sortHandlerMsg = {}
sortInitMsg = {}
for key, data in messageDef.items():
sproject = ''
if 'name' in data:
sortInitMsg[int(data['id'])] = {'key':key,
'id': data['id'],
'desc':data['desc'],
'name':data['name'],
'file':data['file'],
'confirm':0,}
if 'project' in data and 'name' in data:
sproject = ','.join(data['project'])
sortHandlerMsg[int(data['id'])] = {'key':key,
'desc':data['desc'],
'name':data['name'],
'file':data['file'],
'project':sproject}
if 'confirm' in data and data['confirm'] == 1:
sortInitMsg[int(data['id'])]['confirm'] = data['confirm']
#print(sortInitMsg[int(data['id'])])
messageHandlerDef ='''package serverproto
import(
"log"
"reflect"
"rocommon"
)
func registerInfo(id int , msgType reflect.Type, confirmId int) {
rocommon.RegisterMessageInfo(&rocommon.MessageInfo{ID:id,Codec:rocommon.GetCodec(), Type:msgType, ConfirmMsgId:confirmId})
}
'''
messageHandler = '\nfunc GetMessageHandler(sreviceName string) rocommon.EventCallBack {\n\tswitch sreviceName { //note.serviceName must be lower words'
messageHandlerDetail = ''
#具体每个协议的定义
projectList.sort()
for projectName in projectList:
projectNameStr = str.upper(projectName)
messageHandlerDef += '\n//'+ projectNameStr + '\nvar('
messageHandler += '\n\tcase "' + str.lower(projectName) + '":\t//' + projectNameStr + ' message process part\n\t\treturn '
messageHandlerDetail = ' func(e rocommon.ProcEvent) {\n\t\t\tswitch e.Msg().(type) {'
for id in sorted(sortHandlerMsg.keys()):
data = sortHandlerMsg[id]
if projectName in data['project']:
messageHandlerDef += '\n\tHandle_' + projectNameStr + '_' + data['name'] +' =func(e rocommon.ProcEvent){panic("' + data['name'] + ' not implements")}'
messageHandlerDetail += '\n\t\t\tcase *' + data['name'] + ': Handle_' + projectNameStr + '_' + data['name'] + '(e)'
messageHandlerDef += '\n\tHandle_' + projectNameStr +'_Default func(e rocommon.ProcEvent)' + '\n)\n'
messageHandlerDetail += '\n\t\t\tdefault:\n\t\t\t\tif Handle_' + projectNameStr + '_' + 'Default != nil {\n\t\t\t\t\tHandle_' + projectNameStr + '_Default(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n'
messageHandler += messageHandlerDetail
messageHandler += '\n\tdefault: \n\t\treturn nil\n\t}\n}'
#init部分
messageInit = '\nfunc init() {\n\t//协议注册\n\tlog.SetFlags(log.Lshortfile | log.LstdFlags)'
for id in sorted(sortInitMsg.keys()):
data = sortInitMsg[id]
if 'confirm' in data and data['confirm'] == 1:
methodName = data['name']
methodName = methodName[2:len(methodName)-3].strip() + 'Ack'
#print(methodName)
for findId in sortInitMsg.keys():
findData = sortInitMsg[findId]
if methodName in findData['name']:
#print(findData, methodName)
messageInit += '\n\tregisterInfo(' + str(id) +', reflect.TypeOf((*' + data['name'] + ')(nil)).Elem(), ' + str(findData['id']) + ')'
break
else:
if 'Ack' in data['name']:
methodName = data['name']
methodName = methodName[2:len(methodName)-3].strip() + 'Req'
#print(methodName)
bFind = 0
for findId in sortInitMsg.keys():
findData = sortInitMsg[findId]
if methodName in findData['name'] and findData['confirm'] == 1:
bFind = 1
messageInit += '\n\tregisterInfo(' + str(id) +', reflect.TypeOf((*' + data['name'] + ')(nil)).Elem(), ' + str(findData['id']) + ')'
break
if bFind == 0:
messageInit += '\n\tregisterInfo(' + str(id) +', reflect.TypeOf((*' + data['name'] + ')(nil)).Elem(), 0)'
else:
messageInit += '\n\tregisterInfo(' + str(id) +', reflect.TypeOf((*' + data['name'] + ')(nil)).Elem(), 0)'
messageInit += '\n}'
messageText = messageHandlerDef + messageHandler + messageInit
saveFile(fileName, messageText)
def saveRouteTableGo(fileName, messageDef):
sortMsg = {}
for key, data in messageDef.items():
sproject = ''
if 'route' in data and 'project' in data:
sproject = ','.join(data['project'])
sortMsg[int(data['id'])] = {'key':key,
'desc':data['desc'],
'name':data['name'],
'file':data['file'],
'project':sproject,
'route':','.join(data['route'])}
#https://blog.csdn.net/qq_34500270/article/details/82899057
#req和ack映射处理
msgMapKVListTmp = {}
msgMapKVList = {}
for key, data in messageDef.items():
msgMapKVListTmp[int(data['id'])] = data
for key, data in msgMapKVListTmp.items():
matchCS = re.search(r'CS', data['name'])
matchReq = re.search(r'Req', data['name'])
if matchCS and matchCS.start(0) == 0 and matchReq:
ackName = data['name']
ackName = re.sub(r'Req', 'Ack', ackName)
ackName = re.sub(r'CS', 'SC', ackName, 1)
#print('start', data['name'], ackName, data['id'])
if msgMapKVListTmp.get(key+1):
if msgMapKVListTmp[key+1]['name'] == ackName:
#print('ok', data['name'], ackName, data['id'])
msgMapKVList[key] = {
'ack': key+1,
'name': data['name'],
'ackname': ackName,
}
#把路由信息添加到init中
routeText = '''package router
type ReqAckKVInfo struct {
ReqMsgId int32
AckMsgId int32
ReqMsgName string
}
var(
RouteTable = new(MsgRouteList)
//req和ack回复确认匹配协议号
ReqAckKVList = map[int]ReqAckKVInfo{}
)
func addRule(name string, service string, mod string, id int) {
rule := &MsgRouteRule{
MsgName: name,
ServiceName: service,
Mod: mod,
MsgID: id,
}
RouteTable.Rules = append(RouteTable.Rules, rule)
AddRouteRule(rule)
}
//初始化路由信息或者从服务器发现etcd中获取
func init(){'''
#print('sortMsg:', sortMsg)
#print('messageDef:', messageDef)
for id in sorted(sortMsg.keys()):
data = sortMsg[id]
routeText += '\n\taddRule("' + data['name'] + '","' + data['project'] + '","' + data['route'] + '",' + str(id) + ')'
routeText += '\n\n //req和ack消息映射处理'
for key, data in msgMapKVList.items():
keyStr = str(key)
routeText += '\n\tReqAckKVList[' + keyStr + '] = ReqAckKVInfo{' + keyStr + ', ' + str(data['ack']) + ', "' + data['name'] + '|' + data['ackname']+'"}'
routeText += '\n}'
saveFile(fileName, routeText)
def increaseMessageId(msgSection, messageIdMax, pbName, increase=1):
newIndex = 0
if pbName not in messageIdMax:
#查看是否有找到对应的proto枚举区域段
if pbName not in msgSection or len(msgSection[pbName]) <= 0:
newIndex = messageIdMax['max'] + increase
else:
newIndex = msgSection[pbName][0]['begin']
else:
newIndex = messageIdMax[pbName] + increase
if pbName in msgSection:
idList = msgSection[pbName]
#pbName当前对应的区域段ID不够使用
if newIndex > idList[-1]['end']:
newIndex = messageIdMax['max'] +increase
print('pbName area id(msgidconfig.cfg) not enough for use:',pbName)
else:
print('todo...')
#print('11可以处理多个区域段暂时只处理以后后续再扩展..todo...')
messageIdMax[pbName] = messageIdMax['max'] = newIndex
return newIndex
#解析.proto文件
def analysisProto(fileName, info):
print('analysis proto: %s' %fileName)
defines = []
file = open(fileName, 'r', encoding='utf-8')
if file:
gopackage = None
lastLine = ''
#allfile = data2UTF8(file.read())
allfile = file.read()
for line in allfile.split('\n'):
line = line.strip()
if len(line) <= 0:
continue
if gopackage == None:
match = packagekey.search(line)
if match:
gopackage = match.groups()[0].strip()
desc = ''
if lastLine[0:2] == '//':
desc = lastLine[2:].strip() #协议消息注释
#查找定义的消息结构
match = messagedefkey.search(line)
if match:
groups = match.groups()
if groups and len(groups) >=2:
msgdefname,project = groups[0].replace('{', '').strip(), groups[1].strip().split('|')
if len(desc) <=0:
desc = msgdefname
msgData = {'name':msgdefname,'desc':desc,'msgid':messageIdGen(msgdefname, gopackage),'project':project}
if gopackage != None:
msgData['package'] = gopackage
defines.append(msgData)
else:
match = messagekey.search(line)
if match:
groups = match.groups()
if groups and len(groups) >=1:
msgdefname = groups[0].replace('{', '').strip()
if msgdefname.lower()[-3:] in ['ntf','ack','req']:
if len(desc) <= 0:
desc = msgdefname
msgData = {'name':msgdefname,'desc':desc,'msgid':messageIdGen(msgdefname, gopackage)}
if gopackage != None:
msgData['package'] = gopackage
defines.append(msgData)
#包含路由规则
#todo...路由处理
match = messagedefRulekey.search(line)
if match:
groups = match.groups()
if groups and len(groups) >=3:
msgdefname,project = groups[0].replace('{', '').strip(), groups[1].strip().split('|')
routeList = groups[2].strip().split('|')
if len(desc) <=0:
desc = msgdefname
msgData = {'name':msgdefname,'desc':desc,'msgid':messageIdGen(msgdefname, gopackage),'project':project}
if gopackage != None:
msgData['package'] = gopackage
if len(routeList) > 0 :
msgData['route'] = routeList
defines.append(msgData)
#包含回包确认
match = messagedefConfirmKey.search(line)
if match:
groups = match.groups()
if groups and len(groups) >=3:
msgdefname,project = groups[0].replace('{', '').strip(), groups[1].strip().split('|')
routeList = groups[2].strip().split('|')
if len(desc) <=0:
desc = msgdefname
msgData = {'name':msgdefname,'desc':desc,'msgid':messageIdGen(msgdefname, gopackage),'project':project}
if gopackage != None:
msgData['package'] = gopackage
if len(routeList) > 0 :
msgData['route'] = routeList
msgData['confirm'] = 1
#print(msgData)
defines.append(msgData)
lastLine = line
file.close()
if len(defines) > 0:
newName = fileName
nameArr = fileName.replace('\\', '/').split('/')
if nameArr != None and len(nameArr) > 0:
newName = nameArr[-1]
info[newName] = defines #该proto文件对应的消息信息
#输出文件
#1,消息ID枚举定义文件messagedef.proto
#2,处理protoMsg消息转换为项目输出
def saveOutFile(outDir, messageFile, messageFileClient, projectList, msgSection, messagesDef, protoMsgs):
projectMap = {} #模块对应信息例如gategameauth
projectAllMap = {} #相同proto文件中的所有消息数据
commomMsgMap = {} #不属于任何模块的通用信息,只是用作结构处理
messageMap = {} #所有消息的集合
messageIdMax = {} #更新每一个proto文件最大的协议号
#pbName为proto文件名例如login.proto
for pbName,pbData in protoMsgs.items():
for data in pbData:
#哪些模块需要处理该协议
if 'project' in data.keys():
for project in data['project']:
proKey = project
if proKey not in projectMap:
projectMap[proKey] = {}
if pbName not in projectMap[proKey]:
projectMap[proKey][pbName] = [] #哪些proto文件中有该模块的操作
projectMap[proKey][pbName].append({'data':data, 'project':project})
if pbName not in projectAllMap:
projectAllMap[pbName] = []
projectAllMap[pbName].append(data)
else:
if pbName not in commomMsgMap:
commomMsgMap[pbName] = []
commomMsgMap[pbName].append(data)
if pbName not in messageMap:
messageMap[pbName] = []
messageMap[pbName].append(data)
msgid = data['msgid']
#获取pbName中已经在messagedef.proto文件中定义的枚举最大值
if msgid in messagesDef:
msgIndex = messagesDef[msgid]['id']
if pbName not in messageIdMax or messageIdMax[pbName] < msgIndex:
messageIdMax[pbName] = msgIndex
#获取所有协议枚举的最大值
maxIndex = 0
for _,data in messagesDef.items():
if data != None and data['id'] > maxIndex:
maxIndex = data['id']
messageIdMax['max'] = maxIndex
#print('messageIdMax:',messageIdMax)
#print('messageMap:',messageMap)
#print('messagesDef:',messagesDef)
#print('msgSection:', msgSection)
#print('protoMsgs:', protoMsgs)
#保存messagedef.proto
for pbName,msgs in messageMap.items():
for data in msgs:
msgId = data['msgid']
if msgId not in messagesDef:
messageIdMax[pbName] = increaseMessageId(msgSection, messageIdMax, pbName)
messagesDef[msgId] = {'id':messageIdMax[pbName]}
messagesDef[msgId]['desc'] = data['desc']
messagesDef[msgId]['name'] = data['name']
messagesDef[msgId]['file'] = pbName
if 'project' in data:
messagesDef[msgId]['project'] = data['project']
if 'route' in data:
messagesDef[msgId]['route'] = data['route']
if 'confirm' in data:
messagesDef[msgId]['confirm'] = data['confirm']
#print('messageIdMax:',messageIdMax)
#print('messagesDef:',messagesDef)
#生成消息枚举定义文件messagedef.proto
saveMessageDef(messageFile, messageFileClient, messagesDef)
#生成pbbind_gen.go文件
savePBbindGo('pbbind_gen.go', messagesDef, projectList)
#生成路由信息routetable.go
saveRouteTableGo('../baseserver/router/route_table.go', messagesDef)
#生成每个消息的处理回调信息 todo...
#main/////////////////////////////////////////////////////////////////////////////////////////
if __name__ == '__main__':
projects=['gate','game','db','auth','social','fruit','guild','aoi','rank','maprouter',"battlepve", "battleboss","battlerecord","gmweb",
"crossrouter","crossserver","crossrank","gcrossrouter","gcrossmap"]
#导出的proto文件协议对应ID
messageFile = 'messagedef.proto'
messageFileClient = 'messagedefclient.proto'
#每个proto文件的消息ID分段用来区分消息类型
configFile = 'msgidconfig.cfg'
try:
opts,args = getopt.getopt(sys.argv[1:], "hp:c:", ["help","config="])
for opt, arg in opts:
if opt in ("-c", "--config"):
configFile = arg
except getopt.GetoptError:
useage()
sys.exit(1)
msgSection = getMsgSection(configFile)
#print('[%s] msgsection:' %configFile, msgSection)
messagesDef = analysisMessageDef(messageFile)
#print('[%s] messages:'%messageFile, messagesDef)
#解析proto文件
fileList = []
getFileList('./', fileList)
protoMsgs = {}
for file in fileList:
analysisProto(file, protoMsgs)
#print('info:',protoMsgs)
saveOutFile('./', messageFile, messageFileClient, projects, msgSection, messagesDef, protoMsgs)
'''
package serverproto
import (
"rocommon"
"log"
"reflect"
)
//gate
var (
Handle_Gate_LoginReq = func(e rocommon.ProcEvent){panic("LoginReq not implements")}
Handle_Gate_Default func(e rocommon.ProcEvent)
)
func GetMessageHandler(sreviceName string) rocommon.EventCallBack {
switch sreviceName {
case "gate": return gateHandler
case "game": return gameHandler
case "db": return dbHandler
default:
return nil
}
}
//gate消息处理部分
func gateHandler(e rocommon.ProcEvent) {
switch e.Msg().(type) {
case *LoginReq: Handle_Gate_LoginReq(e)
default:
if Handle_Gate_Default != nil {
Handle_Gate_Default(e)
}
}
}
func gameHandler(e rocommon.ProcEvent) {
//todo...
}
func dbHandler(e rocommon.ProcEvent) {
//todo...
}
func init() {
//todo...协议注册
log.SetFlags(log.Lshortfile | log.LstdFlags)
rocommon.RegisterMessageInfo(&rocommon.MessageInfo{
Codec:rocommon.GetCodec(), Type:reflect.TypeOf((*LoginReq)(nil)).Elem(), ID:1})
}
'''
'''
package router
var (
RouteTable = new(MsgRouteList)
)
func init() {
//初始化路由信息或者从服务器发现etcd中获取
RouteTable.Rules = append(RouteTable.Rules, &MsgRouteRule{
MsgName: "name",
ServiceName: "gate|game",
Mod: "pass",
MsgID: 1,
})
}
'''