Record player stats during the game (#784)

## Description:

Record player stats for the analytics worker to import in to to
postgres. This changes defines a new Analytics schema version, `v0.0.2`,
containing additional metadata about each player.

## 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
This commit is contained in:
Scott Anderson
2025-05-21 00:10:29 -04:00
committed by GitHub
parent 228f5b13af
commit 5aa8356513
27 changed files with 581 additions and 161 deletions
+1 -1
View File
@@ -193,6 +193,7 @@ export class ClientGameRunner {
persistentID: getPersistentIDFromCookie(),
username: this.lobby.playerName,
clientID: this.lobby.clientID,
stats: update.allPlayersStats[this.lobby.clientID],
},
];
let winner: ClientID | Team | null = null;
@@ -217,7 +218,6 @@ export class ClientGameRunner {
Date.now(),
winner,
update.winnerType,
update.allPlayersStats,
);
endGame(record);
}
+1 -1
View File
@@ -180,6 +180,7 @@ export class LocalServer {
persistentID: getPersistentIDFromCookie(),
username: this.lobbyConfig.playerName,
clientID: this.lobbyConfig.clientID,
stats: this.allPlayersStats[this.lobbyConfig.clientID],
},
];
if (this.lobbyConfig.gameStartInfo === undefined) {
@@ -194,7 +195,6 @@ export class LocalServer {
Date.now(),
this.winner?.winner ?? null,
this.winner?.winnerType ?? null,
this.allPlayersStats,
);
if (!saveFullGame) {
// Clear turns because beacon only supports up to 64kb
+2 -2
View File
@@ -10,7 +10,7 @@ import traitorIcon from "../../../../resources/images/TraitorIcon.svg";
import { PseudoRandom } from "../../../core/PseudoRandom";
import { ClientID } from "../../../core/Schemas";
import { Theme } from "../../../core/configuration/Config";
import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game";
import { AllPlayers, Cell, nukeTypes, UnitType } from "../../../core/game/Game";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { createCanvas, renderNumber, renderTroops } from "../../Utils";
import { TransformHandler } from "../TransformHandler";
@@ -450,7 +450,7 @@ export class NameLayer implements Layer {
const isSendingNuke = render.player.id() === unit.owner().id();
const notMyPlayer = !myPlayer || unit.owner().id() !== myPlayer.id();
return (
nukeTypes.includes(unit.type()) &&
(nukeTypes as UnitType[]).includes(unit.type()) &&
isSendingNuke &&
notMyPlayer &&
unit.isActive()
+2 -38
View File
@@ -1,4 +1,4 @@
import { LitElement, html } from "lit";
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
@@ -9,12 +9,7 @@ import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
import { translateText } from "../../../client/Utils";
import { EventBus } from "../../../core/EventBus";
import {
AllPlayers,
PlayerActions,
PlayerID,
UnitType,
} from "../../../core/game/Game";
import { AllPlayers, PlayerActions } from "../../../core/game/Game";
import { TileRef } from "../../../core/game/GameMap";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { flattenedEmojiTable } from "../../../core/Util";
@@ -205,28 +200,6 @@ export class PlayerPanel extends LitElement implements Layer {
return time.trim();
}
getTotalNukesSent(otherId: PlayerID): number {
const stats = this.g.player(otherId).stats();
if (!stats) {
return 0;
}
let sum = 0;
const player = this.g.myPlayer();
if (player === null) {
return 0;
}
const nukes = stats.sentNukes[player.id()];
if (!nukes) {
return 0;
}
for (const nukeType in nukes) {
if (nukeType !== UnitType.MIRVWarhead) {
sum += nukes[nukeType];
}
}
return sum;
}
render() {
if (!this.isVisible) {
return html``;
@@ -353,15 +326,6 @@ export class PlayerPanel extends LitElement implements Layer {
</div>
`
: ""}
<!-- Stats -->
<div class="flex flex-col gap-1">
<div class="text-white text-opacity-80 text-sm px-2">
${translateText("player_panel.nuke")}
</div>
<div class="bg-opacity-50 bg-gray-700 rounded p-2 text-white">
${this.getTotalNukesSent(other.id())}
</div>
</div>
<!-- Action buttons -->
<div class="flex justify-center gap-2">