Skip to main content

11 — Multi-version management (Unity / Lua / install)

The package does not ship a full libil2cpp tree; Install overlays patch + zlua-runtime + chosen Lua onto the current Unity stock sources. This document is the implementation spec for UPM package layout, LocalInstaller, Lua version switching, native DLL naming, and Il2Cpp ZLuaConf.inc / Compatible headers (§12). Lua-visible semantics remain governed by other specs in this directory; this document does not change interop semantics.


1. Goals and non-goals​

1.1 Goals​

GoalNotes
Upgradeable UnityDo not ship a full libil2cpp per Unity version
Switchable Lua sourcesSettings pick a version; PUC-Rio downloads from lua.org into a local cache; LuaJIT is manually cloned
Editor DLLs by seriesLogical name lua5{minor} (e.g. lua53); binaries are replaced by developers
Auditable changesModifications to upstream (Unity libil2cpp, PUC-Rio / LuaJIT) exist as patch files
Visible failuresOn patch context mismatch or missing sources, Install fails with an error; silent skip is forbidden

1.2 Non-goals (this phase)​

ItemNotes
Hot-swap already-loaded native DLLs in the same Editor processOn Windows, loaded DLLs cannot be reliably overwritten; after changing series DLLs, restart the Editor
Ship Editor DLLs for every source patch versionEditor development does not require patch-level precision; replace luaXX.dll when needed
Generate and reference C# sources inside a read-only PackageUPM cache is read-only; LUA_DLL maps only by API-family macros (§8)
Deliver every historical Lua / Unity combination at onceFirst ship the primary combos (§10), then add patches / source dirs as needed

2. In-package layout (ZLua~)​

Authoritative package data root: Packages/com.code-philosophy.zlua/ZLua~.

ZLua~/
├── zlua-runtime/ # ZLua native; copied to libil2cpp/zlua at Install
│ ├── ZLuaCommon.h # Assemble Compatible + define ZLUA_LUA_VERSION
│ ├── LuaCompatible.h # Multi Lua / LuaJIT API shim (hand-written)
│ ├── Il2CppCompatible.h # Multi Unity / Tuanjie il2cpp API shim (hand-written)
│ └── generated/
│ └── ZLuaConf.inc # Written by Install/Generate (macros only; §12)
├── patches/
│ ├── libil2cpp/
│ │ ├── 2021.3/
│ │ │ └── 2021.3.0.patch # Covers 2021.3.x (shared-range minimum version)
│ │ ├── 2022.3/
│ │ │ └── 2022.3.0.patch # Covers 2022.3.x; add 2022.3.N.patch at breakpoints
│ │ ├── 6000.5/ # Unity 6000.5.x (preferred over 6000/ fallback)
│ │ │ └── 6000.5.6.patch
│ │ └── 6000/ # Unity 6: try 6000.{minor}/ first, then fall back here
│ │ └── 6000.0.0.patch
│ └── lua/
│ ├── lua-5.1/
│ │ ├── 5.1.0.patch # Covers 5.1.0–5.1.1; gettable FastMT + Win32 ANSI loadlib
│ │ └── 5.1.2.patch # Covers 5.1.2–5.1.5
│ ├── lua-5.2/ # 5.2.0 / 5.2.1 / 5.2.2 / 5.2.4 floors (see in-package README)
│ ├── lua-5.3/ # See in-package README; includes 5.3.0…5.3.3 floors
│ └── lua-5.4/
│ ├── 5.4.0.patch # Covers 5.4.0… until the next floor file
│ ├── 5.4.4.patch # Shared ranges keep only the minimum version filename
│ └── 5.4.7.patch
├── lualib/
└── link.xml

Lua / LuaJIT upstream sources are not shipped in the package. Install writes them into the project-local cache:

Library/ZLua/LuaSrcCache/
├── downloads/ # Optional: keep .tar.gz
├── lua-5.5.0/ # Downloaded and extracted from lua.org
├── lua-5.4.8/
├── lua-5.2.4/
├── lua-5.1.5/
└── luajit-2-1/ # Developer clones manually (no auto-download)

Cache directory name LuaSrcCache is fine; a hierarchical Library/ZLua/cache/lua is equivalent if preferred (implementation follows CommonDirs.LuaSrcCacheDir).

2.1 Directory / version-id naming​

