生成 lua版本的proto 与使用
[前言]
关于如何使用生成proto的exe工具, 参考我的另一篇文章:
https://blog.csdn.net/liuyongjie1992/article/details/115185967
我发现生成lua版本的proto和生成C#版的调用方式不太一样,因此开一片文章记录一下
新建一个bat文件执行这一句话即可调用lua的生成工具
.\protoclient.exe --proto_path=./proto --lua_out=./genpath ./proto/TestProto.proto
.\protoclient.exe是调用当前目录的protoclient.exe文件
--proto_path=./proto是proto的文件夹,请注意是文件夹路径
--lua_out=./genpath 输出后的proto路径
./proto/TestProto.proto找到我们要输入的proto
上述操作不出意外可以顺利生成TestProto_pb.lua文件
很显然lua和C#的exe输入的语句是不一样的
C#版本的执行语句为
.\protogen.exe -i:xxx.proto -o:.\genpath\xxx.cs
[Lua版本的pb使用方法]
以上文的TestMessage.proto为例
生成的lua版pb长这样子
下面这句话是业务代码如何使用Message,项目里各种UI需要向服务器请求数据展示自己的内容,TestProto_pb.TestMessage()就是消息体,表示你想请求哪个内容。
--这是一个lua函数
function HowUseProtoMessage()--提示:在使用TestProto_pb之前别忘了require,下文会介绍如何在项目中批处理多个requirelocal msg = TestProto_pb.TestMessage()msg.id = 1;msg.name = "测试message"--!!!这句话很关键,把一个message序列化成stringlocal NetString = msg:SerializeToString();--如果你想把这个msg发给服务器,那么就把这个string发过去NetMgr.SendMsg(NetString)
end
下面开始进阶版 lua pb的使用
项目中我们通常会给Proto里面的mssage加上id,这样前后端通信的时候就知道你传的到底是个毛线,从而方便分发,添加id的方式有两种。提示!!!:无论protoid是3个数的组合还是1个数,这个protoid都是批处理工具按照一定规则生成出来的枚举,不要手动去添加这个id。
我们给proto加上一个枚举用来做标识,第一种方式是mrid,groupid,unitid三个数的组合,第二种是protoid一个数的。
前后端在通信的时候可以从包头中提取出 第一种或者第二种 id从而得出该数据包要用哪个message解析。
【项目启动时注册消息回调】
下面的Reg函数是在项目启动时调用,所有业务都要根据protoid记录消息体、回调函数、错误处理函数。
--[[注册消息处理函数
proto 消息类,示例Module_XXX_pb.XX
handler 处理函数
errorHandler 错误处理函数(错误码不为0时会调用)
--]]
function Reg(proto, handler, errorHandler)local mid = proto.protoid -- proto.MRID; if mMsgCallBacks[mid] thenlocal callBack = mMsgCallBacks[mid];local callBackMeta = getmetatable(callBack.proto());local newCallBackMeta = getmetatable(proto());GameLog.LogModuleError("msg handler repeat %s %s %d", callBackMeta._descriptor.name, newCallBackMeta._descriptor.name, mid);elselocal callBack = {};callBack.proto = proto;callBack.handler = handler;callBack.errorHandler = errorHandler;mMsgCallBacks[mid] = callBack;end
end
可以在项目启动时,调用一个统一的lua文件把所有业务的回调全部注册。
--所有需要接受服务器通讯的协议都要注册哦,下面这个注册只是举个例子GameNet.Reg(Module_Item_pb.SCItemGetDataRe, ItemMgr.OnGetItemData)
上面注册完了协议回调,那么客户端就等待服务器数据啦。
下面的代码就是收到服务器数据后,C#层把数据传递给lua,请注意,lua不支持byte[]数组,因此数据流会被转化成LuaByte,最终就是个string。
--参数解释:--参数1:protoid是我们自定义的id--参数2:data是C#层接收到的byte[]数组转换成的string
function OnRecvMsg(protoid, data)if mMsgCallBacks[protoid] thenlocal callBack = mMsgCallBacks[protoid]; -- mMsgCallBacks是什么在上文注册中已讲明local msg = callBack.proto();--proto()拿到消息体,此时msg是个空消息体if msg == nil thenGameLog.LogModuleError(NetModule.LOG_TAG,"Failed to OnRecvMsg, cannot find proto for protoid= %d",protoid);endlocal flag, errorMsg = xpcall(msg.ParseFromString, traceback, msg, data);--调用了ParseFromString后,上文的msg不再是空消息体啦,服务器数据已经序列化进去了if flag then--没有错误if callBack.handler then--xpcall调用回调函数,并把消息体当做参数传入该函数,该msg就是服务发过来的messagelocal flag, errorMsg = xpcall(callBack.handler, traceback, msg);if not flag thenOnRecvFail(tid, errorMsg, msg);endelseOnRecvFail(tid, "handler is nil", msg);endendend
end
下面是UI业务层随便一个小例子
-- 下面是UI层业务接受proto消息体的回调函数,msg就是proto里的message
function OnGetItemData(msg)local id = msg.idlocal name = msg.name--数据拿到,下面开始刷UI啦
end
本篇文章到此,lua版pb的使用教程已经完成啦。但是!!!还没有说明protoid的生成规则以及pb文件的批处理require
[protoid]批处理生成
protoid的枚举值不要自己生成,这个前文有提到过
先给大家展示一下我们项目的proto,这么多proto如果要手动添加一个唯一的id显然是不可能的。
[生成protoid的本质]
朋友们!生成protoid的本质是什么,其实就是在message里加一个枚举,这样的好处是,当我们拿到这个消息体时,直接通过 TestMessage.protoid就可以拿到这个枚举值。建议大家再回到上面的文章看一下消息回调注册的地方,加深理解。
如何在原有proto的基础上,加上一个枚举?这就很简单啦,直接用批处理工具写入这句话就好
下面就开始介绍如何批处理写入枚举值,我个人首选python写批处理,真的好用,python工程怎么创建以及环境部署看我的另一篇文章:
https://blog.csdn.net/liuyongjie1992/article/details/112378661
顺便安利一下这个系列的文章,Python版本的打表工具
下面开始讲生成protoid的核心思路:
1:哪些message不需要生成protoid?经常用proto的人都应该知道,很多被引用的message是用来做数据包体,而不是消息包体,我们批处理工具里自己定义了一些头标识,当这个message名前两个字母带着两个大写字母且匹配,那么就给改message生成protoid
msgtype = {'CS':1,'SC':1,'SS':1,'SD':1,'DS':1,'CF':1,'FC':1,'SF':1,'FF':1,'RR':1,'RC':1,'CO':1,'SO':1,'FO':1,'OO':1,'WS':1,'TS':1,'TT':1,'ST':1,'SW':1,'CT':1,'WC':1,'OC':1,'OS':1,'FS':1,'TF':1,'WF':1,'FT':1,'CW':1,'TC':1,
}
这是我们项目中的战斗proto为例,可以看到该message是以SC开头的,那么该message就要生成protoid,而该message内的CampData是一个引用数据体,CampData没有以我们定义的message头开始,那么该message就不会生成protoid
//[返回][游戏服]进入战斗结果
message SCEnterBattle
{optional int64 battleId = 1;repeated CampData camps = 2; // 战斗阵营数据 (仅站位和card_sid)optional int32 totalChapterNum = 3; // 回目数量optional int32 battleSid = 4; // 战役配置idrepeated FightPlayerInfo playerInfo = 5; optional int32 result = 6; // 结果 0:成功 其他:返回对应错误码optional int32 isReady = 7; // 0没准备 1已准备 (用于战前断线重连)optional int32 isFixedArray = 8; // 是否固定阵容0不是 1是optional int64 bettleBeginTime = 9; // 0 不限制, > 0 战斗开始时间
}//阵营数据
message CampData
{ optional int32 id = 1; //阵营idoptional int32 campType = 2; //阵营类型 0:玩家 1:怪物 2:裁判optional int32 energyType = 3; //鬼火类型 0:阵营 1:单体optional int32 energyValue = 4; //鬼火当前值(阵营鬼火)optional int32 energyProgressValue = 5; //鬼火进度当前值optional int32 energyRound = 6; //第几次恢复鬼火repeated UnitData units = 7; //战斗站位数据repeated UnitData cardpool = 8; //卡池中数据optional int32 speed = 9; //阵营速度
}
2:在python工程中遍历所有proto(1步骤被引用的除外),遍历每一个proto的每一行,每检测到一个message消息体,就给消息体添加一个枚举,并且protoid+1向上累积。举个例子,假设这个proto里有10个message,那么该协议内的protoid就是1-10,当遍历到下一个proto时,该proto的message内生成的枚举值就要从11开始啦。这样想是不是非常的简单。遍历到最后,每一个message都有一个唯一的protoid啦
3:步骤2的实现方法可以,但非常愚蠢,因为开发者不希望每一次都把所有proto都生成一遍,因为我正在开发一个UI功能,我只修改这一个proto,却导致我需要把所有proto的protoid都生成一遍,因此有一个简单的解决办法,就是给每个proto定义一个段位
def gengid(modulename):gids = {"Module_AiPet.proto" : 1,"Module_Bag.proto" : 2,"Module_Buff.proto" : 3,"Module_Chat.proto" : 4,"Module_Coin.proto" : 5,"Module_DaySign.proto" : 6,"Module_Faction.proto" : 7,"Module_Friend.proto" : 8,"Module_Gem.proto" : 9,}
注意:每创建一个proto的时候就要手动添加一个段,这样我们生成protoid的时候就不会扰动其他的proto里的protoid,python文件定义的段作为千位,如果是第100个proto,那么它的段位就是100000+ ***后面的个十百位可以让你的proto内定义999个消息体,很显然已经够用了。举个例子:假设是第100个proto里的第65个消息体,那么该message的protoid就是100065。到此为止,protoid的生成教程就完成啦。
[protoid]lua批处理require
批量处理require和上述批量处理protoid可以在一个python工程中连续完成,也可以分步完成, 因为他们之间没有必然联系。
#这是一个python函数,该函数会生成一个AllPB.lua文件,当客户端require该lua文件后,会require所有我们批处理生成的XXXX_pb.lua文件
#这是一个python函数,该函数会生成一个AllPB.lua文件,当客户端require该lua文件后,会require所有我们批处理生成的XXXX_pb.lua文件
def export_require(lua_path):outPath = os.path.abspath(lua_path + "/AllPB.lua")fileObj = open(outPath,"wb")fileObj.write("--this file is generated by tools, do not edit\n\n")fileObj.write("module(...,package.seeall)\n")fileObj.write("function InitModule()\n")fileObj.write(" ------------register NetMsg pb------------\n")for file in os.listdir(lua_path + "\\NetMsg"):if file[-3:] == "lua":fileObj.write("\trequire \"Logic/Proto/NetMsg/"+ file[:-4] +"\"\n")fileObj.write("end\n")fileObj.close()
分析上述代码,也不难看出该函数干了啥
--this file is generated by tools, do not editmodule(...,package.seeall)
function InitModule()------------register NetEnum pb------------------------register NetStruct pb------------------------register NetMsg pb------------require "Logic/Proto/NetMsg/Module_Achievement_pb"require "Logic/Proto/NetMsg/Module_AsyncArena_pb"require "Logic/Proto/NetMsg/Module_Award_pb"require "Logic/Proto/NetMsg/Module_Buildings_pb"require "Logic/Proto/NetMsg/Module_CenturyAdventure_pb"require "Logic/Proto/NetMsg/Module_Chat_pb"require "Logic/Proto/NetMsg/Module_ClientConfig_pb"require "Logic/Proto/NetMsg/Module_CommonInfo_pb"require "Logic/Proto/NetMsg/Module_Condition_pb"require "Logic/Proto/NetMsg/Module_Cross_pb"require "Logic/Proto/NetMsg/Module_Currencys_pb"require "Logic/Proto/NetMsg/NetStruct_Item_pb"require "Logic/Proto/NetMsg/NetStruct_Math_pb"require "Logic/Proto/NetMsg/NetStruct_Thing_pb"
end
最后展示一下AllPB.lua长这样,在项目启动的时候require该文件,则自动require所有生成的pb,是不是很nice!!!!
[完结语]
到此为止,lua版Proto生成与使用教程全部结束,并且还补充了protoid的生成方式。
生成 lua版本的proto 与使用相关推荐
- 5-(基础入门篇)学会刷Wi-Fi模块固件(刷LUA版本固件)
http://www.cnblogs.com/yangfengwu/p/9065559.html 基础教程源码链接请在淘宝介绍中下载,由于链接很容易失效,如果失效请联系卖家,谢谢 https://it ...
- 1-添加自己的Lua执行函数(ESP8266-SDK开发(lua版本))
基础 lua_pushnumber (L, 1); lua_pushnumber (L,3); lua_pushnumber (L,4); return 3; c_sprintf(temp, &quo ...
- git commit规范 、CHANGELOG生成 和版本发布的标准自动化
长期以来,大家是不是受限于这种情况:团队中每位成员提交代码时填写的信息随意,没有一定的规范,在出问题后想要定位到某次提交记录时更是难上加难,或者是加上了 commitlint之类的规范,也没有添加ch ...
- lua版本base64加密和解密
特别注意 Base64 主要不是加密,它主要的用途是把一些二进制数转成普通字符用于网络传输.由于一些二进制字符在传输协议中属于控制字符,不能直接传送需要转换一下就可以了 原理 将文件读入内存,由于读入 ...
- AndroidStudio如何打包生成realease版本的arr包,并上传到Nexus搭建的maven仓库,供项目远程依赖(二)
AndroidStudio如何打包生成realease版本的arr包,并上传到Nexus搭建的maven仓库,供项目远程依赖(二) AndroidStudio如何打包生成realease版本的arr包 ...
- 高效查表判断胡牌算法的lua版本
来源于日本论坛的一套用于麻将的判断胡牌算法,运用查表方式实现.原文链接(http://hp.vector.co.jp/authors/VA046927/mjscore/mjalgorism.html) ...
- 【Groovy】xml 序列化 ( 使用 StreamingMarkupBuilder 生成 xml 数据 | mkp.xmlDeclaration() 生成 xml 版本数据 )
文章目录 一.使用 StreamingMarkupBuilder 生成 xml 数据 二.mkp.xmlDeclaration() 生成 xml 版本数据 三.完整代码示例 一.使用 Streamin ...
- Beyond compare 生成word版本的对比报告
Beyond compare 生成word版本的对比报告 亲测两个c文件的对比报告可按此方法生成 下以两个txt文本文件为例 共两种方法,此为第一种, 1.按下图中序号依次动作 2.按下图中序号依次动 ...
- Cmake生成debug版本和release版本
在Visual Studio中我们可以生成debug版本和release版本的程序,使用Cmake我们也可以达到同样的效果.debug版本的项目生成的可执行文件需要有调试信息并且不需要进行优化,而re ...
最新文章
- java中数组的含义_数组
- Transformer 会接管人工智能?
- m.soudashi.cn 地图_SEO人员怎样挖掘大量关键词库
- 《秋暮登北楼》王武陵
- 【Kubernetes系列】Kubernetes组件介绍
- Python基本语法[二],python入门到精通[四] (转)
- 今天你写控件了吗?----ASP.net控件开发系列之(一)开篇
- Oracle Statspack分析报告详解(一)
- left join 一对多只取一条_Python爬虫教程:验证码的爬取和识别详解
- MegaRAID Storage Manager RAID管理工具实用教程
- Java中List,ArrayList、Vector,map,HashTable,HashMap区别用法
- ATF(ARM Trusted firmware)完成启动流程
- Python实现数据技术|爬虫便可获取免费百度文库付费文档
- 伍斯特理工学院计算机科学硕士,美国伍斯特理工学院数据科学硕士录取
- Android 自定义Drawable实现圆角矩形图片和圆形图片
- 阅读笔记:利用Python进行数据分析第2版——第8章 数据规整:聚合、合并和重塑
- Linux 服务具体解释
- iOS根据生日判断星座
- m8 windows android,M8刷M9 Android ROM完全教程
- Dubbo的介绍以及Dubbox的区别