Repository navigation
LuaUI Files
Two script trees, one on each side. Neither knows the other's paths; they meet only at the numbers in the protocol files.
The two sides do not run the same Lua:
| Client | Server | |
|---|---|---|
| Runtime | LuaJIT - Lua 5.1 | Lua 5.3 |
| Integers | none; every number is a double | real 64-bit integers |
Bitwise and // operators |
not available | available |
string.pack / string.unpack
|
not available | available |
| Unpacking a table | unpack(t) |
table.unpack(t) |
A helper written for a server window will not necessarily run in the client script. Write each side in its own dialect rather than copying code between them.
Two consequences:
-
Bit twiddling. On the server,
flags & 0xFFis ordinary code. On the client, write the arithmetic out (flags % 256) or use LuaJIT'sbitlibrary. - Big numbers. The client cannot hold an exact integer beyond 2^53, so a value that is exact on the server can come back rounded. See Protocol for what to do about item serials.
The payload reader and writer already hide this: each side has its own
implementation with the same method names, so w:dword(v) means the same thing in
both halves regardless of dialect.
Everything lives under Data\Custom\Scripts.
| File | What it is |
|---|---|
UI\UI.lua |
The window library. ui.Window{...}, layout columns and rows, and the helpers every window script uses. You call it, you do not edit it. |
UI\Registry.lua |
The one place window ids and texture slots are named. Every script reads the same table. |
UI\Protocol.lua |
Your wire contract: per system, its actions, result codes and limits. Mirrored on the server. |
UI\Enums.lua |
Named client values, grouped Enums.Group.VALUE the way the server's Defines\Enums.lua is. |
UI\Net.lua |
Routes incoming window packets to the handler that registered for them, and builds outgoing ones. |
UI\Windows\*.lua |
One file per window. This is where you work. |
UI\Windows\UIPlacement.lua |
A ruler for laying windows out - see below. |
SimpleXml.lua |
Small XML reader, for windows that load a catalogue from a file. |
UIPlacement.lua is a small HUD panel that answers the question you have while
writing a window: what number do I type here? Press F11 and it reports, live:
| It shows | Use it for |
|---|---|
cursor |
the mouse position - paste it straight into x/y
|
over |
which Lua window is under the cursor, and its id |
pos |
that window's top-left corner |
size |
its declared size, and in brackets what it covers on screen |
anchor |
what the anchor you gave the helper itself resolved to |
canvas |
the coordinate space all of the above is in |
Placing a control: open your window and the helper, put the cursor where the
control's top-left corner belongs, read cursor, use those two numbers.
Sizing a panel: read cursor at one corner, then the other, and subtract.
Trying an anchor is the same trick applied to the helper itself. Edit its ANCHOR
and MARGIN, press F9, and anchor prints where that combination lands - which
beats guessing whether bottomright with margin = { 6, 140 } clears the panel
already sitting there.
The bracketed pair in size is worth understanding: 280x380 (140x190) means the
window declares 280x380 and covers 140x190 on screen at a render scale of 0.50.
Sizes scale, x/y do not - the same asymmetry the anchors exist for.
Delete the file to drop the helper; nothing depends on it.
Everything in UI\Windows\ is loaded automatically, in alphabetical order, once
the client has connected to the GameServer and received its settings - so the
window scripts only exist from that point on, never on the login screen. There is
no list to register in: dropping a file in the folder is all it takes, and deleting
it removes the window.
Encrypted .usc files are always loaded. Plain .lua files are loaded only in
dev mode (see Lua UI). If a script of yours silently does nothing on
a live server, that setting is the first thing to check.
In dev mode, F9 reloads the whole client-side Lua without restarting the client. Edit a window script, press F9, and the change is on screen.
It is a full reset, not a per-file refresh:
- every window is destroyed, then rebuilt by the scripts that run again
- every
Event.onandnet.onhandler is dropped and re-registered - anything a script kept in a local - a cached list, a counter - starts over
-
loadfires again, andingameright after it if you are already in the world
So a window that shows itself from ingame reopens on its own, and because opening
is a request, the server sees a fresh open and answers with fresh state. Nothing
special is needed to get the data back.
The console reports each pass, and a script with a syntax error says so there rather than reloading silently.
F9 is the client only. The GameServer's Lua reloads through its own console command; a client reload never touches the server half of a window.
The libraries in UI\ are not auto-loaded - they arrive through require:
local ui = require("UI")
local R = require("Registry")
local net = require("Net")
local P = require("Protocol")require follows the same rule as the window scan: it loads the encrypted .usc
of a library whenever one is present, and falls back to a plain .lua only in
dev mode. On a live server a library is loaded from its .usc and never from
source, so the UI\ libraries ship encrypted just like the window scripts do.
Bind them to lowercase locals. UI and Net are global tables the client
already provides, and a local with the same name would hide them.
Everything lives under Data\Plugins\LuaAPI.
| File | What it is |
|---|---|
Includes\UIWindow.lua |
Holds what each window registered and routes calls to it. |
Includes\UIPacket.lua |
Reader and writer for payloads - the server's half of the wire format. |
Includes\Net.lua |
The same, for the general (non-window) channel. |
Defines\UIProtocol.lua |
Mirror of the client's Protocol.lua, plus the window ids. |
CallbacksUI.lua |
The entry points the server calls. You do not normally touch it. |
Windows\*.lua |
One file per window - the server half of what you wrote on the client. |
Unlike the client, the server scans nothing. It loads Main.lua, and everything
else is pulled in from there:
LoadScript(BASE .. "Windows\\CoinExchange.lua")Forget that line and the window will open but never answer - the handlers were never registered. It is the single most common mistake when adding a window.
| You want to | Edit |
|---|---|
| add a window | a new file in client UI\Windows\ + a new file in server Windows\ + one LoadScript line |
| name a new window id or texture | client Registry.lua
|
| add actions or result codes | client Protocol.lua and server Defines\UIProtocol.lua
|
| change how a window looks | only its own client script |
The libraries (UI.lua, Net.lua, UIWindow.lua, UIPacket.lua) are shared by
every window - patching them to suit one window is how the next update overwrites
your work.
MuLua Scripting Plugin | Home
🖼️ Lua UI
- File map
- Creating windows
- Client and server
- Designing a window
- Protocol
- Scrolling and scrollbars
- Controls
- Custom text
- Text input
- Debug console
- Bitmap Slicer
- Encrypting scripts
Maps
Monsters
Shared
Version 1.14 | 2026-09-09