KindRuleExample
Settings / PUC-Rio idlua-{major}.{minor}.{patch}lua-5.5.0
PUC-Rio download URLhttps://lua.org/ftp/lua-{ver}.tar.gzhttps://lua.org/ftp/lua-5.5.0.tar.gz
PUC-Rio cache dirSame as idLuaSrcCache/lua-5.5.0/
Settings / LuaJIT idluajit-{major}.{minor}luajit-2.1
LuaJIT cache dirluajit-{major}-{minor} (developer clone)LuaSrcCache/luajit-2-1/
Lua patch dirpatches/lua/lua-{major}.{minor}/patches/lua/lua-5.4/
Lua patch fileOnly {major}.{minor}.{patch}.patch (no default.patch); shared ranges keep only the minimum version filename5.4.0.patch / 5.4.4.patch / 5.4.7.patch
Unity patch dirpatches/libil2cpp/{major}.{minor}/; Unity 6 may also fall back to patches/libil2cpp/{major}/2022.3/, 6000/
Unity patch fileOnly {major}.{minor}.{patch}.patch (no default.patch); shared ranges keep only the minimum version filename2022.3.0.patch / 6000.0.0.patch

Lua / Unity patch selection (floor, same rule): within the chosen series directory, take the largest {X.Y.Z}.patch whose version is ≤ the current product version; a file whose name matches the current version (or the version with letter suffixes stripped) is a fast hit of that rule. Apply failure → Install fails (do not silently pick another file). Authoring / maintenance: if several patch versions can share one patch body, keep only the minimum version number file for that shared range. default.patch is forbidden (both Lua and libil2cpp). Unity series directories: try {major}.{minor}/ first (e.g. 2022.3/, 6000.3/), then for Unity 6 (major >= 6000) fall back to {major}/ (e.g. 6000/); after the directory is chosen, apply floor file selection inside it.

Forbidden to place a full libil2cpp-{unity} tree or a full upstream Lua source tree under ZLua~ as an install source.

2.2 Dev-time source of truth​

ContentEdit during developmentLand in package
zlua C++build-win64/.../libil2cpp/zluaSync to ZLua~/zlua-runtime
Lua upstreamInstall downloads to Library/ZLua/LuaSrcCache (not in package)Only patches/lua
Changes to Unity libil2cpppatches/libil2cppDo not commit a full libil2cpp tree

3. Install pipeline (LocalInstaller)​

Install output root: Library/ZLua/LocalIl2CppData-{platform}/ (paths per CommonDirs).

3.1 Order (required)​

  1. Resolve Settings luaVersionId (empty → default lua-5.5.0; see §6.2)
  2. Ensure Lua sources in Library/ZLua/LuaSrcCache: reuse if cached; PUC-Rio missing → download from lua.org; LuaJIT missing → fail and prompt for manual clone
  3. Copy official Editor il2cpp (including stock libil2cpp) into the Local directory
  4. Resolve and apply libil2cpp patches (§4)
  5. Copy/overwrite ZLua~/zlua-runtime onto Local.../libil2cpp/zlua
  6. Install the chosen Lua from cache into Local.../libil2cpp/lua: PUC-Rio copy (and patch per §5) into a buildable src/; LuaJIT installs public headers only (see build/02-LUAJIT.md); ensure ZLUA_FAST_METATABLE matches §5.4 / §12.5
  7. Write project Scripting Define Symbols (§7)
  8. Write ZLuaConf.inc (§12; authoritative output under Local libil2cpp/zlua/generated/)
  9. If the package lacks the matching series Editor plugin DLL, warn (do not block Install); developers replace DLLs themselves (§8)
  10. Write install fingerprint (§9)
  11. Clear Il2Cpp / Bee caches; on series / Define changes, prompt to restart the Editor

3.2 Differences from old behavior​

OldNew
Whole-directory replace with packaged full libil2cpp-*stock + patch + zlua-runtime
Lua sources shipped in packageNot shipped; LuaSrcCache + network download / manual clone
Full Unity trees embedded in packageOnly zlua-runtime + patches

4. libil2cpp patch selection​

Changes to Unity stock libil2cpp should stay small (order of tens of hook lines) and always live as patch files.

4.1 Selection algorithm​

