[程序] tolua中使用protobuf3—集成lua-protobuf

[复制链接]
星空 发表于 2019-3-15 14:24:39 | 显示全部楼层 |阅读模式
lua-protobuf最近项目中需要使用lua版本的protobuf库,在Github上找到了lua-protobuf用来替换tolua中自带的pbc库,替换的原因主要有以下:$ t; ]  W/ ~  h' G5 O) M9 q; B
  • 正如lua-protobuf作者所说: pbc返回的并不是纯粹的Lua表,使用不方便。
  • 项目使用protobuf3,pbc已经几年未更新,担心不能很好的支持或不支持protobuf3
    : P5 M% q8 }+ z- h, {1 {2 n' B
但lua-protobuf实际上是一个纯C的protobuf协议实现,为了可以在tolua中使用,将tolua中pbc(pb.c),替换成lua-protobuf(pb.c和pb.h),然后编译成相应平台支持的库文件(win tolua.dll, android libtolua.so, mac tolua.bundle, ios libtolua.a)
1 O- \, c3 L/ g4 f; f' ]lua-protobuf pb.c中luaL_newlib为lua5.2版本才支持的语法,为了支持tolua的luajit(lua版本为5.1)在如下为止做了修改(如果lua版本小于5.2,则使用luaL_register方法,并且需要在C#中导入否则lua代码中不到pb模块):
: |2 u4 `& e( L$ v$ n修改为" \. o; p' C  G3 M: K- O' u- r
1 \% x0 H# q" C: [+ K
  1. LUALIB_API int luaopen_pb_io(lua_State *L)
    * u; f  L" u% G' I2 U7 Q3 u
  2. {5 o+ j+ p! F/ e0 g
  3.     // 。。。
    , t/ k7 `2 s5 k- A/ t, U' Y! m  ^3 `
  4.     if (LUA_VERSION_NUM < 502)* b5 I3 _/ E' W7 [0 e0 \& N
  5.     {
    5 j6 O1 ]2 j* A- f% J
  6.          luaL_register(L, "pb.io", libs);6 o7 P% p( L! I/ p
  7.     }- }* {* ]" e, {& N" d
  8.     else( g: M  K7 E! n! l2 F+ N6 c; p
  9.     {/ {+ n+ @: w/ m4 ?
  10.         luaL_newlib(L, libs);4 v  b2 a: g* B8 O& j  r# w
  11.     }* q2 M: E) s) m' G/ U, k- A
  12.     return 1;0 e5 `4 [% e' I
  13. }
    8 v4 `; n& O% z4 }2 T
  14. / _7 y* Z6 F' p$ J1 q) p( I+ I
  15. LUALIB_API int luaopen_pb_conv(lua_State *L)' e# R+ L6 K" x+ b7 r* g" X
  16. {
    ) q9 W7 z! S6 w& ?. U' g1 x
  17.     // 。。。
    % V0 n. G1 ^. R: H7 b
  18.     if (LUA_VERSION_NUM < 502)9 ~: H5 U4 M  X/ \' ~, x+ t$ ?1 k
  19.     {( A4 D- ]2 q0 g9 K
  20.          luaL_register(L, "pb.io", libs);
    ) W1 `0 W% {: I' Y
  21.     }
    8 k+ D4 N  r* d- y3 F, l
  22.     else
    . b6 I) p/ w5 W" R6 Y% u
  23.     {
    * z' P. [4 f% T
  24.         luaL_newlib(L, libs);
    3 n, i- R4 k8 y( p9 j1 h
  25.     }
    ! O7 D0 |% H- C& M$ g7 L
  26.     return 1;4 r7 c# J1 i+ Q: \7 p  j( m
  27. }- Z5 T$ n  T6 B$ ?
  28. 6 H9 S! M. H/ n; T0 u9 z! @
  29. LUALIB_API int luaopen_pb(lua_State *L)) O" K5 C8 u. Y% t
  30. {# g1 J  X1 a& P
  31.     // 。。。" Q3 s3 U2 M& ?; J" r7 t/ k
  32.     if (LUA_VERSION_NUM < 502)
    7 Z6 l1 Z3 x0 }0 [
  33.     {0 r# D% W* w) W* |6 s! ?, `9 o0 U
  34.          luaL_register(L, "pb", libs);
    & ~3 s% k, B& d4 e: X; P* ]. V
  35.     }; w* D3 a6 h/ X; g2 `
  36.     else
    9 o( {0 T& H9 V% s9 g
  37.     {0 G8 @5 Y1 R1 ^9 E. Y5 U6 i
  38.         luaL_newlib(L, libs);
    7 n2 x4 s; A3 E$ [4 _' g) U
  39.     }
      y" v. x. K5 r2 n% Y
  40.     return 1;
    1 o( x8 ^' Z, o
  41. }
复制代码
自编译tolua如何编译请参考
: W, M, [3 ?' r% ?4 F以下为上面文章补充:
$ N5 G' Y' h: X* L0 A& W' W2 yandroid如何编译各平台使用的库-以编译tolua为例中提到的
0 N+ o$ _# U3 B* }
  将ndk路径添加到mingw-32的环境变量PATH中
9 l% H; D. i4 _. x1 Y8 R( p; o/ T# n  N
可以打开mingw32.ini中MSYS2_PATH_TYPE=inherit的注释,表示使用windows系统配置的环境变量,这样将ndk路径配置到window环境变量中即可正常使用。
6 k/ h' w3 c* t. I) R4 j
5 ?% t# g" b6 ?- O+ Xmingw-32.in完整配置i如下所示:7 p2 }5 R( F+ D, E1 I
  1. MSYS2_PATH_TYPE=inherit
    * ~, J* E( L: F# o8 ^
  2. MSYSTEM=MINGW32
    5 e+ h. m' _/ m, r& }$ Q
复制代码
mac&ios
  • 在mac终端导航到tolua根目录中,直接在终端执行./build_osx.sh和./build_ios.sh即可
  • 如果执行.sh提示权限不足 可以在终端执行:chmod 777  tolua根目录 然后再次执行.sh脚本即可
  • 如果执行build_osx.sh时提示不在支持32位,请打开macnojit 在build Setting移除i386的支持即可
    * S: V7 }+ L5 L$ x  ^
完成编译之后将tolua根目录中Plugins中对于的文件拷贝到Unity Plugins文件中进行替换,并且将lua_protobuf附带的protoc.lua(位于lua-protobuf文件夹)拷贝到工程中。: g" ?" p" `8 _; l. H1 t4 I
导入lua-protobuf2 P4 o# T2 v% ^( H8 h! N
在tolua C#部分 LuaDLL.cs中添加如下代码:  I7 O' X2 q. X9 l% H3 S
, ?0 j* d' U# R/ m& ^3 |
  1. [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    . W9 V8 ?2 L. v* Y
  2. public static extern int luaopen_pb(IntPtr L);
    " \. U& {3 K9 m' |& {" V# C4 i. \
  3. ' l4 l9 }' G' P# F' V; V! z
  4. [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    9 e& G) `+ h+ x5 ]# D& A4 r6 \
  5. public static extern int luaopen_pb_io(IntPtr L);  P. a: ^6 O2 g. x# E) V( v$ J

  6. 8 L3 l; e# A) |0 e
  7. [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
    ( v$ `8 s: {5 V1 d% k
  8. public static extern int luaopen_pb_conv(IntPtr L);
    % ]# o' ^3 x  f; [* ]  L+ z
  9. ' U0 F) _, e: A4 X" e' {0 }
  10. [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]3 t! W  d+ E2 f) l
  11. public static extern int luaopen_pb_buffer(IntPtr L);
    2 O. y; s5 I2 B' E7 s: D% i; O
  12. - ]% C& k: J! B' G4 z7 F5 |
  13. [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]6 ?" |8 l9 o: @$ d
  14. public static extern int luaopen_pb_slice(IntPtr L);2 |. j8 h& [8 ~
复制代码
并且在启动lua时,将上述模块导入即可:
2 e4 U# i  {$ u" r( ]
  1. void OpenLibs()
    ) P7 b1 v& c0 V- R, `* C0 K' R% R
  2. {0 c4 _2 x. v* d6 [" h  l
  3.        lua.OpenLibs(LuaDLL.luaopen_pb_io);2 `" X/ w" g) Q/ f# S9 ~9 Q* }
  4.        lua.OpenLibs(LuaDLL.luaopen_pb_conv);5 g4 F8 A. |9 f6 n' k. A
  5.        lua.OpenLibs(LuaDLL.luaopen_pb_buffer);
    9 h2 Z) n5 n9 f' V: W; q
  6.        lua.OpenLibs(LuaDLL.luaopen_pb_slice);4 C) w5 r' z6 G2 b
  7.        lua.OpenLibs(LuaDLL.luaopen_pb);# o1 A  p5 h* R% J

  8. 2 P  X# t2 @9 h) v- q
  9.        //其他的库
    % \. b( P0 ^5 ?' c+ j& a, s
  10. }
    & I# A8 f9 [% D- H& @
