diff --git a/README.md b/README.md index b6e2ca3..7f53028 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Combined working repo for the TWS Plutonium Zombies GSC scripts across two games ## Layout - [t5/](t5/) — Plutonium **T5** (Call of Duty: Black Ops 1) Zombies scripts. Most-developed baseline. See [t5/README.md](t5/README.md) and [t5/NOTES.md](t5/NOTES.md). -- [t6/](t6/) — Plutonium **T6** (Call of Duty: Black Ops 2) Zombies scripts. Ported from T5; at parity on common, HUD, AFK, lock, telemetry, join and leaderboard. Only `db` and `bank` remain T5-only. See [t6/README.md](t6/README.md), [t6/NOTES.md](t6/NOTES.md), and [t6/tools/check.ps1](t6/tools/check.ps1). +- [t6/](t6/) — Plutonium **T6** (Call of Duty: Black Ops 2) Zombies scripts. Ported from T5; at parity on common, HUD, AFK, lock, telemetry, join and leaderboard. See [t6/README.md](t6/README.md), [t6/NOTES.md](t6/NOTES.md), and [t6/tools/check.ps1](t6/tools/check.ps1). ## Parity @@ -15,21 +15,17 @@ Modules currently in each tree: | Module | T5 | T6 | |------------|-------------------------------|---------------------------------| -| common | `t5/tws_common.gsc` | `t6/tws_t6_common.gsc` | -| hud | `t5/tws_hud.gsc` | `t6/tws_t6_hud.gsc` | -| afk | `t5/tws_afk.gsc` | `t6/tws_t6_afk.gsc` | -| lock | `t5/tws_lock.gsc` | `t6/tws_t6_lock.gsc` | -| telemetry | `t5/tws_telemetry.gsc` | `t6/tws_t6_telemetry.gsc` — queued `game_start` / `heartbeat` / `round_change` / `game_end` matching T5 | -| join | `t5/tws_join.gsc` | `t6/tws_t6_join.gsc` | -| leaderboard | `t5/tws_leaderboard.gsc` | `t6/tws_t6_leaderboard.gsc` | -| db | `t5/tws_db.gsc` | *(not ported)* | -| bank | `t5/tws_bank.gsc` | *(not ported)* | - -`db` and `bank` are T5-only for now. +| common | `t5/tws_t5_common.gsc` | `t6/tws_t6_common.gsc` | +| hud | `t5/tws_t5_hud.gsc` | `t6/tws_t6_hud.gsc` | +| afk | `t5/tws_t5_afk.gsc` | `t6/tws_t6_afk.gsc` | +| lock | `t5/tws_t5_lock.gsc` | `t6/tws_t6_lock.gsc` | +| telemetry | `t5/tws_t5_telemetry.gsc` | `t6/tws_t6_telemetry.gsc` — queued `game_start` / `heartbeat` / `round_change` / `game_end` matching T5 | +| join | `t5/tws_t5_join.gsc` | `t6/tws_t6_join.gsc` | +| leaderboard | `t5/tws_t5_leaderboard.gsc` | `t6/tws_t6_leaderboard.gsc` | ## Map record announcements -Install `tws_leaderboard.gsc` for T5 or `tws_t6_leaderboard.gsc` for T6, +Install `tws_t5_leaderboard.gsc` for T5 or `tws_t6_leaderboard.gsc` for T6, **along with the updated common module** for that game. The new module privately announces the current map's fleet-wide highest recorded round and every tied holder once per connection, including spectator newcomers. diff --git a/t5/NOTES.md b/t5/NOTES.md index 12ef52b..b99bf69 100644 --- a/t5/NOTES.md +++ b/t5/NOTES.md @@ -1,16 +1,19 @@ -# tws-t5-aio notes +# TWS T5 script notes -Reference for the `tws_*.gsc` files in this repo. Platform quirks and +Reference for the `tws_t5_*.gsc` files in this repo. Platform quirks and load-bearing decisions that show up as invariants in the code. ## Install -Copy all `tws_*.gsc` to `storage/t5/scripts/sp/` on the Plutonium T5 server +Copy all `tws_t5_*.gsc` to `storage/t5/scripts/sp/` on the Plutonium T5 server (v2 Plutainer: `app/runtime/plutonium/storage/t5/scripts/sp/`). - `sudo chown -R 1000:1000 ` — Plutainer runs as UID 1000 - `docker exec rcon-cli map_restart` — reloads scripts in place +For existing installations, follow the [upgrade cleanup](README.md#install--upgrade) +before copying the renamed scripts. + ## Platform quirks (T5 SP engine, Plutonium) - **Initialize locals before loops when reading them afterwards.** T5 rejected @@ -18,9 +21,9 @@ Copy all `tws_*.gsc` to `storage/t5/scripts/sp/` on the Plutonium T5 server inside the map-search loop, even though its later use was guarded by the match count. It now starts as `undefined` before that loop in both versions. The JavaScript fixture harness and T6 compiler did not catch this T5 error. -- **One `level waittill("connected")` only.** `tws_common` owns it. A +- **One `level waittill("connected")` only.** `tws_t5_common` owns it. A second listener breaks the T5 SP connect handshake. Per-player work goes - through `tws_common::register_spawn_handler` after `spawned_player`, or + through `tws_t5_common::register_spawn_handler` after `spawned_player`, or `register_connect_handler` for spectator-safe work after the dispatcher yields. Leaderboard uses the latter; its level worker owns all HTTP work. - **Chat uses `level waittill("say", ...)`**, not `callbackPlayerSay`. T5 SP @@ -39,9 +42,9 @@ Copy all `tws_*.gsc` to `storage/t5/scripts/sp/` on the Plutonium T5 server - **Zombies namespace is WaW-derived** (`maps\_zombiemode_*`), not BO2's `maps\mp\zombies\_zm_*`. - **Check `t5-gsc-utils` source as well as its README.** Current source - includes `http::request` and `jsonSerialize` / `jsonParse`, in addition - to file IO, console commands and MySQL. Telemetry requires a DLL built - with these HTTP/JSON components; older builds may lack them. + includes `http::request` and `jsonSerialize` / `jsonParse`. Telemetry and + leaderboard require a DLL built with these HTTP/JSON components; older + builds may lack them. See [HTTP source](https://github.com/alicealys/t5-gsc-utils/blob/main/src/component/http.cpp) and [JSON source](https://github.com/alicealys/t5-gsc-utils/blob/main/src/component/json.cpp). @@ -59,35 +62,14 @@ Copy all `tws_*.gsc` to `storage/t5/scripts/sp/` on the Plutonium T5 server is intercepted by the zombiemode scripts before it reaches our scope, and polling `self.health` misses the zero-crossing (health is rewritten during the game-over sequence). -- **Bank writes DB-first, memory-second.** Every mutation writes to the DB - and only updates `self.bank` / `self.score` after the write confirms. A - dead DB can never create phantom money. Deposits use `INSERT ... ON - DUPLICATE KEY UPDATE`; withdrawals gate on `WHERE balance >= ?` so the DB - itself blocks overdrafts under concurrent commands. -- **Bank command surface is `command::add_sv` (server console), not chat.** - SP-engine chat is unreliable and `chat::register_command` isn't available - in `t5-gsc-utils`. - -## Current state / caveats - -- `bank_load` in `tws_bank.gsc` is a **diagnostic stub**: bare minimum to - isolate what specifically kills the connect handshake. Do not restore the - stripped `getGuid` / MySQL body until the stub passes join cleanly. -- DB credentials in `tws_db.gsc` are hardcoded for the initial fleet test. - Move to dvars (`tws_db_host` / `tws_db_user` / `tws_db_password` / - `tws_db_name`) before this repo goes public — otherwise the fleet's DB - password lives in source. - ## Module map | File | Role | Fires notifies | Reads notifies | |---|---|---|---| -| `tws_common` | Shared config, HUD helpers, `.help` registry, spawn-handler registry, single connect listener + dispatcher | — | `say` | -| `tws_db` | MariaDB pool config + reachability probe | `db_probe_done` | — | -| `tws_hud` | Timer row, zombie count, health bar | `hud_round_changed`, `hud_match_started`, `hud_game_over` | (internal) | -| `tws_bank` | Persistent bank + `bank` / `deposit` / `withdraw` commands | — | `db_probe_done` | -| `tws_afk` | `.afk` / `.back` chat toggle, owns its [AFK] indicator | — | — | -| `tws_join` | `.join` for spectators who have not played on this connection | — | `say`, `end_game`, `game_ended`, `intermission` | -| `tws_lock` | Lobby `g_password` lock at milestone round (`tws_lock_round`, default 20) | — | `hud_round_changed`, `hud_game_over` | -| `tws_telemetry` | Ordered JSON game start, round, 30-second heartbeat, and game end reports with full player snapshots | `telemetry_queued`, `telemetry_finished` (internal) | `end_game`, `game_ended`, `intermission` | -| `tws_leaderboard` | Cached completed-match map record and all tied holders, privately on join | — | `end_game`, `game_ended`, `intermission` | +| `tws_t5_common` | Shared config, HUD helpers, `.help` registry, spawn-handler registry, single connect listener + dispatcher | — | `say` | +| `tws_t5_hud` | Timer row, zombie count, health bar | `hud_round_changed`, `hud_match_started`, `hud_game_over` | (internal) | +| `tws_t5_afk` | `.afk` / `.back` chat toggle, owns its [AFK] indicator | — | — | +| `tws_t5_join` | `.join` for spectators who have not played on this connection | — | `say`, `end_game`, `game_ended`, `intermission` | +| `tws_t5_lock` | Lobby `g_password` lock at milestone round (`tws_lock_round`, default 20) | — | `hud_round_changed`, `hud_game_over` | +| `tws_t5_telemetry` | Ordered JSON game start, round, 30-second heartbeat, and game end reports with full player snapshots | `telemetry_queued`, `telemetry_finished` (internal) | `end_game`, `game_ended`, `intermission` | +| `tws_t5_leaderboard` | Cached completed-match map record and all tied holders, privately on join | — | `end_game`, `game_ended`, `intermission` | diff --git a/t5/README.md b/t5/README.md index ddba5cf..68d8904 100644 --- a/t5/README.md +++ b/t5/README.md @@ -1,9 +1,8 @@ -# tws-t5-aio +# TWS T5 Zombies scripts -Custom HUD + persistent bank + AFK toggle + immediate join + lobby lock + game telemetry + map records for Plutonium T5 -(Black Ops 1) zombies dedicated servers. Split across nine `.gsc` files, -each installable / removable in isolation (with `tws_common` as the shared -hub). +Custom HUD + AFK toggle + immediate join + lobby lock + game telemetry + map records for Plutonium T5 +(Black Ops 1) zombies dedicated servers. Split across seven `.gsc` files, +with `tws_t5_common` as the shared hub. Lobby lock also requires the HUD module. Platform quirks and deploy commands: [NOTES.md](NOTES.md). @@ -11,19 +10,30 @@ Platform quirks and deploy commands: [NOTES.md](NOTES.md). | File | Role | |---|---| -| [tws_common.gsc](tws_common.gsc) | Shared config, HUD helpers, `.help`, connect/spawn registries, single `connected` listener + dispatcher | -| [tws_db.gsc](tws_db.gsc) | MariaDB pool config + reachability probe | -| [tws_hud.gsc](tws_hud.gsc) | Bottom-left HUD stack: timers, zombie count, health bar | -| [tws_bank.gsc](tws_bank.gsc) | Persistent per-player bank + console commands | -| [tws_afk.gsc](tws_afk.gsc) | `.afk` / `.back` chat toggle + `[AFK]` indicator | -| [tws_join.gsc](tws_join.gsc) | `.join` lets newcomers spawn before the current round ends | -| [tws_lock.gsc](tws_lock.gsc) | Lobby `g_password` lock at a milestone round | -| [tws_telemetry.gsc](tws_telemetry.gsc) | Direct HTTP game start, round, heartbeat, and game end reports | -| [tws_leaderboard.gsc](tws_leaderboard.gsc) | Private map high-round record and all tied holders on connection | +| [tws_t5_common.gsc](tws_t5_common.gsc) | Shared config, HUD helpers, `.help`, connect/spawn registries, single `connected` listener + dispatcher | +| [tws_t5_hud.gsc](tws_t5_hud.gsc) | Bottom-left HUD stack: timers, zombie count, health bar | +| [tws_t5_afk.gsc](tws_t5_afk.gsc) | `.afk` / `.back` chat toggle + `[AFK]` indicator | +| [tws_t5_join.gsc](tws_t5_join.gsc) | `.join` lets newcomers spawn before the current round ends | +| [tws_t5_lock.gsc](tws_t5_lock.gsc) | Lobby `g_password` lock at a milestone round | +| [tws_t5_telemetry.gsc](tws_t5_telemetry.gsc) | Direct HTTP game start, round, heartbeat, and game end reports | +| [tws_t5_leaderboard.gsc](tws_t5_leaderboard.gsc) | Private map high-round record and all tied holders on connection | + +## Install / upgrade + +Copy `tws_t5_*.gsc` to `storage/t5/scripts/sp/`, then restart the map. +When upgrading an existing installation, first remove these old scripts from +that directory: + +- `tws_bank.gsc` and `tws_db.gsc` (removed modules) +- `tws_common.gsc`, `tws_hud.gsc`, `tws_afk.gsc`, `tws_join.gsc`, + `tws_lock.gsc`, `tws_telemetry.gsc` and `tws_leaderboard.gsc` (renamed modules) + +Do not leave both common scripts installed: duplicate connection listeners +break T5's connect handshake. Existing dvar names are unchanged. ## Map record announcements -Copy `tws_leaderboard.gsc` and the updated `tws_common.gsc` to +Copy `tws_t5_leaderboard.gsc` and the updated `tws_t5_common.gsc` to `storage/t5/scripts/sp/`, then restart the map. Leaderboard requires a [t5-gsc-utils](https://github.com/alicealys/t5-gsc-utils) build exposing `http::request`, `jsonParse`, `typeof` and `formatString`. A plugin upgrade @@ -31,7 +41,7 @@ requires a full server-process restart. To omit leaderboard, remove its script; the enable dvar does not bypass native linking dependencies. Records are read from the public Electric Cherry API with no credentials and -work independently of telemetry, HUD and the bank. Spectator newcomers receive +work independently of telemetry and HUD. Spectator newcomers receive the same private announcement as playable joins. T5 matches its engine map to the API's map list. [Shared configuration, caching and validation](../README.md#map-record-announcements) apply to both games. @@ -52,62 +62,55 @@ counter and health display. Typing `.help` again restarts that display time. | `.join` | Join mid-round (newcomers only) | Each module registers its own help entries; removing AFK or join also removes -those entries from `.help` after a map restart. The bank's `bank`, `deposit`, and -`withdraw` commands are server-console commands and are not listed in chat help. +those entries from `.help` after a map restart. ## Dependency graph Arrows read "depends on" — solid means the source names the target with -`scripts\sp\tws_common::...`, dashed means the source waits on a level -notify fired by the target. Transitive edges are elided (so `tws_bank` -and `tws_lock` reach `tws_common` through `tws_db` / `tws_hud` on the -graph, but they also call `tws_common` directly). +`scripts\sp\tws_t5_common::...`, dashed means the source waits on a level +notify fired by the target. ```mermaid graph BT - common[tws_common
config + helpers + dispatcher] + common[tws_t5_common
config + helpers + dispatcher] - db[tws_db] - hud[tws_hud] - afk[tws_afk] - join[tws_join] - bank[tws_bank] - lock[tws_lock] - telemetry[tws_telemetry] - leaderboard[tws_leaderboard] + hud[tws_t5_hud] + afk[tws_t5_afk] + join[tws_t5_join] + lock[tws_t5_lock] + telemetry[tws_t5_telemetry] + leaderboard[tws_t5_leaderboard] - db --> common hud --> common afk --> common join --> common + lock --> common telemetry --> common leaderboard --> common - bank -. db_probe_done .-> db lock -. hud_round_changed
hud_game_over .-> hud ``` -- `tws_common` is the only module named by other files at compile time. +- `tws_t5_common` is the only module named by other files at compile time. - All other cross-module coupling flows through level notifies - (`db_probe_done`, `hud_round_changed`, `hud_match_started`, + (`hud_round_changed`, `hud_match_started`, `hud_game_over`) or through spawn handlers registered with - `tws_common::register_spawn_handler` / `register_connect_handler`. -- Any file except `tws_common` can be removed; the rest still load. - `tws_afk` has no downstream consumers — pull it independently. - `tws_bank` needs `tws_db`; `tws_lock` needs `tws_hud`. + `tws_t5_common::register_spawn_handler` / `register_connect_handler`. +- Any file except `tws_t5_common` can be removed; the rest still load. + `tws_t5_afk` has no downstream consumers — pull it independently. + `tws_t5_lock` needs `tws_t5_hud` to receive round and game-over notifications. ## Runtime notifies | Notify | Fired by | Read by | |---|---|---| -| `db_probe_done` | `tws_db` | `tws_bank` (via `bank_deferred_start`) | -| `hud_round_changed` | `tws_hud` | `tws_hud` (round-timer reset), `tws_lock` | -| `hud_match_started` | `tws_hud` | `tws_hud` (match-timer reveal) | -| `hud_game_over` | `tws_hud` | `tws_hud` (freeze timers), `tws_lock` (clear password) | +| `hud_round_changed` | `tws_t5_hud` | `tws_t5_hud` (round-timer reset), `tws_t5_lock` | +| `hud_match_started` | `tws_t5_hud` | `tws_t5_hud` (match-timer reveal) | +| `hud_game_over` | `tws_t5_hud` | `tws_t5_hud` (freeze timers), `tws_t5_lock` (clear password) | --- -## tws_common.gsc +## tws_t5_common.gsc Hub file. Shared config, HUD helpers, spawn-handler registry, and the single `level waittill("connected")` in the fleet. Adding a second @@ -153,31 +156,7 @@ connect listener anywhere else crashes the T5 SP handshake. --- -## tws_db.gsc - -**Features** -- One MariaDB connection pool for the whole script (used by bank now, - leaderboard later) -- Reachability probe: round-trips `SELECT 1` and flips `level.db_online` -- Fires `db_probe_done` so waiters can proceed regardless of outcome - -**Provides** -| Level var / notify | Meaning | -|---|---| -| `level.db_online` | `true` iff the probe query returned without error | -| `level.db_probe_done` | `true` once the probe has completed (either way) | -| notify `db_probe_done` | Fired once after probe completion | - -**Depends on** -- `scripts\sp\tws_common::ensure_config()` -- `t5-gsc-utils` (`mysql::set_config`, `mysql::query`) - -**Config (currently hardcoded — TODO: dvarize before repo goes public)** -- Host `mariadb`, port `3306`, DB `tws_zm`, user `josh`, password `joshster` - ---- - -## tws_hud.gsc +## tws_t5_hud.gsc Bottom-left cluster. Per-player elements spawn via `::hud_start`. @@ -201,56 +180,17 @@ Spawn handler registered: `::hud_start` — threads `hud_timer_row`, `hud_zombie_counter`, `hud_health_display`. **Depends on** -- `scripts\sp\tws_common::` — `ensure_config`, `make_text_elem`, +- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_text_elem`, `make_shader_elem`, `destroy_on_disconnect`, `format_mmss`, `register_spawn_handler` -- Every `level.hud_*` config value from `tws_common` +- Every `level.hud_*` config value from `tws_t5_common` - Stock BO1 state: `level.round_number`, `level.zombie_total`, `level.players`, `self.health`, `self.maxhealth`, `self.name` / `self.playername`, AI entities' `.animname` and `.ignore_enemy_count` --- -## tws_bank.gsc - -Persistent per-player bank. Balances survive server hops within the fleet -because rows are keyed on XUID. - -**Features** -- Server-console commands: `bank`, `deposit `, `withdraw ` -- DB-first / memory-second: `self.bank` and `self.score` only move after - the DB write confirms -- Deposits: `INSERT ... ON DUPLICATE KEY UPDATE` — lazy row create, atomic -- Withdrawals: `WHERE balance >= ?` — DB blocks overdrafts under concurrency -- Score-drift refund guard on deposit -- `bank_load` currently a **diagnostic stub** (bare-minimum to isolate the - connect-handshake crash — do not restore the stripped body until it passes - join) - -**Provides** -| Command | Args | Effect | -|---|---|---| -| `bank` | — | Prints current `Bank` and `Score` to the caller | -| `deposit` | `` | Score → bank, DB-first with refund on race | -| `withdraw` | `` | Bank → score, DB-first with overdraft guard | - -Per-player state: `self.bank` (int), `self.bank_unavailable` (bool), -`self.bank_guid` (set in the full impl — currently unset by the stub). - -**Depends on** -- `scripts\sp\tws_common::` — `ensure_config`, `register_spawn_handler` -- `tws_db` — waits on `db_probe_done`, gates on `level.db_online` -- `t5-gsc-utils` — `mysql::query`, `mysql::prepared_statement`, - `command::add_sv` -- Stock BO1: `self.score` -- Dvar: `tws_server_id` (optional; labels `last_server` on bank rows) -- External: MariaDB schema — `bank(guid, balance, last_name, last_server)` - -Spawn handler registered: `::bank_load`. - ---- - -## tws_afk.gsc +## tws_t5_afk.gsc Self-invoked pause mode. Owns the `[AFK]` indicator element entirely — HUD module has zero knowledge of AFK. @@ -271,7 +211,7 @@ module has zero knowledge of AFK. - Level var: `level.afk_grace_seconds` (default 2) **Depends on** -- `scripts\sp\tws_common::` — `ensure_config`, `make_text_elem`, +- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_text_elem`, `destroy_on_disconnect`, `register_spawn_handler`, `register_help_command` - Shared config: `level.hud_x`, `level.hud_row_match`, `level.hud_font_small`, `level.hud_c_afk` @@ -282,10 +222,10 @@ Spawn handler registered: `::afk_spawn`. --- -## tws_join.gsc +## tws_t5_join.gsc Type `.join` in chat while spectating after joining a match in progress to -spawn immediately. Install with `tws_common.gsc`; no HUD or DB dependency. +spawn immediately. Install with `tws_t5_common.gsc`; no HUD dependency. **Eligibility** @@ -323,7 +263,7 @@ and [spawn entry point](https://github.com/plutoniummod/t5-scripts/blob/main/ZM/ --- -## tws_lock.gsc +## tws_t5_lock.gsc **Features** - Sets `g_password` to a random per-lobby value when the round crosses a @@ -339,8 +279,8 @@ and [spawn entry point](https://github.com/plutoniummod/t5-scripts/blob/main/ZM/ | `level.lock_active` | `true` after the password has been set | **Depends on** -- `scripts\sp\tws_common::` — `ensure_config`, `int_dvar_or` -- `tws_hud` — waits on `hud_round_changed` and `hud_game_over` +- `scripts\sp\tws_t5_common::` — `ensure_config`, `int_dvar_or` +- `tws_t5_hud` — waits on `hud_round_changed` and `hud_game_over` - Stock BO1: `level.round_number` - Dvar: `tws_lock_round` (int, default `20`) @@ -351,17 +291,17 @@ and [spawn entry point](https://github.com/plutoniummod/t5-scripts/blob/main/ZM/ | Dvar | Consumer | Default | Purpose | |---|---|---|---| | `zombiemode` | every module | — | Gate: every `init()` returns early unless `"1"` | -| `tws_lock_round` | `tws_lock` | `20` | Round at which the lobby locks | -| `tws_server_id` | `tws_bank`, `tws_telemetry` | Required for telemetry | Exact registered Electric Cherry server name; also used as the bank label | -| `tws_telemetry_enabled` | `tws_telemetry` | `1` | Set to `0` to disable telemetry at init | -| `tws_telemetry_url` | `tws_telemetry` | `https://ec.thewrightserver.net/api/telemetry` | Electric Cherry telemetry endpoint for JSON POSTs | -| `tws_telemetry_log_level` | `tws_telemetry` | `1` | Read live: `0` warnings/errors/recovery only, `1` lifecycle/request/health logs, `2` also capture/queue logs and response previews | +| `tws_lock_round` | `tws_t5_lock` | `20` | Round at which the lobby locks | +| `tws_server_id` | `tws_t5_telemetry` | Required for telemetry | Exact registered Electric Cherry server name | +| `tws_telemetry_enabled` | `tws_t5_telemetry` | `1` | Set to `0` to disable telemetry at init | +| `tws_telemetry_url` | `tws_t5_telemetry` | `https://ec.thewrightserver.net/api/telemetry` | Electric Cherry telemetry endpoint for JSON POSTs | +| `tws_telemetry_log_level` | `tws_t5_telemetry` | `1` | Read live: `0` warnings/errors/recovery only, `1` lifecycle/request/health logs, `2` also capture/queue logs and response previews | -## tws_telemetry.gsc +## tws_t5_telemetry.gsc Uses `t5-gsc-utils` HTTP/JSON components to POST directly to Electric Cherry's -`/api/telemetry`. Install alongside `tws_common.gsc` in `storage/t5/scripts/sp/`. -No HUD or database dependency. Reports run whether or not a beta is active. +`/api/telemetry`. Install alongside `tws_t5_common.gsc` in `storage/t5/scripts/sp/`. +No HUD dependency. Reports run whether or not a beta is active. Follow the shared [authentication setup](../README.md#telemetry-authentication-t5-and-t6) to configure the service before upgrading. The reporter token is embedded in `level.telemetry_token`; it must match the service's `EC_TELEMETRY_TOKEN`. Short @@ -503,7 +443,7 @@ and [score tracking](https://github.com/plutoniummod/t5-scripts/blob/main/ZM/Com 4. Delay HTTP completion while rounds advance and the match ends. Verify requests remain ordered and retain the stats captured at each event. With the API unreachable, verify transport-error logging and continued - gameplay. Repeat without an active beta and with HUD/DB modules removed. + gameplay. Repeat without an active beta and with the HUD module removed. 5. Stay in one round for over a minute. Verify heartbeats at 30 and 60 seconds after start, with current stats and the same fields as `round_change`. Check that round changes and joins do not reset or duplicate the interval, @@ -522,11 +462,7 @@ and [score tracking](https://github.com/plutoniummod/t5-scripts/blob/main/ZM/Com ## External dependencies -- **[t5-gsc-utils](https://github.com/alicealys/t5-gsc-utils)** — required. - `tws_db` and `tws_bank` need the `mysql::` namespace; `tws_bank` also - needs `command::add_sv`. -- **MariaDB / MySQL server** reachable from the Plutonium container as - `mariadb:3306` (currently hardcoded — see `tws_db.gsc`). -- **Schema** — `bank(guid VARCHAR PRIMARY KEY, balance INT, last_name - VARCHAR, last_server VARCHAR)`. `tws_bank` lazily inserts rows on first - deposit. +[t5-gsc-utils](https://github.com/alicealys/t5-gsc-utils) is required for telemetry +and leaderboard HTTP, JSON, type and string helpers. Install a build exposing +the natives listed in each module's documentation, then restart the server +process. No database is required. diff --git a/t5/tws_bank.gsc b/t5/tws_bank.gsc deleted file mode 100644 index 305f96e..0000000 --- a/t5/tws_bank.gsc +++ /dev/null @@ -1,229 +0,0 @@ -/* - tws_bank.gsc — persistent per-player bank, MariaDB-backed. See NOTES.md - for the DB-first / spawn-dispatch / command surface invariants. -*/ - -#include maps\_utility; -#include common_scripts\utility; - -init() -{ - if (GetDvar(#"zombiemode") != "1") - return; - - scripts\sp\tws_common::ensure_config(); - - bank_init(); - - scripts\sp\tws_common::register_spawn_handler(::bank_load); -} - -bank_init() -{ - if (!isDefined(level.db_probe_done) || !level.db_probe_done) - level thread bank_deferred_start(); - else - bank_start(); -} - -bank_deferred_start() -{ - level waittill("db_probe_done"); - bank_start(); -} - -bank_start() -{ - if (!level.db_online) - return; - - bank_register_commands(); -} - -bank_register_commands() -{ - command::add_sv("bank", ::bank_cmd_bank); - command::add_sv("deposit", ::bank_cmd_deposit); - command::add_sv("withdraw", ::bank_cmd_withdraw); -} - -// DIAGNOSTIC STUB — bare minimum to isolate the connect-handshake crash. -// Do not restore the getGuid / mysql body until this passes join. -bank_load() -{ - self endon("disconnect"); - self iPrintLnBold("^2bank_load: entered"); - self.bank = 0; - self.bank_unavailable = false; - self iPrintLnBold("^2bank_load: done"); -} - -bank_available(player) -{ - if (!level.db_online) - { - player iPrintLnBold("^1Bank offline"); - return 0; - } - - if (is_true(player.bank_unavailable)) - { - player iPrintLnBold("^1Bank unavailable for your account"); - return 0; - } - - if (!isDefined(player.bank)) - { - player iPrintLnBold("^3Bank still loading, try again"); - return 0; - } - - return 1; -} - -bank_cmd_bank(args) -{ - if (!bank_available(self)) - return; - - self iPrintLnBold("Bank: " + self.bank + " Score: " + self.score); -} - -bank_cmd_deposit(args) -{ - if (!bank_available(self)) - return; - - if (args.size < 2) - { - self iPrintLnBold("Usage: deposit "); - return; - } - - amount = int(args[1]); - - if (amount <= 0) - { - self iPrintLnBold("^1Amount must be positive"); - return; - } - - if (amount > self.score) - { - self iPrintLnBold("^1Not enough score"); - return; - } - - self thread bank_deposit_write(amount); -} - -bank_cmd_withdraw(args) -{ - if (!bank_available(self)) - return; - - if (args.size < 2) - { - self iPrintLnBold("Usage: withdraw "); - return; - } - - amount = int(args[1]); - - if (amount <= 0) - { - self iPrintLnBold("^1Amount must be positive"); - return; - } - - if (amount > self.bank) - { - self iPrintLnBold("^1Insufficient balance"); - return; - } - - self thread bank_withdraw_write(amount); -} - -// upsert: first deposit creates the row, later ones increment -bank_deposit_write(amount) -{ - self endon("disconnect"); - - name = bank_display_name(self); - server_id = bank_server_id(); - - q = mysql::prepared_statement( - "INSERT INTO bank (guid, balance, last_name, last_server) VALUES (?, ?, ?, ?) " + - "ON DUPLICATE KEY UPDATE balance = balance + VALUES(balance), " + - "last_name = VALUES(last_name), last_server = VALUES(last_server)", - self.bank_guid, amount, name, server_id); - - q waittill("done", result, affected_rows, error); - - if (isDefined(error) && error != "") - { - self iPrintLnBold("^1Deposit failed"); - return; - } - - // score can change between dispatch and write completing — refund if so - if (amount > self.score) - { - mysql::prepared_statement( - "UPDATE bank SET balance = balance - ? WHERE guid = ?", - amount, self.bank_guid); - self iPrintLnBold("^1Deposit rolled back (score changed)"); - return; - } - - self.score -= amount; - self.bank += amount; - self iPrintLnBold("Deposited " + amount + " Bank: " + self.bank); -} - -// balance guard lives in the WHERE so concurrent commands can't overdraw -bank_withdraw_write(amount) -{ - self endon("disconnect"); - - q = mysql::prepared_statement( - "UPDATE bank SET balance = balance - ? WHERE guid = ? AND balance >= ?", - amount, self.bank_guid, amount); - - q waittill("done", result, affected_rows, error); - - if (isDefined(error) && error != "") - { - self iPrintLnBold("^1Withdraw failed"); - return; - } - - if (!isDefined(affected_rows) || affected_rows == 0) - { - self iPrintLnBold("^1Insufficient balance"); - self thread bank_load(); // re-sync in case memory drifted from DB - return; - } - - self.score += amount; - self.bank -= amount; - self iPrintLnBold("Withdrew " + amount + " Bank: " + self.bank); -} - -bank_display_name(player) -{ - if (isDefined(player.name)) - return player.name; - if (isDefined(player.playername)) - return player.playername; - return "unknown"; -} - -bank_server_id() -{ - id = GetDvar("tws_server_id"); - if (id == "") - return "unknown"; - return id; -} diff --git a/t5/tws_db.gsc b/t5/tws_db.gsc deleted file mode 100644 index d3d9ded..0000000 --- a/t5/tws_db.gsc +++ /dev/null @@ -1,47 +0,0 @@ -/* tws_db.gsc — MariaDB pool + reachability probe. Fires db_probe_done. */ - -#include maps\_utility; -#include common_scripts\utility; - -init() -{ - if (GetDvar(#"zombiemode") != "1") - return; - - scripts\sp\tws_common::ensure_config(); - - db_init(); -} - -// TODO: move creds to dvars before this repo goes public -db_init() -{ - level.db_online = false; - level.db_probe_done = false; - - cfg = spawnStruct(); - cfg.host = "mariadb"; - cfg.user = "josh"; - cfg.password = "joshster"; - cfg.port = 3306; - cfg.database = "tws_zm"; - - mysql::set_config(cfg); - - level thread db_probe(); -} - -// set_config is silent on failure; only a real query surfaces the error -db_probe() -{ - q = mysql::query("SELECT 1 AS ok"); - q waittill("done", result, affected_rows, error); - - if (isDefined(error) && error != "") - level.db_online = false; - else - level.db_online = true; - - level.db_probe_done = true; - level notify("db_probe_done"); -} diff --git a/t5/tws_afk.gsc b/t5/tws_t5_afk.gsc similarity index 80% rename from t5/tws_afk.gsc rename to t5/tws_t5_afk.gsc index 5b81dd3..50ef3a0 100644 --- a/t5/tws_afk.gsc +++ b/t5/tws_t5_afk.gsc @@ -1,5 +1,5 @@ /* - tws_afk.gsc — .afk / .back chat toggle. Owns the [AFK] indicator element. + tws_t5_afk.gsc — .afk / .back chat toggle. Owns the [AFK] indicator element. Chat uses level waittill("say") — callbackPlayerSay never fires on T5 SP dedicated (see NOTES.md). */ @@ -12,13 +12,13 @@ init() if (GetDvar(#"zombiemode") != "1") return; - scripts\sp\tws_common::ensure_config(); - scripts\sp\tws_common::register_help_command(".afk", "Pause with protection"); - scripts\sp\tws_common::register_help_command(".back", "Return from AFK"); + scripts\sp\tws_t5_common::ensure_config(); + scripts\sp\tws_t5_common::register_help_command(".afk", "Pause with protection"); + scripts\sp\tws_t5_common::register_help_command(".back", "Return from AFK"); afk_init(); - scripts\sp\tws_common::register_spawn_handler(::afk_spawn); + scripts\sp\tws_t5_common::register_spawn_handler(::afk_spawn); } afk_init() @@ -50,7 +50,7 @@ afk_spawn() { self endon("disconnect"); - afk = scripts\sp\tws_common::make_text_elem( + afk = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x + 42, level.hud_row_match, "left", level.hud_font_small, level.hud_c_afk); afk SetText("[AFK]"); @@ -59,7 +59,7 @@ afk_spawn() elems = []; elems[elems.size] = afk; - self thread scripts\sp\tws_common::destroy_on_disconnect(elems); + self thread scripts\sp\tws_t5_common::destroy_on_disconnect(elems); } afk_enter() diff --git a/t5/tws_common.gsc b/t5/tws_t5_common.gsc similarity index 98% rename from t5/tws_common.gsc rename to t5/tws_t5_common.gsc index c936d23..d22c64b 100644 --- a/t5/tws_common.gsc +++ b/t5/tws_t5_common.gsc @@ -1,4 +1,4 @@ -/* tws_common.gsc — shared config, HUD helpers, connect/spawn/help registries. */ +/* tws_t5_common.gsc — shared config, HUD helpers, connect/spawn/help registries. */ #include maps\_utility; #include common_scripts\utility; diff --git a/t5/tws_hud.gsc b/t5/tws_t5_hud.gsc similarity index 86% rename from t5/tws_hud.gsc rename to t5/tws_t5_hud.gsc index f370381..8c9d71d 100644 --- a/t5/tws_hud.gsc +++ b/t5/tws_t5_hud.gsc @@ -1,4 +1,4 @@ -/* tws_hud.gsc — bottom-left HUD stack. Timer / zombie count / health bar. */ +/* tws_t5_hud.gsc — bottom-left HUD stack. Timer / zombie count / health bar. */ #include maps\_utility; #include common_scripts\utility; @@ -8,12 +8,12 @@ init() if (GetDvar(#"zombiemode") != "1") return; - scripts\sp\tws_common::ensure_config(); + scripts\sp\tws_t5_common::ensure_config(); level thread round_watcher(); level thread game_over_watcher(); - scripts\sp\tws_common::register_spawn_handler(::hud_start); + scripts\sp\tws_t5_common::register_spawn_handler(::hud_start); } hud_start() @@ -87,18 +87,18 @@ hud_timer_row() level notify("hud_match_started"); } - match = scripts\sp\tws_common::make_text_elem( + match = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, level.hud_row_match, "left", level.hud_font_small, level.hud_c_dim); match.alpha = 0; // revealed by match_timer_bind when round 1 opens - round = scripts\sp\tws_common::make_text_elem( + round = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, level.hud_row_round, "left", level.hud_font_small, level.hud_c_dim); round SetTimerUp(0); // name only shows in co-op — your own name is noise in solo - name = scripts\sp\tws_common::make_text_elem( + name = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, level.hud_row_name, "left", level.hud_font_small, level.hud_c_name); @@ -113,7 +113,7 @@ hud_timer_row() elems[elems.size] = match; elems[elems.size] = round; elems[elems.size] = name; - self thread scripts\sp\tws_common::destroy_on_disconnect(elems); + self thread scripts\sp\tws_t5_common::destroy_on_disconnect(elems); self thread reset_timer_each_round(round); self thread match_timer_bind(match); @@ -177,22 +177,22 @@ freeze_timers_on_game_over(match_elem, round_elem) level waittill("hud_game_over"); if (isDefined(match_elem) && isDefined(level.match_start_time)) - match_elem SetText(scripts\sp\tws_common::format_mmss(getTime() - level.match_start_time)); + match_elem SetText(scripts\sp\tws_t5_common::format_mmss(getTime() - level.match_start_time)); if (isDefined(round_elem) && isDefined(level.round_start_time)) - round_elem SetText(scripts\sp\tws_common::format_mmss(getTime() - level.round_start_time)); + round_elem SetText(scripts\sp\tws_t5_common::format_mmss(getTime() - level.round_start_time)); } hud_zombie_counter() { self endon("disconnect"); - label = scripts\sp\tws_common::make_text_elem( + label = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, level.hud_row_count, "left", level.hud_font_large, level.hud_c_label); label SetText("Zombies"); - count = scripts\sp\tws_common::make_text_elem( + count = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_col_count, level.hud_row_count, "left", level.hud_font_large, level.hud_c_count); count SetValue(0); @@ -200,7 +200,7 @@ hud_zombie_counter() elems = []; elems[elems.size] = label; elems[elems.size] = count; - self thread scripts\sp\tws_common::destroy_on_disconnect(elems); + self thread scripts\sp\tws_t5_common::destroy_on_disconnect(elems); last = -1; @@ -245,12 +245,12 @@ hud_health_display() bar_width = level.hud_bar_width; // -2 on both axes so the back frames the bar with 2px padding - back = scripts\sp\tws_common::make_shader_elem(self, level.hud_x - 2, level.hud_row_bar - 2); + back = scripts\sp\tws_t5_common::make_shader_elem(self, level.hud_x - 2, level.hud_row_bar - 2); back.alpha = 0.5; back.color = (0, 0, 0); back SetShader("black", bar_width + 4, level.hud_bar_height + 4); - bar = scripts\sp\tws_common::make_shader_elem(self, level.hud_x, level.hud_row_bar); + bar = scripts\sp\tws_t5_common::make_shader_elem(self, level.hud_x, level.hud_row_bar); bar.color = level.hud_c_high; bar SetShader("white", bar_width, level.hud_bar_height); @@ -260,17 +260,17 @@ hud_health_display() cur is right-aligned so digits grow leftward and the slash + max never shift. */ - cur = scripts\sp\tws_common::make_text_elem( + cur = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_col_health + 26, level.hud_row_bar + 3, "right", level.hud_font_mid, level.hud_c_high); cur SetValue(self.health); - slash = scripts\sp\tws_common::make_text_elem( + slash = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_col_health + 30, level.hud_row_bar + 3, "center", level.hud_font_mid, level.hud_c_high); slash SetText("/"); - max_hp = scripts\sp\tws_common::make_text_elem( + max_hp = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_col_health + 34, level.hud_row_bar + 3, "left", level.hud_font_mid, level.hud_c_high); max_hp SetValue(self.maxhealth); @@ -285,7 +285,7 @@ hud_health_display() elems[elems.size] = cur; elems[elems.size] = slash; elems[elems.size] = max_hp; - self thread scripts\sp\tws_common::destroy_on_disconnect(elems); + self thread scripts\sp\tws_t5_common::destroy_on_disconnect(elems); last_width = bar_width; last_health = -1; diff --git a/t5/tws_join.gsc b/t5/tws_t5_join.gsc similarity index 91% rename from t5/tws_join.gsc rename to t5/tws_t5_join.gsc index 815cd5e..f9410ab 100644 --- a/t5/tws_join.gsc +++ b/t5/tws_t5_join.gsc @@ -1,4 +1,4 @@ -/* tws_join.gsc - .join lets a newcomer leave spectator mode before round end. */ +/* tws_t5_join.gsc - .join lets a newcomer leave spectator mode before round end. */ #include maps\_utility; #include common_scripts\utility; @@ -8,9 +8,9 @@ init() if (GetDvar(#"zombiemode") != "1") return; - scripts\sp\tws_common::ensure_config(); - scripts\sp\tws_common::register_help_command(".join", "Join mid-round (newcomers only)"); - scripts\sp\tws_common::register_spawn_handler(::join_spawn); + scripts\sp\tws_t5_common::ensure_config(); + scripts\sp\tws_t5_common::register_help_command(".join", "Join mid-round (newcomers only)"); + scripts\sp\tws_t5_common::register_spawn_handler(::join_spawn); level thread join_chat_listener(); } diff --git a/t5/tws_leaderboard.gsc b/t5/tws_t5_leaderboard.gsc similarity index 97% rename from t5/tws_leaderboard.gsc rename to t5/tws_t5_leaderboard.gsc index 56c7c61..efd370f 100644 --- a/t5/tws_leaderboard.gsc +++ b/t5/tws_t5_leaderboard.gsc @@ -1,4 +1,4 @@ -/* tws_leaderboard.gsc - private map record announcement, once per connection. */ +/* tws_t5_leaderboard.gsc - private map record announcement, once per connection. */ #include maps\_utility; #include common_scripts\utility; @@ -24,7 +24,7 @@ init() level.tws_lb_pending = undefined; level.tws_lb_cache = undefined; level.tws_lb_attempt = 0; - scripts\sp\tws_common::register_connect_handler(::lb_welcome); + scripts\sp\tws_t5_common::register_connect_handler(::lb_welcome); level thread lb_worker(); } @@ -99,11 +99,11 @@ lb_show_hud(snapshot) self endon("disconnect"); self endon("tws_lb_hud_done"); - scripts\sp\tws_common::ensure_config(); + scripts\sp\tws_t5_common::ensure_config(); // Match .help's position, typography and row spacing below the health bar. y = level.hud_row_bar + level.hud_bar_height + 20; elems = []; - title = scripts\sp\tws_common::make_text_elem( + title = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, y, "left", level.hud_font_mid, level.hud_c_afk); title.alpha = 0; if (snapshot.round == 0) @@ -114,7 +114,7 @@ lb_show_hud(snapshot) for (i = 0; i < 3; i++) { - row = scripts\sp\tws_common::make_text_elem( + row = scripts\sp\tws_t5_common::make_text_elem( self, level.hud_x, y + 22 + i * 18, "left", level.hud_font_small, level.hud_c_count); row.alpha = 0; @@ -122,7 +122,7 @@ lb_show_hud(snapshot) } // Cleanup runs independently of the welcome thread's endon handlers. - self thread scripts\sp\tws_common::destroy_on_disconnect(elems); + self thread scripts\sp\tws_t5_common::destroy_on_disconnect(elems); self thread lb_hud_end_game(elems, "end_game"); self thread lb_hud_end_game(elems, "game_ended"); self thread lb_hud_end_game(elems, "intermission"); @@ -303,7 +303,7 @@ lb_collect(request, pending) lb_log(message) { - print("[tws_leaderboard] " + message); + print("[tws_t5_leaderboard] " + message); } // Pure data helpers below are exercised by tools/check_leaderboard.mjs. diff --git a/t5/tws_lock.gsc b/t5/tws_t5_lock.gsc similarity index 85% rename from t5/tws_lock.gsc rename to t5/tws_t5_lock.gsc index 6e2e4d7..dac21a3 100644 --- a/t5/tws_lock.gsc +++ b/t5/tws_t5_lock.gsc @@ -1,5 +1,5 @@ /* - tws_lock.gsc — lobby g_password lock at milestone round. Existing players + tws_t5_lock.gsc — lobby g_password lock at milestone round. Existing players are unaffected; g_password is only checked at connect. Cleared on hud_game_over so the next lobby opens unlocked. */ @@ -12,14 +12,14 @@ init() if (GetDvar(#"zombiemode") != "1") return; - scripts\sp\tws_common::ensure_config(); + scripts\sp\tws_t5_common::ensure_config(); lock_init(); } lock_init() { - level.lock_round = scripts\sp\tws_common::int_dvar_or("tws_lock_round", 20); + level.lock_round = scripts\sp\tws_t5_common::int_dvar_or("tws_lock_round", 20); level.lock_active = false; // recover from a prior lobby that crashed before clearing diff --git a/t5/tws_telemetry.gsc b/t5/tws_t5_telemetry.gsc similarity index 95% rename from t5/tws_telemetry.gsc rename to t5/tws_t5_telemetry.gsc index 3f41312..aec6db5 100644 --- a/t5/tws_telemetry.gsc +++ b/t5/tws_t5_telemetry.gsc @@ -1,4 +1,4 @@ -/* tws_telemetry.gsc - Electric Cherry game reports via t5-gsc-utils. */ +/* tws_t5_telemetry.gsc - Electric Cherry game reports via t5-gsc-utils. */ #include maps\_utility; #include common_scripts\utility; @@ -8,11 +8,11 @@ init() if (GetDvar(#"zombiemode") != "1") return; - scripts\sp\tws_common::ensure_config(); + scripts\sp\tws_t5_common::ensure_config(); if (GetDvar("tws_telemetry_enabled") == "0") { - print("[tws_telemetry] Disabled by tws_telemetry_enabled=0"); + print("[tws_t5_telemetry] Disabled by tws_telemetry_enabled=0"); return; } @@ -25,7 +25,7 @@ init() if (getSubStr(toLower(level.telemetry_url), 0, 8) != "https://") { - print("^1[tws_telemetry] Telemetry disabled: tws_telemetry_url must use HTTPS"); + print("^1[tws_t5_telemetry] Telemetry disabled: tws_telemetry_url must use HTTPS"); return; } @@ -34,14 +34,14 @@ init() level.telemetry_token = "e03a03f7d22418f0c062b0b2a7317fd91b1194c4400e84b469866de501180dc2"; if (level.telemetry_token.size < 32) { - print("^1[tws_telemetry] Telemetry disabled: embedded telemetry token must be at least 32 characters"); + print("^1[tws_t5_telemetry] Telemetry disabled: embedded telemetry token must be at least 32 characters"); return; } level.telemetry_server = GetDvar("tws_server_id"); if (level.telemetry_server == "") { - print("^1[tws_telemetry] Set tws_server_id to the registered Electric Cherry server name"); + print("^1[tws_t5_telemetry] Set tws_server_id to the registered Electric Cherry server name"); return; } @@ -66,7 +66,7 @@ init() telemetry_log(1, "Initialized; waiting for match start. log_level=" + telemetry_log_level()); // Use the shared dispatcher; a second connected listener breaks T5 SP. - scripts\sp\tws_common::register_spawn_handler(::telemetry_game_start); + scripts\sp\tws_t5_common::register_spawn_handler(::telemetry_game_start); level thread telemetry_game_end_watcher(); level thread telemetry_sender(); @@ -367,7 +367,7 @@ telemetry_log(verbosity, message) if (verbosity > telemetry_log_level()) return; - print("[tws_telemetry] server=" + level.telemetry_server + " map=" + GetDvar("mapname") + " time_ms=" + getTime() + " " + message); + print("[tws_t5_telemetry] server=" + level.telemetry_server + " map=" + GetDvar("mapname") + " time_ms=" + getTime() + " " + message); } // Escape newlines/control characters and bound response/error output. Do not diff --git a/t6/NOTES.md b/t6/NOTES.md index 4cc28aa..8bcb4aa 100644 --- a/t6/NOTES.md +++ b/t6/NOTES.md @@ -1,8 +1,7 @@ # T6 port notes -Baseline: the current files in `C:\Users\josh1\Documents\Code\TWS-T5-AIO`. -The T5 originals were not modified. DB, bank, their schema and the added example -config have been removed from this project. +T6 is ported from T5. Both source trees contain common, HUD, AFK, join, lock, +telemetry and leaderboard modules; neither includes bank or database modules. ## Differences retained for T6 and requested fixes @@ -16,7 +15,7 @@ config have been removed from this project. | Timers | Seed elapsed time for late joiners; stop round watcher/reset on game over | T6 timer behavior; keep frozen timers frozen | | AFK | Maintain control freeze, disable guns/grenades, guard player state and stale grace threads | Requested movement/shooting fix; retain `.afk` / `.back` and two-second grace | | Telemetry | Global `httpPost(url, options)`; T5-parity queue + heartbeat + game_start/end + watchdog + log-level dvar; per-player `guid` sources from Plutonium's `player.guid` engine property (T5 uses `player getGuid()` method); per-match `kills`/`headshots`/`downs`/`revives` fall back to `0` when `player.stats[]` is unpopulated | T6 has no `getGuid` method but exposes the persistent identity as `.guid` on the player entity; `_zm_stats` writes only into persistent stat storage, so `player.stats[]` is not a stock T6 field. Payload keys match T5 for downstream shape parity | -| Join | Port of T5 `tws_join.gsc`; uses `level.spawnplayer` / `level.custom_spawnplayer` (lowercase) and `maps\mp\zombies\_zm::spectator_respawn`; readiness gated on `self.sessionstate == "spectator"` instead of T5's `self.initialized` | T6 stock spectator respawn hook lives in `_zm.gsc`; `self.initialized` is a T5 SP zombiemode marker that T6 stock does not populate the same way | +| Join | Port of T5 `tws_t5_join.gsc`; uses `level.spawnplayer` / `level.custom_spawnplayer` (lowercase) and `maps\mp\zombies\_zm::spectator_respawn`; readiness gated on `self.sessionstate == "spectator"` instead of T5's `self.initialized` | T6 stock spectator respawn hook lives in `_zm.gsc`; `self.initialized` is a T5 SP zombiemode marker that T6 stock does not populate the same way | Common retains the T5 HUD constants, helpers and first-spawn dispatcher. HUD again owns the `hud_game_over` notification fan-out. Lock again waits on HUD @@ -46,7 +45,7 @@ not an external configuration system. ## Leaderboard join announcements -The module is `tws_t6_leaderboard.gsc` in T6 and `tws_leaderboard.gsc` in T5. +The module is `tws_t6_leaderboard.gsc` in T6 and `tws_t5_leaderboard.gsc` in T5. Common's existing connection listener now also dispatches optional connection handlers after a one-frame yield. Leaderboard waits for player readiness and uses a level-owned diff --git a/tools/check_leaderboard.mjs b/tools/check_leaderboard.mjs index 168210d..332b09a 100644 --- a/tools/check_leaderboard.mjs +++ b/tools/check_leaderboard.mjs @@ -42,7 +42,7 @@ function functions(source) { } function engine(game) { - const source = readFileSync(new URL(`${game}/${game === 't6' ? 'tws_t6_leaderboard' : 'tws_leaderboard'}.gsc`, root), 'utf8'); + const source = readFileSync(new URL(`${game}/tws_${game}_leaderboard.gsc`, root), 'utf8'); const all = functions(source); const excluded = new Set(['lb_worker', 'lb_welcome', 'lb_get', 'lb_collect', 'lb_log', 'lb_show_hud', 'lb_hud_help_visible', 'lb_hud_end_game', 'lb_hud_destroy']); const table = []; @@ -89,7 +89,7 @@ for (const game of ['t5', 't6']) { const { context: c, source, all, dvars, responses, queries } = engine(game); const option = mapOption(game); check(`${game} exactly one connect listener across modules`, () => { - const common = readFileSync(new URL(game === 't5' ? 't5/tws_common.gsc' : 't6/tws_t6_common.gsc', root), 'utf8'); + const common = readFileSync(new URL(`${game}/tws_${game}_common.gsc`, root), 'utf8'); assert.equal((common.match(/waittill\("connected"/g) ?? []).length, 1); assert(!source.includes('waittill("connected"')); assert(!all.get('lb_welcome').includes('spawned_player')); @@ -212,11 +212,11 @@ for (const game of ['t5', 't6']) { assert.equal(responses.length, 0, `${game} unused responses`); } check('paired data helpers stay identical', () => { - const t5 = functions(readFileSync(new URL('t5/tws_leaderboard.gsc', root), 'utf8')); + const t5 = functions(readFileSync(new URL('t5/tws_t5_leaderboard.gsc', root), 'utf8')); const t6 = functions(readFileSync(new URL('t6/tws_t6_leaderboard.gsc', root), 'utf8')); for (const [name, fn] of t5) { if (!['lb_selection', 'lb_welcome', 'lb_get', 'lb_log'].includes(name)) { - const normalized = t6.get(name).replaceAll('scripts\\zm\\tws_t6_common::', 'scripts\\sp\\tws_common::'); + const normalized = t6.get(name).replaceAll('scripts\\zm\\tws_t6_common::', 'scripts\\sp\\tws_t5_common::'); assert.equal(normalized, fn, name); } }