Suppose current Unity is 2022.3.62f1 (compare as 2022.3.62 triplet, ignoring letter suffixes like f1 / t11):

  1. Try series directories in order: {major}.{minor}/, then if major >= 6000 fall back to {major}/ (e.g. 6000.3/ → 6000/)
  2. In the first existing series directory:
    • If an exact file exists (full version string / stripped 2022.3.62, etc.) → use it (floor fast path)
    • Else among all {major}.{minor}.{patch}.patch files, take the largest whose version is ≤ 2022.3.62 (e.g. only 2022.3.0.patch → use it)
  3. No usable patch in any candidate directory → Install fails
  4. Do not use default.patch anymore

After a file is chosen, apply failure → Install fails (must not silently switch to another file).

Maintenance convention (same as Lua §5.3): for consecutive Editor patch versions that can share one body, commit only the range’s minimum version-number file; when upstream context changes so the old floor no longer applies, add a new floor patch at that breakpoint.

4.1.1 Maintained series and deltas​

DirectoryBaseline Editor (authoring reference)CoverageIn-package floor file
2021.3/2021.3.45f22021.3.x2021.3.0.patch (no AnUnresolvedCallStubWasNotFound*; provides no-op return false)
2022.3/2022.3.62f32022.3.x2022.3.0.patch (real unresolved stub detection + LuaAppDomain::Initialize)
6000.5/6000.5.6f16000.5.x6000.5.6.patch (API deltas vs 6000.0, e.g. GenericMethod.h; runtime shims via Il2CppCompatible.h)
6000/6000.0.71f16000.0.x / 6000.3.x (and fallback when no exact 6000.{minor}/)6000.0.0.patch (floor hit; prefer exact 6000.{minor}/ if present)

4.2 Apply and validation​

  • Prefer unified diff; Install may --check / dry-run first
  • After apply, do minimal validation (e.g. agreed hook symbols or anchor file content appear)
  • Context drift (Unity patch-level edits around hooks) → fail; add a floor patch for that breakpoint ({major}.{minor}.{patch}.patch) or update the shared file

4.3 Boundary with zlua-runtime​

OwnershipContent
patches/libil2cppInserts / small edits to Unity’s existing .cpp/.h (init, compile lists, etc.)
zlua-runtimeZLua’s own source tree; do not “stuff” large implementations by editing Unity files

If zlua-runtime depends on internal APIs that vary with Unity / Lua, solve it with §12 Compatible + conf conditional compilation inside runtime — do not re-ship a full libil2cpp for that reason.


5. Lua sources and patches​

5.1 Obtaining sources (not in the UPM package)​

EngineBehavior
PUC-RioReuse if LuaSrcCache/{id} already has a complete src/; else download https://www.lua.org/ftp/{id}.tar.gz and extract there
LuaJITDoes not auto-download; developers clone sources into LuaSrcCache/ (directory name per implementation, e.g. luajit-2.1). Il2Cpp does not copy the whole tree into libil2cpp/lua; see build/02-LUAJIT.md

For series that support VM patches (§5.2), ZLua’s VM changes live in-package under patches/lua; Install applies them to a clean tree in the cache.

5.2 Whether to apply Lua VM patches​

Settings / engineInstall applies patches/lua?Written to libil2cpp/lualuaPatchKey (fingerprint)
PUC-Rio 5.1.xYes (§5.3 floor; dir patches/lua/lua-5.1/)Full buildable src/ (entry files removed)Actual {X.Y.Z}.patch filename chosen
PUC-Rio 5.2.xYes (§5.3 floor; dir patches/lua/lua-5.2/)Full buildable src/ (entry files removed)Actual {X.Y.Z}.patch filename chosen
PUC-Rio 5.3+ (including lua-5.3.0 …)Yes (§5.3 floor)Full buildable src/ (entry files removed)Actual {X.Y.Z}.patch filename chosen
LuaJITNoPublic headers only; static libs from developer Plugins (build docs)none

Notes:

  • PUC-Rio 5.1+ (including 5.2.x) must apply the floor patch under the matching series directory; missing file or apply failure → Install fails.
  • LuaJIT must not look up or apply series patches.
  • On combinations without FastMT / without a VM patch, Install must ensure ZLUA_FAST_METATABLE 0 (PUC writes local luaconf.h; LuaJIT writes into the installed headers’ luaconf.h; see §5.4).
  • Fingerprint luaPatchKey is none when there is no patch (§9).
  • Why LuaJIT cannot compile from a full in-tree source, and Il2Cpp platform surface (Android/iOS .a, WebGL disabled), follow build/02-LUAJIT.md.

