diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 3ffb77e36..0cd0db4fb 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -7,11 +7,12 @@ import { GameStartInfo, PlayerRecord, ServerMessage, + Winner, } from "../core/Schemas"; import { createGameRecord } from "../core/Util"; import { ServerConfig } from "../core/configuration/Config"; import { getConfig } from "../core/configuration/ConfigLoader"; -import { Cell, Team, UnitType } from "../core/game/Game"; +import { Cell, UnitType } from "../core/game/Game"; import { TileRef } from "../core/game/GameMap"; import { ErrorUpdate, @@ -186,6 +187,13 @@ export class ClientGameRunner { this.lastMessageTime = Date.now(); } + private getWinner(update: WinUpdate): Winner { + if (update.winner[0] !== "player") return update.winner; + const clientId = this.gameView.playerBySmallID(update.winner[1]).clientID(); + if (clientId === null) return; + return ["player", clientId]; + } + private saveGame(update: WinUpdate) { if (this.myPlayer === null) { return; @@ -199,14 +207,7 @@ export class ClientGameRunner { stats: update.allPlayersStats[this.lobby.clientID], }, ]; - let winner: ClientID | Team | null = null; - if (update.winnerType === "player") { - winner = this.gameView - .playerBySmallID(update.winner as number) - .clientID(); - } else { - winner = update.winner as Team; - } + const winner = this.getWinner(update); if (this.lobby.gameStartInfo === undefined) { throw new Error("missing gameStartInfo"); @@ -220,7 +221,6 @@ export class ClientGameRunner { startTime(), Date.now(), winner, - update.winnerType, ); endGame(record); } diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index 12b8956bf..312395a24 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -193,8 +193,7 @@ export class LocalServer { this.turns, this.startedAt, Date.now(), - this.winner?.winner ?? null, - this.winner?.winnerType ?? null, + this.winner?.winner, ); if (!saveFullGame) { // Clear turns because beacon only supports up to 64kb diff --git a/src/client/Transport.ts b/src/client/Transport.ts index b6f9bd9e4..0c84fbaf9 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -6,7 +6,6 @@ import { GameType, PlayerID, PlayerType, - Team, Tick, UnitType, } from "../core/game/Game"; @@ -14,7 +13,6 @@ import { PlayerView } from "../core/game/GameView"; import { AllPlayersStats, ClientHashMessage, - ClientID, ClientIntentMessage, ClientJoinMessage, ClientLogMessage, @@ -23,6 +21,7 @@ import { Intent, ServerMessage, ServerMessageSchema, + Winner, } from "../core/Schemas"; import { LobbyConfig } from "./ClientGameRunner"; import { LocalServer } from "./LocalServer"; @@ -142,9 +141,8 @@ export class SendSetTargetTroopRatioEvent implements GameEvent { export class SendWinnerEvent implements GameEvent { constructor( - public readonly winner: ClientID | Team, + public readonly winner: Winner, public readonly allPlayersStats: AllPlayersStats, - public readonly winnerType: "player" | "team", ) {} } export class SendHashEvent implements GameEvent { @@ -537,7 +535,6 @@ export class Transport { type: "winner", winner: event.winner, allPlayersStats: event.allPlayersStats, - winnerType: event.winnerType, } satisfies ClientSendWinnerMessage; this.sendMsg(JSON.stringify(msg)); } else { diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index c287530a8..ce0739e50 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -2,9 +2,8 @@ import { LitElement, css, html } from "lit"; import { customElement, state } from "lit/decorators.js"; import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; -import { Team } from "../../../core/game/Game"; import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { GameView, PlayerView } from "../../../core/game/GameView"; +import { GameView } from "../../../core/game/GameView"; import { SendWinnerEvent } from "../../Transport"; import { Layer } from "./Layer"; @@ -185,26 +184,23 @@ export class WinModal extends LitElement implements Layer { const updates = this.game.updatesSinceLastTick(); const winUpdates = updates !== null ? updates[GameUpdateType.Win] : []; winUpdates.forEach((wu) => { - if (wu.winnerType === "team") { - this.eventBus.emit( - new SendWinnerEvent(wu.winner as Team, wu.allPlayersStats, "team"), - ); - if (wu.winner === this.game.myPlayer()?.team()) { + if (wu.winner[0] === "team") { + this.eventBus.emit(new SendWinnerEvent(wu.winner, wu.allPlayersStats)); + if (wu.winner[1] === this.game.myPlayer()?.team()) { this._title = translateText("win_modal.your_team"); } else { this._title = translateText("win_modal.other_team", { - team: wu.winner, + team: wu.winner[1], }); } this.show(); } else { - const winner = this.game.playerBySmallID( - wu.winner as number, - ) as PlayerView; + const winner = this.game.playerBySmallID(wu.winner[1]); + if (!winner.isPlayer()) return; const winnerClient = winner.clientID(); if (winnerClient !== null) { this.eventBus.emit( - new SendWinnerEvent(winnerClient, wu.allPlayersStats, "player"), + new SendWinnerEvent(["player", winnerClient], wu.allPlayersStats), ); } if ( diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 377db35b0..5d681040e 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -391,11 +391,18 @@ export const ServerMessageSchema = z.union([ // Client +export const WinnerSchema = z + .union([ + z.tuple([z.literal("player"), ID]), + z.tuple([z.literal("team"), SafeString]), + ]) + .optional(); +export type Winner = z.infer; + export const ClientSendWinnerSchema = z.object({ type: z.literal("winner"), - winner: z.union([ID, TeamSchema]).nullable(), + winner: WinnerSchema, allPlayersStats: AllPlayersStatsSchema, - winnerType: z.enum(["player", "team"]), }); export const ClientHashSchema = z.object({ @@ -451,8 +458,7 @@ export const GameEndInfoSchema = GameStartInfoSchema.extend({ end: z.number(), duration: z.number().nonnegative(), num_turns: z.number(), - winner: z.union([ID, SafeString]).nullable().optional(), - winnerType: z.enum(["player", "team"]).nullable().optional(), + winner: WinnerSchema, }); export type GameEndInfo = z.infer; diff --git a/src/core/Util.ts b/src/core/Util.ts index 8cea53baf..fc2b40bca 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -1,15 +1,15 @@ import DOMPurify from "dompurify"; import { customAlphabet } from "nanoid"; import twemoji from "twemoji"; -import { Cell, Team, Unit } from "./game/Game"; +import { Cell, Unit } from "./game/Game"; import { GameMap, TileRef } from "./game/GameMap"; import { - ClientID, GameConfig, GameID, GameRecord, PlayerRecord, Turn, + Winner, } from "./Schemas"; import { @@ -191,8 +191,7 @@ export function createGameRecord( allTurns: Turn[], start: number, end: number, - winner: ClientID | Team | null, - winnerType: "player" | "team" | null, + winner: Winner, ): GameRecord { const duration = Math.floor((end - start) / 1000); const version = "v0.0.2"; @@ -211,7 +210,6 @@ export function createGameRecord( duration, num_turns, winner, - winnerType, }, version, gitCommit, diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 5c38a2911..1e244ae9f 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -594,8 +594,10 @@ export class GameImpl implements Game { setWinner(winner: Player | Team, allPlayersStats: AllPlayersStats): void { this.addUpdate({ type: GameUpdateType.Win, - winner: typeof winner === "string" ? winner : winner.smallID(), - winnerType: typeof winner === "string" ? "team" : "player", + winner: + typeof winner === "string" + ? ["team", winner] + : ["player", winner.smallID()], allPlayersStats, }); } diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index 1ccc20013..00b6f9b92 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -178,8 +178,7 @@ export interface WinUpdate { type: GameUpdateType.Win; allPlayersStats: AllPlayersStats; // Player id or team name. - winner: number | Team; - winnerType: "player" | "team"; + winner: ["player", number] | ["team", Team]; } export interface HashUpdate { diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 949bd2b68..47a289b7a 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -562,8 +562,7 @@ export class GameServer { this.turns, this._startTime ?? 0, Date.now(), - this.winner?.winner ?? null, - this.winner?.winnerType ?? null, + this.winner?.winner, ), ); }