复制代码
验证集成首先需要验证是否集成成功:  `* b" ?* C( C6 W+ K

( M& q% g! s7 U1 h9 I1 }2 ^8 N使用lua-protobuf作者提供的例子并且修改为proto3协议:
* V# a2 D6 z4 S& n7 y5 ]+ \  X: |& U: a
  1. local pb = require "pb"
    8 L- H& \- f$ g, k) d7 A3 q+ X
  2. local protoc = require "3rd/lua-protobuf/protoc"
    - N5 i0 e6 v5 h* s9 l; c& X

  3. % Y* H' e( F. s' _& L% \# ?
  4. -- load schema from text
    * H3 Y. A! j. @  I4 ?
  5. assert(protoc:load [[
    / x- b  D: W* O1 S/ R# |* m4 C
  6.     syntax = "proto3";
    ; b$ n% s3 y! n% L, x' t
  7. 6 j  ]9 @/ h) C4 x; Q/ y+ F
  8.     message Phone {- N7 X, H7 f4 @! Z
  9.         string name = 1;. j' v; @( O9 [3 m# v, I/ q* @# s
  10.         int64 phonenumber = 2;
    # Z5 ?  b. ^% o  O/ ^/ H
  11.     }
    2 m# Z" H+ e0 t7 o* c4 ~3 F
  12.     message Person {, U$ H- L5 c5 J* K5 F/ a: |
  13.         string name = 1;
    : w1 q4 r0 G6 ^. X
  14.         int32 age = 2;4 h/ ]0 G( J. q* t* D, E: C
  15.         string address = 3;; `, {; x0 a. N
  16.         repeated Phone contacts = 4;) L4 N' j* D. j) s
  17.     } ]])
    ! Q" E, _1 ~" i: s9 i$ c$ N6 D* d
  18. " \- q$ J- Q" ^* p
  19. -- lua table data
    4 R+ {4 X& O# Q$ w- q% W
  20. local data = {; W; P- k3 [0 {2 n. g
  21.     name = "ilse",+ [7 c) q5 r' H" a5 s0 K7 \
  22.     age = 18,  `% q0 n/ ~% o6 z/ R/ c% W. O
  23.     contacts = {
    ) V& L) Y* {9 v0 ^" |" M. ?
  24.         { name = "alice", phonenumber = 12312341234 },0 N* p  O# g7 m1 N- _' L
  25.         { name = "bob", phonenumber = 45645674567 }
    ( z. g+ L! B2 U/ o1 }8 [
  26.     }( P) e4 U" ?! Q5 b) m4 f
  27. }' A& S# t) A/ [5 w
  28. & s) Q1 [+ {; u- {6 Z
  29. -- encode lua table data into binary format in lua string and return
    . T6 \6 r6 n  A. P# E) D) R
  30. local bytes = assert(pb.encode("Person", data))
    / @% S; r8 S) J2 v( v
  31. print(pb.tohex(bytes))* l2 |7 L" l$ N+ g- T5 S
  32. -- and decode the binary data back into lua table8 s" R, i) _. ~; M  y( O7 @0 T
  33. local data2 = assert(pb.decode("Person", bytes))( O& W6 f: z2 v& V7 G. c& d
  34. print(require "3rd/lua-protobuf/serpent".block(data2))
    / _5 m5 |# a4 H  U9 g$ k; F
  35. 8 O: y7 Z1 |2 s7 d* O
复制代码
如果可以正常打印(最好在移动平台上也进行测试),则表示集成成功。  ]# g- {! X3 g; D6 Y$ C
lua-protobuf基本用法首先可以使用上面的测试例子的用法:直接使用lua中字符串加载协议,优点为不用使用其额外的文件,缺点在与protobuf协议为服务端和客户端共同使用,修改不方便。/ N3 k* _  n. I$ V0 ^5 X* c3 B# E
lua-protobuf 使用说明中作者展示另一种使用方式,动态加载生成的二进制pb文件。小编推荐这种用法,并且在当前开发项目中进行了进一步的实践。
  f! ~" Z1 z1 q2 A9 w4 k以下为具体的做法:
: b% @2 J, J' Y% Q) E0 W9 n首先在protoc下载相应的版本protoc.exe版本,并且配置到环境变量中。7 `+ t) |& C+ L4 F0 }- E/ _& U
然后新建用于处理proto文件的目录:3 N5 ]9 d9 p: s5 m
目录结构如下:8 y( L7 g* V; f8 \: F' W

& O7 ?8 d3 g9 Q( F %E6%89%A7%E8%A1%8Cbat%E5%89%8D-300x71.jpg
5 ]. b5 K1 h* a, G- H, {执行bat之后目录如下:
3 _2 z- l7 M2 m+ n
, o' l1 N2 h* U8 t2 ^3 r0 c3 z* z" S %E6%89%A7%E8%A1%8Cbat%E4%B9%8B%E5%90%8E-300x97.jpg
# j6 r) W5 Y. S$ k; [6 I4 H: X- VProto的目录中存放了.proto协议文件,proto2pb.bat、proto2java.bat和proto2cs.bat为生成相对于代码的批处理目录,bat文件在生成对于的文件之后,将自动拷贝到对于的工程中。/ j) w% F* f- |; {
项目中proto协议设置在目前开发的项目中对于每一个请求返回消息流程对应一个proto协议文件,协议内部嵌套Request和Response,如果为服务端推送则只需要Response,如果不需要服务端返回则只需要Request
% A1 `& D3 I4 }& J2 }5 o9 `% c: U8 t$ D3 q( m* B3 E5 r# x2 M
以下具体的协议的实例(GetTableId.proto):1 D7 |& B' ~$ u+ V
, q3 v7 a) B7 R; B* P' A
  1. syntax = "proto3";
    2 b$ G1 A% V- E. P7 g4 v
  2. package Test;
    # y) x; e  {% Z7 b

  3. : b  y) n/ D& [4 C! N
  4. option java_package = "com.project.protocol";" u& s, `: p8 }0 L
  5. option java_outer_classname = "GetGetTableIdOuter"
    # u7 @$ u$ b% R  b+ z2 \) o9 m
  6. message GetTableId {6 G0 d7 o5 U& U# {
  7.     message Request{
    6 n1 x0 h0 }" z$ u  V/ d; h
  8.         string roomId = 1;+ v) D) H9 J* f% C! _- j
  9.         int32 tableId = 2;
    % \$ E) Y) m" M: P/ G6 W& x
  10.     }
    % @% B; d) ~- O
  11.     message Response {$ u" o" G& p' G6 A. Q
  12.         string tableId = 1;2 L" g3 T8 T/ s1 R' O. [" v
  13. }
复制代码
bat文件的源代码8 q# |1 n" L9 O
proto2pb.bat代码如下:
! @) H' U; C! U4 ^/ T4 ^8 V; [0 L) `$ ], a  ~  u  D7 @9 p
  1. @echo off
    . _6 E' j$ m& ]! [
  2. cd Proto% g0 A) [+ s( `+ ]4 `2 [; S* P) V
  3. REM 生成二进制文件pb(首先删除在重新生成)
    & d, x* T! T' b, O/ f' |
  4. rd /s /q ..\pb% W6 ]6 M4 C  h9 x8 Y- O3 T9 m
  5. md ..\pb
    2 \; E. K+ S, J- D, i
  6. set pbPath=..\pb\
    ; z& U4 g1 }+ t. z  {, x
  7. set pb=.pb  C6 H+ {  w8 Q0 B; T8 `
  8. for %%i in (*.*) do (
    . f/ N% a5 u9 R: u
  9.     echo %%i, E6 e# D' q2 B% q
  10.     protoc -o %pbPath%%%i%pb% %%i
    5 Z6 a* q" P$ {3 z. d( }
  11. )
    1 Z, b2 q4 I8 G# K5 n
  12. & v2 c8 _1 r" u% \* k  W$ l  l
  13. echo pb create success
    + e- k9 |& l5 m% ]( I
  14. echo.- P% w2 o9 K! n5 `: ~
  15. 4 i! J  m1 f; j  x
  16. cd ..\pb( c. [* e1 ^/ Z" D: r/ t; n" u
  17. SetLocal EnableDelayedExpansion; D7 b% L- L: \
  18. REM 要查找的文件类型1 U" C" w! y$ t# X  \
  19. set ext=*.pb
    ! j& A* j* L: _3 K1 a2 [
  20. REM 重新命名文件
    / H7 R' @) i. x  u9 w6 ]9 e( ]
  21. for /r %%a in (!ext!) do (
    " R# y6 j' b6 T) {
  22.     REM 文件名/ n3 H5 f6 @* O7 b1 [+ i# h. ^6 d
  23.     set fn=%%~na. q& H- [1 j) V! ^1 a
  24.     REM 后缀0 Q8 ~, r* h  N5 G
  25.     set en=%%~xa
      I% S0 k" `" c/ `$ F. u' J
  26.     REM 把字符串的最后4个字符赋值给变量hou
    . a$ L, C) E, [, w4 _
  27.     set hou=!fn:~0,-6!
    ( v2 x  A2 e6 C# N; b
  28.     echo !hou!!en!
    . g% H: D6 ]2 E6 l) y/ X( v+ c
  29.     rename "%%a" "!hou!!en!"! y7 `! u) n0 I" @
  30. )* Z) o5 C, R' T
  31. echo pb rename success2 g, Y5 O: x5 U# j
  32. echo.
    # v* X% l5 [2 l8 Q- V# W; q
  33. 9 X7 f. {5 x$ @/ p9 h/ C0 L
  34. REM 项目中pb文件存放目录
    " E4 |; x0 ~. u# y  M
  35. set destPath=E:\source\client\xxx\Assets\StreamingAssets\lua\pb
    4 l, v4 M) {9 V$ }8 c- [- n
  36. " d3 q0 M1 c  L9 D
  37. rd /s /q %destPath%6 v$ x& b: T/ S0 w& J+ ]; y4 p% R
  38. md %destPath%
    2 `2 Z* V2 L- X) x% y- W
  39. 1 d9 q; p; T2 s$ {! M8 M
  40. echo copy to %destPath%
    4 d& z/ Q7 g, y. @
  41. xcopy ..\pb %destPath% /s /e
    , _6 {  A" C) Z3 h
  42. pause" R% R$ b6 Y1 P* e# j' D" n
  43. , {) {' _4 k0 v; a2 A# ~
复制代码
proto2java.bat
2 ?  w0 m, G( F: j3 }' @8 n2 e$ f) z
  H( h; ?# d1 [! [0 c4 [
  1. @echo off
    , P4 U& k5 h- n6 n
  2. 3 t& Q/ I" H( G% f: J' Q
  3. cd Proto" i( a; m) |& J8 J0 d0 C8 |7 p3 k2 a
  4. REM 生成java8 V3 k6 d$ {4 `) j5 ?, G

  5. & W2 z5 l# o0 M& u/ S5 U
  6. rd /s /q ..\Java- h$ W# z0 i& j( J; R. O
  7. md ..\Java  n5 ?# X0 S( I5 @: G' s
  8. # w7 `$ ^# c6 ~* D3 T# W
  9. set dest_path="..\Java"
    1 ^4 Z8 ^9 @* |2 t0 o
  10. for %%i in (*.*) do (7 F# E; q/ b# t' g& t4 `
  11.     echo %%i6 ]" w2 i( p& R  s4 |6 p% o6 P: [
  12.     protoc --java_out=%dest_path% %%i- g- _" j) l* B" p& |
  13. )
    ! c% C' q; x% v; }
  14. + |! y5 J' }+ `. f
  15. echo java success' W. s: q; e& ?: |- m; |

  16. 5 _# }- R, n1 ~' ?
  17. REM 拷贝文件3 f2 F: O' h" q0 T% X
  18. REM echo.$ M2 i  n) l! e3 n. _. [9 }) Z& F: }
  19. 2 V1 |+ i& s/ R4 \9 U
  20. set destPath=java代码目标目录9 Q% f; P+ T5 Z) \
  21. rd /s /q %destPath%+ O8 f. u/ k. D: n

  22. ' J0 d6 U6 \. S6 r0 T$ T5 g) w
  23. md %destPath%
    # `: |  R* d3 e5 H* H9 x
  24. echo copy to %destPath%
    1 g% X! C! y; ?2 m+ K
  25. xcopy ..\pb %destPath% /s /e1 f& w; U' h3 K. Z, H- }
  26. pause
    : W( w9 z5 i6 J( K0 C
  27. & `/ m# n. w3 ?7 f) G( F- X+ @
复制代码
proto2cs.bat
  ]- a0 n9 z6 X1 M& H( L) D; ]8 ~( A+ R8 i! R4 B* z
  1. @echo off
    . _% w( \: x) v# I' c: I" `

  2. ' H- k# K/ g3 J0 G
  3. cd Proto  b& r' j6 q# K% z6 t
  4. REM 生成java
    3 q( b6 c# w# n7 F7 ]4 I& [

  5. - O" q& r0 J" l3 I% w
  6. rd /s /q ..\Java
    5 M0 ]- d! g  L* c+ g  }
  7. md ..\Java
    1 l2 Z5 v- i) k3 U8 J6 S9 N( g
  8. ! k! k/ U9 D; G6 R5 G+ U
  9. set dest_path="..\Java"
    : {& W2 S5 R, l6 D" A, [! Z, c
  10. for %%i in (*.*) do (3 s9 d. \7 y# r+ W1 _+ o/ J
  11.     echo %%i! w4 i. c; t# e7 L6 Y
  12.     protoc --csharp_out=%dest_path% %%i. i' M' H' i2 Z. o
  13. )
    ) Z: \3 A  m$ G1 d3 M/ Q7 k# G

  14. - p- j& g. A( X8 N9 [2 [
  15. echo java success" v. \+ R' ?' {8 n4 f; Y

  16. 9 U$ p1 h1 Y  a/ K6 h
  17. REM 拷贝文件
    9 j5 b$ B% q8 Y: G. d
  18. REM echo.
    * V0 g+ y, w2 t1 y

  19. 5 X! `1 Q* c* i2 T: X
  20. set destPath=java代码目标目录
    " K2 z& A5 n; x: {3 Z
  21. rd /s /q %destPath%0 {+ Q* J  n& C
  22. 2 A. `3 y+ G/ S5 z  t
  23. md %destPath%1 \* o# m0 N( |# R
  24. echo copy to %destPath%
    $ e0 h) T8 {, B4 w
  25. xcopy ..\pb %destPath% /s /e
    ; a- @  l" f6 @
  26. pause; Q- H; i2 z( x) T' ]* K
  27. : W: h- L: {1 y: V
复制代码

; u; a8 C* X( s* n
" Z, B; A' P2 K  J& @, e0 h4 u; W
哎...今天够累的,签到来了游戏源码下载...
回复

使用道具 举报

16422756 发表于 2019-4-19 18:11:04 | 显示全部楼层
楼主我把lua-protobuf集成到tolua后,使用protoc:load [[string]]是可以的,但是使用assert(pb.loadfile "addressbook.pb")提示Main.lua:20: addressbook.pb: No such file or directory
/ C$ ~. f( H' `5 x& X' u
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则