199 lines
10 KiB
Markdown
199 lines
10 KiB
Markdown
# Mission telemetry
|
|
|
|
Mission → Statistics contains the administrator toggle. `telemetryEnabled` is
|
|
saved in the `mission` section of `$profile:Escapists.json` and defaults to `1`,
|
|
including when upgrading an older configuration. The page explains the data
|
|
collected and shows delivery status. Existing administrator authentication and
|
|
save-before-apply behavior also apply to this setting.
|
|
|
|
Telemetry runs only on the mission authority, never independently on joining
|
|
clients. Workbench runs do not collect unless explicitly launched with the
|
|
`errTelemetryTest` diagnostic flag. There is no statistics website or production
|
|
receiver included in this change.
|
|
|
|
## Receiving URL and configuration
|
|
|
|
The bundled receiving URL belongs in `addons/core/Configs/ERR/Telemetry.conf`.
|
|
It is currently empty pending the receiver's actual URL. Until configured, no
|
|
telemetry is collected or sent, even with the switch enabled. The menu reports
|
|
this condition rather than claiming that statistics are being delivered.
|
|
|
|
An operator can override the URL and optionally name/authenticate their instance
|
|
with `$profile:EscapistsTelemetry.json`:
|
|
|
|
```json
|
|
{
|
|
"endpoint": "https://YOUR-HOST/api/mission-events",
|
|
"serverName": "My Escape server",
|
|
"token": "receiver-issued-instance-token"
|
|
}
|
|
```
|
|
|
|
Use the full POST endpoint, with its exact trailing-slash convention. HTTPS is
|
|
required. URL credentials, fragments and query parameters are rejected. The
|
|
optional token goes in the HTTPS JSON body, never in a URL, menu snapshot, log,
|
|
Workshop resource, or persisted event queue. Configuration loads when the mission
|
|
starts. An empty endpoint overrides the bundled default and prevents sending.
|
|
|
|
`serverId` is a generated UUID persisted in
|
|
`$profile:EscapistsTelemetryState.json`. Keep this file/profile for continuity;
|
|
use different profiles for different server instances. Do not clone this state
|
|
file into a second server. `serverName` is a display label, not an identity key.
|
|
|
|
## Wire format
|
|
|
|
One asynchronous JSON POST per event, `Content-Type: application/json`. The
|
|
receiver must accept chunked request bodies (used by the native HTTP client):
|
|
|
|
```json
|
|
{
|
|
"token": "receiver-issued-instance-token",
|
|
"event": {
|
|
"schemaVersion": 1,
|
|
"eventId": "3f16ef25-13f7-46ad-967f-5e84f36cb84b",
|
|
"missionId": "a82b4f20-8f32-4978-80f6-c54099185c58",
|
|
"serverId": "74f74822-cf6d-4fc1-856c-1768afe025f4",
|
|
"serverName": "My Escape server",
|
|
"type": "mission_finished",
|
|
"occurredAt": 1792936800,
|
|
"elapsedSeconds": 3521,
|
|
"partial": false,
|
|
"phase": 5,
|
|
"outcome": "victory",
|
|
"previousMissionId": "",
|
|
"previousOutcome": "",
|
|
"terrain": "{RESOURCE_GUID}Worlds/Example.ent",
|
|
"scenario": "Escape",
|
|
"gameVersion": "engine build version",
|
|
"mods": [{"id": "2528466DE220D91C", "name": "Co10 Escape Reintroduced", "version": ""}],
|
|
"modsTruncated": false,
|
|
"playersTruncated": false,
|
|
"connectedPlayers": 2,
|
|
"peakPlayers": 4,
|
|
"players": [
|
|
{"id": "game-account-UUID", "name": "Player", "authenticated": true,
|
|
"connected": true, "participated": true, "escaped": true,
|
|
"connectedSeconds": 3200}
|
|
],
|
|
"reason": "Escape complete: 2 escaped; 2 did not escape.",
|
|
"preparationError": "",
|
|
"settings": {"telemetryEnabled": 1},
|
|
"droppedEvents": 0
|
|
}
|
|
}
|
|
```
|
|
|
|
The example abbreviates the players and settings arrays/maps. Actual `settings`
|
|
contains the saved mission setting values; `-1` retains its meaning of Random,
|
|
not an observed random roll. `phase` is 0 preparing, 1 prison, 2 radio search,
|
|
3 radio defence, 4 extraction, 5 victory, 6 defeat. `mods` describes mounted
|
|
non-vanilla addons; version is the locally active Workshop revision when exposed
|
|
by the engine, otherwise empty. Never infer the newest Workshop version from an
|
|
empty value. Terrain is the actual loaded world resource, including takeover
|
|
scenarios, rather than the next-round terrain preference.
|
|
|
|
## Event and outcome semantics
|
|
|
|
| Event | Meaning |
|
|
| --- | --- |
|
|
| `mission_started` | This mission instance began collection, usually during preparation. |
|
|
| `mission_restarted` | A subsequent instance started in the same profile; links the previous mission and its last known outcome. Also followed by `mission_started`. |
|
|
| `phase_changed` | An actual nonterminal mission transition. Use elapsed times for milestone durations. |
|
|
| `heartbeat` | A snapshot every five minutes while unfinished. |
|
|
| `telemetry_resumed` | Re-enabled during the same mission; the mission ID stays the same and `partial` is true. |
|
|
| `mission_finished` | Exactly one local result snapshot, before normal teardown. |
|
|
|
|
| Outcome | Count as a gameplay result? |
|
|
| --- | --- |
|
|
| `victory` | Yes, successful mission. |
|
|
| `defeat` | Yes, failed mission, including administrator abandonment. |
|
|
| `preparation_failed` | No, startup error; inspect `preparationError`. |
|
|
| `restarted_empty` | No, automatic last-disconnect or idle-empty restart. |
|
|
| `interrupted` | No, session ended without a gameplay result. |
|
|
| `unfinished` in `previousOutcome` | No confirmed ending survived. May be a crash/forced stop. Do not infer a defeat. |
|
|
|
|
Player names and stable game account UUIDs support history across servers. No IP
|
|
addresses, chat, inventories, precise player positions or credentials are in an
|
|
event. Names can change; aggregate by authenticated `id`, not `name` or temporary
|
|
connection number. Missing backend identities use a mission-local connection key
|
|
with `authenticated: false`; exclude those keys from global player rankings.
|
|
|
|
`players` retains observed participants and spectators, including disconnected
|
|
players. `connected` records the end/snapshot connection state, `participated`
|
|
records whether the mission admitted a life, and `escaped` uses the authoritative
|
|
extraction roster. A connected spectator or a left-behind participant is not an
|
|
escapee. Count individual completions using `escaped`, not the mission's victory
|
|
alone. `connectedSeconds` is sampled connection time (roughly five-second
|
|
precision), not survival time. Reconnects reuse the authenticated identity.
|
|
|
|
## Reliable ingestion and aggregation
|
|
|
|
Return any **2xx only after durable storage**, including for an already accepted
|
|
event. Other responses/timeouts retry the identical event with the same event ID.
|
|
The receiver must enforce uniqueness on `(serverId, eventId)` and aggregate by
|
|
`(serverId, missionId)`. Network delivery is at-least-once, not exactly-once.
|
|
A crash between acceptance and local acknowledgement can produce a duplicate.
|
|
|
|
Suggested tables:
|
|
|
|
- `events`: unique server/event key, mission key, event time, type, JSON payload.
|
|
- `missions`: unique server/mission key, terrain, start/end, outcome, peak count,
|
|
partial flag; a repeated start/restart event must not reset a terminal outcome.
|
|
- `mission_players`: unique server/mission/player key, name, participation,
|
|
connected-at-end, escaped, sampled connected duration.
|
|
|
|
Useful queries from terminal mission snapshots:
|
|
|
|
- Per-server failures: count distinct missions with `outcome = 'defeat'` grouped
|
|
by `serverId`; add terrain for per-map difficulty.
|
|
- Global failures/wins: the same counts across all server IDs, without counting
|
|
startup errors, empty restarts or unknown/interrupted sessions as defeats.
|
|
- Player completions: count distinct missions where an authenticated player's
|
|
final `escaped` flag is true. Also expose attempts, left-behind results,
|
|
connected-at-end rosters and best successful duration if desired.
|
|
- Milestone times: phase-event elapsed time differences for prison, radio and
|
|
extraction. Treat missing phases, dropped events and partial runs as unknown.
|
|
|
|
Validate schema and limits and authenticate operator-issued tokens if statistics
|
|
must be trusted. The bundled addon contains no shared secret. These are reports
|
|
from community-hosted missions, not independent anti-cheat attestations.
|
|
|
|
## Buffering, disabling and limits
|
|
|
|
Requests have a 15-second timeout, with a 30-second callback watchdog. Retries
|
|
back off from 10 seconds to five minutes. A five-second dispatch tick sends one
|
|
request at a time. Disk writes occur on events and acknowledgement, not every
|
|
player sample. Delivery never blocks mission startup or shutdown.
|
|
|
|
The profile queue holds at most 64 events of 64 KiB each. On overflow it discards
|
|
queued heartbeats first, then the oldest non-in-flight event; `droppedEvents`
|
|
reports cumulative loss. Players and mods are each capped at 128 with explicit
|
|
truncation flags. The previous valid queue generation is retained as `.bak` for
|
|
recovery. An unreadable queue suspends telemetry and preserves evidence for
|
|
repair instead of silently replacing the server identity.
|
|
|
|
Unsent final results survive normal scenario reloads and are retried in the next
|
|
enabled instance. Abrupt termination can lose the latest event; the next start's
|
|
`previousOutcome` remains unfinished if no result was saved. A permanently
|
|
stopped host cannot send further data.
|
|
|
|
Disabling immediately stops collection/new requests and discards queued events
|
|
and recovery copies. It cannot recall an HTTP request already submitted or erase
|
|
records already accepted by the receiver. Re-enabling does not reconstruct the
|
|
disabled interval, and marks subsequent events partial. Changing the receiver URL
|
|
discards unsent data instead of forwarding old records to a different recipient.
|
|
|
|
Native API references: [RestContext](https://community.bistudio.com/wikidata/external-data/arma-reforger/EnfusionScriptAPIPublic/interfaceRestContext.html) and [RestCallback](https://community.bistudio.com/wikidata/external-data/arma-reforger/EnfusionScriptAPIPublic/interfaceRestCallback.html).
|
|
|
|
### Round combat counters
|
|
|
|
Player records additionally include `kills`, `friendlyKills`, `deaths`, `downed`
|
|
and `recoveries`, using the same server-owned counters as the after-mission
|
|
screen. These fields are additive to schema version 1. Older queued records may
|
|
not contain them; treat absent values as unavailable rather than inventing a
|
|
historical count. Kills exclude friendly kills, suicides, vehicles and editor
|
|
kills. Downed means an unconscious episode, while a death is a fatal loss.
|
|
Recoveries include self-recovery and are not healer-attributed revives.
|
|
Local result tracking continues when telemetry is disabled; sending still follows
|
|
the existing telemetry setting and endpoint configuration.
|