跳到主要内容

构建 — 第三方原生模块(socket / cjson 等)

约定游戏工程如何把 非 zlua 随附 的 Lua C 模块(如 lua-cjson、luasocket)接到 ZLua。 把具体第三方库 vendoring 进 com.code-philosophy.zlua。 Editor 先例:04-EMMYLUA-DEBUGGER;多版本:11-MULTI-VERSION;宿主:01-HOST-API。 使用指南:第三方原生插件


1. 目标与非目标

1.1 目标

约定
业务 API统一 require("modname"),与实现形态无关
Editor动态库 + package.cpath + requireluaopen_*
Player (Il2Cpp)静态链接进同一产物 + luaL_requiref / package.preload
纯 Lua仅经 moduleLoader / 自定义 searcher,无原生
ABI与 Settings luaVersionId 同一系列

1.2 非目标

态度
随包分发 socket / cjson 二进制或源码不做(许可、体积、版本矩阵)
Player 上依赖 dlopen + cpath不推荐(iOS / WebGL;Android 成本高)
改变 C#↔Lua 互操作语义不做
替代 moduleLoader 加载 .lua不做;原生与源码路径正交

1.3 现状(实现边界)

能力行为
luaL_openlibs仅标准库(Mono LuaEnv / Il2Cpp LuaEnv::RegisterLibs
moduleLoader返回模块源码(string / byte[]),不加载 .dll/.so
EmmyLua emmy_core包内唯一正式 C 模块集成先例
公共 OpenLib / RegisterNativeModule API当前无;产品化钩子见 §6

2. 形态对照

形态宿主加载
A 纯 LuaEditor + PlayermoduleLoader(name)load
B 原生动态仅 Editor Monopackage.cpathrequireluaopen_*
C 原生静态Il2Cpp Player链接 luaopen_*luaL_requiref / preload

luaVersionId 后:所有原生插件必须 按新系列重编 并切换目录。


3. Editor(形态 B)

3.1 布局

系列(与 Plugins/lua/<series>/Plugins/emmylua/<series>/ 同一逻辑名)分目录,例如:

<project>/zlua-native-modules/
lua55/<os>/cjson.<ext>
lua55/<os>/socket_core.<ext>
luajit21/<os>/...

<ext>:Windows dll,macOS dylib,Linux Editor so

3.2 PluginImporter

与 EmmyLua 相同:所有平台 enabled: 0。 理由:模块由 Lua require/loadlib 加载;Unity 先 LoadLibraryrequire 属于未定义/双载。

3.3 注入

LuaAppDomain.Initialize 完成且标准库已打开之后:

  1. 解析当前系列与 OS,得到绝对目录 absDir
  2. package.cpath 尚无该目录项,则追加 absDir/?.ext
  3. 按需 require 或仅依赖业务首次 require
  4. 复合库(luasocket):先保证 socket.core 可被 C 加载,纯 Lua 外壳走 moduleLoader

实现可参考包内 EmmyLuaDebugger.BuildInitChunk(防重复 append、缺目录只记日志等策略由工程自定)。

3.4 链接

  • 插件须 动态链接 到同系列 Editor Lua DLL(如 lua55.dll),符号与调用约定一致。
  • 禁止在插件内再静态嵌入一份 Lua 解释器。

4. Il2Cpp Player(形态 C)

4.1 纳入链接

任选其一(工程自管构建):

  • 将插件 .c/.cpp 编入与 libil2cpp(含 libil2cpp/lua)相同的编译单元;或
  • 提供平台静态库(.a / .lib),在 iOS / Android / Windows Il2Cpp 链接阶段并入;iOS 导出对 Lua 为 __Internal 可见。

须与 Install 选定的 Lua 小版本 / Define(ZLUA_LUA_5_x / ZLUA_USE_LUAJIT 等)一致。

4.2 注册时机

luaL_openlibs 之后、业务脚本执行 之前(概念上紧接 LuaEnv::RegisterLibs 尾部):

luaL_requiref(L, "cjson", luaopen_cjson, 1);
lua_pop(L, 1);

或写入 package.preload["cjson"] = luaopen_cjson(及 luasocket 的 socket.core 等),再 require 纯 Lua 外壳。

4.3 禁止

  • 以 Player 运行时 package.cpath + 独立 .so 作为 主路径(尤其 iOS、WebGL)。
  • 假设未重新 Install / 出包 即可拾取新的 luaopen_* 符号。

5. 项目侧 Bootstrap(推荐)

NativeModuleBootstrap.Install(...)
Editor → 解析 series、拼 cpath、可选预 require
Player → 空操作(注册已在 native 完成)或断言 package.loaded

生命周期:Initialize(moduleLoader) → Bootstrap → 业务 require

纯 Lua 外壳(如官方 socket.lua)一律走同一 moduleLoader,保证 Editor/Player 路径一致。


6. 产品化钩子(预留,非当前实现)

若需减少各项目重复代码,zlua 后续增加无具体库依赖的薄扩展点:

建议
MonoluaL_openlibs 之后可选回调 / Settings 搜索路径列表
Il2CppRegisterLibs 之后弱符号或生成表(如 ZLuaNativeModules.inc)调用 luaL_requiref
SettingsluaAliasXmlPaths 类似的路径约定字段(可选)

仍不默认 vendoring 任何第三方库源码或二进制。


7. 库对照(信息性)

require / open备注
lua-cjsoncjson / luaopen_cjson注意 5.3+ integer 与 JIT 分编
luasocketsocket + socket.core / luaopen_socket_core移动端阻塞与权限;可用 C# 网络栈替代
纯 Lua JSON任意模块名形态 A

8. 验收

  • 选定系列下 Editor require 目标模块成功
  • 至少一款 Player 目标同脚本成功
  • PluginImporter 均为 disabled
  • 切换 luaVersionId 后旧二进制不可用且有明确失败,而非静默 ABI 错乱
  • 插件未静态嵌入第二份 Lua

相关文档