diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 68794eedd..55946dce0 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -331,6 +331,7 @@ export class ClientGameRunner { Date.now(), update.winner, this.lobby.gameStartInfo.lobbyCreatedAt, + this.lobby.gameStartInfo.visibleAt, ); endGame(record); } diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index e0dbdd5fc..8b5ed10fc 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -526,6 +526,7 @@ export const PlayerSchema = z.object({ export const GameStartInfoSchema = z.object({ gameID: ID, lobbyCreatedAt: z.number(), + visibleAt: z.number().optional(), config: GameConfigSchema, players: PlayerSchema.array(), }); diff --git a/src/core/Util.ts b/src/core/Util.ts index f3dd45aad..3010b7fb6 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -251,6 +251,8 @@ export function createPartialGameRecord( winner: Winner, // lobby creation time (ms). Defaults to start time for singleplayer. lobbyCreatedAt?: number, + // Time the lobby became visible to players (ms). + visibleAt?: number, ): PartialGameRecord { const duration = Math.floor((end - start) / 1000); const num_turns = allTurns.length; @@ -262,13 +264,14 @@ export function createPartialGameRecord( const actualLobbyCreatedAt = lobbyCreatedAt ?? start; const lobbyFillTime = Math.max( 0, - start - Math.min(actualLobbyCreatedAt, start), + start - (visibleAt ?? actualLobbyCreatedAt), ); const record: PartialGameRecord = { info: { gameID, lobbyCreatedAt: actualLobbyCreatedAt, + visibleAt, lobbyFillTime, config, players, diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 8a41d4edb..da724214c 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -87,6 +87,8 @@ export class GameServer { private lobbyInfoIntervalId: ReturnType | null = null; + private visibleAt?: number; + constructor( public readonly id: string, readonly log_: Logger, @@ -98,6 +100,9 @@ export class GameServer { private publicGameType?: PublicGameType, ) { this.log = log_.child({ gameID: id }); + if (startsAt !== undefined) { + this.visibleAt = Date.now(); + } } private get lobbyCreatorID(): ClientID | undefined { @@ -558,6 +563,8 @@ export class GameServer { public setStartsAt(startsAt: number) { this.startsAt = startsAt; + // Record when the lobby first became visible to players, used to measure lobby fill time. + this.visibleAt ??= Date.now(); } public numClients(): number { @@ -656,6 +663,7 @@ export class GameServer { const result = GameStartInfoSchema.safeParse({ gameID: this.id, lobbyCreatedAt: this.createdAt, + visibleAt: this.visibleAt, config: this.gameConfig, players: this.activeClients.map((c) => ({ username: c.username, @@ -1001,6 +1009,7 @@ export class GameServer { Date.now(), this.winner?.winner, this.createdAt, + this.visibleAt, ), ), );