5.3 Patch selection algorithm (only series that “need patch” per §5.2)​

Suppose Settings is lua-5.4.8, series directory patches/lua/lua-5.4/:

  1. Ensure cache sources are available (§5.1)
  2. List all {major}.{minor}.{patch}.patch in the series directory (ignore other filenames; leftover historical default.patch must not be chosen)
  3. Among files with version ≤ 5.4.8, pick the largest (e.g. 5.4.0 / 5.4.4 / 5.4.7 → 5.4.7.patch; if 5.4.8.patch exists, hit it directly)
  4. No qualifying file, or apply fails → Install fails (no auto-downgrade to another series, and no fallback to a “greater” version patch)
  5. Copy patched src/ into Local.../libil2cpp/lua
  6. Then validate / force ZLUA_FAST_METATABLE per §5.4

Maintenance convention: for consecutive patch versions that can share one body, commit only the range’s minimum version-number file; when upstream context changes so the old floor no longer applies, add a new floor patch at that breakpoint (still named with the new range’s minimum).

Do not bake IDE helper files (e.g. .clangd) into patches.

5.4 ZLUA_FAST_METATABLE (FastMT) support matrix​

ZLUA_FAST_METATABLE is decided only by post-Install libil2cpp/lua/luaconf.h (§12.1); Il2Cpp runtime and Lua VM must compile with the same macro value.

Engine / patch versionFastMTInstall / patch requirements
PUC-Rio 5.1.0–5.1.5May enable (floors: 5.1.0 / 5.1.2; hooks luaV_gettable / luaV_settable, raw get first)Must apply patches/lua/lua-5.1/ floor patch (includes zlua_fastmt.*, Table cache fields, Win32 ANSI loadlib)
PUC-Rio 5.2.0–5.2.4May enable (floors: 5.2.0 / 5.2.1 / 5.2.2 / 5.2.4; same gettable hooks + raw-first)Must apply patches/lua/lua-5.2/ floor patch (also opens luai_num* / luai_hashnum under Il2Cpp lump)
PUC-Rio 5.3.0 / 5.3.1May enable (floor patch sets 1; hooks luaV_gettable / luaV_settable, raw get first)Apply series VM patch
PUC-Rio ≥ 5.3.2 (including 5.3.3…5.3.6, 5.4.x, 5.5.x)May enable (default 1 via corresponding floor patch; hooks luaV_finishget / finishset)Apply series VM patch
LuaJITUnsupported (must be 0)Do not apply VM patch; Install headers only and inject ZLUA_FAST_METATABLE 0; ZLuaCommon.h may #error on ZLUA_USE_LUAJIT && ZLUA_FAST_METATABLE

Rationale summary:

  • ≥ 5.3.2: FastMT hooks are luaV_finishget / finishset.
  • 5.1.x / 5.2.x / 5.3.0 / 5.3.1: no finishget / finishset; FastMT hooks luaV_gettable / luaV_settable. Implementations must luaH_get (raw) first, and only on miss take the sealed FastMT path (otherwise type-table fields such as FullName would be intercepted).
  • LuaJIT: no PUC-Rio FastMT series patch; always legacy / Dispatch, macro fixed at 0.

Manually overriding unsupported combos with -DZLUA_FAST_METATABLE=1 → undefined behavior; Installer should write back 0 for unsupported combos.

5.5 Editor DLL vs Player​

PathPUC-RioLuaJIT
Il2Cpp PlayerExact patch-version sources from cache into libil2cpp/lua; plus patches/lua when §5.2 requiresHeaders only into libil2cpp/lua; Android/iOS static .a provided by developer in Plugins (see build/02-LUAJIT.md)
Editor (Mono)Plugins/lua/<series>/ (e.g. lua53/{lua53.dll,lua53.dylib}); Mono does not implement FastMTPlugins/lua/luajit20/, luajit21/; also needs zlua_mono_gate (same dirs); Mono does not implement FastMT

Editor and Player need not match patch number / build options bit-for-bit, but API family and key macros should align. Missing Editor native libs → Install warns only, does not block. Restart Editor after switching series.

Shipped with this package’s Editor (current maintenance):

