88 lines
3.8 KiB
SQL
88 lines
3.8 KiB
SQL
CREATE TABLE servers (
|
|
server_id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
|
|
created_at INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE events (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
server_id TEXT NOT NULL REFERENCES servers(server_id),
|
|
event_id TEXT NOT NULL,
|
|
mission_id TEXT NOT NULL,
|
|
received_at INTEGER NOT NULL,
|
|
payload_hash TEXT NOT NULL,
|
|
payload TEXT NOT NULL CHECK (json_valid(payload)),
|
|
UNIQUE (server_id, event_id)
|
|
);
|
|
CREATE INDEX events_mission ON events(server_id, mission_id, id);
|
|
|
|
CREATE TABLE missions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
server_id TEXT NOT NULL REFERENCES servers(server_id),
|
|
mission_id TEXT NOT NULL,
|
|
event_id TEXT NOT NULL,
|
|
terminal INTEGER NOT NULL,
|
|
outcome TEXT NOT NULL,
|
|
elapsed_seconds INTEGER NOT NULL,
|
|
occurred_at INTEGER NOT NULL,
|
|
received_at INTEGER NOT NULL,
|
|
partial INTEGER NOT NULL,
|
|
peak_players INTEGER NOT NULL,
|
|
dropped_events INTEGER NOT NULL,
|
|
payload TEXT NOT NULL,
|
|
UNIQUE (server_id, mission_id)
|
|
);
|
|
CREATE INDEX missions_server_page ON missions(server_id, id DESC);
|
|
CREATE INDEX missions_outcome ON missions(outcome, server_id);
|
|
|
|
-- An event and its mission projection commit together. Duplicate inserts never
|
|
-- fire this trigger. First terminal snapshot wins; late starts/heartbeats cannot
|
|
-- reopen a mission. Elapsed mission time takes precedence over wall-clock skew.
|
|
CREATE TRIGGER project_mission AFTER INSERT ON events BEGIN
|
|
INSERT INTO missions (
|
|
server_id, mission_id, event_id, terminal, outcome, elapsed_seconds,
|
|
occurred_at, received_at, partial, peak_players, dropped_events, payload
|
|
) VALUES (
|
|
NEW.server_id, NEW.mission_id, NEW.event_id,
|
|
json_extract(NEW.payload, '$.type') = 'mission_finished',
|
|
json_extract(NEW.payload, '$.outcome'),
|
|
json_extract(NEW.payload, '$.elapsedSeconds'),
|
|
json_extract(NEW.payload, '$.occurredAt'), NEW.received_at,
|
|
json_extract(NEW.payload, '$.partial'), json_extract(NEW.payload, '$.peakPlayers'),
|
|
json_extract(NEW.payload, '$.droppedEvents'), NEW.payload
|
|
) ON CONFLICT(server_id, mission_id) DO UPDATE SET
|
|
event_id = excluded.event_id, terminal = excluded.terminal,
|
|
outcome = excluded.outcome, elapsed_seconds = excluded.elapsed_seconds,
|
|
occurred_at = excluded.occurred_at, received_at = excluded.received_at,
|
|
partial = MAX(missions.partial, excluded.partial),
|
|
peak_players = MAX(missions.peak_players, excluded.peak_players),
|
|
dropped_events = MAX(missions.dropped_events, excluded.dropped_events),
|
|
payload = excluded.payload
|
|
WHERE missions.terminal = 0 AND (
|
|
excluded.terminal = 1 OR excluded.elapsed_seconds > missions.elapsed_seconds OR
|
|
(excluded.elapsed_seconds = missions.elapsed_seconds AND excluded.occurred_at >= missions.occurred_at)
|
|
);
|
|
END;
|
|
|
|
-- Final snapshots alone feed rankings: spectator and temporary connection IDs
|
|
-- never become global player records. Missing legacy counters remain NULL.
|
|
CREATE VIEW final_players AS
|
|
SELECT m.server_id, m.mission_id, m.outcome, m.elapsed_seconds,
|
|
m.partial, json_extract(m.payload, '$.playersTruncated') AS players_truncated,
|
|
json_extract(p.value, '$.id') AS player_id,
|
|
json_extract(p.value, '$.name') AS name,
|
|
json_extract(p.value, '$.escaped') AS escaped,
|
|
json_extract(p.value, '$.connected') AS connected,
|
|
json_extract(p.value, '$.connectedSeconds') AS connected_seconds,
|
|
json_extract(p.value, '$.kills') AS kills,
|
|
json_extract(p.value, '$.friendlyKills') AS friendly_kills,
|
|
json_extract(p.value, '$.deaths') AS deaths,
|
|
json_extract(p.value, '$.downed') AS downed,
|
|
json_extract(p.value, '$.recoveries') AS recoveries
|
|
FROM missions m, json_each(m.payload, '$.players') p
|
|
WHERE m.terminal = 1 AND m.outcome IN ('victory', 'defeat')
|
|
AND json_extract(p.value, '$.authenticated') = 1
|
|
AND json_extract(p.value, '$.participated') = 1;
|