diff --git a/apps/realtime/src/pickup/match-stats.ts b/apps/realtime/src/pickup/match-stats.ts index 550b5e5..df690fc 100644 --- a/apps/realtime/src/pickup/match-stats.ts +++ b/apps/realtime/src/pickup/match-stats.ts @@ -14,6 +14,8 @@ type MatchPlayerIndex = { bySteamId: Map; }; +const latchedMatchGuidByMatchId = new Map(); + function asObject(value: unknown): Record | null { return value != null && typeof value === "object" && !Array.isArray(value) ? (value as Record) @@ -449,6 +451,27 @@ function parseSummaryUpdate(event: MatchStatsEvent) { }; } +function isMatchGuidLockedEventType(eventType: string) { + return eventType === "PLAYER_STATS" || eventType === "MATCH_REPORT"; +} + +function readEventMatchGuid(event: MatchStatsEvent) { + return readString( + pickFirst( + event.data.MATCH_GUID, + event.data.matchGuid, + event.data.match_guid, + ), + ); +} + +function isQuitPlayerStatsEvent(event: MatchStatsEvent) { + return ( + event.type === "PLAYER_STATS" && + (readBoolean(pickFirst(event.data.QUIT, event.data.quit)) ?? false) + ); +} + async function insertRawEvent( client: PoolClient, matchId: string, @@ -846,8 +869,21 @@ export async function applyPickupMatchStats( const client = await pool.connect(); try { await client.query("begin"); + let latchedMatchGuid = latchedMatchGuidByMatchId.get(matchId) ?? null; for (const event of parsed.events) { + if (isMatchGuidLockedEventType(event.type)) { + const eventMatchGuid = readEventMatchGuid(event); + if (latchedMatchGuid) { + if (eventMatchGuid !== latchedMatchGuid) { + continue; + } + } else if (eventMatchGuid) { + latchedMatchGuid = eventMatchGuid; + latchedMatchGuidByMatchId.set(matchId, eventMatchGuid); + } + } + await insertRawEvent(client, matchId, event); if ( @@ -862,7 +898,7 @@ export async function applyPickupMatchStats( await upsertStatsSummary(client, matchId, event); } - if (event.type === "PLAYER_STATS") { + if (event.type === "PLAYER_STATS" && !isQuitPlayerStatsEvent(event)) { await upsertPlayerStats(client, matchId, event, index); } diff --git a/apps/web/components/pages/pickup-match-page.tsx b/apps/web/components/pages/pickup-match-page.tsx index e70fe8f..85d40ef 100644 --- a/apps/web/components/pages/pickup-match-page.tsx +++ b/apps/web/components/pages/pickup-match-page.tsx @@ -82,7 +82,7 @@ const weaponChartColors: Record = { GRENADE: "#029012", NAILGUN: "#ec4899", BFG: "#14b8a6", - GAUNTLET: "#f43f5e", + GAUNTLET: "#00FFFF", CHAINGUN: "#64748b", } @@ -235,6 +235,14 @@ function formatNumber(value: number | null) { return value == null ? "-" : value.toLocaleString() } +function truncateLabel(value: string, maxLength: number) { + if (value.length <= maxLength) { + return value + } + + return `${value.slice(0, Math.max(0, maxLength - 1))}\u2026` +} + function formatAccuracy(stat: PickupMatchWeaponStat) { if (typeof stat.accuracy === "number") { return `${Math.round(stat.accuracy)}%` @@ -634,6 +642,8 @@ function PlayerAxisTick({ .toUpperCase() : "" const profileHref = player ? getPlayerProfileHref(player) : null + const displayLabel = truncateLabel(player?.label ?? payload?.value ?? "", 14) + const avatarClipId = player ? `weapon-chart-avatar-${player.id}` : null if (typeof x !== "number" || typeof y !== "number") { return null @@ -641,7 +651,15 @@ function PlayerAxisTick({ return ( + {avatarClipId ? ( + + + + + + ) : null} + {player?.label ?? payload?.value ?? ""} - {player?.label ?? payload?.value ?? ""} + {displayLabel} @@ -822,7 +841,7 @@ function WeaponDamageChart({ detail }: { detail: PickupMatchDetail }) { dataKey="playerKey" tick={} tickLine={false} - tickMargin={10} + tickMargin={22} type="category" width={150} /> diff --git a/devenv.nix b/devenv.nix index 85dd269..5658caa 100644 --- a/devenv.nix +++ b/devenv.nix @@ -72,8 +72,10 @@ in fi if [ ! -f .devenv/state/pnpm-install.stamp ] || [ pnpm-lock.yaml -nt .devenv/state/pnpm-install.stamp ]; then - echo "[devenv] installing workspace dependencies" - pnpm install --frozen-lockfile + echo "[devenv] installing web + realtime workspace dependencies" + pnpm install --frozen-lockfile \ + --filter @qltracker/web... \ + --filter @qltracker/realtime... touch .devenv/state/pnpm-install.stamp fi