SeriesWindowsmacOS
lua51 … lua55.dll.dylib (universal preferred)
luajit21.dll.dylib
luajit20.dll.dylib (x86_64 only; upstream 2.0 has no arm64)
zlua_mono_gatePlugins/lua/zlua_mono_gate.dllPlugins/lua/libzlua_mono_gate.dylib (universal)

Shipped EmmyLua debug libs: build/04-EMMYLUA-DEBUGGER.md (emmylua/luajit/ does not distinguish 2.0/2.1).

LuaJIT + Il2Cpp: release surface is Android / iOS only (developer-provided static .a); Il2Cpp Player on Win / macOS / Linux / WebGL etc. is unsupported. Details: build/02-LUAJIT.md.


6. Settings: choosing a Lua version​

FieldMeaning
luaVersionIde.g. lua-5.4.8 / luajit-2.1; empty → §6.2

6.1 Obligations after change​

Re-run Install after switching; prompt Editor restart when Define / series DLL changes. Il2Cpp packaging should block when not Installed or fingerprint mismatches.

6.2 Default version​

  • Field default / empty: fixed lua-5.5.0
  • Install writes that default back if empty
  • Download failure (e.g. official FTP has no such version) → Install fails and prompts to check the version id; must not silently switch to another major version

7. Scripting Define Symbols​

Written by the Installer (or the same logic triggered by Settings save) into the project Defines — do not generate C# files inside a read-only Package.

7.1 Macro naming (must use ZLUA_ prefix)​

MacroWhen definedUse
ZLUA_USE_LUAJITLuaJIT selectedEngine differences; with ZLUA_LUAJIT_2_0 / ZLUA_LUAJIT_2_1 chooses luajit20 / luajit21
ZLUA_LUAJIT_2_1luajit-2.1Editor logical name luajit21
ZLUA_LUAJIT_2_0luajit-2.0Editor logical name luajit20
ZLUA_LUA_5_5PUC-Rio 5.5.x (any patch)API family + Editor DLL logical name lua55
ZLUA_LUA_5_4PUC-Rio 5.4.x (any patch)API family + Editor DLL logical name lua54
ZLUA_LUA_5_3PUC-Rio 5.3.x (any patch)API family + Editor DLL logical name lua53
ZLUA_LUA_5_2PUC-Rio 5.2.x (any patch)API family + Editor DLL logical name lua52
ZLUA_LUA_5_1PUC-Rio 5.1.x (non-JIT)API family + Editor DLL logical name lua51

Notes:

  • No need for exact patch macros like ZLUA_LUA_5_4_7: source patch version only affects the Install-copied tree and fingerprint, not LUA_DLL mapping.
  • API-family macros carry the old discussion’s “LUA_FEAT_5_4_X” meaning, named ZLUA_LUA_5_4 — do not use a confusing _X suffix.
  • LuaJIT: define ZLUA_USE_LUAJIT, plus ZLUA_LUAJIT_2_0 or ZLUA_LUAJIT_2_1 by minor (aligned with LuaDllName / Plugins dirs).

7.2 Mutual exclusion​

Only one “engine + API family / JIT minor” set at a time. Installer removes old ZLUA_LUA_* / ZLUA_USE_LUAJIT / ZLUA_LUAJIT_2_* before writing the new set for the current luaVersionId.


8. Native DLL naming and LuaDllName (by series)​

8.1 Packaging policy​

ItemRule
LayoutPlugins/lua/<series>/ (PUC / LuaJIT) + zlua_mono_gate* in the same dirs (see 03)
Ship granularityOptionally ship series libs; developers may replace per series they use
Logical name (PUC)lua + major + minor → lua51…lua55 (no patch digit)
Logical name (LuaJIT)luajit20 / luajit21 (matches Settings luajit-2.0 / luajit-2.1; not a vague luajit)
InstallWarn if missing; do not fail
Settings source id (examples)API family / JIT macrosEditor logical nameWindowsmacOS
lua-5.1.5ZLUA_LUA_5_1lua51lua51.dlllua51.dylib
lua-5.2.4ZLUA_LUA_5_2lua52lua52.dlllua52.dylib
lua-5.3.6 / lua-5.3.0ZLUA_LUA_5_3lua53lua53.dlllua53.dylib
lua-5.4.7 / lua-5.4.1ZLUA_LUA_5_4lua54lua54.dlllua54.dylib
lua-5.5.0ZLUA_LUA_5_5lua55lua55.dlllua55.dylib
luajit-2.0ZLUA_USE_LUAJIT + ZLUA_LUAJIT_2_0luajit20luajit20.dllluajit20.dylib (x86_64 only)
luajit-2.1ZLUA_USE_LUAJIT + ZLUA_LUAJIT_2_1luajit21luajit21.dllluajit21.dylib

