Xlua官方教程里有,在创建lua虚拟机时,可以添加3个处理数据的库
_luaEnv = new LuaEnv();
_luaEnv.AddLoader(CustomLoaderMethod);
_luaEnv.AddBuildin("rapidjson", XLua.LuaDLL.Lua.LoadRapidJson);
_luaEnv.AddBuildin("lpeg", XLua.LuaDLL.Lua.LoadLpeg);
_luaEnv.AddBuildin("pb", XLua.LuaDLL.Lua.LoadLuaProfobuf);
Json和ProtoBuffer是我们开发中常见的工具,还有一个不是很常见的lpeg,找了几篇文章,大家自行跳转
https://www.jianshu.com/p/e8e1c5abfdbbhttps://www.jianshu.com/p/e8e1c5abfdbbLPEG库简介_suzuiyue的博客-CSDN博客_lpegLPEG库简介LPEG是一个供lua使用的基于 Parsing Expression Grammars 的模式匹配库,这篇文章只是讲其如何使用,并不涉及底层如何实现。LPEG 的函数主要分为三类,第一类是创建Pattern的构造函数,第二类是 Capture 函数, 第三类则是 match 等函数。 Capture 就是指一个Pattern,当前匹配时会产生某些捕获的值。Match 等函数lpeg.https://blog.csdn.net/suzuiyue/article/details/52905372
在lua当中如何使用这三个库呢,下面挨个介绍
1:Json的使用
引用rapidjson有几种方式
--调用方式1,声明一个全局变量RapidJson,代码执行后,可以在其他.lua文件中直接调用RapidJson 而不需要再次require('rapidjson')
RapidJson = require('rapidjson')--调用方式2,脚本内局部使用
local rapidJson = require('rapidjson')--调用方式3,方法内局部使用
function XXX()local rapidJson = require('rapidjson')
end
rapidjson具体使用,在调用encode和decode方法前,要先require('rapidjson')
--反序列化
local t = rapidjson.decode('{"a":123}')
print(t.a)
t.a = 456
--序列化
local s = rapidjson.encode(t)
print('json', s)
2:ProtoBuffer的使用
pb的库在C#层已经添加,到了lua中可以直接调用,使用pb的前提肯定得让lua层的pb加载协议,协议才是序列化、反序列化的前提。
提示:Xlua的PB库支持proto2和proto3,需要在协议里标明是2还是3
xlua中有两种加载proto的方式,下面分别介绍
第一种加载proto方式:常规加载脚本文件,然后传递给pb加载库
以proto2版本的协议举例,这是一个常见的请求进入战斗协议,命名为Fight.proto文件,里面还引用了NetStruct_Fight.proto文件
syntax="proto2";
import "NetStruct_Fight.proto";message CSEnterBattle
{optional int32 battleSid = 1; // 战役配置idoptional int32 isFixedArray = 2; // 是否固定阵容0不是 1是
}//[返回][游戏服]进入战斗结果
message SCEnterBattle
{optional int64 battleId = 1; //服务器战斗序号repeated CampData camps = 2; // 战斗阵营数据 (仅站位和card_sid)
}
local pcNew = nilfunction StartLoadProto()--protoc是一个lua文件,一般是lua库里带着的此处需要引用一下protoc = require "Framework/protoc"pcNew= protoc.new()--由于我的Fight.proto里引用了另一个proto,因此先加载引用LoadProto("NetStruct_Fight")--加载完引用后,再加载proto才不会报错LoadProto("Fight")
endfunction LoadProto(fileName)--C#资源加载器local AssetsData = CS.MoAssetText()AssetsData:Load("Battle/Proto/"..fileName..".txt")--不管你怎么加载,一定是从Unity里把协议文本取出来local protoString = AssetsData:GetText()--这一句是核心语句,协议文本加载入pb库,现在pb就知道该如何解析bytes数据啦assert(pcNew:load(protoString,fileName..".proto"))end
第一种加载文本的方式可以根据自己的框架来写,我这里是在lua中生成一个C#文本加载器,然后从C#层加载文件。
下面开始介绍第二种加载proto方式
assert(ProtocLib:load [[syntax = "proto3";message CSEnterBattle{int32 battleSid = 1; // 战役配置idint32 isFixedArray = 2; // 是否固定阵容0不是 1是}//[返回][游戏服]进入战斗结果message SCEnterBattle{int64 battleId = 1; //服务器战斗序号CampData camps = 2; // 战斗阵营数据 }//阵营数据message CampData{ int32 id = 1; //阵营idint32 UnitId = 2; //阵营类型}]]
)
仔细看上面代码你会发现,其实就是把proto协议里的内容全部都写到一个lua脚本里去,这个lua脚本包括了所有引用和非引用的proto数据结构,我这里的文件叫protoDefine.lua.txt文件,我们可以写一个批处理工具,把协议全都按行写入到一个文件中。如果你的协议非常多,那么会生成一个几千行甚至几万行的lua文件。项目启动的时候,在合适的位置调用 require "Net/ProtoDefine"加载这个脚本即可把协议加载到库。
上面介绍了两种加载proto协议的方式
加载完proto协议,我们当然要使用它呀,也就是序列化和反序列化数据
function UsePB()--第一步,加载bytes,请注意,EnterBattleMsg是我本地已经序列化出来的二进制数据文件测试用--将来肯定是从服务器获取二进制数据local bytes = LoadBytes("EnterBattleMsg")--反序列化,使用proto库里的SCEnterBattle结构体序列化该bytes数据local SCEnterBattleTab = assert(pb.decode('SCEnterBattle', bytes))--请注意,反序列化后得到SCEnterBattleTab,它是个table--获取数据如下print("战役Id: ".. SCEnterBattleTab.battleId)--序列化语句local bytes = pb.encode('SCEnterBattle', SCEnterBattleTab)--此时我们又得到了一个最初的bytes二进制数据print(pb.tohex(bytes))--打印数据
endfunction LoadBytes(fileName)--C#资源加载器local AssetsData = CS.MoAssetText()--同步加载文件AssetsData:Load("Battle/Proto/"..fileName..".bytes")--不管你怎么加载,一定是从Unity里把二进制数据读出来return AssetsData:GetBytes()end
3:lpeg的使用
lpeg的功能非常多,建议直接去看上面分享的链接,详细看一下使用
local lpeg = require "lpeg"local match = lpeg.match
local P = lpeg.P-- value是string的情况
local ps = P'ab'
print(match(ps, "a")) ---> nil
print(match(ps, "abcd")) ---> 3 注:只匹配"ab"