Files
EscapistsReintroduced/docs/telemetry.md
T
2026-11-01 19:49:47 +01:00

16 KiB
Raw Blame History

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. A deployable Cloudflare Worker/D1 receiver is provided in services/telemetry-worker. It includes automatic server enrollment and protected mission/player query APIs. The Worker must be deployed separately. There is no statistics website.

Receiving URL and configuration

The bundled receiving URL in addons/core/Configs/ERR/Telemetry.conf is https://co10escape-telemetry.zacekjakub.workers.dev/api/mission-events. Any host running the updated mission can report without contacting the receiver administrator or editing its configuration. Mission → Statistics remains the administrator opt-out. This source change requires a new Workshop release; 0.0.125 does not contain automatic enrollment.

Before the first request, the host saves its own randomly generated credential in $profile:EscapistsTelemetryCredentials.json. The Worker enrolls an unknown server ID on its first valid event and stores only the credential hash. Later requests must present the same credential. Enrollment and the initial event are committed together. Disabling telemetry prevents enrollment and delivery and discards pending events; the credential remains for continuity when re-enabled. No shared secret or receiver administrator credential is bundled in the mod.

Keep the credentials file with the profile state when moving a server. Damaged credentials suspend delivery without silently rotating them. The file retains up to 16 server/endpoint pairs, so changing receiving URLs does not send one receiver's secret to another. Explicit operator-issued tokens still override automatic credentials. The default receiver accepts community reports, not independently verified gameplay or identities.

An operator can override the URL and optionally name/authenticate their instance with $profile:EscapistsTelemetry.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 explicit or automatically generated 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):

{
  "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": "Everon",
    "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 island name resolved by the reporting game server from the loaded root scene (for example Everon), using the mission title resolver. It does not use the Game Master scenario filename or the next-round terrain preference. Reports through 0.0.129 contain world resource paths instead; existing queued payloads retain their original values and event IDs.

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

The default receiver also returns public map aggregates from GET /api/summary (and GET /api/mission-events). terrain filters the reported island name (or world resource in older reports); serverId adds the installation's record for that map. Summaries expose counts only; detailed player and mission records still require a read/admin token.

After durably accepting a victory or defeat, the Worker returns a community summary containing matching map/server/mission/event/outcome identifiers plus server and allServers records. Each record has completed rounds, wins, failures, win rate and partial-round coverage. The mission validates these identifiers and counts before displaying the current map's record on its result screen. Retries never increase the totals twice. Startup failures, interrupted sessions and empty restarts are excluded from win/failure totals. If a summary cannot be delivered before the next mission starts, the screen reports it as unavailable and keeps local statistics visible. Once a confirmed record is displayed, failures uploading older queued events cannot replace it with the unavailable message.

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.

The Worker validates schema and limits and binds each server to its private credential after the first accepted event. Automatic enrollment proves continuity of a claimed installation, not its operator's identity or gameplay honesty. Operator-created server records are labelled separately. These are reports from community-hosted missions, not independent anti-cheat attestations.

Published clients through 0.0.129 can send fractional elapsedSeconds. The receiver accepts finite, nonnegative durations within the integer range and rounds down before hashing and storage. This also lets existing saved queues retry without edits; retries still use the original event IDs and count once.

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 and RestCallback.

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.

Browser dashboard

The receiver serves a private statistics dashboard at https://co10escape-telemetry.zacekjakub.workers.dev/dashboard. Use the receiver’s read-only token or admin token to view server records and last receipt times, mission outcomes, player counters, mods, settings and raw event history. Tokens are held only in browser-tab memory. See the receiver guide for deployment, pagination and freshness semantics. This requires only the Worker update and receipt index migration; it does not require a new mission release.

Automatic server names

New events first read game.name from BackendApi.GetRunningDSConfig(). If unavailable or blank, they try ServerLobbyApi.GetServerConfig().GetName() and then GetGame().GetServerInfo().GetName(). The optional profile serverName remains an explicit override. Names are trimmed and bounded to 120 characters. Resolution repeats for each new event so startup metadata delays do not permanently leave a server unnamed. Neither scenario titles nor account identities are used. The installation UUID and queued historical events remain unchanged.

The receiver updates automatic server labels from nonblank accepted reports, using server-reported event time to prevent older queued names from replacing a newer one. As with all telemetry timestamps, bad server clocks can affect that ordering. Managed labels remain operator-owned. Mission rows use the historical reported name where present and the current installation label otherwise. Automatic discovery needs a new Workshop release; existing blank records only acquire names after an updated server reports.

Validation: 2026-11-01 evidence records 21 receiver/client tests plus two bundled-client checks. Native compilation, explicit override and absent-metadata fallback pass without script failures; the 13 inherited resource/world errors remain. The local Workbench fixture has no configured server name, so the real dedicated-server name must still be confirmed from the first report after publishing.

Dedicated-name follow-up: validation evidence records the added GetRunningDSConfig().game.name priority path. Production-method tests cover dedicated names and fallback conditions; native Workbench compilation and available-metadata checks pass. Workbench still has no running dedicated configuration, so this does not prove recovery on a live dedicated server.