# 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](Protocol-Reference) and [Operations Reference](Operations-Reference). ## Boot sequence On `Events.OnServerStarted`, bootstrap code runs, in order: 1. Run internal self-tests (JSON and base64 codecs), if enabled. 2. Register the base RCON options (`ProtocolVersion`, `WorldStats`, `Request`, `Response`, `LastResponse`). 3. Register every operation into the operation registry. 4. Publish the initial `RconDataBridge_ProtocolVersion` value. 5. 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: ```text RCON client | | changeoption RconDataBridge_Request 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](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](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 `op` value returns `OP_NOT_ALLOWED` rather 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](Protocol-Reference#9-client-implementation-checklist).