11 KiB
Glitch / Odin integration lab
A separate experiment for Glitch: the Dark Machine, hosted in the Glitch repository. Odin stays untouched. This is a first vertical slice, not a migrated room or a deployment release.
Whole-room simulation
The supplied Glitch GDD now has an isolated multi-cabinet room simulation with puzzles, boss, cells, calibration, launch and Teddy captions. Run py -3 lab.py start-room and open http://127.0.0.1:18777/?room=1 after building. See the full walkthrough and test coverage. It uses separate ports and never sends GPIO outputs. Chatterbox Teddy speech and a phone-accessible LAN demo are available: run py -3 lab.py start-room --speech --lan on the configured GPU host. See voice prerequisites and the optional Piper fallback. Enable audio in the Teddy tab on each listening device; see the walkthrough for setup and timing.
Status
The Rust controller, simulated cabinet, and real Godot sidecar build successfully. Rust tests, Flutter analysis/widget test, Python launcher tests, a headless HTTP/NATS/Godot lifecycle test, and the real Pac-Man score-completion adapter test have passed locally.
Physical RFID/Pi testing is still pending. The existing simulator remains the default. The new explicit Godot mode is a source-only lab, excluded from production exports. The Godot score test reports existing Pac-Man resource-retention warnings at shutdown; its assertions pass.
Relationship to Odin
Reference: sibling Odin repository, commit eecb42c2364d915bc79ea99b16a7dad8f71baf1b.
The reference uses Rust/async-nats for control, MessagePack for prop messages, and JSON for game/Flutter messages. This lab follows that separation. It is newly written, with no dependency on or modifications to Odin, no copied production credentials, and no Odin puzzle rules.
The Flutter screen is a small Glitch-specific starting point, not a fork of the full Odin UI yet. We should establish the transport and message contract before migrating its room layout, services, routing, and puzzle controls. Escape Gallery is the app identity; Glitch is the current room.
Flutter web GM (127.0.0.1:18767)
| HTTP JSON / polling
Rust controller/gateway (127.0.0.1:18766)
| NATS request/reply, named-field MessagePack
Local NATS (127.0.0.1:14222)
|
Rust simulated cabinet_01
The HTTP browser gateway is experimental. It avoids assuming the existing Odin Dart NATS client works in browsers. The owner may prefer a supported NATS WebSocket client or a different gateway.
Prerequisites
Install these on the development computer, then open a new terminal:
- Rust stable, including the native compiler/linker required by its installer. This project uses edition 2024.
- Flutter stable, with Dart 3.9.2 or newer within Dart 3.x. Run
flutter doctor. - NATS Server, with
nats-serveron PATH. - Python 3.10 or newer.
No Docker, Starfish, Pi login, room tokens, or existing production server are required for desktop tests. Dependency fetching needs internet access on the build computer. Commit Cargo.lock and gm/pubspec.lock after the first successful dependency resolution to pin the tested versions.
Build and start on Windows
From the Glitch repository root:
cd labs/glitch_odin
py -3 lab.py preflight
py -3 lab.py build
py -3 lab.py start
Run each command separately and stop if it reports an error. Build runs Rust tests, compiles the two-role Rust executable, resolves Flutter dependencies, runs analysis and widget tests, and builds the browser app. It does not export or change Godot.
Keep the startup terminal open. When it prints READY, visit http://127.0.0.1:18767 on this same computer. Ctrl+C stops only the child processes created by this launcher. An occupied port causes startup to fail instead of terminating another service.
On Linux/macOS, use python3 in place of py -3.
First acceptance test
- Confirm the amber simulated-cabinet banner and POWERED OFF / Waiting for operator.
- Click Power on. The phase becomes booting for four seconds.
- Wait for ready. Credits must still be zero.
- Click Simulate RFID scan. Credits become one.
- Click Simulate player Start. Credits return to zero and phase becomes playing.
- Click Simulate win. Phase becomes won.
- Click Reset test to return to powered off.
In another terminal, run the automated test while the lab is running:
cd labs/glitch_odin
py -3 lab.py smoke
This test resets the simulated cabinet, exercises the actual HTTP/NATS/MessagePack path, verifies boot gating, credit gating, duplicate scan handling, stale-session rejection, and expiry, then leaves the cabinet powered off. Do not interact with the GM controls during this test.
To check unavailable-state behavior, run the three backend components manually in separate terminals instead of using the supervisor:
nats-server -c nats.conf
.\target\debug\glitch-odin-lab.exe cabinet
.\target\debug\glitch-odin-lab.exe controller
py -3 -m http.server 18767 --bind 127.0.0.1 --directory gm/build/web
Stop the cabinet process. The UI should show disconnected and disable controls after the next poll/timeout. Restart it: a new session begins powered off. No previous command should be replayed automatically.
Contract and limits
- Subject:
escape_gallery.glitch.lab.v1.cabinet_01.request. - Commands: snapshot, power_on, scan, start, win, reset.
- Mutations require an ID, matching cabinet-process session, and expiry within 30 seconds.
- Replies contain acceptance, duplicate status, a message, and the current cabinet snapshot.
- MessagePack uses named fields. This is not wire-compatible with Odin's generated tuple messages.
- The simulator remembers the last 256 commands in memory. It is not a persistent command journal.
- Reset clears simulated game state but keeps the process session and replay cache. Restart creates a new session.
- Ready state advances when queried after the boot deadline; the simulator has no rendering loop.
- HTTP 200 can contain a rejected command: clients must inspect
accepted. - No responder/timeouts produce HTTP 503. An unconfirmed delivery is never treated as success or automatically retried.
- There is no room autopilot, next-cabinet sequencing, authentication, durable recovery, or generated Odin schema yet. The opt-in Godot sidecar is experimental.
- All listeners are loopback only. The unauthenticated test gateway is deliberately unsuitable for LAN deployment.
- BCM23 remains an LED. I2C button integration is still pending hardware identification.
What we need next
- Owner confirmation of the current Odin branch and permission/strategy for sharing its framework crates.
- Generator source, canonical schema, subjects, command acknowledgements, and state/session semantics.
- Choose the browser transport and verify the target Flutter build on laptops and iPads.
- Replace the experimental sidecar boundary with the agreed generated contract and, if desired, a Rust GDExtension. Only one controller may own a cabinet.
- Run the same acceptance flow on a disconnected test cabinet, then connect the known RFID driver. Keep the I2C board disabled until identified.
- Add room autopilot and multi-cabinet sequencing, then migrate the useful Odin GM room views.
- Integrate Starfish deployment/SSH management after runtime communication works.
The current Python controller, Godot exports, Pi services, and deployment guide remain the working deployment path until these gates pass.
Development checks
py -3 -m unittest discover -s . -p test_runner.py -v
cargo test
cd gm
flutter analyze
flutter test
Build products are ignored locally. The lab has a Godot import ignore marker and is excluded from game exports.
Real Godot cabinet test (desktop)
Stop the running simulator with Ctrl+C first. From this lab folder:
py -3 lab.py build
py -3 lab.py start-godot
The launcher starts the local NATS broker, JSON controller, Rust Godot sidecar, a separate real Pac-Man scene, and the Flutter web server. Open http://127.0.0.1:18767. It should explicitly say REAL GODOT CABINET.
- Power on from the GM screen.
- Watch the actual four-second boot and character intro in Godot.
- Click Simulate RFID scan. This desktop fixture is explicitly enabled by the launcher; it enters the game's normal RFID admission path.
- Press your mapped player Start in Godot, or Start game in the GM screen.
- Play to 500 points. The GM phase should become won. Simulate win is disabled for real Godot.
- Reset test returns the cabinet to powered off.
Set GLITCH_LAB_GODOT to the absolute executable path if the repository's Windows Godot executable is unavailable. The lab scene is tests/odin_lab/cabinet.tscn; it refuses to run without --glitch-lab. It does not load the managed production cabinet connection or invoke room reward mappings.
The sidecar talks only to 127.0.0.1:18768 using one newline-delimited JSON command/reply per TCP connection. The current Rust layer owns NATS and MessagePack. This is not a GDExtension yet.
Automated checks
Stop the interactive lab before running these:
py -3 godot_smoke.py
This starts and stops its own headless Godot, broker, sidecar and controller. It tests the real transport, injected scan admission, Start, reset, offline 503, and a new process session after restart. No physical devices are used. The original lab.py smoke intentionally refuses real Godot.
From the repository root, the score-completion test exercises Pac-Man's actual score objective:
.\Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://tests/odin_lab/check_adapter.gd -- --glitch-lab --lab-inject-rfid
Physical RFID: next bench test, not a deployment change
The lab can read the existing local GPIO bridge when Godot is started with --gpio=real instead of --lab-inject-rfid. These options cannot be combined. Leave BCM23 as an LED; I2C buttons remain unimplemented. No output command is exposed by the lab.
For that bench test, manually run NATS, the controller, the sidecar (binary argument godot, not cabinet), and Godot on the same test host. Start the known RFID-only bridge configuration separately. Godot arguments are:
--path <project-root> res://tests/odin_lab/cabinet.tscn -- --glitch-lab --gpio=real --lab-rfid-device=cab1_rfid1
Use the device ID actually configured on that reader. Without --lab-inject-rfid, the GM scan button is disabled: a fresh physical card scan is required after boot. This initial test accepts any nonempty card UID, matching the existing test policy. A Pi needs its own Rust build, source project/assets and a compatible Godot executable; the Windows binary is not portable to it. This path has not been physically verified yet. Do not run another cabinet/Godot owner or another reader driver on the same hardware.
See Odin message audit for reusable system/timer/lifecycle definitions and the new Glitch-specific messages still needed.