Skip to main content

LuaAppDomain

LuaAppDomain is ZLua's only public host entry: initialize / domain-reset Lua, and use GetFunction to obtain a Delegate for a Lua function by module and name. The host surface does not expose Shutdown; clearing the script world goes only through Reset.

Canonical example: zlua-demo Bootstrap.cs

API​

namespace ZLua
{
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;
}
}

Authoritative details: spec/01-HOST-API.md, spec/10-LIFETIME.md §7.


Initialize(moduleLoader)​

ParameterDescription
moduleLoaderFunc<string, object>; returns Lua source as string or byte[] by module name

Only on first use (or when there is no main lua_State yet) create the state and install the loader. Calling again when already initialized → throws (use Reset; no longer supports “swap loader only”).

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitZLuaOnStartup()
{
LuaAppDomain.Initialize(LoadLuaModule);
}

LoadLuaModule handles Editor (LuaScripts/*.lua) vs Player (StreamingAssets/*.lua.txt) path differences; see installation guide.


Reset(moduleLoader)​

Hot reload or clear the script world: tear down the single main lua_State, then recreate it with the given loader.

BehaviorDescription
At call siteSchedule only: store the loader; later schedules keep the last one. Does not lua_close immediately
When appliedThis frame’s EndOfFrame (LuaFramePump / WaitForEndOfFrame): drain pending refs → shut down Registry / module cache → lua_close → rebuild via the Initialize path and install the loader
Old delegatesAfter EndOfFrame applies, invoking old GetFunction delegates → throws a C# exception; the host must re-GetFunction and discard field caches
Il2CppProcess-level Bridge / XML tables / InternalCall are kept; only state-level resources are rebuilt
// Clear the script world after hot reload (safe mid C#↔Lua call; queues only)
LuaAppDomain.Reset(LoadLuaModule);
// From the next frame: drop old Action/Func, re-GetFunction

GetFunction<T>(luaModule, luaMethodName)​

Resolve a Lua function by module and method name, bind it to delegate type T, and return it.

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);
RuleDescription
TMust be a concrete MulticastDelegate type (e.g. Action<> / Func<>)
luaModule / luaMethodNameMust match LoadLuaModule module name and Lua return { ... } key
CachingCaller's responsibility (on hot paths, store in a field / local before invoking); after Reset takes effect, old delegates are invalid and must be rebound
MarshalInvoke on the returned delegate follows the Marshal cheatsheet; [LuaMarshalAs] may apply

Missing module, key that is not a function, or failure to bind as T → throws a C# exception.


Initialization Flow​

Dual-Runtime Forwarding​

LuaAppDomain itself lives in ZLua.Common; real logic is implemented by the backend assembly:

EnvironmentAssemblyImplementation type
Unity EditorZLua.MonoLuaMonoAppDomain
Il2Cpp PlayerZLua.Il2CppLuaIl2CppAppDomain

Application.isEditor chooses which backend to load; the public API stays the same.

Lifetime and FramePump​

After init, LuaFramePump is registered and, in the Unity frame loop, handles:

  • LateUpdate: deferred release of ref / userdata (ProcessPendingRefReleases)
  • WaitForEndOfFrame: run a scheduled Reset (domain teardown + rebuild)

Usually no manual frame-pump calls are needed.

Relationship to LuaEnv​

TypeVisibilityDescription
LuaAppDomainpublicSole entry for game code (Initialize / Reset / GetFunction)
LuaEnvpublic (Mono module)Wrapper for underlying lua_State; created by the backend. Do not new LuaEnv() in business code

Standard integration path: LuaAppDomain.Initialize → GetFunction / CSharp access; use Reset to clear after hot reload.

Module Loading Convention​

The return value of moduleLoader("app") is loaded with require semantics. The module argument to GetFunction(..., "app", ...) must match.

EnvironmentPath
Editor{ProjectRoot}/LuaScripts/app.lua
PlayerStreamingAssets/LuaScripts/app.lua.txt

Common Errors​

SymptomFix
Lua module loader is not configuredInitialize not called, or loader is null
Second Initialize throwsMain state already exists; use Reset(loader)
require / GetFunction failsCheck module name, file path, .lua.txt suffix, return table keys
Old delegate throws after ResetDiscard cached Action/Func, re-GetFunction
No Lua scripts in PlayerConfirm Sync script ran; StreamingAssets contains target files
Marshal / bind failureCross-check Marshal cheatsheet and T signature