Manual/Client Internals/Lua scripting architecture
v1.0.364 notes · build 2026-11-07 12:10 UTC

Lua scripting architecture

xLua boot · module tree · dispatch · at-rest obfuscation
#lua #client

The C# tier boots and spins up xLua; C# retains transport, login plumbing, and the world-map viewport protocol. Command split: 3,115 Lua commands, 54 C#-only (from the 1.0.364 LWLF bundle, version 825, 18,733 modules).

Module tree (file counts)

UI/ 11,709; Net/ 3,117 (dispatch core + 3,104 Message command classes); DataCenter/ 2,483 (833 lazy-singleton manager names in the registry); Scene/ 951; Chat/ 172; Framework/ 113 (engine, loaded first); rest in Common/, Global/, Util/, RedPoint/. (The earlier 1.0.351 manifest undercounted UI//Net//DataCenter/ — path-truncation artifact.)

Boot sequence

  1. XLuaManager.Initialize() → new LuaEnv(); AddLoader(CustomLoader)
  2. require("Common.Main") — table/string/bit/Unity-math utils
  3. require("Framework.FrameworkMain") — BaseClass OOP, DataCenter service locator, event system
  4. GameEntry.Lua.StartGame() → require("GameMain"); GameMain.Start()
  5. LuaEntry:init() — player/session state root
  6. PBController.InitPBConfig() (no-op this build; real load via PBController.InitBytes/LoadBytes)
  7. ~90 × DataCenter.<Manager>:Startup()

require() resolution order (for "DataCenter.Global.LuaEntry")

  1. dev-only local override; 2. table-config ZIP lookup (if path contains LuaDatatable); 3. raw disk read in Unity Editor; 4. LWLF bundle (production); 5. TextAsset fallback.

OOP

OptClass.Declare (self-optimizing); constructors chain root-to-leaf; metatable indirection only for classes defining getters. Example: BaseClass("MailGetMessage", SFSBaseMessage), SFSBaseMessage.OnCreate, self.sfsObj:PutUtfString("uid", mailId).

Hot-update

Four independently-versioned payloads — asset bundles, C# .mdl assemblies (dllres manifest), Lua bundle, data tables. The Lua bootstrap (XLuaManager, custom loader) lives in C#.

Dispatch static

C# MessageFactory.DispatchResponse (1.0.364) handles 71 wire names itself; the remainder via GameMain.Protocal → SFSNetwork.HandleMessage → handler named by MsgMap[wire]. 18 of the 71 overlap with Lua — C# wins; Lua still builds the requests world.march.formation.new, world.march.change, world.march.speed.up, train.send, train.batch.send. 54 C#-only (53 of 71 + world.get.cross), incl. login.ext, check.device.change, change.user.parseid, init.error, world.get.block, world.get.new, world.get.cross, push.world.march.new, push.world.march.del, push.user.off. Shared _id request counter, resets on reconnect. FutureManager.update() is a no-op (no protocol timeout/retry). SFSNetwork.GetMsgType uses MsgMap[wire] both directions; replies with no handler fall through to the chat manager.

At-rest obfuscation ("SuperEncrypt") static

In BaseUtils' EncryptUtils: encrypted chunk header 0B 45 4E 43; rest of the 32-byte Lua 5.3 header replaced by S bytes; source name XORed with a 32-byte ChaCha-derived key; body XORed with ChaCha8 keystream; _ENV tail masked; keys from dummy-constant derivation (same as the file wrappers).

Hot-patch C# from Lua static

CommonUtils.Lua_File_Write = arbitrary sandbox file IO incl. persistentDataPath/Assemblies/*.mdl loaded by FM_Mono at next start (unused in 1.0.364); xLua [Hotfix] IL injection inactive (HotfixDelegateBridge never called); DevPatchRuntime unused.

notes build 2026-11-07 12:10 UTC · lw-notes@9c47e1b · sources: own capture/analysis cross-checked against the public dossier