diff --git a/server/routes/games/gameAdapter.ts b/server/routes/games/gameAdapter.ts index 101e2fb..5429472 100644 --- a/server/routes/games/gameAdapter.ts +++ b/server/routes/games/gameAdapter.ts @@ -4,8 +4,7 @@ * without requiring immediate refactoring of all game logic */ -import type { GameRoom, GameSession, GameSessionMetadata } from './gameMetadata'; -import type { Session } from '../room/types'; +import type { GameRoom, GameSession, GameSessionMetadata } from "./gameMetadata"; /** * Proxy handler for Session objects @@ -15,13 +14,13 @@ import type { Session } from '../room/types'; const sessionProxyHandler: ProxyHandler = { get(target: GameSession, prop: string | symbol): any { // Direct properties take precedence - if (prop in target && prop !== 'metadata') { + if (prop in target && prop !== "metadata") { return (target as any)[prop]; } // Map game-specific properties to metadata - if (typeof prop === 'string') { - const gameProps = ['color', 'player', 'resources']; + if (typeof prop === "string") { + const gameProps = ["color", "player", "resources"]; if (gameProps.includes(prop)) { return target.metadata?.[prop as keyof GameSessionMetadata]; } @@ -33,20 +32,33 @@ const sessionProxyHandler: ProxyHandler = { set(target: GameSession, prop: string | symbol, value: any): boolean { // Direct properties const directProps = [ - 'id', 'userId', 'name', 'ws', 'live', 'lastActive', 'keepAlive', - 'connected', 'has_media', 'protected', 'bot_run_id', 'bot_provider_id', - 'bot_instance_id', '_initialSnapshotSent', '_getBatch', '_pendingMessage', - '_pendingTimeout' + "id", + "userId", + "name", + "ws", + "live", + "lastActive", + "keepAlive", + "connected", + "has_media", + "protected", + "bot_run_id", + "bot_provider_id", + "bot_instance_id", + "_initialSnapshotSent", + "_getBatch", + "_pendingMessage", + "_pendingTimeout", ]; - if (typeof prop === 'string' && directProps.includes(prop)) { + if (typeof prop === "string" && directProps.includes(prop)) { (target as any)[prop] = value; return true; } // Game-specific properties go to metadata - if (typeof prop === 'string') { - const gameProps = ['color', 'player', 'resources']; + if (typeof prop === "string") { + const gameProps = ["color", "player", "resources"]; if (gameProps.includes(prop)) { if (!target.metadata) { target.metadata = {}; @@ -63,8 +75,8 @@ const sessionProxyHandler: ProxyHandler = { has(target: GameSession, prop: string | symbol): boolean { if (prop in target) return true; - if (typeof prop === 'string') { - const gameProps = ['color', 'player', 'resources']; + if (typeof prop === "string") { + const gameProps = ["color", "player", "resources"]; return gameProps.includes(prop) && target.metadata !== undefined; } return false; @@ -78,13 +90,13 @@ const sessionProxyHandler: ProxyHandler = { const gameProxyHandler: ProxyHandler = { get(target: GameRoom, prop: string | symbol): any { // Direct room properties - const roomProps = ['id', 'name', 'sessions', 'state', 'created', 'lastActivity', 'private']; - if (typeof prop === 'string' && roomProps.includes(prop)) { + const roomProps = ["id", "name", "sessions", "state", "created", "lastActivity", "private"]; + if (typeof prop === "string" && roomProps.includes(prop)) { return (target as any)[prop]; } // Game properties from metadata - if (typeof prop === 'string' && target.metadata) { + if (typeof prop === "string" && target.metadata) { if (prop in target.metadata) { return (target.metadata as any)[prop]; } @@ -95,14 +107,14 @@ const gameProxyHandler: ProxyHandler = { set(target: GameRoom, prop: string | symbol, value: any): boolean { // Direct room properties - const roomProps = ['id', 'name', 'sessions', 'state', 'created', 'lastActivity', 'private']; - if (typeof prop === 'string' && roomProps.includes(prop)) { + const roomProps = ["id", "name", "sessions", "state", "created", "lastActivity", "private"]; + if (typeof prop === "string" && roomProps.includes(prop)) { (target as any)[prop] = value; return true; } // Game properties to metadata - if (typeof prop === 'string') { + if (typeof prop === "string") { if (!target.metadata) { target.metadata = {} as any; } @@ -115,8 +127,8 @@ const gameProxyHandler: ProxyHandler = { }, has(target: GameRoom, prop: string | symbol): boolean { - const roomProps = ['id', 'name', 'sessions', 'state', 'created', 'lastActivity', 'private']; - if (typeof prop === 'string') { + const roomProps = ["id", "name", "sessions", "state", "created", "lastActivity", "private"]; + if (typeof prop === "string") { if (roomProps.includes(prop)) return true; if (target.metadata && prop in target.metadata) return true; } @@ -147,7 +159,9 @@ export function wrapGameSessions(game: GameRoom): GameRoom { // Wrap each session const wrappedSessions: Record = {}; for (const id in game.sessions) { - wrappedSessions[id] = wrapSession(game.sessions[id]); + if (game.sessions[id]) { + wrappedSessions[id] = wrapSession(game.sessions[id]); + } } wrappedGame.sessions = wrappedSessions; diff --git a/server/routes/games/gameMetadata.ts b/server/routes/games/gameMetadata.ts index 3d8c608..7266e22 100644 --- a/server/routes/games/gameMetadata.ts +++ b/server/routes/games/gameMetadata.ts @@ -3,7 +3,7 @@ * These extend the base Room/Session types with Settlers of Catan specific data */ -import type { Player, Turn, Placements, DevelopmentCard } from './types'; +import type { Player, Turn, Placements, DevelopmentCard } from "./types"; /** * Game-specific session metadata @@ -163,7 +163,7 @@ export function migrateGameToRoomFormat(oldGame: any): GameRoom { id: oldGame.id, name: oldGame.id, // Game ID is the room name sessions: newSessions, - state: oldGame.state || 'lobby', + state: oldGame.state || "lobby", created: Date.now(), lastActivity: Date.now(), private: false, @@ -180,7 +180,9 @@ export function migrateRoomToGameFormat(room: GameRoom): any { // Convert sessions back const oldSessions: Record = {}; for (const sessionId in sessions) { - oldSessions[sessionId] = migrateSessionToOldFormat(sessions[sessionId]); + if (sessions[sessionId]) { + oldSessions[sessionId] = migrateSessionToOldFormat(sessions[sessionId]); + } } return { diff --git a/server/routes/games/helpers.ts b/server/routes/games/helpers.ts index 8b77527..3ce799a 100644 --- a/server/routes/games/helpers.ts +++ b/server/routes/games/helpers.ts @@ -167,3 +167,22 @@ export const setForSettlementPlacement = (game: Game, limits: number[] | undefin game.turn.actions = ["place-settlement"]; game.turn.limits = { corners: limits }; }; + +// Adjust a player's resource counts by a deltas map. Deltas may be negative. +export const adjustResources = (player: Player, deltas: Partial>): void => { + if (!player) return; + let total = player.resources || 0; + const keys = Object.keys(deltas || {}); + keys.forEach((k) => { + const v = deltas[k] || 0; + // update named resource slot if present + try { + const current = (player as any)[k] || 0; + (player as any)[k] = current + v; + total += v; + } catch (e) { + // ignore unexpected keys + } + }); + player.resources = total; +}; diff --git a/server/routes/games/types.ts b/server/routes/games/types.ts index 420edf5..78ea220 100644 --- a/server/routes/games/types.ts +++ b/server/routes/games/types.ts @@ -1,6 +1,6 @@ export type ResourceKey = "wood" | "brick" | "sheep" | "wheat" | "stone"; -export type ResourceMap = Partial> & { [k: string]: any }; +export type ResourceMap = Partial>; export interface Player { name?: string; @@ -124,7 +124,7 @@ export interface Game { turn: Turn; pipOrder?: number[]; tileOrder?: number[]; - borderOrder?: number[]; + resources?: number; tiles?: any[]; pips?: any[]; dice?: number[];