Improve territory drawing performances (#696)

## Description:
Territory drawing are computing the same information for each pixel
drawn.
A few fixes can noticably improve the performances:
### Caching colors
some colors are computed each time they are retrieved, and according to
the profiler the border color is the most expensive to compute each
frame:


![image](https://github.com/user-attachments/assets/147c2e9d-0ce2-4859-92de-c6f99b763642)


This PR adds a cache for this color. The other colors have almost no
impact on performance, so there's no need to cache them.

After caching:

![image](https://github.com/user-attachments/assets/7b2601b2-19c9-4535-8315-6b2fe328731f)

### Retrieving player info
Drawing the player territory leads to unecessary and expensive data
gathering, such as the anonymous name list:

![442410324-2e7dbcc9-e7ba-4051-b38f-3ae57cb072e8](https://github.com/user-attachments/assets/67894616-ecec-4748-85f9-9f665c58aab1)

Instead, after retrieving the proper data directly:

![442410386-0eaffd72-045d-40b7-96c3-e04030de3d40](https://github.com/user-attachments/assets/ff59f988-9f80-4f52-b972-a155d6ae94dd)


## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

IngloriousTom
This commit is contained in:
DevelopingTom
2025-05-28 06:43:33 +02:00
committed by GitHub
parent 3a481d3d16
commit b6596c88ee
4 changed files with 39 additions and 21 deletions
+7 -3
View File
@@ -23,8 +23,8 @@ import { UserSettings } from "../game/UserSettings";
import { GameConfig, GameID } from "../Schemas";
import { assertNever, simpleHash, within } from "../Util";
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
import { pastelTheme } from "./PastelTheme";
import { pastelThemeDark } from "./PastelThemeDark";
import { PastelTheme } from "./PastelTheme";
import { PastelThemeDark } from "./PastelThemeDark";
const JwksSchema = z.object({
keys: z
@@ -165,6 +165,8 @@ export abstract class DefaultServerConfig implements ServerConfig {
}
export class DefaultConfig implements Config {
private pastelTheme: PastelTheme = new PastelTheme();
private pastelThemeDark: PastelThemeDark = new PastelThemeDark();
constructor(
private _serverConfig: ServerConfig,
private _gameConfig: GameConfig,
@@ -460,7 +462,9 @@ export class DefaultConfig implements Config {
return this.bots();
}
theme(): Theme {
return this.userSettings()?.darkMode() ? pastelThemeDark : pastelTheme;
return this.userSettings()?.darkMode()
? this.pastelThemeDark
: this.pastelTheme;
}
attackLogic(
+15 -8
View File
@@ -19,7 +19,10 @@ import {
} from "./Colors";
import { Theme } from "./Config";
export const pastelTheme = new (class implements Theme {
type ColorCache = Map<string, Colord>;
export class PastelTheme implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private background = colord({ r: 60, g: 60, b: 60 });
@@ -69,19 +72,17 @@ export const pastelTheme = new (class implements Theme {
if (team !== null) {
return this.teamColor(team);
}
if (player.info().playerType === PlayerType.Human) {
if (player.type() === PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
}
if (player.info().playerType === PlayerType.Bot) {
if (player.type() === PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
}
textColor(player: PlayerView): string {
return player.info().playerType === PlayerType.Human
? "#000000"
: "#4D4D4D";
return player.type() === PlayerType.Human ? "#000000" : "#4D4D4D";
}
specialBuildingColor(player: PlayerView): Colord {
@@ -94,12 +95,18 @@ export const pastelTheme = new (class implements Theme {
}
borderColor(player: PlayerView): Colord {
if (this.borderColorCache.has(player.id())) {
return this.borderColorCache.get(player.id())!;
}
const tc = this.territoryColor(player).rgba;
return colord({
const color = colord({
r: Math.max(tc.r - 40, 0),
g: Math.max(tc.g - 40, 0),
b: Math.max(tc.b - 40, 0),
});
this.borderColorCache.set(player.id(), color);
return color;
}
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
@@ -177,4 +184,4 @@ export const pastelTheme = new (class implements Theme {
spawnHighlightColor(): Colord {
return this._spawnHighlightColor;
}
})();
}
+15 -8
View File
@@ -19,7 +19,10 @@ import {
} from "./Colors";
import { Theme } from "./Config";
export const pastelThemeDark = new (class implements Theme {
type ColorCache = Map<string, Colord>;
export class PastelThemeDark implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private background = colord({ r: 0, g: 0, b: 0 });
@@ -69,19 +72,17 @@ export const pastelThemeDark = new (class implements Theme {
if (team !== null) {
return this.teamColor(team);
}
if (player.info().playerType === PlayerType.Human) {
if (player.type() === PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
}
if (player.info().playerType === PlayerType.Bot) {
if (player.type() === PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
}
textColor(player: PlayerView): string {
return player.info().playerType === PlayerType.Human
? "#ffffff"
: "#e6e6e6";
return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6";
}
specialBuildingColor(player: PlayerView): Colord {
@@ -94,12 +95,18 @@ export const pastelThemeDark = new (class implements Theme {
}
borderColor(player: PlayerView): Colord {
if (this.borderColorCache.has(player.id())) {
return this.borderColorCache.get(player.id())!;
}
const tc = this.territoryColor(player).rgba;
return colord({
const color = colord({
r: Math.max(tc.r - 40, 0),
g: Math.max(tc.g - 40, 0),
b: Math.max(tc.b - 40, 0),
});
this.borderColorCache.set(player.id(), color);
return color;
}
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
@@ -179,4 +186,4 @@ export const pastelThemeDark = new (class implements Theme {
spawnHighlightColor(): Colord {
return this._spawnHighlightColor;
}
})();
}
+2 -2
View File
@@ -182,12 +182,12 @@ export class PlayerView {
return this.data.flag;
}
name(): string {
return userSettings.anonymousNames() && this.anonymousName !== null
return this.anonymousName !== null && userSettings.anonymousNames()
? this.anonymousName
: this.data.name;
}
displayName(): string {
return userSettings.anonymousNames() && this.anonymousName !== null
return this.anonymousName !== null && userSettings.anonymousNames()
? this.anonymousName
: this.data.name;
}