1
0

209 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";
/**
* 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) {
if (game.sessions[id]) {
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;
}