Show map rankings for player personal records

This commit is contained in:
2026-10-08 00:32:32 -04:00
parent 3b270f79a7
commit 8de9247ff2
7 changed files with 108 additions and 6 deletions
+1
View File
@@ -39,6 +39,7 @@ export interface PlayerMapRecord extends PlayerStats {
name: string;
lastSeen: string;
records: PersonalBestRecords;
ranks: Record<PersonalBestMetric, number>;
}
export interface PlayerMatch {
id: number;
+23 -2
View File
@@ -36,10 +36,31 @@ export function getPlayerProfile(guid: string, requestedOffset = 0,
const mapStats = handle.prepare(`SELECT m.game, m.map_key AS key, m.map_name AS name,
MAX(mp.last_seen_at) AS lastSeen, ${totals} ${scope}
GROUP BY m.game, m.map_key ORDER BY highestRound DESC, kills DESC, name COLLATE NOCASE, m.game, key`)
.all(guid) as Array<Omit<PlayerMapRecord, 'records'>>;
.all(guid) as Array<Omit<PlayerMapRecord, 'records' | 'ranks'>>;
const [records, ...mapRecords] = getPersonalBestRecords(handle, [{ guid, ...stats },
...mapStats.map(map => ({ guid, ...map, map: map.key }))]);
const maps = mapStats.map((map, index) => ({ ...map, records: mapRecords[index] }));
// Rank all three bests in one pass over this player's maps. Filter the GUID
// only after ranking competitors so ties and skipped ranks match the leaderboard.
const mapRanks = handle.prepare(`WITH totals AS (
SELECT requested.key AS requestId, mp.guid,
MAX(mp.highest_round) AS highestRound, MAX(mp.kills) AS bestKills, MAX(mp.best_score) AS bestScore
FROM json_each(?) requested
JOIN leaderboard_matches m ON m.game = json_extract(requested.value, '$.game')
AND m.map_key = json_extract(requested.value, '$.key')
JOIN leaderboard_match_players mp ON mp.match_id = m.id
WHERE m.game IN ('t5', 't6') AND m.status = 'completed' AND mp.has_stats = 1
AND m.highest_round >= ${MIN_RECORDED_ROUND} AND mp.highest_round >= ${MIN_RECORDED_ROUND}
AND EXISTS (SELECT 1 FROM leaderboard_players p WHERE p.guid = mp.guid AND p.game IN ('t5', 't6'))
GROUP BY requested.key, mp.guid
), ranked AS (
SELECT requestId, guid,
RANK() OVER (PARTITION BY requestId ORDER BY highestRound DESC) AS highestRound,
RANK() OVER (PARTITION BY requestId ORDER BY bestKills DESC) AS bestKills,
RANK() OVER (PARTITION BY requestId ORDER BY bestScore DESC) AS bestScore
FROM totals
) SELECT highestRound, bestKills, bestScore FROM ranked WHERE guid = ? ORDER BY requestId`)
.all(JSON.stringify(mapStats.map(({ game, key }) => ({ game, key }))), guid) as PlayerMapRecord['ranks'][];
const maps = mapStats.map((map, index) => ({ ...map, records: mapRecords[index], ranks: mapRanks[index] }));
const matches = handle.prepare(`SELECT m.id, m.game, m.server, m.server_deleted_at, m.map_name AS mapName,
m.started_at AS startedAt, mp.last_seen_at AS lastSeen, m.status, mp.completed,
mp.highest_round AS highestRound, mp.rounds_survived AS roundsSurvived,
+2 -1
View File
@@ -10,7 +10,7 @@
{#each maps as map (`${map.game}:${map.key}`)}
<article>
<header><h3><a href={`/leaderboard?${new URLSearchParams({ game: map.game, map: map.key, metric })}`}>{map.name}</a></h3><span>{map.game === 't5' ? 'Black Ops I' : 'Black Ops II'} · {number.format(map.gamesPlayed)} {map.gamesPlayed === 1 ? 'game' : 'games'}</span></header>
<dl>{#each records as record}<div><dt>{record.label}</dt><dd><a href={contextHref(`/matches/${map.records[record.key].matchId}`, returnTo ?? "/leaderboard")} aria-label={`${map.name} ${record.label}: ${number.format(map[record.key])}, Match #${map.records[record.key].matchId}`} title={map.records[record.key].endedAt ? $timeFormat.dateTime(map.records[record.key].endedAt!) : 'Completed match'}>{number.format(map[record.key])}</a></dd></div>{/each}</dl>
<dl>{#each records as record}<div><dt>{record.label}</dt><dd><a href={contextHref(`/matches/${map.records[record.key].matchId}`, returnTo ?? "/leaderboard")} aria-label={`${map.name} ${record.label}: ${number.format(map[record.key])}, Match #${map.records[record.key].matchId}`} title={map.records[record.key].endedAt ? $timeFormat.dateTime(map.records[record.key].endedAt!) : 'Completed match'}>{number.format(map[record.key])}</a><small aria-label={`${map.name} ${record.label} rank ${map.ranks[record.key]}`}>Rank #{number.format(map.ranks[record.key])}</small></dd></div>{/each}</dl>
<p>{number.format(map.kills)} total kills · {number.format(map.roundsSurvived)} observed rounds</p>
</article>
{/each}
@@ -23,5 +23,6 @@
dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); margin: var(--space-3) 0; }
dd { font-family: var(--font-numeric); font-size: var(--text-lg); font-variant-numeric: tabular-nums; margin-top: var(--space-1); }
dd a { display: inline-flex; align-items: center; min-height: 44px; }
dd small { display: block; color: var(--text-secondary); font-family: var(--font-body); font-size: var(--text-xs); }
a { color: var(--accent-primary-hover); }
</style>