204 lines
12 KiB
Markdown
204 lines
12 KiB
Markdown
# Persistent AFK reserve
|
||
|
||
AFK `2.0.0` replaces unlimited protected breaks on both T5 and T6. A GUID owns
|
||
one free allowance and saved reserve across servers, games, maps and reconnects.
|
||
Electric Cherry owns balances, policy, purchase transactions and the time ledger.
|
||
The game owns verified contribution events, player commands, countdown rendering,
|
||
protection and accepted stop decisions. This uses the existing bank account and
|
||
private instance credential, not a second points bank.
|
||
|
||
## Commands and behavior
|
||
|
||
| Command | Result |
|
||
| --- | --- |
|
||
| `.afk` | Reserve available time, acknowledge its start, then enter protected AFK |
|
||
| `.back` | Stop time accounting and return, preserving the existing two-second manual grace |
|
||
| `.afk balance` | Show free, saved and currently reserved seconds as a clock |
|
||
| `.afk buy 10` | Quote ten minutes and its bank-point price |
|
||
| `.afk buy 10 confirm` | Accept that quote within 30 seconds |
|
||
|
||
Return with `.back` before purchasing; an existing reservation never extends
|
||
automatically. Purchases require whole policy packs; the initial pack is 10 minutes for 10,000
|
||
points. There is no automatic purchase. EC atomically rejects insufficient funds,
|
||
pending bank transfers, changed quotes and over-capacity purchases without a
|
||
point debit. A submitted purchase retains its ID until EC acknowledges it.
|
||
|
||
The central countdown and “Protection ends when this timer reaches zero.” remain
|
||
visible even with `.hud` hidden. The final minute is larger and red; warnings
|
||
sound at 60, 30, and each of the final ten seconds. At zero, AFK protection ends
|
||
immediately, including in the damage callback; the script restores its own controls,
|
||
weapons and zombie-targeting state. Expiration has no spectator transition, purchase
|
||
or extra grace. Repeating `.afk` does not restart the timer or reserve twice.
|
||
|
||
AFK `2.1.0` allows solo players and the last active player to enter; all players
|
||
may be AFK at the same time. Entry still rejects downed/dead/spectating players
|
||
and stock protection/control restrictions. A default 15-second reentry cooldown prevents repeated
|
||
combat toggles. Manual `.back` retains a separate two-second grace. The AFK damage
|
||
callback passes through when it does not own protection, preserving stock game
|
||
invulnerability. Other mods that restrict controls should set
|
||
`player.tws_afk_external_controls` while their restriction applies.
|
||
|
||
AFK leaves player session state unchanged, so stock player-count zombie scaling
|
||
continues to include AFK players. Losing the last non-AFK teammate does not end
|
||
protected breaks or force a match end. Authorized time continues to count down;
|
||
normal expiration and stock match-end cleanup still apply. Verify these rules
|
||
on each deployed map/mode.
|
||
|
||
## Rewards
|
||
|
||
Initial EC policy:
|
||
|
||
| Source | Time |
|
||
| --- | --- |
|
||
| Free allowance | 30 minutes per rolling 24 hours |
|
||
| Five minutes of active play | 1 minute |
|
||
| Completed round with participation | 15 seconds |
|
||
| 100 kills | 30 seconds |
|
||
| 25 headshot kills | 15 seconds |
|
||
| Each successful teammate revive | 30 seconds |
|
||
|
||
Saved earned and purchased time has a combined six-hour capacity and no expiry.
|
||
Free time is separate, non-accumulating and consumed first. EC retains fractional
|
||
reward progress across reconnects. Round/kill/headshot rewards share eight minutes
|
||
per rolling active hour; headshots stack with kills. Active-play rewards and
|
||
**every successful revive** are outside that cap. There is no per-teammate,
|
||
per-match or hourly revive limit. Retrying a report cannot award it again.
|
||
|
||
Both engines consume stock `zom_kill`, `player_revived` and `end_of_round`
|
||
notifications. T5 also attaches to stock hellhound spawn/death callbacks because
|
||
those enemies omit `zom_kill`; T6 hellhounds already emit the stock zombie death
|
||
event. Completed round numbers are deduplicated locally. Headshots require a lethal head/helmet hit with a qualifying damage
|
||
type. No reward uses spendable score or `score_total`. A round requires a kill,
|
||
revive, or at least 30 seconds of active engagement and eligibility at completion.
|
||
|
||
The one-second activity sampler uses movement over four units, a view change over
|
||
two degrees or a changed input button to refresh a 15-second engagement interval.
|
||
The stock `is_reviving` state counts support; holding a zombie that targets
|
||
the player within 512 units counts while deliberate input occurred in the last
|
||
60 seconds. Kill/revive events also refresh engagement. AFK, dead and spectator
|
||
time do not earn rewards. Contributions after manual `.back` count immediately,
|
||
including during its separate two-second grace. This permits support and zombie
|
||
holding without a minimum kill rate, while passive connection alone earns nothing.
|
||
|
||
Stock integration was checked against
|
||
[T5 scripts 70689b8](https://github.com/plutoniummod/t5-scripts/tree/70689b8288e60079d48926657e670c41aab4abdf)
|
||
and [T6 scripts 2d84403](https://github.com/plutoniummod/t6-scripts/tree/2d8440321602f22f63f5335817110d60ddf223d5).
|
||
Recheck callback contracts if the stock runtime changes.
|
||
|
||
## Configuration and coordinated rollout
|
||
|
||
1. Back up EC and the game runtime journals. Deploy EC #151 and restart its service.
|
||
2. Update all EC host agents to **1.12.0+** before enabling these scripts. This
|
||
adds AFK settlement before deleting/rebuilding stopped runtime storage.
|
||
3. Build and install TWS `t5-plugin` and `t6-plugin` **1.4.0+** and matching AFK
|
||
**2.1.0** scripts. Require common **1.4.0+**, telemetry **1.0.0+**, and
|
||
Plutonium **r5334+** for `SetTextUnlimited`. Fully restart game-server processes.
|
||
4. Retain EC's private `tws_bank_token` and exact `tws_server_id`; telemetry must
|
||
establish the current match before AFK commands can be authorized.
|
||
5. Keep `tws-afk/t5/` and `tws-afk/t6/`, relative to the process working directory,
|
||
on persistent writable storage with one process per server/endpoint scope.
|
||
|
||
Upgrading AFK scripts from `2.0.0` to `2.1.0` adds no dependency requirement:
|
||
copy the matching script for each game and restart the map. No full server-process
|
||
restart is required for this script-only update.
|
||
|
||
| Game dvar | Default | Meaning |
|
||
| --- | --- | --- |
|
||
| `tws_afk_enabled` | enabled | `0` disables new AFK service startup |
|
||
| `tws_afk_cooldown_seconds` | `15` | Reentry cooldown, clamped to 2–300 seconds |
|
||
| `tws_afk_warning_sound` | `zmb_perks_packa_ready` | Local warning sound alias; verify on deployed maps |
|
||
|
||
Economic configuration lives in EC's `EC_AFK_*` environment settings. See the
|
||
[EC AFK guide](https://gitea.thewrightserver.net/josh/electric-cherry/src/branch/main/docs/guides/afk.md)
|
||
for exact rates, rolling-window accounting and JSON schemas. The fixed game
|
||
endpoint is `https://ec.thewrightserver.net/api/afk`; requests reuse the private
|
||
`ec_bank_` credential. Shared telemetry tokens cannot spend or reserve time.
|
||
|
||
## API and recovery ownership
|
||
|
||
The matching native API `1.0.0` exposes `afkVersion`, `afkResponse`, `afkStore`,
|
||
`afkLoad`, `afkRemove`, `afkPrepare`, `afkAuthorize`, `afkBegin`, `afkRemaining`,
|
||
`afkCheckpoint`, `afkStop`, and `afkRecover`. Responses must validate their complete
|
||
schema and request/session identity before authorization takes effect. Requests
|
||
are immutable; journal writes precede network transmission and native activation.
|
||
|
||
EC actions are `balance`, `event`, `purchase`, `reserve`, `start` and `settle`.
|
||
Events carry cumulative counters scoped to GUID, instance and match, with a
|
||
monotonic sequence. The native journal coalesces newer cumulative event snapshots
|
||
to bound outage queues; EC prevents retries and reordered reports awarding twice.
|
||
Revive counts still increase for every distinct successful revive. Kill/active
|
||
progress is flushed at most every 30 seconds and on normal disconnect/end; a hard
|
||
process crash can lose progress that had not yet reached the local journal.
|
||
Already accepted rewards and journaled snapshots persist.
|
||
|
||
Protection starts only after reservation and start acknowledgments, a final
|
||
player-state recheck, and a durable native clock start. Once authorized, an EC
|
||
outage cannot shorten protection; a local monotonic deadline still ends it at zero.
|
||
Manual return, disconnect, death and match end stop accounting immediately. A
|
||
failed stop-journal write freezes the original stop time for retry rather than
|
||
charging during the delay. Never replay a reserve/start from a background sender.
|
||
|
||
Before activation, an interrupted session recovers as `never_started` with zero
|
||
consumption. Its tombstone also blocks delayed reserve/start delivery. After
|
||
activation, a crash with no durable stop becomes `unresolved`, preserving its last
|
||
confirmed checkpoint and full reservation. Absence of a heartbeat never releases
|
||
spendable time. An administrator must reconcile such a stopped session using EC's
|
||
audited correction endpoint; corrected charges remain distinct from exact durations.
|
||
Do not delete journals or change IDs to unblock a hold.
|
||
|
||
The EC agent independently proves the old process is stopped before recovering
|
||
journals during remove/rebuild/cancel-create. It sends requests only to its enrolled
|
||
controller using the assigned teardown operation and host credential. Lost replies,
|
||
unrecognized endpoints or malformed journals preserve runtime storage for retry.
|
||
|
||
## Validation
|
||
|
||
Portable checks (engine shims are not live Plutonium):
|
||
|
||
```sh
|
||
node tools/check_afk.mjs
|
||
python3 tools/test_plugin_afk.py --json-header /path/to/pinned/json.hpp
|
||
node tools/check_bank.mjs
|
||
node tools/check_hud.mjs
|
||
node tools/check_telemetry.mjs
|
||
```
|
||
|
||
Use each plugin's pinned JSON header; the native harness also accepts a path with
|
||
`{game}` for different T5/T6 dependency revisions. It compiles native utilities
|
||
and actual registration adapters with ASan/UBSan, then exercises separate-process
|
||
journal recovery. T6 source compilation follows its [script instructions](../t6/scripts/README.md#validation).
|
||
T5 linking and behavior require a live server.
|
||
|
||
Before production activation, validate **both engines**:
|
||
|
||
- Enter/return/expire, reconnect, die, change maps and restart the process; compare
|
||
the displayed countdown and EC balances/ledger. At expiry attempt moving, firing,
|
||
throwing equipment and receiving zombie damage immediately, including `.hud` off.
|
||
- Try T5 entry while downed, then after a successful revive. Check T6 laststand,
|
||
afterlife, intermission and stock control restrictions; no stock protection should
|
||
be removed. Repeat rapid `.afk`/`.back` and concurrent commands during slow replies.
|
||
- Earn each reward; revive the same teammate repeatedly beyond the performance cap.
|
||
Verify passive, spectating, AFK and bank point changes earn nothing. Test support
|
||
and zombie holding. Carry partial progress across maps/servers/T5/T6.
|
||
- Confirm the displayed purchase price; try expired/changed quotes, insufficient
|
||
bank funds, pending bank transfers and capacity overflow. Drop purchase replies
|
||
and retry without a second debit. Attempt simultaneous reservations on two servers.
|
||
- Disconnect EC after authorization; the countdown must continue and expire locally.
|
||
Drop reserve/start/stop replies, reorder delivery, fill/fail journal storage, and
|
||
restart. Verify never-started refunds, durable exact stops and unconfirmed crash
|
||
holds. Correct only a proven stopped session and confirm the ledger audit.
|
||
- Enter as the only connected player and as the last active player with downed,
|
||
dead, spectating or AFK teammates. Put every player in AFK. Kill/disconnect the
|
||
last active teammate, including while reserve/start replies are pending; verify
|
||
protection continues without a forced match end. Check solo return/expiration,
|
||
normal stock match-end settlement, afterlife/Grief recovery and zombie scaling.
|
||
- Rebuild/remove with journals from both games, retire the instance credential,
|
||
interrupt the agent after a lost reply, and retry. Runtime deletion must wait for
|
||
acknowledged recovery and cannot authorize a new purchase.
|
||
|
||
Validation performed for the initial `2.0.0` change: 39 AFK behavior checks, 14 core HUD and
|
||
56 service HUD checks passed. All nine T6 scripts compiled and assembled with
|
||
the upstream [gsc-tool compiler 8f1d6bd](https://github.com/xensik/gsc-tool/tree/8f1d6bd4d51efdc619629b9254fb439427a6d306)
|
||
using its T6 PC compiler/assembler library in a temporary local driver. No Windows
|
||
DLL build, production deployment or live T5/T6 validation is implied by these checks.
|