Files
PQS/docs/ARCHITECTURE.md
T
2026-07-01 11:08:54 -05:00

233 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.15.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 25 + current pods
+ clock + rotating info panel. 10s refresh.
- **`smallcombinedqueue.php`** — compact version of the main wall (missions
15 + current pods + info panel), 5s refresh.
- **`genconqueue.php`** — "extended" wall showing missions 615 (further-out
queue). 30s refresh.
- **`combinedqueue2.php`** — mid-range wall showing missions 610 + 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 `<font>` 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.
```