6.9 KiB
Architecture
This page covers how RconDataBridge is built internally: the module map, the boot sequence, the security model, and the Project Zomboid engine quirks the mod works around. It's aimed at anyone reading or modifying the mod's own Lua code, rather than consuming the protocol from outside; for that, see Protocol Reference and Operations Reference.
Boot sequence
On Events.OnServerStarted, bootstrap code runs, in order:
- Run internal self-tests (JSON and base64 codecs), if enabled.
- Register the base RCON options (
ProtocolVersion,WorldStats,Request,Response,LastResponse). - Register every operation into the operation registry.
- Publish the initial
RconDataBridge_ProtocolVersionvalue. - Start the request pipeline and the world and player telemetry pollers.
Events.OnServerStarted is known to fire more than once per boot in
Project Zomboid, so every step above guards against running twice: option
registration checks for an existing option first and wraps the underlying
engine call in a protected call as a second line of defense, and each
poller's init() function is guarded by a local flag, so its event
listeners are only ever added once.
Request flow
Once booted, a single request travels through the modules like this:
RCON client
|
| changeoption RconDataBridge_Request <base64 JSON>
v
RconDataBridge_Request option (single mailbox slot)
|
| polled every N ticks (Poll Interval setting)
v
Request pipeline
|-- base64 decode, size check ----> reject: SCHEMA_ERROR / PAYLOAD_TOO_LARGE
|-- JSON decode, envelope check --> reject: SCHEMA_ERROR
|-- idempotency check ------------> known id: replay the cached response
|-- rate limit check -------------> reject: RATE_LIMITED
v
Operation registry (allowlist, exact-name lookup)
|-- unknown op -------------------> reject: OP_NOT_ALLOWED
v
Operation handler (Ops/RDB_Op*.lua)
|-- handler throws ---------------> reject: INTERNAL_ERROR
v
Response envelope { ok, data } or { ok: false, error }
|
| every outcome above, success or rejection, is also written to the audit log
v
RconDataBridge_Response / RconDataBridge_LastResponse
|
| showoptions, base64 decode
v
RCON client
See Protocol Reference for what each error code means and how a client should consume this cycle.
Module responsibilities
Options registry
A thin wrapper over Project Zomboid's server options store. Registering an
option needs both an "add option" call and a separate "add to public
options" call, or it never shows up in showoptions, since that command
reads a different internal list than the one a plain add populates.
Request pipeline
Polls RconDataBridge_Request on a configurable tick interval, since
there's no "option changed" event. Decodes base64 to JSON, validates the
envelope, checks idempotency and the rate limit, dispatches through the
operation registry, publishes the response, and always logs the outcome to
the audit trail, including rejections.
Operation registry
The allowlist; Dispatch is an exact-name lookup into a table, nothing more; new operations are added one file per operation.
Security
Payload size cap (4096 bytes decoded), envelope schema validation, and a global, not per-connection, sliding-window rate limit, since RCON requests carry no caller identity to limit by individually.
Idempotency
Replays the cached response for a repeated request id within a
configurable window, protecting side-effecting operations, such as
save_server, from retries. After the window expires, the same id runs
fresh.
Config
A facade over admin-tunable settings. A small set of values, such as the maximum payload size, are fixed constants; everything else is read live from Sandbox Options. See Configuration.
World stats
Refreshed roughly once an in-game minute from the engine's game clock,
cached in memory, and mirrored directly to RconDataBridge_WorldStats, so
get_world_stats and the option always agree, and reading the option skips
a request/response round trip entirely.
Player snapshot
The source of truth for player data, both online and offline, persisted so it survives a restart, since the engine has no API for offline player data on its own.
JSON and base64 codecs
Self-contained shared codecs with no external dependencies. Each runs its own self-test at boot when the debug self-test setting is enabled.
Security model
A handful of rules define the mod's security boundary and are treated as hard constraints rather than style preferences:
- No new transport, port, or authentication layer. RCON access is the only access control, by design.
- No dynamic operation dispatch. The operation registry only ever does an exact-name table lookup; an RCON request can never cause arbitrary Lua code to run.
- All operations are explicitly allowlisted. An unrecognized
opvalue returnsOP_NOT_ALLOWEDrather than being interpreted in any way. - Every remote action is logged, with its request id, operation, result, and timestamp, including rejected requests, through the audit log described above.
- Requests are validated and bounded on payload size, envelope schema, and rate, before an operation handler ever runs.
Engine constraints the mod works around
A number of behaviours in the code exist specifically to work around gaps or quirks in the Project Zomboid server engine, rather than being arbitrary design choices. The straightforward ones:
| Constraint | Why | How it's handled |
|---|---|---|
| No "option changed" event | Doesn't exist in the engine | Tick-throttled polling in the request pipeline |
| No connect or disconnect event | Doesn't exist in the engine | Diffing the online roster against the last known set |
| No offline-player registry API | Doesn't exist in the engine | Player snapshots are persisted independently |
OnServerStarted fires more than once per boot |
Engine behavior | Every initializer guards on a local flag |
As a special client facing quirk:
RCON responses can span multiple packets
Confirmed from the server's own RCON response handling: a large response,
such as a showoptions call once this mod's options are added to the
roughly 140 built-in ones, gets chunked across multiple packets with no
explicit end-of-response marker.
This is a client-side concern rather than a mod-side one. Any RCON client must accumulate every packet sharing a request id rather than resolving on the first one it receives. See the client checklist in Protocol Reference.