195 lines
5.4 KiB
TypeScript
195 lines
5.4 KiB
TypeScript
/**
|
|
* Game Adapter - Provides backward compatibility layer
|
|
* This allows existing game code to work with the new Room/Session architecture
|
|
* without requiring immediate refactoring of all game logic
|
|
*/
|
|
|
|
import type { GameRoom, GameSession, GameSessionMetadata } from './gameMetadata';
|
|
import type { Session } from '../room/types';
|
|
|
|
/**
|
|
* Proxy handler for Session objects
|
|
* Intercepts property access to provide backward compatibility
|
|
* Maps session.color -> session.metadata.color, etc.
|
|
*/
|
|
const sessionProxyHandler: ProxyHandler<GameSession> = {
|
|
get(target: GameSession, prop: string | symbol): any {
|
|
// Direct properties take precedence
|
|
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 (gameProps.includes(prop)) {
|
|
return target.metadata?.[prop as keyof GameSessionMetadata];
|
|
}
|
|
}
|
|
|
|
return (target as any)[prop];
|
|
},
|
|
|
|
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'
|
|
];
|
|
|
|
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 (gameProps.includes(prop)) {
|
|
if (!target.metadata) {
|
|
target.metadata = {};
|
|
}
|
|
(target.metadata as any)[prop] = value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Unknown properties
|
|
(target as any)[prop] = value;
|
|
return true;
|
|
},
|
|
|
|
has(target: GameSession, prop: string | symbol): boolean {
|
|
if (prop in target) return true;
|
|
if (typeof prop === 'string') {
|
|
const gameProps = ['color', 'player', 'resources'];
|
|
return gameProps.includes(prop) && target.metadata !== undefined;
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Proxy handler for Game/Room objects
|
|
* Maps game.players -> game.metadata.players, etc.
|
|
*/
|
|
const gameProxyHandler: ProxyHandler<GameRoom> = {
|
|
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)) {
|
|
return (target as any)[prop];
|
|
}
|
|
|
|
// Game properties from metadata
|
|
if (typeof prop === 'string' && target.metadata) {
|
|
if (prop in target.metadata) {
|
|
return (target.metadata as any)[prop];
|
|
}
|
|
}
|
|
|
|
return (target as any)[prop];
|
|
},
|
|
|
|
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)) {
|
|
(target as any)[prop] = value;
|
|
return true;
|
|
}
|
|
|
|
// Game properties to metadata
|
|
if (typeof prop === 'string') {
|
|
if (!target.metadata) {
|
|
target.metadata = {} as any;
|
|
}
|
|
(target.metadata as any)[prop] = value;
|
|
return true;
|
|
}
|
|
|
|
(target as any)[prop] = value;
|
|
return true;
|
|
},
|
|
|
|
has(target: GameRoom, prop: string | symbol): boolean {
|
|
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;
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Wrap a session object with backward compatibility proxy
|
|
*/
|
|
export function wrapSession(session: GameSession): GameSession {
|
|
return new Proxy(session, sessionProxyHandler);
|
|
}
|
|
|
|
/**
|
|
* Wrap a game/room object with backward compatibility proxy
|
|
*/
|
|
export function wrapGame(game: GameRoom): GameRoom {
|
|
return new Proxy(game, gameProxyHandler);
|
|
}
|
|
|
|
/**
|
|
* Wrap all sessions in a game/room
|
|
*/
|
|
export function wrapGameSessions(game: GameRoom): GameRoom {
|
|
const wrappedGame = wrapGame(game);
|
|
|
|
// Wrap each session
|
|
const wrappedSessions: Record<string, GameSession> = {};
|
|
for (const id in game.sessions) {
|
|
wrappedSessions[id] = wrapSession(game.sessions[id]);
|
|
}
|
|
|
|
wrappedGame.sessions = wrappedSessions;
|
|
return wrappedGame;
|
|
}
|
|
|
|
/**
|
|
* Initialize metadata for a session if it doesn't exist
|
|
*/
|
|
export function ensureSessionMetadata(session: GameSession): void {
|
|
if (!session.metadata) {
|
|
session.metadata = {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize metadata for a game/room if it doesn't exist
|
|
*/
|
|
export function ensureGameMetadata(game: GameRoom): void {
|
|
if (!game.metadata) {
|
|
game.metadata = {
|
|
developmentCards: [],
|
|
players: {},
|
|
placements: { corners: [], roads: [] },
|
|
turn: {},
|
|
} as any;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to access session's game metadata safely
|
|
*/
|
|
export function getSessionGameData(session: GameSession): GameSessionMetadata {
|
|
ensureSessionMetadata(session);
|
|
return session.metadata!;
|
|
}
|
|
|
|
/**
|
|
* Helper to access game's metadata safely
|
|
*/
|
|
export function getGameMetadata(game: GameRoom) {
|
|
ensureGameMetadata(game);
|
|
return game.metadata;
|
|
}
|