← Engineering log
Party Games Godot 4 Google Drive AI Agents

RobOS AI Multiplayer Game Theater: Dynamic Godot 4 HTTPS Games, Per-Player Google Doc Launchers, and 2-Player Hangman

We built RobOS AI Multiplayer Game Theater (packages/game-theater), a dynamic multiplayer game stage where each player enters through a personal 1-button Google Doc (?auth=<256-bit-token>) into a Godot 4.3 HTTPS WebAssembly Client. Starting from an empty stage, any authenticated RobOS Game User in the lobby can ask the RobOS Agent to generate or modify both the authoritative Node.js multiplayer server (server.js) and the Godot 4 GDScript client (client.gd) live — verified end-to-end by dynamically generating a 2-player game of Hangman with random Round 1 starter selection, alternating Phrase Chooser turns, server-side secret masking, and vector outline symbol font rendering.

Live 2-player Hangman match running inside the RobOS AI Multiplayer Game Theater Godot 4.3 HTTPS WebAssembly Client with custom gallows canvas, masked phrase, A-Z letter buttons, lobby seats, and live chat
2-Player Hangman Inside the Godot 4.3 HTTPS Web Client. Starting from an empty stage, the RobOS Agent dynamically generated both the authoritative multiplayer server.js and the Godot 4 client.gd stage — enforcing a strict 2-player seat cap, randomly picking the Round 1 Phrase Chooser, alternating roles every round, and drawing the gallows procedurally via Control._draw().

Why start with a blank stage instead of a fixed game? Most multiplayer party platforms ship a hardcoded catalog of mini-games. With RobOS AI Multiplayer Game Theater (packages/game-theater), we inverted the model: the app boots as an empty multiplayer lobby backed by an authoritative Node.js HTTP/HTTPS + WebSocket server (:19228 / :19229) and a Godot 4.3 HTTPS WebAssembly Client. Once players join the lobby through their personal Google Docs, any authenticated RobOS Game User can describe a brand-new multiplayer game — or ask the RobOS Agent to change the active game’s rules, scoring, or visuals — and watch the stage hot-reload live across every connected browser.

1. Architecture: Per-Player Google Docs → Node VM Server → Godot 4 WebAssembly Stage

