Files
tws-zombies-scripts/t5/scripts/NOTES.md
T

100 lines
6.4 KiB
Markdown

# TWS T5 script notes
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_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 <app>` — Plutainer runs as UID 1000
- `docker exec <container> 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
leaderboard's `chosen` variable at load time when its only assignment was
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_t5_common` owns it. A
second listener breaks the T5 SP connect handshake. Per-player work goes
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
dedicated disables the sound subsystem, cascading through the say-callback
dispatch — `callbackPlayerSay` never fires.
- **`SetTimerUp(N)` only ticks when `N == 0`** on T5 SP. Non-zero seeds
render "0:00" but don't advance. Late joiners see 0, not real elapsed;
authoritative elapsed lives in `level.match_start_time`.
- **Dynamic numbers use `SetValue`.** Repeatedly generating unique strings
risks exhausting the text config-string pool; identical strings can be reused
by the engine's [string lookup](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game_mp/g_utils_mp.cpp#L46).
Composite HP uses numeric elements around a static slash. Help updates two
multiline columns only on page changes, and record rows skip unchanged text.
- **`NewClientHudElem(player)`, not `NewHudElem()`.** `NewHudElem` is
globally owned; in co-op every player's HUD draws on top of every other
player's. Fall back to `NewHudElem` only if elements don't render at all
(diagnostic).
- **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`. 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).
## Load-bearing decisions
- **Match / round timers anchor to first spawn / first observed round
transition — never level init.** Anchoring at init included the map-load
+ connect + intro window, producing "displayed 4:00 → frozen 7:00" drift.
Many BO1 maps pre-set `round_number = 1` at init, so the round watcher
fires on transitions only and the timer row seeds on first spawn as a
fallback.
- **Connection-time HUD creation must not seed clocks.** The upper-right core
is available to waiting spectators with placeholders. Timer updates retain
their existing spawn/round lifecycle, independently of element creation.
- **One service window per player.** Common owns fixed geometry, ordered
four-command help pages, record display state and cleanup. Help pauses record
page time; `.leaders` during help replaces the pending replay from page one.
Keep both services independent of the HUD module. The core never moves or
resizes when a service opens, and the AFK indicator uses its reserved footer.
- **Bound HUD element counts, including hidden elements.** Core + AFK use
15 + 1 archived elements; the service uses 19 nonarchived elements regardless
of command/page count. Help reuses two multiline columns and one muted column
backdrop. Hiding an element does not free its slot. The
[BO1 HUD update](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game/g_hudelem.cpp#L2596)
shows 31 slots per bucket; this is not proof of current Plutonium stock HUD
usage, which still needs live validation.
- **Game-over trigger is level-scope, raced across three candidate
notifies** (`end_game` / `game_ended` / `intermission`) since BO1 maps
are inconsistent. Per-player triggers do not work — `self waittill("death")`
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).
HUD `1.2.0`, leaderboard `1.1.0` and AFK `1.0.1` require matching common `1.2.0+`.
Install the changed scripts together and restart the map. There is no new
native-plugin/backend dependency or full server-process restart requirement.
Run the [shared HUD checks](../../README.md#hud-validation); portable fixtures
do not replace T5 live loading, layout and lifecycle validation.
## Module map
| File | Role | Fires notifies | Reads notifies |
|---|---|---|---|
| `tws_t5_common` | Shared config, HUD helpers, paginated `.help`, service window, spawn-handler registry, single connect listener + dispatcher | — | `say`, game-end notifications |
| `tws_t5_hud` | Upper-right core: branding, timer row, zombie count, HP and 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` |