Switching source patch within a series (e.g. 5.4.1→5.4.7) does not change LUA_DLL or Plugins filenames — only the embedded Il2Cpp source tree. To use another build in the Editor, replace the matching files under Plugins/lua/<series>/ (note Windows load locks; restart Editor).

EmmyLua debug module directory emmylua/luajit/ differs from the Editor logical names above: 2.0/2.1 share one emmy_core (see 04).

8.2 LuaDllName.cs​

Standalone file (suggested path):

Packages/com.code-philosophy.zlua/Runtime/Mono/Lvm/LuaDllName.cs

Role: define LUA_DLL by API family / JIT only. LuaDll.cs only references that constant.

namespace ZLua
{
public static class LuaDllName
{
#if UNITY_IPHONE && !UNITY_EDITOR
public const string LUA_DLL = "__Internal";
#elif ZLUA_LUAJIT_2_1
public const string LUA_DLL = "luajit21";
#elif ZLUA_LUAJIT_2_0
public const string LUA_DLL = "luajit20";
#elif ZLUA_USE_LUAJIT
public const string LUA_DLL = "luajit21";
#elif ZLUA_LUA_5_5
public const string LUA_DLL = "lua55";
#elif ZLUA_LUA_5_4
public const string LUA_DLL = "lua54";
#elif ZLUA_LUA_5_3
public const string LUA_DLL = "lua53";
#elif ZLUA_LUA_5_2
public const string LUA_DLL = "lua52";
#elif ZLUA_LUA_5_1
public const string LUA_DLL = "lua51";
#else
// Default matches Settings default lua-5.5.0 → series lua55.
public const string LUA_DLL = "lua55";
#endif
}
}

When adding a series: add series DLL, LuaDllName branch, API-family macro, and Installer mapping. When adding a source patch within a series: only add lua-versions (and needed patches); no need to change LuaDllName or add Plugins filenames.

8.3 API trimming in LuaDll.cs​

Enable/disable declarations by API family / JIT macros (may split files). Source patch differences do not enter #if.

8.4 Windows load locks​

  • After switching series (lua53↔lua54) or replacing an in-use same-named DLL, restart the Editor.
  • Install must prompt restart when series or Defines change.

9. Fingerprint and reinstall​

Fingerprint (JSON or equivalent key/value) must at least include:

FieldNotes
unityVersionApplication.unityVersion at install time
luaVersionIdActual source id used (including §6.2 default resolution)
luaSeriese.g. lua-5.3 / lua-5.4 / luajit (for DLL comparison)
libil2cppPatchKeyPatch directory key actually chosen (exact or major)
luaPatchKeyLua patch directory key actually chosen, or none
packageContentStampPackage content change stamp (may evolve from existing max-mtime policy)
definesWritten ZLUA_* set (for diagnostics)

Any of the following → NeedReinstall is true:

  • Package content stamp changed
  • Settings luaVersionId (or default resolution) mismatches fingerprint
  • Current Unity version mismatches fingerprint
  • Local tree missing

10. Phased support scope​

PhaseScope
P0Default lua-5.5.0 + download cache + lua-5.3 patch + Unity 2022.3 patch + zlua-runtime
P1lua-5.1.x / lua-5.2.x (full patch-version FastMT + series patches) + lua-5.4.x / lua-5.5.x with matching series patches / DLLs
P2LuaJIT (manual clone; Editor ✅; Il2Cpp Android / iOS only)

11. Implementation checklist​

  • ZLua~ has no full libil2cpp and no shipped Lua upstream sources
  • PUC-Rio: download from lua.org/ftp on cache miss
  • LuaJIT: only accept manual sources under LuaSrcCache/luajit-{major}-{minor}
  • Default luaVersionId = lua-5.5.0
  • Missing Plugins DLL warns only
  • libil2cpp patch: floor (largest {X.Y.Z}.patch ≤ current), no default.patch; fail without downgrade
  • lua patch: apply for PUC-Rio 5.1+ (including 5.2.x) (§5.2); only LuaJIT skips with luaPatchKey=none
  • FastMT: all PUC-Rio 5.x may enable; only LuaJIT forces ZLUA_FAST_METATABLE 0 (§5.4)
  • Define / LuaDllName / fingerprint / restart prompts complete
  • LuaCompatible.h / Il2CppCompatible.h / ZLuaConf.inc match §12
  • Install writes Local ZLuaConf.inc; Generate/All validates or rewrites; stale silent use forbidden

