60 lines
4.5 KiB
Markdown
60 lines
4.5 KiB
Markdown
|
|
# The Luprex System
|
||
|
|
|
||
|
|
This is **Luprex**, a game engine built on top of Unreal that uses Lua as its scripting language.
|
||
|
|
The system consists of two parts:
|
||
|
|
|
||
|
|
1. The Luprex DLL. Stores the state of the world. Handles networking and lua scripting. The networking is automatic, scripters don't write networking code. The luprex DLL is event-driven, deterministic, OS-independent, no I/O. Pure standard-compliant C++. Organized as a library with struct `EngineWrapper` as the top-level API.
|
||
|
|
|
||
|
|
2. The Unreal-based Driver. Its job is to peer into the luprex DLL and render whatever it sees. It is also responsible for sending OS events (like TCP/IP events) into the Luprex DLL. Uses struct EngineWrapper access the Luprex DLL. There's also a command-line driver for the game's server.
|
||
|
|
|
||
|
|
The luprex DLL never calls into the Unreal driver. Output goes through polled buffers. This separation enables deterministic replay for debugging: the driver logs all events, and can replay them to reproduce crashes.
|
||
|
|
|
||
|
|
## Architecture: World Models and Predictive Reexecution
|
||
|
|
|
||
|
|
There are four types of world models, each a separate `World` instance with its own Lua interpreter:
|
||
|
|
|
||
|
|
- **Master** (server, one) — authoritative state, executes commands immediately when they arrive.
|
||
|
|
- **Server-synchronous** (server, one per client) — executes commands when the acknowledgement is issued.
|
||
|
|
- **Client-synchronous** (client, one) — executes commands when the same acknowledgement arrives; determinism keeps it in perfect sync with its server-synchronous counterpart.
|
||
|
|
- **Asynchronous** (client, one) — snapshot of client-synchronous with predictions applied for responsive rendering; rolled back when server confirms.
|
||
|
|
|
||
|
|
The synchronous models lag behind the master but stay in lockstep with each other. The asynchronous model fills the latency gap for the player.
|
||
|
|
|
||
|
|
Two update channels flow into the synchronous models:
|
||
|
|
1. **Command acknowledgements** — for the client's own actions, keeping the two synchronous models in lockstep.
|
||
|
|
2. **Difference transmission** — for everything else (other players' actions, server-side events, tangibles entering/leaving visibility).
|
||
|
|
|
||
|
|
See `Docs/Predictive-Reexecution.md` for the full explanation.
|
||
|
|
|
||
|
|
## Architecture: Lua / Unreal Separation
|
||
|
|
|
||
|
|
Lua scripts have no access to the Unreal API whatsoever. The scripter works with plain Lua tables, animation queues of key-value tuples, and coroutines. There are no "unreal bindings." The Luprex DLL is engine-agnostic — Unreal (or any other front end) interprets the animation queues and renders accordingly.
|
||
|
|
|
||
|
|
## Architecture: Tangibles
|
||
|
|
|
||
|
|
Tangibles are game objects. Each has:
|
||
|
|
- A **Lua table** — the scripter stores arbitrary game data here.
|
||
|
|
- An **animation queue** — a fixed-length sequence of key-value animation steps.
|
||
|
|
- A **C++ tangible** — holds the ID, animation queue, positional tracker, etc.
|
||
|
|
- A **metatable** — engine-reserved; contains __id, __index (class), __threads.
|
||
|
|
|
||
|
|
Animation steps contain **transient** values (like `action`) that don't propagate, and **persistent** values (like `xyz`, `facing`, `plane`) that carry forward automatically.
|
||
|
|
|
||
|
|
On the Unreal side, **Tangible Actor blueprints** (TangibleStaticMesh, TangibleSkeletalMesh, TangibleCharacter) monitor the animation queue and perform the visual animations. Custom blueprints can interpret the queue in any way they want.
|
||
|
|
|
||
|
|
## Architecture: Lua Environment
|
||
|
|
|
||
|
|
- **Patched Lua runtime** — deterministic table iteration, deterministic table length, flag bits on tables, generalized less-than, C++ exceptions instead of longjmp, and more. See `Docs/A-Summary-of-our-Lua-Patches.md`.
|
||
|
|
- **LuaStack API** — custom C++ API replacing the standard Lua C API. Uses `LuaDefStack`/`LuaExtStack` with `LuaArg`/`LuaVar`/`LuaRet` slots mapped to stack positions. See `Docs/Our-In-House-Lua-API.md`.
|
||
|
|
- **LuaDefine macro** — declares Lua-callable C++ functions and auto-registers them in a global registry for automatic insertion into the Lua environment.
|
||
|
|
- **eng::malloc heap** — custom deterministic memory allocator for the driven portion, ensuring reproducible addresses during replay.
|
||
|
|
|
||
|
|
## Architecture: GUI System
|
||
|
|
|
||
|
|
Blueprints call into Lua via two mechanisms:
|
||
|
|
- **Invokes** — change world state, forwarded to server, executed in order per predictive reexecution rules.
|
||
|
|
- **Probes** — read-only, return data to blueprints, run locally on client.
|
||
|
|
|
||
|
|
Look-at widgets, hotkeys, and menus are built on top of this. The menu system is implemented entirely in "user space" Lua and blueprint code. See `Docs/Displaying-Widget-Blueprints.md`.
|
||
|
|
|