From 8dda3d8e55d2fbde81f2207b56f53dbc1b540b6c Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Fri, 18 Sep 2026 00:18:05 -0400 Subject: [PATCH] feature: only list our own players on the faceit and premier boards Importing a demo mints a players row for everyone in the match, so the external boards filled up with ratings for people who have never been on the platform. Both now require a sign-in and a finished 5stack match. --- .../functions/leaderboard/get_leaderboard.sql | 34 +++++++-- test/external-rank-leaderboard.spec.ts | 69 ++++++++++++++++++- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/hasura/functions/leaderboard/get_leaderboard.sql b/hasura/functions/leaderboard/get_leaderboard.sql index 6ed5bdec..07edcd51 100644 --- a/hasura/functions/leaderboard/get_leaderboard.sql +++ b/hasura/functions/leaderboard/get_leaderboard.sql @@ -150,6 +150,12 @@ $$; -- therefore take no window, season, match type or source - a player's FACEIT -- rating is what it is whether or not they played here this week, and filtering -- one by "last 7 days" would empty the board rather than narrow it. +-- +-- They do take one filter the other categories get for free by construction: +-- the board is OUR players. Importing a demo mints a players row for everyone +-- in the match, so without this the board fills up with strangers who have +-- never signed in here - a rating we scraped in passing, attached to someone +-- who is not on the platform. Signed in at least once AND played a match here. CREATE OR REPLACE FUNCTION public._leaderboard_external_rank(_rating TEXT) RETURNS SETOF public.leaderboard_entries LANGUAGE plpgsql STABLE @@ -168,7 +174,17 @@ BEGIN 0, p.custom_avatar_url FROM public.players p - WHERE p.faceit_elo IS NOT NULL + WHERE p.last_sign_in_at IS NOT NULL + AND EXISTS ( + SELECT 1 + FROM public.match_lineup_players mlp + JOIN public.match_lineups ml ON ml.id = mlp.match_lineup_id + JOIN public.matches m ON m.id = ml.match_id + WHERE mlp.steam_id = p.steam_id + AND m.source = '5stack' + AND m.status = 'Finished' + ) + AND p.faceit_elo IS NOT NULL ORDER BY p.faceit_elo DESC, p.name ASC; ELSE RETURN QUERY @@ -183,9 +199,19 @@ BEGIN 0, p.custom_avatar_url FROM public.players p - -- The demo importer writes 0 for a player who has not placed this - -- season; ranked as a number that sorts as the worst rating in the game. - WHERE NULLIF(p.premier_rank, 0) IS NOT NULL + WHERE p.last_sign_in_at IS NOT NULL + AND EXISTS ( + SELECT 1 + FROM public.match_lineup_players mlp + JOIN public.match_lineups ml ON ml.id = mlp.match_lineup_id + JOIN public.matches m ON m.id = ml.match_id + WHERE mlp.steam_id = p.steam_id + AND m.source = '5stack' + AND m.status = 'Finished' + ) + -- The demo importer writes 0 for a player who has not placed this + -- season; ranked as a number that sorts as the worst rating in the game. + AND NULLIF(p.premier_rank, 0) IS NOT NULL ORDER BY p.premier_rank DESC, p.name ASC; END IF; END; diff --git a/test/external-rank-leaderboard.spec.ts b/test/external-rank-leaderboard.spec.ts index 03c19074..1014b8a9 100644 --- a/test/external-rank-leaderboard.spec.ts +++ b/test/external-rank-leaderboard.spec.ts @@ -15,6 +15,7 @@ describe("external rank leaderboard (SQL-driven)", () => { db = await bootMigratedDb("ExternalRankLeaderboardTest"); postgres = db.postgres; fx = new Fixtures(postgres, 76561196200000000n); + await fx.region("TestExternalRank"); }, 600_000); afterAll(async () => { @@ -22,32 +23,55 @@ describe("external rank leaderboard (SQL-driven)", () => { }); beforeEach(async () => { + await postgres.query("DELETE FROM matches"); + await postgres.query("DELETE FROM match_options"); await postgres.query("DELETE FROM players"); }); + // A rated player who also belongs here: signed in at least once and played a + // match on this platform. Anyone who has not is off the board entirely. const withRanks = async (ranks: { name?: string; faceitElo?: number | null; faceitLevel?: number | null; premierRank?: number | null; + signedIn?: boolean; + playedHere?: boolean; }) => { const steamId = await fx.player(ranks.name); await postgres.query( `UPDATE players SET faceit_elo = $2, faceit_skill_level = $3, - premier_rank = $4 + premier_rank = $4, + last_sign_in_at = CASE WHEN $5 THEN now() ELSE NULL END WHERE steam_id = $1::bigint`, [ steamId, ranks.faceitElo ?? null, ranks.faceitLevel ?? null, ranks.premierRank ?? null, + ranks.signedIn ?? true, ], ); + + if (ranks.playedHere ?? true) { + await playAMatch(steamId); + } + return steamId; }; + const playAMatch = async (steamId: string, status = "Finished") => { + const match = await fx.match({ regions: ["TestExternalRank"] }); + await fx.lineupPlayer(match.lineup_1_id, steamId); + await postgres.query("UPDATE matches SET status = $2 WHERE id = $1", [ + match.id, + status, + ]); + return match; + }; + type Entry = { player_steam_id: string; player_name: string; @@ -135,6 +159,49 @@ describe("external rank leaderboard (SQL-driven)", () => { }); }); + describe("who belongs on an external board", () => { + it("leaves out a rated player who has never signed in", async () => { + // an imported demo can mint a player row for someone who has no account + // here; their FACEIT rating is not ours to put on a board + await withRanks({ faceitElo: 3000, signedIn: false }); + await withRanks({ premierRank: 25000, signedIn: false }); + + expect(await board("faceit_elo")).toHaveLength(0); + expect(await board("premier_rank")).toHaveLength(0); + }); + + it("leaves out a rated player who has never played here", async () => { + await withRanks({ faceitElo: 3000, playedHere: false }); + await withRanks({ premierRank: 25000, playedHere: false }); + + expect(await board("faceit_elo")).toHaveLength(0); + expect(await board("premier_rank")).toHaveLength(0); + }); + + it("counts a player who has signed in and played", async () => { + await withRanks({ faceitElo: 3000 }); + await withRanks({ premierRank: 25000 }); + + expect(await board("faceit_elo")).toHaveLength(1); + expect(await board("premier_rank")).toHaveLength(1); + }); + + it("does not count a match that never finished", async () => { + const player = await withRanks({ faceitElo: 2000, playedHere: false }); + await playAMatch(player, "Canceled"); + + expect(await board("faceit_elo")).toHaveLength(0); + }); + + it("counts a player once however many matches they have played", async () => { + const player = await withRanks({ faceitElo: 2000 }); + await playAMatch(player); + await playAMatch(player); + + expect(await board("faceit_elo")).toHaveLength(1); + }); + }); + it("gives a player their rank on an external board", async () => { await withRanks({ faceitElo: 3000 }); const middle = await withRanks({ faceitElo: 2000 });