6.2 KiB
Integration Project
This is Luprex, a game engine built on top of Unreal that uses Lua as its scripting language. The system consists of two parts:
-
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
EngineWrapperas the top-level API. -
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.
Build System
- Use
build.pyfor all builds. Do NOT follow Epic's standard build instructions. build.py all— full rebuild (engine, game, intellisense, project files)build.py c++— lightweight rebuild (use if you've only edited c++ files)- Lua and Blueprint edits don't require any kind of build.
Directory Structure
luprex/— The Luprex DLL.Source/Integration/— Game module C++ source (Unreal-side driver code)Content/— Unreal assetsDocs/— Documentation. When trying to understand this system, start with the markdown files in the Docs directory.Config/— Unreal config filesEnginePatches/— Custom engine modifications- `Plugins/UEWingman/' - An MCP that gives you control over the unreal editor.
../integration.UE/- the unreal engine source tree
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:
- Command acknowledgements — for the client's own actions, keeping the two synchronous models in lockstep.
- 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/LuaExtStackwithLuaArg/LuaVar/LuaRetslots mapped to stack positions. SeeDocs/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.
Key Documentation
Look in the Docs directory for important documentation.
Workflow
- Do not use git to make changes (commit, push, branch, etc.). Read-only git commands (status, log, diff, etc.) are fine.
- Work at the user's pace. Do not start coding until the user says it is time.
- If an instruction ends with an ellipsis (
...), the user has more to say. Wait for the next message before acting. - Do not output multiple paragraphs. Doing so is very rude. You are having a conversation, give the other person a chance to speak. For most questions, 3-4 sentences is the maximum, unless you've been asked to give a detailed explanation.
Coding Conventions
- Prefer early returns and
continueto reduce nesting (never-nester style). - Do not use static functions in Unreal code. Use class methods instead.
- Use
LogLuprexIntegrationfor log messages inside Source/. Use LogTemp inside Plugins/.