142 lines
9.5 KiB
Markdown
142 lines
9.5 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 counter text uses `SetTextUnlimited`; HP uses `SetValue`.**
|
||
Repeatedly generating unique strings through `SetText` 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).
|
||
The centered `N ZOMBIES` element uses Plutonium r5334+'s `SetTextUnlimited`
|
||
only when the count changes. Composite HP retains numeric elements around a
|
||
static slash and `HP` suffix; the group appears only while injured. Help
|
||
reuses up to nine explicitly positioned command/description pairs, 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 unboxed core is
|
||
available to waiting spectators with placeholders: timers upper left,
|
||
branding top center and zombies/health bottom center. Timer updates retain
|
||
their existing spawn/round lifecycle, independently of element creation.
|
||
- **Visibility is separate from updates.** HUD owns the exact, case-insensitive
|
||
`.hud` chat command and its help entry. Common's core wrappers register each
|
||
element and preserve its intended alpha behind a per-player visibility mask;
|
||
health and AFK changes use `set_core_alpha()`. Hidden data/timers keep updating
|
||
until game over freezes them. Help, records and stock elements remain separate.
|
||
Visibility defaults on, survives death/respawn during the connection/map, and
|
||
resets on reconnect or map restart. AFK protection is unaffected.
|
||
- **One service view per player.** Common owns the unboxed upper-right service
|
||
area, ordered help rows, record display state and cleanup. Help shows all
|
||
nine installed commands; extra registrations use nine rows per page. Help
|
||
pauses record page time; `.leaders` during help replaces the pending replay
|
||
from page one. Keep both services independent of the HUD module. Services
|
||
never reposition the core, and AFK remains centered above the zombie count.
|
||
- **Bound HUD element counts, including hidden elements.** Core + AFK use
|
||
10 + 1 archived elements. Common allocates only the active service view:
|
||
19 nonarchived elements for nine-command help (20 with a multi-page counter),
|
||
or four for records (five with a multi-page counter), and none while idle.
|
||
Switching help/record views destroys the previous view first; pages reuse
|
||
its elements. Keep the controller and pending requests separate from element
|
||
lifetime so paused records resume normally. Both engines use explicit help
|
||
rows; BO1 can concatenate newline-separated HUD text. Hiding an element does
|
||
not free its slot. The previous 24-element persistent service allocation was
|
||
consistent with the reported core corruption after records first appeared.
|
||
The exact live T5 SP capacity remains unverified. The
|
||
[BO1 HUD update](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game/g_hudelem.cpp#L2596)
|
||
comes from an MP-based reconstruction and is not proof of T5 SP limits or
|
||
current Plutonium stock usage. Validate automatic records, help, view switches
|
||
and dismissal alongside stock notifications and other installed HUD mods;
|
||
the core must remain intact throughout and after every service closes.
|
||
- **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.4.2` requires matching common `1.4.0+` and
|
||
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) for
|
||
`SetTextUnlimited`. On r5334+, update the HUD script and restart the map;
|
||
older Plutonium clients and servers need updates and full restarts first.
|
||
AFK `1.2.0` and leaderboard `1.1.0` remain compatible; AFK still requires
|
||
common `1.4.0+`.
|
||
The full help order is `.help`, `.hud`, `.afk`, `.back`, `.join`, `.leaders`,
|
||
`.b`, `.d`, `.w`: one page of nine commands, with full `<amount|all>` bank
|
||
syntax. `.hud` is absent when HUD is omitted. Records use right-aligned
|
||
`Map record | Round N` and up to three bounded holder lines, with no footer
|
||
and no single-page counter. Both views retain 12-second page timing.
|
||
Fullscreen X/Y overlay anchors use direct normalized `640 × 480` coordinates.
|
||
Dynamic health fills use `make_overlay_width()` like their tracks. Apply
|
||
`int()` after `max()` in that helper: native `max()` returns a float, but
|
||
`SetShader` requires an integer width. Legacy panel helpers and their `0.75`
|
||
horizontal conversion remain available for existing consumers. Font scale is
|
||
at least `1.0`. Numeric HP appears only while injured; the core and services
|
||
have no panel backgrounds. The counter is one `N ZOMBIES` element centered
|
||
at the health track's midpoint with uniform scale `1.25`, starting as
|
||
`-- ZOMBIES`. There is no new native-plugin/backend dependency or full
|
||
server-process restart requirement when already using Plutonium r5334+.
|
||
Run the [shared HUD checks](../../README.md#hud-validation); portable fixtures
|
||
do not replace T5 live loading, layout and lifecycle validation. Check the
|
||
counter's centering at `0`, `6`, `9`, `10`, `99` and `100`, after `.hud`
|
||
hides/shows it, and after game over freezes the count.
|
||
|
||
## 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` | Unboxed core overlays and private `.hud` visibility toggle | `hud_round_changed`, `hud_match_started`, `hud_game_over` | `say`, internal notifications |
|
||
| `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` | Automatic `g_password` lock (`tws_lock_round`, default 20) plus majority `.lock` / `.unlock` votes | `tws_lock_game_over` (internal) | `say`, `hud_round_changed`, `hud_game_over`, `end_game`, `game_ended`, `intermission` |
|
||
| `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` |
|