12. Il2Cpp runtime compatibility layer (ZLuaConf / Compatible)​

This section constrains only the Il2Cpp Player native tree (zlua-runtime). Editor Mono continues to use §7 Scripting Defines + §8 LuaDllName and does not consume ZLuaConf.inc.

12.1 Design goals​

Express version facts with three non-overlapping sources of truth:

Source of truthExpresses
Generated ZLuaConf.incEngine family (JIT or not), Lua API family, Unity / Tuanjie version, reconciliation string
Lua headers (LUA_VERSION_NUM, etc.)Official Lua numeric version; mapped to ZLUA_LUA_VERSION via ZLuaCommon.h
luaconf.h (after Install + §5.4)Sole decider of ZLUA_FAST_METATABLE (conf / Compatible must not redefine); unsupported FastMT combos must be 0

12.2 File roles​

FileNatureRole
zlua-runtime/generated/ZLuaConf.incGeneratedMacros only; no #include, no logic, do not hand-edit
zlua-runtime/LuaCompatible.hHand-writtenUse conf if available → pick Lua headers (official lua.hpp / JIT extern "C") → API shims (AbsIndex, IsInteger, NewUserData, PCall, etc.)
zlua-runtime/Il2CppCompatible.hHand-writtenTuanjie vs Unity il2cpp API diffs (e.g. Calloc); depends on engine macros in conf
zlua-runtime/ZLuaCommon.hHand-writtenAssembles the above; #define ZLUA_LUA_VERSION LUA_VERSION_NUM; asserts / arch macros; no longer a dump of scattered compat details
libil2cpp/lua/luaconf.hUpstream ± patch / Install forceDefines ZLUA_FAST_METATABLE (§5.4)

ZLuaCommon.h include order (convention):

  1. generated/ZLuaConf.inc
  2. LuaCompatible.h (includes Lua headers and provides shims)
  3. Il2CppCompatible.h (il2cpp headers + engine shims)
  4. Then:
#ifndef ZLUA_LUA_VERSION
#define ZLUA_LUA_VERSION LUA_VERSION_NUM
#endif

/* ZLUA_FAST_METATABLE must already be defined by luaconf.h */
#ifndef ZLUA_FAST_METATABLE
#error "ZLUA_FAST_METATABLE must be defined by lua/luaconf.h"
#endif

12.3 ZLuaConf.inc generated macros​

