Expand Glitch operations hub, cabinet authoring, deployment, and RFID support

Operator hub
- Introduce Escape Gallery branding and Glitch: the Dark Machine room views
- Add cabinet illustrations, Pac-Man marquee/gameplay artwork, and waiting states
- Add device feedback panels with RFID readings, input/output states, and stale status
- Add logged operator hardware interventions with request deduplication

Room and cabinet authoring
- Add a visual custom-PCB connection editor with per-cabinet device inventory
- Support device IDs, bindings, wiring notes, input polarity, and validation
- Add physical-room label/dimension toggles and editable doorway annotations
- Preserve doorway changes through undo, save, and revert
- Expand cabinet profiles, room layout, and Glitch scenario resources

Scenarios and gameplay
- Add per-assignment game configuration and runtime feature validation
- Add timed test wins and Frogger individual-home objectives
- Add the three-cabinet timed/score/Frogger test scenario
- Add Cabinet 1 Pac-Man RFID credits, credit sound, and CREDIT 0/1 display
- Require physical Start after an accepted scan and prevent duplicate credits

Pi hardware and deployment
- Add GPIO feedback sampling and a separate multiplexed RFID helper
- Validate reader pin ownership and input/output conflicts
- Add SSH cabinet startup and Pi desktop-session service support
- Add master packaging, site setup, SSH enrollment, and staged cabinet releases
- Verify deployment manifests and support explicit activation/status/log commands

Documentation and verification
- Consolidate the root README around the current architecture and setup workflow
- Add the complete master/Pi/RFID export and deployment walkthrough
- Document network isolation, credentials, card identification, and rollback
- Add PCB, room-editor, feedback, credit, deployment, and controller tests
- Isolate protocol tests from designer-edited cabinet configurations

Known limitations
- RFID helper compilation and physical Pi commissioning remain unverified
- Fleet provisioning does not yet configure RFID-only installations completely
- Broader Pac-Man tests retain a ghost-capture assertion failure
- Staged exports must be rebuilt and checked before deploying runtime 2.3
This commit is contained in:
Jeff Luke committed 2026-10-17 14:53:54 -07:00
1 parent 705ec31e0b
commit c1b2195834
90 files changed
+4969 -456

No files matched your search

+21 -2
View File
@@ -1,5 +1,5 @@
extends Node
const VERSION := "arcade-cabinet-2"
const VERSION := "arcade-cabinet-2.3"
var cabinet_id := "cabinet_01"
var database: ProjectDatabase = preload("res://data/project_database.tres")
var cabinet: CabinetProfile
@@ -14,6 +14,14 @@ var busy := false
var executing := false
var event_number := 0
var notice: Label
var test_win_remaining := -1.0
func _process(delta: float) -> void:
if phase != "playing" or test_win_remaining < 0.0 or game == null: return
test_win_remaining -= delta
if test_win_remaining <= 0.0:
test_win_remaining = -1.0
game.win_game()
func _ready() -> void:
var config := ConfigFile.new()
@@ -47,9 +55,13 @@ func _ready() -> void:
func poll() -> void:
if busy or executing: return
busy = true
if game != null and phase == "ready" and game.has_method("receive_device_feedback") and HardwareManager.real_gpio and HardwareManager.client != null:
game.receive_device_feedback(HardwareManager.client.get_feedback())
var sent := reports.duplicate(true)
var result: Dictionary = await connection.post("/cabinet/poll", {
"cabinet": cabinet_id, "protocol": 2, "boot": boot, "version": VERSION,
"features": ["assignment_config_v1", "device_feedback_v1", "rfid_credit_v1"],
"feedback": HardwareManager.client.get_feedback() if HardwareManager.real_gpio and HardwareManager.client != null else {},
"gpio": "real" if HardwareManager.real_gpio else "simulation", "reports": sent,
})
busy = false
@@ -70,6 +82,7 @@ func poll() -> void:
notice.text = "Waiting for the room operator" if game == null else ""
func unload() -> void:
test_win_remaining = -1.0
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
if is_instance_valid(game):
remove_child(game)
@@ -77,6 +90,10 @@ func unload() -> void:
game = null
func event(name: String) -> void:
if name == "started" and game != null:
test_win_remaining = float(game.run_config.get("test_auto_win_seconds", -1.0))
elif name in ["won", "lost"]:
test_win_remaining = -1.0
event_number += 1
reports.append({"kind": "event", "id": boot + ":" + str(event_number), "session": session, "event": name})
phase = {"started": "playing", "won": "won", "lost": "lost"}.get(name, phase)
@@ -110,7 +127,9 @@ func execute(command: Dictionary) -> void:
if str(definition.game_id) == "missile_command": game.set("relative_mouse_aim", true)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if str(definition.game_id) in ["missile_command", "tempest"] else Input.MOUSE_MODE_VISIBLE
add_child(game)
game.configure({})
game.configure(command.get("game_config", {}).duplicate(true))
if game.has_method("receive_device_feedback") and HardwareManager.client != null:
game.receive_device_feedback(HardwareManager.client.get_feedback())
game.game_started.connect(func(): event("started"))
game.game_won.connect(func(): event("won"))
game.game_lost.connect(func(): event("lost"))