The first handler
The MPP side is the same handshake → pull → invocation loop as the
Python adapter tutorial; only the handler is
new.
Sanitize viewer text
Section titled “Sanitize viewer text”One rule before the code: viewer text is about to be embedded in a console
command, and the Zandronum console treats ; as a command separator and "
as a string delimiter. Unsanitized, a viewer could redeem hello"; quit and
execute arbitrary console commands on the server. Strip both (plus \ and
non-printable characters) before quoting:
def sanitize_chat(message): cleaned = "".join( ch for ch in message if 0x20 <= ord(ch) < 0x7F and ch not in ('"', ';', '\\') ) return cleaned.strip()The handler
Section titled “The handler”def handle_chat_queue(rcon): def handler(sock, inv_id, event, params): if event != "say": failed(sock, inv_id, f"unknown event: {event}") return message = sanitize_chat(params.get("message", "")) if not message: failed(sock, inv_id, "message empty after sanitization") return try: rcon.command(f'say "{message}"') except RconError as e: failed(sock, inv_id, str(e)) return applied_done(sock, inv_id, {"message": message}) return handler(failed and applied_done are the small frame helpers from the
Python adapter tutorial:
failed sends a failed frame with a reason, applied_done sends
applied then done.)
The failed path matters as much as the happy path: when the game server is
down, the Invocation fails with a reason and the viewer can be refunded —
the Pack must never silently swallow a paid redeem.
Run it
Section titled “Run it”Start the bridge with your Pack, set DOOM_RCON_PASSWORD in its
environment, redeem a say Cue, and watch the message land in the game’s
chat.
One event, end to end. Next: a second event that earns its own queue.