MacroValuesNotes
ZLUA_USE_LUAJIT0 | 1Aligns with C# Scripting Define ZLUA_USE_LUAJIT; do not use old name ZLUA_LUAJIT
ZLUA_LUA_API_FAMILY501 / 502 / 503 / 504 / 505 …API family: official from chosen series (5.1→501, 5.2→502, 5.3→503…); LuaJIT convention 501 (capability checks still prefer ZLUA_USE_LUAJIT)
ZLUA_TUANJIE_ENGINE0 | 11 = Tuanjie engine, 0 = Unity
ZLUA_UNITY_VERSIONdecimal integerSee §12.4; filled on both Unity and Tuanjie as the “Unity version line” encoding
ZLUA_TUANJIE_VERSION0 or decimalFixed 0 on Unity; on Tuanjie, Tuanjie engine version encoding (different from Unity)
ZLUA_CONF_IDstring literalLogging / reconciliation, e.g. `"lua-5.3.8

Explicitly not generated:

MacroReason
ZLUA_LUA_VERSIONMapped from LUA_VERSION_NUM in ZLuaCommon.h; not written into conf
ZLUA_FAST_METATABLEDecided only by luaconf.h, keeping Table ABI aligned with the VM

12.4 Numeric encoding rules​

No leading 0 (C preprocessor treats as octal; digits 8/9 make it illegal).

MacroEncodingExample
ZLUA_UNITY_VERSIONYYYY * 10000 + minor * 100 + patch (minor/patch two digits each, usually < 100)2021.3.45 → 20210345
ZLUA_TUANJIE_VERSIONmajor * 10000 + minor * 100 + patch (same; 0 on Unity)1.9.3 → 10903
ZLUA_LUA_API_FAMILYmajor * 100 + minor (no patch)5.4 → 504; JIT → 501

Comparison example: #if ZLUA_UNITY_VERSION >= 20220300.

Exact patch-version string reconciliation uses ZLUA_CONF_ID and Install fingerprint (§9) — do not use numeric macros as patch-level identity.

12.5 Capability-check priority​

ScenarioWriting
Is LuaJIT?#if ZLUA_USE_LUAJIT
Official API family (5.3 vs 5.4…)#if !ZLUA_USE_LUAJIT && (LUA_VERSION_NUM >= 504), or ZLUA_LUA_API_FAMILY >= 504
Exact id reconciliationZLUA_CONF_ID / fingerprint
Tuanjie vs Unity API#if ZLUA_TUANJIE_ENGINE, optionally with ZLUA_UNITY_VERSION / ZLUA_TUANJIE_VERSION
FastMT#if ZLUA_FAST_METATABLE (from luaconf.h only); support surface in §5.4 (all PUC-Rio 5.x may enable; only LuaJIT must be 0)

Unsupported FastMT combos: Install must make ZLUA_FAST_METATABLE in luaconf.h be 0. ZLuaCommon.h may #error on ZLUA_USE_LUAJIT && ZLUA_FAST_METATABLE.

12.6 Generation timing and authoritative path​

StepRole
LocalInstaller (authoritative)After every successful Install must write Local libil2cpp/zlua/generated/ZLuaConf.inc; content from Settings luaVersionId, Application.unityVersion, Tuanjie detection
ZLua/Generate/All (validate)Rewrite or validate the same semantics; mismatch with Settings / fingerprint → fail or force refresh; silent use of stale conf forbidden
Player buildRecognizes only conf in the Local tree; in-package ZLua~/zlua-runtime/generated follows existing stub policy and is not the sole source of truth (UPM read-only may prevent writes)

Editor side should centralize version encoding (suggested EngineVersionUtil, alongside LuaVersionUtil): Tuanjie detection, ZLUA_UNITY_VERSION / ZLUA_TUANJIE_VERSION / ZLUA_CONF_ID generation — avoid scattered parsers.

12.7 Examples​

Official Lua 5.3.8 + Unity 2021.3.45:

/* Generated by ZLua Install/Generate. Do not edit. */
#define ZLUA_USE_LUAJIT 0
#define ZLUA_LUA_API_FAMILY 503
#define ZLUA_TUANJIE_ENGINE 0
#define ZLUA_UNITY_VERSION 20210345
#define ZLUA_TUANJIE_VERSION 0
#define ZLUA_CONF_ID "lua-5.3.8|unity-2021.3.45|tuanjie-0"

LuaJIT 2.1 + Tuanjie (illustrative):

/* Generated by ZLua Install/Generate. Do not edit. */
#define ZLUA_USE_LUAJIT 1
#define ZLUA_LUA_API_FAMILY 501
#define ZLUA_TUANJIE_ENGINE 1
#define ZLUA_UNITY_VERSION 20220362
#define ZLUA_TUANJIE_VERSION 10903
#define ZLUA_CONF_ID "luajit-2.1|unity-2022.3.62|tuanjie-1.9.3"

12.8 Mapping to Mono / C# Defines​

Il2Cpp (conf / headers)Editor Mono
ZLUA_USE_LUAJIT (0/1)#define ZLUA_USE_LUAJIT (present = enabled)
ZLUA_LUA_API_FAMILYZLUA_LUA_5_1 / ZLUA_LUA_5_3 / … (one mutually exclusive set)
ZLUA_UNITY_* / ZLUA_TUANJIE_*No equivalent conf; Mono does not depend on them
ZLUA_LUA_VERSION ← LUA_VERSION_NUMNone; P/Invoke trimmed by §8.3 API family
ZLUA_FAST_METATABLE (luaconf)N/A (no embedded Lua VM sources)

13. Doc-map update notes​

After this file is part of spec/**:

  • Package layout, Install, multi-version, and Il2Cpp conf/Compatible are authoritative here
  • Outdated wording in 00-OVERVIEW such as “full in-package libil2cpp-2022 tree” should be revised as implementation lands
  • Implementation notes such as impl/IL2CPP.md must not override this document; on conflict, revise this document first or seek confirmation