From d01be405c74a0651bc5fd5428920c70eb84437ca Mon Sep 17 00:00:00 2001 From: Demonessica <37988730+Demonessica@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:19:40 -0700 Subject: [PATCH] Cache maxTroops during leaderboard update (#4379) **Add approved & assigned issue number here:** Resolves #4316 ## Description: At the beginning of the leaderboard update cycle, evaluates `maxTroops` once for each `PlayerView` and uses the cached value for the rest of the `maxTroops` lookups in the function. Measured a reduction in `updateLeaderboard` processing time from 6ms/sec down to 2ms/sec (measured over the first minute of a singleplayer game on world map and default settings). ## Please complete the following: - [ ] I have added screenshots for all UI updates - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: Demonessica --- src/client/hud/layers/Leaderboard.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/client/hud/layers/Leaderboard.ts b/src/client/hud/layers/Leaderboard.ts index 7e292bb3e..34588ad3a 100644 --- a/src/client/hud/layers/Leaderboard.ts +++ b/src/client/hud/layers/Leaderboard.ts @@ -71,38 +71,46 @@ export class Leaderboard extends LitElement implements Controller { if (this.game === null) throw new Error("Not initialized"); const myPlayer = this.game.myPlayer(); - let sorted = this.game.playerViews(); + interface PlayerViewTroopsCache { + pv: PlayerView; + maxTroops: number; + } const compare = (a: number, b: number) => this._sortOrder === "asc" ? a - b : b - a; const maxTroops = (p: PlayerView) => this.game!.config().maxTroops(p); + const sorted: PlayerViewTroopsCache[] = this.game + .playerViews() + .map((p) => ({ pv: p, maxTroops: maxTroops(p) })); + switch (this._sortKey) { case "gold": - sorted = sorted.sort((a, b) => - compare(Number(a.gold()), Number(b.gold())), + sorted.sort((a, b) => + compare(Number(a.pv.gold()), Number(b.pv.gold())), ); break; case "maxtroops": - sorted = sorted.sort((a, b) => compare(maxTroops(a), maxTroops(b))); + sorted.sort((a, b) => compare(a.maxTroops, b.maxTroops)); break; default: - sorted = sorted.sort((a, b) => - compare(a.numTilesOwned(), b.numTilesOwned()), + sorted.sort((a, b) => + compare(a.pv.numTilesOwned(), b.pv.numTilesOwned()), ); } const numTilesWithoutFallout = this.game.numLandTiles() - this.game.numTilesWithFallout(); - const alivePlayers = sorted.filter((player) => player.isAlive()); + const alivePlayers = sorted.filter((player) => player.pv.isAlive()); const playersToShow = this.showTopFive ? alivePlayers.slice(0, 5) : alivePlayers; - this.players = playersToShow.map((player, index) => { - const maxTroops = this.game!.config().maxTroops(player); + this.players = playersToShow.map((playerCache, index) => { + const player = playerCache.pv; + const maxTroops = playerCache.maxTroops; return { name: player.displayName(), position: index + 1, @@ -126,7 +134,7 @@ export class Leaderboard extends LitElement implements Controller { let place = 0; for (const p of sorted) { place++; - if (p === myPlayer) { + if (p.pv === myPlayer) { break; } }