Add persistent AFK reserves for both engines (Refs #12)
This commit is contained in:
+196
@@ -0,0 +1,196 @@
|
||||
# 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.
|
||||
|
||||
Entry rejects downed/dead/spectating players, stock protection/control restrictions,
|
||||
and the last active player. 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. If the last non-AFK active player dies or leaves,
|
||||
the scripts release AFK sessions and send the stock end-game signal. Stock afterlife,
|
||||
`no_end_game_check` and Grief reset recovery defer this behavior. A lobby with no
|
||||
AFK player is unaffected. 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.0.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.
|
||||
|
||||
| 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/lobby 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.
|
||||
- Reject the last active player's entry. Kill/disconnect the last active teammate
|
||||
while another is AFK; validate normal end reporting and all AFK settlement.
|
||||
Exercise stock recovery exceptions and confirm expected 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 this 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.
|
||||
Reference in New Issue
Block a user