diff --git a/shared/games/registry.ts b/shared/games/registry.ts new file mode 100644 index 0000000..9a997ce --- /dev/null +++ b/shared/games/registry.ts @@ -0,0 +1,18 @@ +import type { GamePlugin } from "./types"; + +const games = new Map(); + +export const gameRegistry = { + register(plugin: GamePlugin) { + if (games.has(plugin.slug)) { + throw new Error(`Game "${plugin.slug}" is already registered`); + } + games.set(plugin.slug, plugin); + }, + get(slug: string): GamePlugin | undefined { + return games.get(slug); + }, + list(): GamePlugin[] { + return Array.from(games.values()); + }, +}; diff --git a/shared/games/types.ts b/shared/games/types.ts new file mode 100644 index 0000000..5da77d2 --- /dev/null +++ b/shared/games/types.ts @@ -0,0 +1,23 @@ +export interface GamePlugin { + slug: string; + name: string; + minPlayers: number; + maxPlayers: number; + + createInitialState(players: string[]): TState; + handleAction(state: TState, action: TAction, playerId: string): GameResult; + getPlayerView(state: TState, playerId: string): unknown; + getSpectatorView(state: TState): unknown; + + isGameOver?(state: TState): GameOverResult | null; + onPlayerDisconnect?(state: TState, playerId: string): TState; +} + +export type GameResult = + | { ok: true; state: TState } + | { ok: false; error: string }; + +export type GameOverResult = { + winner: string | null; + reason: string; +};