3 个版本
0.1.2 | 2024年5月10日 |
---|---|
0.1.1 | 2023年10月14日 |
0.1.0 | 2023年10月13日 |
#360 在 编码
3MB
2K SLoC
包含 (Windows DLL, 2.5MB) protobuf-example/lua_protobuf_rs.dll, (DOS exe, 240KB) protobuf-example/lua.exe, (Windows DLL, 240KB) protobuf-example/lua54.dll
lua-protobuf-rs
该项目使用 Rust 的 protobuf 实现,编写的可以在 Lua 中解析 protobuf 的库
可以在运行时加载 proto 文件,将 protobuf 二进制数据解析成 Lua table、将 Lua table 编码成二进制数据以及 protobuf 在 Lua 下的反射功能
绑定的 API 可以到 这里 查看,绑定了几个比较关键的 struct, FileDescriptor
MessageDescriptor
FieldDescriptor
EnumDescriptor
,这几个结构其实就表示了 proto 的文件、消息、以及字段、枚举的信息
使用
在 Lua 中直接解析 proto
--- @type LuaProtoc
local luaProtoc = require("lua_protobuf_rs")
---@language "protobuf"
local proto = [[
syntax="proto3";
message Player{
int64 id = 1;
int64 world_id = 2;
string nickname = 3;
int32 exp = 4;
}
message LoginRequest{
int64 id = 1;
int64 world_id = 2;
}
message LoginResponse{
Player player = 1;
}
]]
local protoc = luaProtoc.parse_proto(proto)
local player = {
id = 2347239423213,
world_id = 234872389,
nickname = "mikai233",
exp = 22000,
}
local player_bytes = protoc:encode("Player", player)
local decode_player = protoc:decode("Player", player_bytes)
print(decode_player.id)
local login_response_bytes = protoc:encode("LoginResponse", {})
local decode_login_response = protoc:decode("LoginResponse", login_response_bytes)
print(decode_login_response.player.id)
在 Lua 中解析 proto 文件
- player.proto
syntax = "proto3";
package com.mikai233;
message Player{
int64 id = 1;
int64 world_id = 2;
string nickname = 3;
int32 exp = 4;
}
- login.proto
syntax = "proto3";
import "player.proto";
package com.mikai233;
message LoginRequest{
int64 id = 1;
int64 world_id = 2;
}
message LoginResponse{
Player player = 1;
}
--- @type LuaProtoc
local luaProtoc = require("lua_protobuf_rs")
local protos = luaProtoc.list_protos({ "proto" })
local protoc = luaProtoc.parse_files(protos, { "proto" })
local player = {
id = 2347239423213,
world_id = 234872389,
nickname = "mikai233",
exp = 22000,
}
local player_bytes = protoc:encode("com.mikai233.Player", player)
local decode_player = protoc:decode("com.mikai233.Player", player_bytes)
print(decode_player.id)
local login_response_bytes = protoc:encode("com.mikai233.LoginResponse", {})
local decode_login_response = protoc:decode("com.mikai233.LoginResponse", login_response_bytes)
print(decode_login_response.player.id)
反射
更多反射 API 请查看文档
--- @type LuaProtoc
local luaProtoc = require("lua_protobuf_rs")
local protos = luaProtoc.list_protos({ "proto" })
local protoc = luaProtoc.parse_files(protos, { "proto" })
local player_descriptor = protoc:message_descriptor_by_name("com.mikai233.Player")
for _, field in pairs(player_descriptor:fields()) do
print("field name: " .. field:name() .. " number: " .. field:number())
end
print("====")
local login_response_descriptor = protoc:message_descriptor_by_name("com.mikai233.LoginResponse")
for _, field in pairs(login_response_descriptor:fields()) do
local rt = field:runtime_field_type()
if rt.singular then
local singular = rt.singular
print(singular.message:name())
end
end
proto 代码提示
可以使用 gen_lua
来生成 Lua 的 proto 模板文件,以获得更好的代码编写体验,本项目是基于 EmmyLua 插件做的注解提示
---@class LoginRequest
---@field id number
---@field world_id number
local LoginRequest
---@class LoginResponse
---@field player Player
local LoginResponse
xLua 插件集成
设置环境变量 LUA_LIB_NAME
LUA_LIB
为 xlua 的头文件目录以及 xlua 的库名,然后重新编译此项目即可 注意 xlua 的版本一定要和此项目的 lua 版本要对应
[DllImport("lua_protobuf_rs", CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_lua_protobuf_rs(System.IntPtr L);
[MonoPInvokeCallback(typeof(LuaDLL.lua_CSFunction))]
public static int LoadProtobufRs(System.IntPtr L)
{
return luaopen_lua_protobuf_rs(L);
}
编译
得益于 Cargo,编译变得非常简单,只需要安装 Rust,然后执行 cargo build --release
就可以得到当前平台的库文件
对于不同的 Lua 版本,只需要修改 Cargo.toml
中的 default
字段重新编译即可
交叉编译
如果需要编译到不同平台,可以使用 交叉编译,需要 Docker,操作难度很小
- 编译到 Linux:
cross build --target x86_64-unknown-linux-gnu --release
- 编译到 Android:
cross build --target armv7-linux-androideabi --release
注意事项
对于非 oneof
类型的字段,在将二进制消息解析成 table 时,如果该字段没有设置值,都会给一个默认值,而对于 oneof
类型的字段,如果都没有设置过值,那么这些字段都将不存在,也就是说 oneof
的所有字段只可能存在一个或者都不存在
依赖项
~8–21MB
~275K SLoC