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 initializedthrows (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.InitializeGetFunction / 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