Add comprehensive wiki documentation for RconDataBridge, covering installation, configuration, protocol details, operation references, and internal architecture.

This commit is contained in:
2026-08-27 17:27:11 +02:00
parent 807bd9f4ef
commit 9b8e11f260
7 changed files with 829 additions and 0 deletions

169
wiki/Architecture.md Normal file
View File

@@ -0,0 +1,169 @@
# 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 <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](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).