have client send winner to server

This commit is contained in:
Evan
2025-01-24 20:07:12 -08:00
parent d053a82ff6
commit a5a2f46099
8 changed files with 58 additions and 10 deletions
+7 -1
View File
@@ -16,6 +16,8 @@ export class LocalServer {
private paused = false
private winner: ClientID | null = null
constructor(
private serverConfig: ServerConfig,
@@ -58,6 +60,9 @@ export class LocalServer {
}
this.intents.push(clientMsg.intent)
}
if (clientMsg.type == "winner") {
this.winner = clientMsg.winner
}
}
private endTurn() {
@@ -92,7 +97,8 @@ export class LocalServer {
players,
this.turns,
this.startedAt,
Date.now()
Date.now(),
this.winner
)
// Clear turns because beacon only supports up to 64kb
record.turns = []
+23 -1
View File
@@ -2,7 +2,7 @@ import { Config, ServerConfig } from "../core/configuration/Config"
import { SendLogEvent } from "../core/Consolex"
import { EventBus, GameEvent } from "../core/EventBus"
import { AllianceRequest, AllPlayers, Cell, GameType, Player, PlayerID, PlayerType, UnitType } from "../core/game/Game"
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ClientPingMessageSchema, GameConfig, ClientLogMessageSchema } from "../core/Schemas"
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ClientPingMessageSchema, GameConfig, ClientLogMessageSchema, ClientSendWinnerSchema } from "../core/Schemas"
import { LobbyConfig } from "./ClientGameRunner"
import { LocalServer } from "./LocalServer"
import { UsernameInput } from "./UsernameInput";
@@ -93,6 +93,12 @@ export class SendSetTargetTroopRatioEvent implements GameEvent {
) { }
}
export class SendWinnerEvent implements GameEvent {
constructor(
public readonly winner: ClientID
) { }
}
export class Transport {
private socket: WebSocket
@@ -132,6 +138,7 @@ export class Transport {
this.eventBus.on(SendLogEvent, (e) => this.onSendLogEvent(e))
this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e))
this.eventBus.on(SendWinnerEvent, (e) => this.onSendWinnerEvent(e))
}
private startPing() {
@@ -375,6 +382,21 @@ export class Transport {
}
}
private onSendWinnerEvent(event: SendWinnerEvent) {
if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
const msg = ClientSendWinnerSchema.parse({
type: "winner",
clientID: this.lobbyConfig.clientID,
gameID: this.lobbyConfig.gameID,
winner: event.winner,
})
this.sendMsg(JSON.stringify(msg))
} else {
console.log('WebSocket is not open. Current state:', this.socket.readyState);
console.log('attempting reconnect')
}
}
private sendIntent(intent: Intent) {
if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
const msg = ClientIntentMessageSchema.parse({
+1
View File
@@ -81,6 +81,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventB
if (!(winModel instanceof WinModal)) {
console.error('win modal not found')
}
winModel.eventBus = eventBus
winModel.game = game
const optionsMenu = document.querySelector('options-menu') as OptionsMenu
+4
View File
@@ -7,6 +7,8 @@ import { Layer } from './Layer';
import { GameUpdateType } from '../../../core/game/GameUpdates';
import { PseudoRandom } from '../../../core/PseudoRandom';
import { simpleHash } from '../../../core/Util';
import { EventBus } from '../../../core/EventBus';
import { SendWinnerEvent } from '../../Transport';
const lowRadiationVictoryQuotes = [
@@ -147,6 +149,7 @@ export const defeatQuotes = [
@customElement('win-modal')
export class WinModal extends LitElement implements Layer {
public game: GameView
public eventBus: EventBus
private rand: PseudoRandom;
@@ -300,6 +303,7 @@ export class WinModal extends LitElement implements Layer {
this.game.updatesSinceLastTick()[GameUpdateType.WinUpdate]
.forEach(wu => {
const winner = this.game.playerBySmallID(wu.winnerID) as PlayerView
this.eventBus.emit(new SendWinnerEvent(winner.clientID()))
if (winner == this.game.myPlayer()) {
this._title = 'You Won!'
if (this.game.numTilesWithFallout() / this.game.numLandTiles() > .6) {
+11 -3
View File
@@ -31,13 +31,14 @@ export type BuildUnitIntent = z.infer<typeof BuildUnitIntentSchema>
export type Turn = z.infer<typeof TurnSchema>
export type GameConfig = z.infer<typeof GameConfigSchema>
export type ClientMessage = ClientPingMessage | ClientIntentMessage | ClientJoinMessage | ClientLogMessage
export type ClientMessage = ClientSendWinnerMessage | ClientPingMessage | ClientIntentMessage | ClientJoinMessage | ClientLogMessage
export type ServerMessage = ServerSyncMessage | ServerStartGameMessage | ServerPingMessage
export type ServerSyncMessage = z.infer<typeof ServerTurnMessageSchema>
export type ServerStartGameMessage = z.infer<typeof ServerStartGameMessageSchema>
export type ServerPingMessage = z.infer<typeof ServerPingMessageSchema>
export type ClientSendWinnerMessage = z.infer<typeof ClientSendWinnerSchema>
export type ClientPingMessage = z.infer<typeof ClientPingMessageSchema>
export type ClientIntentMessage = z.infer<typeof ClientIntentMessageSchema>
export type ClientJoinMessage = z.infer<typeof ClientJoinMessageSchema>
@@ -224,11 +225,16 @@ export const ServerMessageSchema = z.union([
// Client
const ClientBaseMessageSchema = z.object({
type: z.enum(['join', 'intent', 'ping', 'log']),
type: z.enum(['winner', 'join', 'intent', 'ping', 'log']),
clientID: ID,
gameID: ID,
})
export const ClientSendWinnerSchema = ClientBaseMessageSchema.extend({
type: z.literal('winner'),
winner: ID,
})
export const ClientLogMessageSchema = ClientBaseMessageSchema.extend({
type: z.literal('log'),
severity: z.nativeEnum(LogSeverity),
@@ -254,6 +260,7 @@ export const ClientJoinMessageSchema = ClientBaseMessageSchema.extend({
})
export const ClientMessageSchema = z.union([
ClientSendWinnerSchema,
ClientPingMessageSchema,
ClientIntentMessageSchema,
ClientJoinMessageSchema,
@@ -276,5 +283,6 @@ export const GameRecordSchema = z.object({
durationSeconds: z.number(),
date: SafeString,
num_turns: z.number(),
turns: z.array(TurnSchema)
turns: z.array(TurnSchema),
winner: ID.nullable()
})
+4 -2
View File
@@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid';
import twemoji from 'twemoji';
import DOMPurify from 'dompurify';
import { Cell, Game, Player, Unit } from "./game/Game";
import { GameConfig, GameID, GameRecord, PlayerRecord, Turn } from './Schemas';
import { ClientID, GameConfig, GameID, GameRecord, PlayerRecord, Turn } from './Schemas';
import { customAlphabet, nanoid } from 'nanoid';
import { andFN, GameMap, manhattanDistFN, TileRef } from './game/GameMap';
@@ -207,7 +207,8 @@ export function CreateGameRecord(
players: PlayerRecord[],
turns: Turn[],
start: number,
end: number
end: number,
winner: ClientID | null
): GameRecord {
const record: GameRecord = {
id: id,
@@ -235,6 +236,7 @@ export function CreateGameRecord(
record.players = players
record.durationSeconds = Math.floor((record.endTimestampMS - record.startTimestampMS) / 1000)
record.num_turns = turns.length
record.winner = winner
return record;
}
+1 -1
View File
@@ -79,7 +79,7 @@ async function archiveToBigQuery(gameRecord: GameRecord) {
duration_seconds: gameRecord.durationSeconds,
number_turns: gameRecord.num_turns,
game_mode: gameRecord.gameConfig.gameType,
winner: null,
winner: gameRecord.winner,
difficulty: gameRecord.gameConfig.difficulty,
map: gameRecord.gameConfig.gameMap,
players: gameRecord.players.map(p => ({
+7 -2
View File
@@ -5,7 +5,6 @@ import WebSocket from 'ws';
import { slog } from "./StructuredLog";
import { CreateGameRecord } from "../core/Util";
import { archive } from "./Archive";
import { arc } from "d3";
export enum GamePhase {
@@ -31,6 +30,8 @@ export class GameServer {
private lastPingUpdate = 0
private winner: ClientID | null = null
constructor(
public readonly id: string,
public readonly createdAt: number,
@@ -89,6 +90,9 @@ export class GameServer {
this.lastPingUpdate = Date.now()
client.lastPing = Date.now()
}
if (clientMsg.type == "winner") {
this.winner = clientMsg.winner
}
} catch (error) {
console.log(`error handline websocket request in game server: ${error}`)
}
@@ -190,7 +194,8 @@ export class GameServer {
playerRecords,
this.turns,
this._startTime,
Date.now()
Date.now(),
this.winner
)
)
} else {