完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
标准 Lua 与 C 交互要走繁琐的 C API:管理栈、压参数、注册函数。LuaJIT 的 FFI(Foreign Function Interface)把这一整套跳过了——在 Lua 里直接声明 C 的函数签名和数据结构,然后像调用普通 Lua 函数一样调用 C 库。参数自动转换、结构体直接当 table 用,性能还远高于传统 C API 路径。
local ffi = require("ffi")
ffi.cdef[[
typedef struct { int x; int y; } Point;
int printf(const char *fmt, ...);
uint32_t crc32(uint32_t crc, const uint8_t *buf, size_t len);
]]
ffi.C.printf("hello %s, %d\n", "ffi", 42)
ffi.cdef 声明"有哪些 C 函数和类型",ffi.load("zlib") 加载动态库后用 zlib.crc32(...) 调用;系统标准库甚至不需要 load,直接 ffi.C 就能用。C 结构体在 Lua 侧通过 ffi.new("Point") 创建,字段用点号访问,语法上和 table 几乎没有区别,但内存是连续的 C 内存——遍历一个上千元素的结构体数组,FFI 比 table 快一个数量级,这就是它被列为"性能大招"的原因。
第一,调系统能力。 取毫秒级时间戳、计算 CRC、操作文件锁,这些标准 Lua 没有的能力,FFI 一行声明直接获得,省去为小功能编译扩展的成本。
第二,热点计算下沉。 纯 Lua 做大量数值循环(寻路打分、伤害公式批量预演)慢,用 FFI 把数组放进 ffi.new("double[?]", n) 连续内存里运算,配合 LuaJIT 的 JIT 编译,热点循环常常能提速数倍。
第三,对接现成 C 库。 加解密、压缩、图片处理,开源 C 库遍地都是,写好 cdef 就能当 Lua 模块用。
FFI 依赖 LuaJIT——标准 Lua 5.1/5.4 里没有这东西,用之前先确认引擎跑的是 LuaJIT 且版本够新。其次,cdata 不受 Lua GC 的常规直觉管理:ffi.new 分配的托管内存会自动释放,但 ffi.gc/手动 malloc 的内存要自己负责,用错就是崩溃。再次,JIT 编译器遇到某些 FFI 调用会放弃优化(trace abort),把 FFI 调用放在循环里时用 jit.flush 与统计确认它真的被编译了,而不是想当然地快。
最后是兼容性:iOS 等平台禁止 JIT 时 LuaJIT 会退化到解释器模式,FFI 仍可用但性能优势打折。把 FFI 调用封装在独立模块里、上层逻辑不感知,平台差异来了才好替换。
callscriptex(actor, scriptname, arr)
调用传奇脚本命令,FFI 主要解决的是脚本命令做不到的事,边界要清楚
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| scriptname | string | 脚本接口(必填参数) |
| arr | any | 参数1~参数10(必填参数) |
callscript(actor, filename, label)
调用TXT脚本命令,老文本命令的调用方式与返回值规则不同
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| filename | string | 文件名(必填参数) |
| label | integer | 标签(必填参数) |
getconst(actor, varname)
获取常量,拿到引擎侧常量值,避免在 FFI 层重复定义
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象 |
| varname | string | 常量名称支持带尖括号和不带尖括号或$Name |
release_print(msg)
打印消息到控制台,FFI 调用出错通常直接崩,返回值检查要靠打印
| 参数 | 类型 | 说明 |
|---|---|---|
| msg | any | 打印内容 |
printusetime(play, on, off)
打印脚本总耗时(微秒),跨语言边界的开销必须实测,否则优化方向会错
| 参数 | 类型 | 说明 |
|---|---|---|
| play | object | 玩家对象 |
| on | integer | 1=开始技术2=结束计时并打印耗时 |
| off | integer | 2=结束计时 |
md5str(str)
MD5加密,动态库与脚本的配对校验
| 参数 | 类型 | 说明 |
|---|---|---|
| str | string | “需要加密的文本 |