From 5011c7041413ecf78cc74031e4c781aafa5130d1 Mon Sep 17 00:00:00 2001 From: Cyd Date: Wed, 1 Jul 2026 11:08:54 -0500 Subject: [PATCH] Add architecture documentation Co-Authored-By: Claude Opus 4.8 --- docs/ARCHITECTURE.md | 232 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 docs/ARCHITECTURE.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..fbc155a --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,232 @@ +# PQS — Player Queueing System + +Documentation generated from a full read of the codebase (2026-07-01). + +## 1. What this is + +PQS is a **player queueing and mission-management system for a pod-based +mech-combat simulator** (Virtual World / BattleTech-style networked cockpits). +It is deployed at large venues to register, queue, and cycle several thousand +players through a fixed number of physical pods across a day. + +The system runs entirely on an **air-gapped LAN**. There is no external +internet dependency and DB credentials (`pqs`/`pqs` on `localhost`) are a +formality of that closed network. + +Branding strings in the code reference "FallOut Shelter" and "Cougar CON" +(the operator / an event); the sim itself is referred to as **FS** (the game +pods that consume `getFS*.php`). + +## 2. Technology stack + +| Layer | Technology | +|-------|-----------| +| Server | PHP (written for PHP 5.4/5.5, `mysqli` procedural API) | +| Database | MySQL 5.1–5.5, MyISAM/InnoDB mix (`pqs` schema) | +| Frontend | Static HTML + jQuery 1.x, AJAX polling via `setInterval` | +| Pod / kiosk hardware | Raspberry Pi running `omxplayer` for intro/trainer videos, GPIO buttons (`docs/omx_button.py`, `docs/get.py`) | +| Time sync | `time.php` shells out to `sudo date -s` to set box clock from a browser | + +There is **no framework, no router, no build step, no dependency manager**. +Each `.php` file is a standalone page reached directly by URL. Shared logic +lives in `includes/`. + +## 3. Physical / deployment topology + +``` + ┌─────────────────────────────────────────┐ + │ Air-gapped venue LAN (10.0.0.x) │ + │ │ + Players ─────▶│ Registration kiosk → callsign.php │ + │ │ + Staff ─────▶│ Reg console → registration.php │ + Staff ─────▶│ Game/ops console → console.php │ + │ │ + Big TVs ─────▶│ Queue displays → combinedqueue.php │ + │ smallcombinedqueue │ + │ genconqueue.php │ + │ │ + Game pods ────▶│ Poll getFSgame.php (mission settings) │ + (FS sim) │ Poll getFSplayers.php (roster/pods CSV) │ + │ │ + Pi video ────▶│ Poll playFSTrainer.php (missing file) │ + │ → triggers omxplayer intro video │ + └─────────────────────────────────────────┘ + All → single PHP + MySQL host +``` + +## 4. Core domain model + +The whole system revolves around **missions** (one pod launch) that fill up +with **players**, then get **committed** (launched) one at a time. + +### Lifecycle of a mission + +1. **Build** — missions are pre-created (`registration.php?build=N`) or created + on demand when a player registers and no open mission exists. +2. **Fill** — players self-register at `callsign.php` (kiosk) or staff add them + in `registration.php`. Each mission holds up to `MissionSize` players + (= number of active pods). +3. **Lock** — staff can lock a mission so no new players join + (`locked = 1`). +4. **Commit** — at `console.php` ("Commit Mission"), the lowest-numbered queued + mission's players are: + - copied into `pqs_currentmission` (who is in the pods right now), + - archived into `pqs_pastmissions` (history, with `ctime` timestamp), + - deleted from `pqs_queue`, + - the mission row marked `Completed = 1`. +5. **Display** — `ctime` of the last commit + `timepergame + timetoreset` + drives every "ETD" (estimated departure) and countdown on the display walls. + +### Group registration (squads) + +`callsign.php` supports **Group Leader / Group Member** flows: a leader reserves +`N` slots in one mission, creating placeholder rows `LEADER-2`, `LEADER-3`…; +members later "claim" a placeholder by matching `CallSign LIKE 'who-%'`. +Gated by the `GroupAvalible` config flag. + +## 5. Database schema (`docs/pqs.sql`) + +| Table | Purpose | +|-------|---------| +| `pqs_queue` | Players currently waiting, keyed to a `MissionID`. Core working set. | +| `pqs_mission` | One row per mission: size, #players, completed/locked flags, gametype, map, time. | +| `pqs_currentmission` | Snapshot of who is in the pods for the mission that just launched. | +| `pqs_pastmissions` | Append-only history (callsign, mech, mission, `ctime`). Drives all ETD math. | +| `pqs_gameconfig` | Single-row global settings: pod count, time-per-game, weather, radar, friendly fire, etc. | +| `pqs_pods` | Single-row 16 booleans: which physical pods (`pod01`..`pod16`) are active. | +| `pqs_mechinfo` | Static catalog of ~63 mechs (class, tons, stats, roster #, image). | +| `pqs_mapinfo` | Static catalog of maps and which game types each supports. | +| `pqs_gametype` | Static catalog of the 10 game modes. | +| `pqs_infopanel` | Single `Updated` flag used to trigger display-wall ad rotation refresh. | + +Notes: +- `pqs.sql` is the canonical schema; `dbbuild.sql` is an older SQLyog dump with + sample data. +- "Number of pods" is duplicated in three places (`pqs_gameconfig.numpods`, + the 16 booleans in `pqs_pods`, and each `pqs_mission.MissionSize`) and kept + in sync manually by `console.php` on every config save. + +## 6. File-by-file reference + +### Player-facing (kiosk) +- **`callsign.php`** — the player registration kiosk. 1024×768 themed screen: + enter callsign, pick class → mech, optional group role, submit. Inserts into + `pqs_queue`, auto-creating a mission if none is open. Shows the assigned + mission # and ETD, then returns to itself. + +### Staff-facing +- **`registration.php`** — staff registration console. Add / edit / delete + players, add one player to multiple future missions, build missions in bulk, + edit a mission's game type & map, lock missions, search. Polls + `includes/getreg.php` every 1s. +- **`console.php`** — game-ops console. Sets global game configuration + (time limits, pods active, weather/radar/damage rules, etc. → `pqs_gameconfig` + + `pqs_pods`), locks and **commits** missions. Polls `getconsole.php` + + `mechpods.php` every 2s. +- **`history.php`** — paginated per-mission history viewer (prev/next), reads + `pqs_pastmissions` joined to `pqs_mission`. +- **`search.php`** — look up a callsign across queued missions with ETDs. +- **`time.php`** — sets the server clock from the browser's JS time via + `sudo date -s` (keeps the air-gapped box's clock correct without NTP). + +### Display walls (read-only, auto-refresh) +- **`combinedqueue.php`** — main wall: next mission + groups 2–5 + current pods + + clock + rotating info panel. 10s refresh. +- **`smallcombinedqueue.php`** — compact version of the main wall (missions + 1–5 + current pods + info panel), 5s refresh. +- **`genconqueue.php`** — "extended" wall showing missions 6–15 (further-out + queue). 30s refresh. +- **`combinedqueue2.php`** — mid-range wall showing missions 6–10 + current pods + + clock + info panel, 10s refresh. + +### Pod / hardware integration (plain-text output, no HTML) +- **`getFSgame.php`** — emits the next mission's settings as newline-delimited + values (gametype, map, visibility, weather, … cameraship) for the sim to read. +- **`getFSplayers.php`** — emits 16 CSV lines (one per pod slot): + `active,callsign,rosterNum,team,unit` — the pod loadout for the next mission. +- **`playFSTrainer.php`** — *referenced by `docs/get.py` but missing from the + repo*; was polled by a Raspberry Pi to trigger the trainer video. + +### `includes/` (AJAX fragments & shared code) +- `config.php` — DB connect + load `pqs_gameconfig`/`pqs_pods` into `$_SESSION`. +- `getreg.php` / `getqueue.php` / `getconsole.php` / `getcurrent.php` / + `getgroup.php` — HTML fragments rendered into the consoles/walls via AJAX. +- `now.php` — clock + "estimated time remaining" for current mission. +- `mechpods.php` — echoes active pod count from session. +- `api.php` — tiny AJAX endpoint: toggle a player's "merc" flag, poll the + infopanel `Updated` flag. +- `infopanel.php` / `smallinfopanel.php` — rotating advertisement/announcement + slides (jQuery Cycle). +- `graph.php` — GD-generated progress-bar PNG (appears unused by current pages). + +### `docs/` (assets & operator scratch) +- `pqs.sql`, `dbbuild.sql` — schema / seed dumps. +- `omx_button.py`, `get.py` — Raspberry Pi video-kiosk helper scripts. +- `Clear Code.txt` — the SQL to reset for a new event (truncate the 4 live tables). +- `readme screen adjust.txt`, `SQL_TCPip_Fixes.reg`, installer `.exe`s, `.mp4` + intro/roleplay videos, spare image sets. + +## 7. Known issues, risks & technical debt + +These matter most for the "improve vs. rewrite" decision. + +**Correctness / concurrency** +- **No transactions or locking.** Filling a mission is a read-`numplayers` → + compare → `UPDATE +1` sequence with no atomicity. With thousands of players + and a kiosk plus staff console writing concurrently, **race conditions can + overfill a pod bay** ("Containment Bay OVERLOADED" is literally a handled UI + state). `numplayers` is a manually-maintained counter that can drift from the + actual `COUNT(*)` in `pqs_queue`. +- Pod-count is denormalized across three tables and reconciled by hand. + +**Security (mitigated only by the air gap)** +- **SQL injection is pervasive** — nearly every query interpolates `$_GET`/ + `$_POST` directly (e.g. `console.php` `nummercs`, `api.php` `playerid`, + callsign fields). Safe *only* because the network is closed and trusted. +- `time.php` passes a request value into `exec("sudo date -s …")` — command + execution by design; would be dangerous on any reachable network. +- `api.php` and parts of `registration.php`/`console.php` still emit `DEBUG:` + output to the page. + +**Maintainability** +- Presentation, business logic, and SQL are fully interleaved in every file; + connection boilerplate is copy-pasted dozens of times. +- Deprecated `mysql_error()` calls remain alongside `mysqli_*` (would fatal on + PHP 7+). The code targets an EOL PHP 5.x runtime. +- 16 pods are hard-coded as `pod01..pod16` everywhere (schema, console, FS + output) — changing pod count is a code edit, not config. +- Display refresh is fixed-interval full-fragment polling (no change detection + except the one infopanel flag). +- Inline `` tags, table layouts, and absolute pixel sizes throughout. + +**Runtime assumptions** +- Timezone hard-coded to `America/Chicago` in most files. +- URLs hard-coded to `/pqs/…` and to server IP via `$_SERVER['SERVER_ADDR']`. + +## 8. Strengths worth preserving + +- The **domain model is sound and battle-tested**: mission → fill → lock → + commit → history, with ETD projected from the last commit time. Any rewrite + should keep this exact lifecycle. +- It is **operationally simple**: drop PHP files on one box, point kiosks and + TVs at URLs. No build, no services to orchestrate — valuable at a venue. +- Feature set is complete for real events: group/squad reservations, per-mission + game/map overrides, merc tracking, bulk mission build, history, live walls, + and direct pod integration. + +## 9. Improve vs. rewrite — starting point + +If **improving in place**, the highest-value, lowest-risk fixes first: +1. Make mission-fill atomic (transaction + `SELECT … FOR UPDATE`, or derive + `numplayers` from `COUNT(*)`) to end overfill races. +2. Parameterize all queries (mysqli prepared statements) and remove `DEBUG:` + output. +3. Centralize DB access into one include; replace remaining `mysql_*` calls. +4. Make pod count and timezone configuration-driven, not hard-coded. + +If **rewriting from scratch**, preserve section 4's lifecycle and section 5's +tables as the spec, and treat `getFSgame.php` / `getFSplayers.php` output +formats as a **hard contract** the physical pods depend on — document those +byte-for-byte before changing anything on the sim side. +```