Focus player records and add squad leaderboards
This commit is contained in:
@@ -32,6 +32,20 @@ export function isLeaderboardMetric(value: string): value is LeaderboardMetric {
|
||||
return metrics.has(value);
|
||||
}
|
||||
|
||||
/** Pick a usable initial map without ranking every player across the fleet first. */
|
||||
export function defaultLeaderboardMap(options: Pick<LeaderboardOptions, 'game' | 'players'> = {}): string | null {
|
||||
const conditions = ["m.game IN ('t5', 't6')", "m.status = 'completed'", `m.highest_round >= ${MIN_RECORDED_ROUND}`,
|
||||
`EXISTS (SELECT 1 FROM leaderboard_match_players mp WHERE mp.match_id = m.id
|
||||
AND mp.has_stats = 1 AND mp.highest_round >= ${MIN_RECORDED_ROUND})`];
|
||||
const parameters: Array<string | number> = [];
|
||||
if (options.game) { conditions.push('m.game = ?'); parameters.push(options.game); }
|
||||
if (options.players) { conditions.push('m.start_players = ?'); parameters.push(options.players); }
|
||||
const row = db().prepare(`SELECT m.map_key AS key FROM leaderboard_matches m
|
||||
WHERE ${conditions.join(' AND ')} ORDER BY m.game, m.map_name COLLATE NOCASE, m.map_key, m.id DESC LIMIT 1`)
|
||||
.get(...parameters) as { key: string } | undefined;
|
||||
return row?.key ?? null;
|
||||
}
|
||||
|
||||
/** Rank each GUID once within the selected matches; player search keeps their actual rank. */
|
||||
export function getLeaderboard(options: LeaderboardOptions = {}): LeaderboardResponse {
|
||||
return readLeaderboard(options);
|
||||
@@ -39,13 +53,13 @@ export function getLeaderboard(options: LeaderboardOptions = {}): LeaderboardRes
|
||||
|
||||
/** Select the exact player after ranking everyone eligible in the requested scope. */
|
||||
export function getPlayerLeaderboardEntry(guid: string,
|
||||
options: Pick<LeaderboardOptions, 'game' | 'map' | 'metric'> = {}): LeaderboardEntry | null {
|
||||
return readLeaderboard({ game: options.game, map: options.map, metric: options.metric, limit: 1 }, guid);
|
||||
options: Pick<LeaderboardOptions, 'game' | 'map' | 'players' | 'metric'> = {}): LeaderboardEntry | null {
|
||||
return readLeaderboard({ game: options.game, map: options.map, players: options.players, metric: options.metric, limit: 1 }, guid);
|
||||
}
|
||||
|
||||
function addRecordSources(handle: ReturnType<typeof db>, entries: RankedEntry[], options: LeaderboardOptions): LeaderboardEntry[] {
|
||||
const records = getPersonalBestRecords(handle, entries.map(entry => ({
|
||||
...entry, game: options.game || undefined, map: options.map || undefined
|
||||
...entry, game: options.game || undefined, map: options.map || undefined, players: options.players
|
||||
})));
|
||||
return entries.map((entry, index) => ({ ...entry, records: records[index] }));
|
||||
}
|
||||
@@ -59,9 +73,10 @@ function readLeaderboard(options: LeaderboardOptions, exactGuid?: string): Leade
|
||||
const offset = Number.isSafeInteger(options.offset) ? Math.min(MAX_LEADERBOARD_OFFSET, Math.max(0, options.offset!)) : 0;
|
||||
const conditions = ["m.game IN ('t5', 't6')", "m.status = 'completed'", 'mp.has_stats = 1', `m.highest_round >= ${MIN_RECORDED_ROUND}`,
|
||||
`mp.highest_round >= ${MIN_RECORDED_ROUND}`];
|
||||
const parameters: string[] = [];
|
||||
const parameters: Array<string | number> = [];
|
||||
if (options.game) { conditions.push('m.game = ?'); parameters.push(options.game); }
|
||||
if (options.map) { conditions.push('m.map_key = ?'); parameters.push(options.map); }
|
||||
if (options.players) { conditions.push('m.start_players = ?'); parameters.push(options.players); }
|
||||
const qualification = isEfficiencyMetric(metric)
|
||||
? `WHERE measures.gamesPlayed >= ${EFFICIENCY_MIN_GAMES} AND measures.${metric} IS NOT NULL`
|
||||
+ (metric === 'headshotRate' ? ` AND measures.kills >= ${EFFICIENCY_MIN_KILLS}` : '')
|
||||
@@ -101,7 +116,7 @@ function readLeaderboard(options: LeaderboardOptions, exactGuid?: string): Leade
|
||||
catch { /* Standalone history tools can operate without cache metadata. */ }
|
||||
let cache = scopeCaches.get(handle);
|
||||
if (!cache) { cache = new Map(); scopeCaches.set(handle, cache); }
|
||||
const cacheKey = JSON.stringify([options.game ?? '', options.map ?? '', metric]);
|
||||
const cacheKey = JSON.stringify([options.game ?? '', options.map ?? '', options.players ?? '', metric]);
|
||||
let saved = cache.get(cacheKey);
|
||||
if (!saved || revision < 0 || saved.revision !== revision) {
|
||||
const rows = handle.prepare(`${aggregated}, playerSummary AS (
|
||||
|
||||
Reference in New Issue
Block a user