#coding=utf-8 import sys,os,re,chardet,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 = {} #模块对应信息,例如gate,game,auth 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... #game///////////////////////////////////////////////////////////////////////////////////////// if __name__ == '__main__': projects=['gate','game','db','auth','social','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, }) } '''