跳到主要内容

01 — 宿主 API

LuaAppDomain(含 GetFunction)、[LuaMarshalAs][LuaAlias]。 C#→Lua / Lua→C# Marshal 细节见 marshal/


1. LuaAppDomain

1.1 职责

ZLua.LuaAppDomain 是宿主唯一推荐的初始化门面。Common 引用 Mono/Il2Cpp;在 Initialize / GetFunction 时按环境 反射创建 后端嵌套类型 Runtime : ILuaRuntime

环境后端宿主类型程序集创建方式
EditorLuaMonoAppDomainZLua.MonoActivator.CreateInstance(…+Runtime)
PlayerLuaIl2CppAppDomainZLua.Il2Cpp同上(#if !UNITY_EDITOR 分支)
public interface ILuaRuntime
{
void Initialize(Func<string, object> moduleLoader);
void Reset(Func<string, object> moduleLoader);
void ProcessPendingRefReleases();
Delegate GetFunction(Type delegateType, string luaModule, string luaMethodName);
}

public static class LuaAppDomain
{
public static void Initialize(Func<string, object> moduleLoader);
public static void Reset(Func<string, object> moduleLoader);

public static T GetFunction<T>(string luaModule, string luaMethodName)
where T : MulticastDelegate;

internal static void ProcessPendingRefReleases(); // 由 LuaFramePump 驱动
}

使用 RuntimeInitializeOnLoadMethod / SetRuntime 做隐式注册;首次 Initialize(或 GetFunction)时解析后端。 宿主面 暴露 Shutdown;整域拆掉再重建只走 Reset

1.2 整域重置(Reset

用于热更或清空脚本世界:拆掉当前唯一主 lua_State,再以给定 loader 重建。

API行为
Reset(loader)仅预约:保存 loader,在本帧 EndOfFrameLuaFramePump / WaitForEndOfFrame)才真正执行 teardown + 重建。多次预约以最后一次 loader 为准。真正执行时:排空 pending ref → 关闭 Registry / 模块缓存 → lua_close → 新建 lua_State 并安装 loader。Il2Cpp 进程级 Bridge / XML 表 / InternalCall 保留
Initialize(loader)仅首次(或进程内尚无主 state)创建 lua_State 并安装 loader。已初始化时再次调用 → 抛异常(须 Reset再支持「只换 loader」)。

契约:

  1. Reset 调用当下不拆 state;EndOfFrame 应用之后,对 GetFunction 委托的调用 → 抛 C# 异常。
  2. 旧委托 一律作废;宿主须在 Reset 生效后重新 GetFunction 并丢弃字段里缓存的 Action / Func
  3. 因真正 teardown 在 EndOfFrame,允许在 C#↔Lua 调用中途调用 Reset(只排队);勿在同一帧 EndOfFrame 之后仍使用旧委托。
  4. Editor Emmy:teardown 随 lua_close 停止调试器;重建后按 Settings 决定是否重启。

详见 10-LIFETIME.md §7。

1.3 模块加载器

moduleLoader(moduleName) 由宿主提供,返回 Lua 模块源码(通常为 string)。native 通过 __zlua_load_module 与 package.searchers 集成。

约定:

  • 模块名与 GetFunctionluaModule 字符串一致
  • loader 失败应抛出明确异常,避免 silent nil

1.4 帧泵

LuaAppDomain.Initialize 注册 LuaFramePumpLateUpdate 排空 pending ref;WaitForEndOfFrame 执行已预约的 Reset。详见 10-LIFETIME.md

1.5 Editor 调试器(可选)

Editor Mono:若 Settings enableDebugger 为 true,LuaMonoAppDomain.Initialize 在现有初始化完成之后调用 LuaEnv.StartDebugger(注入 emmy_coretcpListen、可选 waitIDE)。规范见 build/04-EMMYLUA-DEBUGGER.md改变 GetFunction / Marshal 语义;Il2Cpp Player 适用本入口。


2. GetFunction — C# 调用 Lua

C#→Lua 的 唯一正式入口:按模块名与方法名取得绑定好的 Delegate,再由调用方 Invoke(或直接调用)。

2.1 签名

public static T GetFunction<T>(string luaModule, string luaMethodName)
where T : MulticastDelegate;
参数说明
luaModule非空;交给 moduleLoader / require 的模块名
luaMethodName非空;模块 return { ... } 表中的键名
T具体委托类型(如 ActionAction<float>Func<int,int,int>

2.2 行为

  1. luaModule 加载(或命中已加载)模块表
  2. module[luaMethodName],须为 Lua function
  3. T 的签名将 function Marshal 为 closed delegate(规则同 marshal/09-FUNCTION.md
  4. 返回该 T 实例

缓存: API 不保证跨调用复用同一 delegate 实例;热路径由调用方自行保存(字段 / 局部变量)。须在 Initialize 之后再调用(例如 Awake);放在与 RuntimeInitializeOnLoadMethod 同类型的 static 字段初始化器中。Reset 生效后旧委托一律作废,须重新 GetFunction

2.3 示例

// 一次性 / 启动期取得
var add = LuaAppDomain.GetFunction<Func<int, int, int>>("app", "add");
int sum = add(10, 20);

var onTick = LuaAppDomain.GetFunction<Action<float>>("game", "OnTick");
onTick(0.016f);
-- app.lua
local function add(a, b) return a + b end
return { add = add }

2.4 错误

条件行为
Initialize / loader 未配置抛 C# 异常
模块加载失败 / 键不存在 / 非 function抛 C# 异常(含可诊断信息)
T 无法从该 function 绑定(签名不兼容等)抛 C# 异常

2.5 调用与 Marshal

对返回的 delegate 执行 Invoke 时:

2.6 流程(概念)

GetFunction<T>(module, method)
→ require / 取模块表
→ 取 Lua function
→ Marshal 为 T
→ 返回 T

此后:T.Invoke(...)
→ marshal 参数(含 ref → OpaqueValue)
→ lua_pcall
→ marshal 返回值 / ref 写回
→ 异常边界转换(§6)

Il2Cpp C# 层初始化仍为薄壳(与 GetFunction 无关):

public static class LuaIl2CppAppDomain
{
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void InitializeInternal(Func<string, object> moduleLoader);

public static void Initialize(Func<string, object> moduleLoader)
=> InitializeInternal(moduleLoader);
}

3. [LuaMarshalAs] — Marshal 标注

3.1 作用范围

可标注位置说明
参数控制 Lua↔C# 该形参的 Push/Pop
返回值控制 C#→Lua 返回 Push
字段 / 属性控制成员读写时的 marshal(codegen 消费)

禁止标注在 方法 上(绑定期 LuaMarshalAsConfigurationException)。

完整选项见 marshal/02-MARSHAL-AS.md

3.2 常用选项(概要)

LuaMarshalType用途
Default按类型默认规则
OpaqueValueC#→Lua 强制 opaque lightuserdata(by-val 引用类型 / struct)
Table / UnpackedValuesstruct / closed 泛型 structTable 另允许 Nullable<struct>(须 Members

默认规则摘要:

  • C#→Lua ref/in/outOpaqueValue(无需再标)
  • by-val 基元 / enum → Lua boolean / integer / number / string
  • class → ByObj userdata;struct → ByVal 或 Handle(见 struct 分册)
  • params T[] → 同 szarray 单栈槽(table / userdata / nil);支持尾部多槽收集; 专用 LuaMarshalType

3.3 校验时机

  • Mono Attribute: 非法组合 → 错误日志 + 回退 Default(见 marshal/02-MARSHAL-AS.md §4.1),不抛绑失败。
  • Il2Cpp Generate / MarshalAs XML: 配置错误可 硬失败(§4.2)。

4. [LuaAlias] — 方法 Lua 别名

[LuaAlias("run_i32")]
public void Run(int value) { ... }

[LuaAlias("Foo")] // 允许与已有方法名 / 其它别名重复
public void Bar(string s) { ... }
  • 定义于 ZLua.Common
  • 等价于用该字符串作为该方法的 唯一最终 Lua 名替换默认名 MethodInfo.Name,不再双挂)
  • 预编译 DLL 可用 独立 XML(Settings luaAliasXmlPaths,根元素 ZLuaAlias);不得写进 MarshalAs XML。见 04-METHOD-OVERLOAD.md §5.4
  • 允许与其它别名或已有方法名重复;重复时该最终名下多候选,调用走 重载分派(见 04-METHOD-OVERLOAD.md §5)
  • 若某最终名下仅此一候选(例如独立的 run_i32),则为 direct closure

完整规则见 04-METHOD-OVERLOAD.md §3、§5。


5. Lua→C#:无需 [MonoLuaCallback]

Lua 调用 C# 成员时,native 在 EnsureBinding 阶段为每个 public 成员生成桥接 closure 并写入三表。不需要不提供 业务侧 [MonoLuaCallback] 标记。

每种 ReducedType(Il2Cpp)完整签名(Mono Emit) 对应唯一桥接入口。


6. 异常边界

6.1 C# 调 Lua

方向行为
Lua error()捕获为 C# 异常(LuaException 或包装类型);泄漏未处理 native longjmp 到托管栈外
C# 异常传入 native在边界转换为 Lua error 或记录后 rethrow(实现统一)

脚本 不应 依赖 pcall 内捕获 C# 异常的具体类型字符串;仅保证「失败可检测」。

6.2 Lua 调 C#

方向行为
C# 抛异常转换为 luaL_error 等价消息;Mono / Il2Cpp 文案一致或等价
Lua 侧使用 pcall 捕获错误字符串

6.3 Opaque 与边界

Opaque handle 仅在 产生它的那次 C#→Lua 调用返回前有效;跨 pcall 保存后再用 → error。见 marshal/04-OPAQUE.md10-LIFETIME.md


7. Codegen 约束(摘要)

约束
[LuaAlias]允许与默认名 / 其它别名重复;按最终名分组(见 overload §5)
[LuaMarshalAs]禁止 method 级;非法 Members → bind 失败
Mono Emit无法 Emit 的签名 必须显式失败,禁止 silent Method.Invoke 热路径
Il2Cpp stub未覆盖签名 → 构建期或首次绑定失败(MethodBridge 等,见 impl/codegen/

C#→Lua 依赖 IL weave / 专用 stub:经 GetFunction → Delegate 桥完成。


8. 相关文档

文档内容
00-OVERVIEW.md双运行时、初始化
04-METHOD-OVERLOAD.mddispatch、register_method
marshal/01-OVERVIEW.mdMarshal 总览
marshal/09-FUNCTION.mdDelegate ↔ Lua function
10-LIFETIME.mdGC、单 lua_State
reference/csharp/lua-app-domain.md程序员 API 页