RobOS AI Multiplayer Game Theater 4-column architecture diagram showing Per-Player Google Docs with 256-bit auth tokens, the HTTPS and WSS Game Theater Server with Sandboxed Node VM and Vector Symbol Font endpoints, the Dynamic RobOS Agent Studio with Dual Validation Loop, and the Godot 4.3 WebAssembly Client
End-to-End Multiplayer Architecture. Capability-token Google Docs authenticate each player into the HTTPS/WSS server, where the RobOS Agent compiles and validates server.js + client.gd before hot-mounting the stage inside every player’s Godot 4 WebAssembly client.
flowchart LR
  subgraph Docs["1. Per-Player Google Docs (Drive API)"]
    direction TB
    D1["Player 1 Google Doc
1-Click Launch (?auth=256-bit-token)"] D2["Player 2 Google Doc
1-Click Launch (?auth=256-bit-token)"] end subgraph Server["2. Game Theater HTTPS + WSS Server (:19228 / :19229)"] direction TB AUTH["HMAC Session & Seat Gate
Verifies RobOS Game User & maxPlayers"] VM["Sandboxed Node VM Runtime
createInitialState · onPlayerAction · getPlayerView"] FONT["Vector Symbol Font Endpoint
/api/fonts/symbols2.ttf · dejavu.ttf"] end subgraph Agent["3. Dynamic RobOS Agent Studio"] direction TB PROMPT["Prompt from Any Lobby Player
Generate New Game / Modify Active Game"] VAL["Dual Validation & Self-Repair Loop
Node VM Check + Headless Godot 4.3 Compiler"] PROMPT --> VAL end subgraph Godot["4. Godot 4.3 HTTPS WebAssembly Client"] direction TB LOBBY["TheaterClient.gd
Lobby Presence, Seats, Chat & AI Prompt UI"] STAGE["GameSandboxHost.gd
Hot-Mounts GDScript.new() Control Stage"] LOBBY <--> STAGE end D1 --> AUTH D2 --> AUTH AUTH --> VM VM <--> LOBBY LOBBY --> PROMPT VAL --> VM VAL --> STAGE FONT --> LOBBY

2. Passwordless Player Identity via Per-Player Google Docs

Typing passwords or pairing codes on phones, tablets, or laptops before a quick game slows everyone down. Following the same capability-launcher pattern we built for RobOS Google Calendar and RobOS Shopping Lists, packages/game-theater/lib/player-doc-sync.js provisions a dedicated RobOS AI Multiplayer Game Theater folder in Google Drive containing:

  • One Personal Google Doc per Player (RobOS Game Theater — <Player> (Player Launch Doc)): Each player receives a cryptographically random 256-bit token embedded in a prominent launch button (?auth=<token>) and shared exclusively with that player’s Google account via Drive permissions (role: 'reader').
  • HMAC-Signed Session Cookie + Dual HTTPS Delivery: Clicking the launch button exchanges the capability token for an HttpOnly HMAC-SHA256 session cookie (robos_theater_player) over either the outbound HTTPS tunnel or the LAN ECDSA P-256 HTTPS server on :19229 (generated automatically by lib/tls-cert.js so Godot 4’s WebAssembly SharedArrayBuffer / Secure Context requirements are satisfied on local Wi-Fi too).
  • Host Directory Google Doc: An owner-only index doc keeps quick links to every player’s launch document in one place.
RobOS AI Multiplayer Game Theater desktop Electron studio showing synthetic players with synced Google Docs, Play in Godot 4 buttons, and the dynamically generated Hangman server.js inspector
Desktop Game Theater Studio (packages/game-theater). Manage players, sync per-player Google Docs, inspect or hot-reload server.js and client.gd, or open isolated per-player Godot 4 windows.
Rendered per-player Google Doc launcher showing the 1-click Launch RobOS AI Multiplayer Game Theater button and LAN HTTPS fallback link
Per-Player Google Doc Pass. Shared only with that player’s Google account; 1 click signs them straight into the Godot 4 multiplayer lobby as their verified RobOS Game User.

3. Dual-Validated Dynamic Generation: Building 2-Player Hangman Live

Whether triggered from the desktop studio, CLI, or directly inside the Godot 4 Web Client by any player in the lobby, lib/agent-builder.js asks the configured RobOS Agent (robos-lib/ai-agent) to emit a complete JSON game cartridge containing manifest, CommonJS serverJs, and Godot 4.3 clientGd. Before any cartridge reaches connected players, it must pass a two-stage compiler gate (with automatic error-feedback repair retries if either check fails):

  1. Restricted Node VM Server Contract Check (lib/game-runtime.js): Compiles server.js inside an isolated node:vm context (no filesystem or network access), verifies meta, calls createInitialState(mockPlayers), and asserts that getPlayerView(state, viewerUid) returns valid per-player dictionaries without leaking opponent secrets.
  2. Headless Godot 4.3 GDScript Compiler & Render Check (godot/scripts/validate_client.gd): Runs godot4 --headless -s res://scripts/validate_client.gd to compile client.gd with GDScript.reload(), instantiate the Control node, and pump both player views and empty waiting states through apply_view(view, me, lobby) to catch Godot 4 strict type-inference errors before broadcast.

Using this live pipeline, we prompted the empty stage to create 2-Player Hangman:

  • Strict 2-Player Cap (minPlayers: 2, maxPlayers: 2): Once two players claim seats, any third player attempting join_seat receives an explicit MAX_PLAYERS_REACHED rejection while still being able to watch or chat in the lobby.
  • Random Starter, Strictly Alternating Phrase Chooser: Round 1 picks chooserIdx via crypto.randomInt(0, 2); every next_round advances chooserIdx = (state.chooserIdx + 1) % 2 so both players alternate setting phrases and solving them.
  • Zero-Leak Secret Phrase Masking: During the choosing and guessing phases, getPlayerView(state, viewerUid) sends secretPhrase: null and only the masked pattern (R O _ O _ T _ E _ T E R) to the Guesser, revealing the full phrase to both players only when phase === 'round_over'.

4. Solving Broken Emoji Tofu Boxes in Godot 4 WebAssembly

When we first opened the exported Godot 4.3 WebAssembly client in a live desktop window, Unicode emojis across headers, buttons, and multi-codepoint ZWJ player avatars (such as 🧙‍♂️ = U+1F9D9 U+200D U+2642 U+FE0F) rendered as hex tofu boxes. Investigating with headless Godot 4 shaping tests revealed two subtle engine details:

  • Godot 4’s built-in ThemeDB.fallback_font bundles only Latin/Cyrillic OpenSans (even basic symbols like ▶ and ✓ are absent).
  • Attaching Linux’s NotoColorEmoji.ttf passes FontFile.has_char() (because the cmap table lists every emoji), but Godot 4’s wasm32 FreeType build does not rasterize PNG-compressed CBDT/CBLC color bitmap tables in WebAssembly.

We fixed this cleanly in lib/theater-server.js and godot/scripts/TheaterClient.gd by serving lightweight, pure TrueType vector outline symbol fonts (NotoSansSymbols2-Regular.ttf, NotoSansSymbols-Regular.ttf, and DejaVuSans.ttf) from /api/fonts/{symbols2,symbols,dejavu}.ttf, attaching them via FontFile.data to ThemeDB.fallback_font.fallbacks, stripping ZWJ/variation selectors (U+200D, U+FE0F), and verifying every codepoint through _has_glyph() before rendering.

5. Install (From Source), Launch & Run the E2E Suite

# 1. Install the GNOME "(From Source)" launcher icon & menu entry $ node scripts/source-shortcuts/source-shortcuts.cjs install game-theater # 2. Start the HTTP (:19228) + LAN HTTPS (:19229) server & sync per-player Google Docs $ node packages/game-theater/serve.js $ node packages/game-theater/bin/game-theater.js sync-docs # 3. Dynamically generate a 2-player Hangman game via the RobOS Agent $ node packages/game-theater/bin/game-theater.js generate \ "Create a 2-player game of Hangman (max 2 players), randomly select starter on Round 1 then alternate being the Phrase Chooser." \ --title "Hangman" # 4. Run the unit + real headless Chrome WebAssembly + Godot 4 compiler E2E suite $ node --test packages/robos-test/tests/game-theater/unit.test.js \ packages/robos-test/tests/game-theater/hangman-e2e.test.js ✔ Dynamically Generated 2-Player Hangman — End-to-End Verification (2/2 pass) ✔ RobOS AI Multiplayer Game Theater (2/2 pass)