@aprovan/patchwork-image-boardgameio 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +559 -0
- package/dist/index.js +1392 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/p2p/authentication.ts","../src/p2p/db.ts","../src/p2p/session-storage.ts","../src/p2p/host.ts","../src/p2p/transport.ts","../src/bot-manager.ts","../src/mount.ts","../src/setup.ts","../src/multiplayer.ts"],"sourcesContent":["/**\n * Settings Context for boardgame.io widgets\n *\n * Provides game settings to board components via React context.\n * Uses window.React at runtime (preloaded by the compiler).\n */\n\n// Generic settings type - games can define their own specific types\nexport type GameSettings = Record<string, unknown>;\n\n// Get React from window (preloaded at runtime)\ninterface ReactLike {\n createContext: (defaultValue: unknown) => unknown;\n useContext: (context: unknown) => unknown;\n createElement: (\n type: unknown,\n props?: unknown,\n ...children: unknown[]\n ) => unknown;\n}\n\nconst getReact = (): ReactLike => {\n const win = window as unknown as { React?: ReactLike };\n if (!win.React) {\n throw new Error(\n '[boardgameio] React not found on window. Ensure React is preloaded.',\n );\n }\n return win.React;\n};\n\n// Lazy-init context to ensure React is available\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet SettingsContext: any = null;\n\nfunction getSettingsContext(): unknown {\n if (!SettingsContext) {\n SettingsContext = getReact().createContext({});\n }\n return SettingsContext;\n}\n\nexport interface SettingsProviderProps {\n settings: GameSettings;\n children: unknown;\n}\n\nexport function SettingsProvider({\n settings,\n children,\n}: SettingsProviderProps): unknown {\n const React = getReact();\n const Context = getSettingsContext();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return React.createElement(\n (Context as any).Provider,\n { value: settings },\n children,\n );\n}\n\n/**\n * Hook to access game settings from within a board component.\n *\n * @example\n * ```tsx\n * function MyBoard({ G, ctx, moves }: BoardProps) {\n * const settings = useSettings<{ difficulty: string }>();\n * // ...\n * }\n * ```\n */\nexport function useSettings<T extends GameSettings = GameSettings>(): T {\n const React = getReact();\n const Context = getSettingsContext();\n return React.useContext(Context) as T;\n}\n","import type { P2PDB } from './db.js';\nimport type { ClientMetadata } from './types.js';\n\nexport function generateCredentials(): string {\n const bytes = new Uint8Array(64);\n crypto.getRandomValues(bytes);\n return btoa(String.fromCharCode(...bytes));\n}\n\nexport function generateKeyPair(): { publicKey: string; privateKey: string } {\n const privateKey = new Uint8Array(64);\n crypto.getRandomValues(privateKey);\n const publicKey = privateKey.slice(0, 32);\n return {\n publicKey: btoa(String.fromCharCode(...publicKey)),\n privateKey: btoa(String.fromCharCode(...privateKey)),\n };\n}\n\nexport function signMessage(message: string, privateKey: string): string {\n const msgBytes = new TextEncoder().encode(message);\n const keyBytes = Uint8Array.from(atob(privateKey), (c) => c.charCodeAt(0));\n const signature = new Uint8Array(msgBytes.length + 64);\n signature.set(msgBytes);\n signature.set(keyBytes.slice(0, 64), msgBytes.length);\n return btoa(String.fromCharCode(...signature));\n}\n\nfunction verifyMessage(\n signedMessage: string,\n publicKey: string,\n playerID: string,\n): boolean {\n try {\n const sigBytes = Uint8Array.from(atob(signedMessage), (c) =>\n c.charCodeAt(0),\n );\n const keyBytes = Uint8Array.from(atob(publicKey), (c) => c.charCodeAt(0));\n if (sigBytes.length < 64 || keyBytes.length < 32) return false;\n const msgBytes = sigBytes.slice(0, -64);\n const decoded = new TextDecoder().decode(msgBytes);\n return decoded === playerID;\n } catch {\n return false;\n }\n}\n\nexport function authenticate(\n matchID: string,\n clientMetadata: ClientMetadata,\n db: P2PDB,\n): boolean {\n const { playerID, credentials, message } = clientMetadata;\n const { metadata } = db.fetch(matchID);\n\n if (!metadata) return false;\n\n // Spectators don't need auth\n if (\n playerID === null ||\n playerID === undefined ||\n !(+playerID in metadata.players)\n ) {\n return true;\n }\n\n const existingCredentials = metadata.players[+playerID]?.credentials;\n const isMessageValid = credentials\n ? !!message && verifyMessage(message, credentials, playerID)\n : false;\n\n // First connection: store credentials\n if (!existingCredentials && isMessageValid) {\n db.setMetadata(matchID, {\n ...metadata,\n players: {\n ...metadata.players,\n [+playerID]: { ...metadata.players[+playerID], credentials },\n },\n });\n return true;\n }\n\n // No credentials anywhere: allow\n if (!existingCredentials && !credentials) return true;\n\n // Credentials match\n if (existingCredentials === credentials && isMessageValid) return true;\n\n return false;\n}\n","import type { LogEntry, Server, State, StorageAPI } from 'boardgame.io';\n\nexport class P2PDB {\n private initialState = new Map<string, State>();\n private state = new Map<string, State>();\n private log = new Map<string, LogEntry[]>();\n private metadata = new Map<string, Server.MatchData>();\n\n public connect(): void {}\n\n public createMatch(matchID: string, opts: StorageAPI.CreateMatchOpts): void {\n this.initialState.set(matchID, opts.initialState);\n this.state.set(matchID, opts.initialState);\n this.log.set(matchID, opts.initialState._stateID === 0 ? [] : []);\n this.metadata.set(matchID, opts.metadata);\n }\n\n public setState(matchID: string, state: State, deltalog?: LogEntry[]): void {\n this.state.set(matchID, state);\n if (deltalog) {\n const existing = this.log.get(matchID) ?? [];\n this.log.set(matchID, [...existing, ...deltalog]);\n }\n }\n\n public setMetadata(matchID: string, metadata: Server.MatchData): void {\n this.metadata.set(matchID, metadata);\n }\n\n public fetch<O extends StorageAPI.FetchOpts>(\n matchID: string,\n ): StorageAPI.FetchResult<O> {\n return {\n state: this.state.get(matchID),\n initialState: this.initialState.get(matchID),\n log: this.log.get(matchID),\n metadata: this.metadata.get(matchID),\n } as StorageAPI.FetchResult<O>;\n }\n\n public wipe(matchID: string): void {\n this.initialState.delete(matchID);\n this.state.delete(matchID);\n this.log.delete(matchID);\n this.metadata.delete(matchID);\n }\n\n public listMatches(): string[] {\n return [...this.metadata.keys()];\n }\n}\n","import type { LogEntry, Server, State } from 'boardgame.io';\n\nconst STORAGE_PREFIX = 'p2p-session:';\nconst SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport interface PersistedSession {\n matchID: string;\n gameName: string;\n numPlayers: number;\n state: State;\n initialState: State;\n log: LogEntry[];\n metadata: Server.MatchData;\n createdAt: number;\n updatedAt: number;\n}\n\nfunction getStorageKey(matchID: string): string {\n return `${STORAGE_PREFIX}${matchID}`;\n}\n\n/**\n * Check if a session exists and is not expired\n */\nexport function hasValidSession(matchID: string): boolean {\n const session = loadSession(matchID);\n return session !== null;\n}\n\n/**\n * Load a persisted session from localStorage\n * Returns null if not found or expired\n */\nexport function loadSession(matchID: string): PersistedSession | null {\n try {\n const key = getStorageKey(matchID);\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n\n const session = JSON.parse(raw) as PersistedSession;\n const now = Date.now();\n\n // Check if session has expired\n if (now - session.updatedAt > SESSION_TTL_MS) {\n console.log('[P2PSessionStorage] Session expired, removing:', matchID);\n removeSession(matchID);\n return null;\n }\n\n console.log('[P2PSessionStorage] Loaded session:', {\n matchID,\n stateID: session.state._stateID,\n age: Math.round((now - session.updatedAt) / 1000) + 's',\n });\n\n return session;\n } catch (err) {\n console.error('[P2PSessionStorage] Failed to load session:', err);\n return null;\n }\n}\n\n/**\n * Save or update a session in localStorage\n */\nexport function saveSession(session: PersistedSession): void {\n try {\n const key = getStorageKey(session.matchID);\n session.updatedAt = Date.now();\n const serialized = JSON.stringify(session);\n localStorage.setItem(key, serialized);\n console.log('[P2PSessionStorage] Saved session:', {\n matchID: session.matchID,\n stateID: session.state._stateID,\n gameName: session.gameName,\n sizeBytes: serialized.length,\n });\n } catch (err) {\n console.error('[P2PSessionStorage] Failed to save session:', err);\n // Check for quota exceeded\n if (err instanceof DOMException && err.name === 'QuotaExceededError') {\n console.warn('[P2PSessionStorage] Storage quota exceeded, cleaning up old sessions');\n cleanupExpiredSessions();\n }\n }\n}\n\n/**\n * Update just the state portion of a session (faster than full save)\n */\nexport function updateSessionState(\n matchID: string,\n state: State,\n log?: LogEntry[],\n): void {\n const session = loadSession(matchID);\n if (!session) return;\n\n session.state = state;\n if (log) session.log = log;\n saveSession(session);\n}\n\n/**\n * Remove a session from localStorage\n */\nexport function removeSession(matchID: string): void {\n try {\n const key = getStorageKey(matchID);\n localStorage.removeItem(key);\n console.log('[P2PSessionStorage] Removed session:', matchID);\n } catch (err) {\n console.error('[P2PSessionStorage] Failed to remove session:', err);\n }\n}\n\n/**\n * Remove all expired sessions from localStorage\n */\nexport function cleanupExpiredSessions(): void {\n try {\n const now = Date.now();\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (!key?.startsWith(STORAGE_PREFIX)) continue;\n\n try {\n const raw = localStorage.getItem(key);\n if (!raw) continue;\n const session = JSON.parse(raw) as PersistedSession;\n if (now - session.updatedAt > SESSION_TTL_MS) {\n keysToRemove.push(key);\n }\n } catch {\n keysToRemove.push(key);\n }\n }\n\n for (const key of keysToRemove) {\n localStorage.removeItem(key);\n console.log('[P2PSessionStorage] Cleaned up expired session:', key);\n }\n } catch (err) {\n console.error('[P2PSessionStorage] Cleanup failed:', err);\n }\n}\n\n/**\n * Extend the TTL of a session (call periodically to keep session alive)\n */\nexport function touchSession(matchID: string): void {\n const session = loadSession(matchID);\n if (session) {\n saveSession(session);\n }\n}\n","import type {\n ChatMessage,\n CredentialedActionShape,\n Game,\n Server,\n State,\n} from 'boardgame.io';\nimport { P2PDB } from './db.js';\nimport { authenticate } from './authentication.js';\nimport type { Client, ClientAction } from './types.js';\nimport {\n loadSession,\n saveSession,\n updateSessionState,\n type PersistedSession,\n} from './session-storage.js';\n\ntype UpdateArgs = [string, State, CredentialedActionShape.Any];\ntype ChatArgs = [string, ChatMessage, string | undefined];\n\nexport class P2PHost {\n private clients = new Map<Client, Client>();\n private hostClient: Client | null = null;\n private matchID: string;\n private db: P2PDB;\n private game: Game;\n private numPlayers: number;\n private state: State;\n\n public constructor({\n game,\n numPlayers = 2,\n matchID,\n }: {\n game: Game;\n numPlayers?: number;\n matchID: string;\n }) {\n this.matchID = matchID;\n this.game = game;\n this.numPlayers = numPlayers;\n this.db = new P2PDB();\n\n // Try to restore from a previous session\n const gameName = game.name ?? 'unknown';\n console.log('[P2PHost] Checking for existing session:', { matchID, gameName });\n \n const existingSession = loadSession(matchID);\n if (existingSession) {\n console.log('[P2PHost] Found existing session:', {\n matchID,\n sessionGameName: existingSession.gameName,\n expectedGameName: gameName,\n stateID: existingSession.state._stateID,\n });\n \n if (existingSession.gameName === gameName) {\n console.log('[P2PHost] Restoring session from storage');\n\n this.state = existingSession.state;\n this.db.createMatch(matchID, {\n initialState: existingSession.initialState,\n metadata: existingSession.metadata,\n });\n this.db.setState(matchID, existingSession.state, existingSession.log);\n \n // Refresh the session timestamp to prevent expiry\n saveSession(existingSession);\n return;\n } else {\n console.log('[P2PHost] Game name mismatch, creating new session');\n }\n } else {\n console.log('[P2PHost] No existing session found, creating new');\n }\n\n // Create fresh state for new session\n const initialState = this.createInitialState();\n this.state = initialState;\n\n const players: Record<number, Server.PlayerMetadata> = {};\n for (let i = 0; i < numPlayers; i++) {\n players[i] = { id: i };\n }\n\n const metadata: Server.MatchData = {\n gameName: game.name ?? 'unknown',\n players,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n this.db.createMatch(matchID, {\n initialState,\n metadata,\n });\n\n // Persist the new session\n this.persistSession(initialState, [], metadata);\n }\n\n private persistSession(\n state: State,\n log: unknown[],\n metadata?: Server.MatchData,\n ): void {\n const existingSession = loadSession(this.matchID);\n const session: PersistedSession = {\n matchID: this.matchID,\n gameName: this.game.name ?? 'unknown',\n numPlayers: this.numPlayers,\n state,\n initialState: existingSession?.initialState ?? state,\n log: log as PersistedSession['log'],\n metadata: metadata ?? existingSession?.metadata ?? {\n gameName: this.game.name ?? 'unknown',\n players: {},\n createdAt: Date.now(),\n updatedAt: Date.now(),\n },\n createdAt: existingSession?.createdAt ?? Date.now(),\n updatedAt: Date.now(),\n };\n saveSession(session);\n }\n\n private createInitialState(): State {\n // Mark all players as active in Stage.NULL ('') so they can all make moves\n const activePlayers: Record<string, string> = {};\n for (let i = 0; i < this.numPlayers; i++) {\n activePlayers[String(i)] = '';\n }\n\n const ctx = {\n numPlayers: this.numPlayers,\n turn: 1,\n currentPlayer: '0',\n playOrder: Array.from({ length: this.numPlayers }, (_, i) => String(i)),\n playOrderPos: 0,\n phase: '',\n activePlayers,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const G = this.game.setup?.({ ctx, ...ctx } as any) ?? {};\n\n return {\n G,\n ctx,\n plugins: {},\n _stateID: 0,\n _undo: [],\n _redo: [],\n } as State;\n }\n\n public registerClient(client: Client): void {\n if (!authenticate(this.matchID, client.metadata, this.db)) {\n console.log(\n '[P2PHost] Client auth failed for playerID:',\n client.metadata.playerID,\n );\n return;\n }\n\n console.log(\n '[P2PHost] Registered client for playerID:',\n client.metadata.playerID,\n );\n this.clients.set(client, client);\n this.syncClient(client);\n }\n\n public registerHostClient(client: Client): void {\n // Host client is always trusted, skip auth\n console.log(\n '[P2PHost] Registered host client for playerID:',\n client.metadata.playerID,\n );\n this.hostClient = client;\n this.syncClient(client);\n }\n\n public unregisterClient(client: Client): void {\n this.clients.delete(client);\n }\n\n public processAction(client: Client, data: ClientAction): void {\n switch (data.type) {\n case 'sync':\n this.syncClient(client);\n break;\n case 'update':\n this.handleUpdate(data.args as UpdateArgs);\n break;\n case 'chat':\n this.broadcastChat(data.args as ChatArgs);\n break;\n }\n }\n\n private syncClient(client: Client): void {\n const { state, log } = this.db.fetch(this.matchID);\n const playerID = client.metadata.playerID;\n const filteredState = this.filterStateForPlayer(state, playerID);\n client.send({\n type: 'sync',\n args: [this.matchID, { state: filteredState, log }],\n });\n }\n\n private filterStateForPlayer(\n state: State | undefined,\n _playerID: string | null,\n ): State | undefined {\n return state;\n }\n\n private handleUpdate([matchID, , action]: UpdateArgs): void {\n if (matchID !== this.matchID) return;\n\n const currentState = this.state;\n if (!currentState) return;\n\n const moveName = action.payload?.type;\n const moveArgs = action.payload?.args ?? [];\n const playerID = action.payload?.playerID;\n\n // Determine current player from either ctx.currentPlayer or G.current\n const ctxCurrentPlayer = currentState.ctx.currentPlayer;\n const gCurrent = (currentState.G as { current?: number })?.current;\n const currentPlayer = gCurrent !== undefined ? String(gCurrent) : ctxCurrentPlayer;\n\n console.log('[P2PHost] handleUpdate:', { moveName, moveArgs, playerID, currentPlayer });\n\n // Validate that the player making the move is the current player\n if (playerID !== currentPlayer) {\n console.log('[P2PHost] Rejected move: wrong player', { playerID, currentPlayer });\n return;\n }\n\n if (moveName && this.game.moves?.[moveName]) {\n const move = this.game.moves[moveName];\n const ctx = { ...currentState.ctx, playerID };\n // Deep clone G to avoid frozen state issues\n const G = JSON.parse(JSON.stringify(currentState.G));\n\n console.log(\n '[P2PHost] Before move, G.players:',\n G.players?.map((p: { id: number; bet: number }) => ({\n id: p.id,\n bet: p.bet,\n })),\n );\n\n if (typeof move === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n move({ G, ctx } as any, ...moveArgs);\n }\n\n console.log(\n '[P2PHost] After move, G.players:',\n G.players?.map((p: { id: number; bet: number }) => ({\n id: p.id,\n bet: p.bet,\n })),\n );\n\n const newState: State = {\n ...currentState,\n G,\n _stateID: currentState._stateID + 1,\n };\n\n this.state = newState;\n this.db.setState(this.matchID, newState);\n \n // Persist state to localStorage for host reconnection\n const { log } = this.db.fetch(this.matchID);\n updateSessionState(this.matchID, newState, log);\n \n console.log(\n '[P2PHost] State updated, broadcasting to',\n this.clients.size,\n 'clients',\n );\n this.broadcastState();\n } else {\n console.log(\n '[P2PHost] Move not found:',\n moveName,\n 'Available:',\n Object.keys(this.game.moves || {}),\n );\n }\n }\n\n private broadcastState(): void {\n const { state, log } = this.db.fetch(this.matchID);\n\n // Notify host client first\n if (this.hostClient) {\n const playerID = this.hostClient.metadata.playerID;\n const filteredState = this.filterStateForPlayer(state, playerID);\n this.hostClient.send({\n type: 'sync',\n args: [this.matchID, { state: filteredState, log }],\n });\n }\n\n // Notify remote clients\n for (const client of this.clients.values()) {\n const playerID = client.metadata.playerID;\n const filteredState = this.filterStateForPlayer(state, playerID);\n client.send({\n type: 'sync',\n args: [this.matchID, { state: filteredState, log }],\n });\n }\n }\n\n private broadcastChat(args: ChatArgs): void {\n if (this.hostClient) {\n this.hostClient.send({ type: 'chat', args });\n }\n for (const client of this.clients.values()) {\n client.send({ type: 'chat', args });\n }\n }\n}\n","import type {\n ChatMessage,\n CredentialedActionShape,\n Game,\n State,\n} from 'boardgame.io';\nimport { generateKeyPair, signMessage } from './authentication.js';\nimport { P2PHost } from './host.js';\nimport type { ClientAction, ClientMetadata } from './types.js';\nimport { hasValidSession, cleanupExpiredSessions } from './session-storage.js';\n\ndeclare global {\n interface Window {\n Peer?: new (id?: string, options?: object) => PeerInstance;\n __peerConfig?: {\n host?: string;\n port?: number;\n path?: string;\n secure?: boolean;\n iceServers?: RTCIceServer[];\n };\n }\n}\n\ninterface DataConnection {\n on(\n event: 'data' | 'open' | 'close' | 'error',\n handler: (data?: unknown) => void,\n ): void;\n send(data: unknown): void;\n close(): void;\n}\n\ninterface PeerInstance {\n on(\n event: 'open' | 'connection' | 'error' | 'close',\n handler: (data?: unknown) => void,\n ): void;\n connect(\n peerId: string,\n options?: { reliable?: boolean; serialization?: string },\n ): DataConnection;\n destroy(): void;\n id: string;\n}\n\ntype TransportData = {\n type: 'sync' | 'update' | 'chat';\n args: unknown[];\n};\n\nexport interface P2PTransportOpts {\n isHost?: boolean;\n peerOptions?: object;\n onError?: (error: Error) => void;\n}\n\n// Default ICE servers for NAT traversal\nconst DEFAULT_ICE_SERVERS = [\n { urls: 'stun:stun.l.google.com:19302' },\n { urls: 'stun:stun1.l.google.com:19302' },\n { urls: 'stun:stun2.l.google.com:19302' },\n // Free TURN servers from Open Relay Project\n {\n urls: 'turn:openrelay.metered.ca:80',\n username: 'openrelayproject',\n credential: 'openrelayproject',\n },\n {\n urls: 'turn:openrelay.metered.ca:443',\n username: 'openrelayproject',\n credential: 'openrelayproject',\n },\n {\n urls: 'turn:openrelay.metered.ca:443?transport=tcp',\n username: 'openrelayproject',\n credential: 'openrelayproject',\n },\n];\n\nexport interface TransportConfig {\n gameName: string;\n playerID: string | null;\n matchID: string;\n credentials?: string;\n numPlayers: number;\n game: Game;\n transportDataCallback: (data: TransportData) => void;\n}\n\nexport class P2PTransport {\n private peer: PeerInstance | null = null;\n private connection: DataConnection | null = null;\n private host: P2PHost | null = null;\n private isHost: boolean;\n private game: Game;\n private gameName: string;\n private playerID: string | null;\n private matchID: string;\n private credentials?: string;\n private numPlayers: number;\n private privateKey?: string;\n private transportDataCallback: (data: TransportData) => void;\n private peerOptions?: object;\n private onError?: (error: Error) => void;\n private connected = false;\n private connectionStatusCallbacks: Set<(connected: boolean) => void> =\n new Set();\n private hostRetryCount = 0;\n private maxHostRetries = 3;\n private lastPeerConfig: object | null = null;\n\n public constructor(config: TransportConfig, opts: P2PTransportOpts = {}) {\n console.log('[P2PTransport] Constructor called with config:', {\n gameName: config.gameName,\n playerID: config.playerID,\n matchID: config.matchID,\n numPlayers: config.numPlayers,\n credentials: config.credentials ? '(present)' : '(none)',\n });\n console.log('[P2PTransport] Options:', {\n isHost: opts.isHost,\n });\n\n this.gameName = config.gameName;\n this.playerID = config.playerID;\n this.matchID = config.matchID;\n this.numPlayers = config.numPlayers;\n this.game = config.game;\n this.transportDataCallback = config.transportDataCallback;\n this.isHost = opts.isHost ?? false;\n this.peerOptions = opts.peerOptions;\n this.onError = opts.onError;\n this.setCredentials(config.credentials);\n }\n\n private get hostID(): string {\n return `boardgameio-${this.gameName}-matchid-${this.matchID}`;\n }\n\n private setCredentials(credentials?: string): void {\n if (!credentials) {\n const { publicKey, privateKey } = generateKeyPair();\n this.credentials = publicKey;\n this.privateKey = privateKey;\n } else {\n this.credentials = credentials;\n }\n }\n\n private get metadata(): ClientMetadata {\n return {\n playerID: this.playerID,\n credentials: this.credentials,\n message:\n this.privateKey && this.playerID\n ? signMessage(this.playerID, this.privateKey)\n : undefined,\n };\n }\n\n public connect(): void {\n // Cleanup any expired sessions on connect\n cleanupExpiredSessions();\n\n const Peer = window.Peer;\n if (!Peer) {\n this.onError?.(new Error('PeerJS not loaded'));\n return;\n }\n\n const globalPeerConfig = window.__peerConfig ?? {};\n const iceServers =\n globalPeerConfig.iceServers && globalPeerConfig.iceServers.length > 0\n ? globalPeerConfig.iceServers\n : DEFAULT_ICE_SERVERS;\n\n // Merge default ICE servers with any user-provided options\n const baseConfig = {\n host: globalPeerConfig.host,\n port: globalPeerConfig.port,\n path: globalPeerConfig.path,\n secure: globalPeerConfig.secure,\n debug: 2, // Warnings and errors\n config: {\n iceServers,\n },\n };\n\n const peerConfig = {\n ...baseConfig,\n ...this.peerOptions,\n config: {\n ...baseConfig.config,\n ...(this.peerOptions && (this.peerOptions as { config?: object }).config\n ? (this.peerOptions as { config?: object }).config\n : {}),\n },\n };\n\n // Check if this is a reconnection (host has existing session)\n const isReconnection = this.isHost && hasValidSession(this.matchID);\n \n console.log(\n `[P2PTransport] Connecting as ${\n this.isHost ? 'HOST' : 'CLIENT'\n }, hostID: ${this.hostID}${isReconnection ? ' (reconnecting)' : ''}`,\n );\n\n // If host is reconnecting, delay to allow old PeerJS connection to clear\n if (isReconnection) {\n console.log('[P2PTransport] Host reconnecting, waiting for PeerJS ID to clear...');\n setTimeout(() => this.createPeer(Peer, peerConfig), 2000);\n } else {\n this.createPeer(Peer, peerConfig);\n }\n }\n\n private createPeer(\n Peer: NonNullable<typeof window.Peer>,\n peerConfig: object,\n ): void {\n this.lastPeerConfig = peerConfig;\n this.peer = new Peer(this.isHost ? this.hostID : undefined, peerConfig);\n\n this.peer.on('open', (id) => {\n console.log(`[P2PTransport] Peer opened with ID: ${id}`);\n if (this.isHost) {\n this.host = new P2PHost({\n game: this.game,\n numPlayers: this.numPlayers,\n matchID: this.matchID,\n });\n\n this.host.registerHostClient({\n metadata: this.metadata,\n send: (data) => this.notifyClient(data as TransportData),\n });\n // Host is ready immediately\n console.log('[P2PTransport] Host ready, waiting for connections');\n this.onConnect();\n } else {\n // Non-host connects to the host peer\n console.log(`[P2PTransport] Client connecting to host: ${this.hostID}`);\n this.connectToHost();\n // onConnect() will be called when data connection opens\n }\n });\n\n this.peer.on('connection', (conn) => {\n console.log(\n '[P2PTransport] Incoming connection from:',\n (conn as DataConnection & { peer?: string }).peer,\n );\n const dataConn = conn as DataConnection;\n if (!this.host) return;\n\n const client = {\n metadata: { playerID: null } as ClientMetadata,\n send: (data: unknown) => dataConn.send(data),\n };\n\n dataConn.on('open', () => {\n console.log('[P2PTransport] Data connection opened');\n this.host?.registerClient(client);\n });\n\n dataConn.on('data', (data) => {\n const action = data as ClientAction;\n if (action.type === 'sync' && 'metadata' in (data as object)) {\n client.metadata = (data as { metadata: ClientMetadata }).metadata;\n }\n this.host?.processAction(client, action);\n });\n\n dataConn.on('close', () => {\n this.host?.unregisterClient(client);\n });\n });\n\n this.peer.on('error', (err) => {\n const error = err as Error & { type?: string };\n console.error('[P2PTransport] Peer error:', error.type, error.message);\n\n // Handle specific PeerJS error types\n if (error.type === 'peer-unavailable') {\n console.error(\n '[P2PTransport] Host peer not found. Is the host connected?',\n );\n this.onError?.(error);\n } else if (error.type === 'unavailable-id') {\n // Host ID is still held by old connection - retry after delay\n if (this.isHost && this.hostRetryCount < this.maxHostRetries) {\n this.hostRetryCount++;\n const delay = 2000 * this.hostRetryCount; // Exponential backoff\n console.log(\n `[P2PTransport] Peer ID unavailable, retrying in ${delay}ms (${this.hostRetryCount}/${this.maxHostRetries})...`,\n );\n this.peer?.destroy();\n this.peer = null;\n setTimeout(() => {\n const Peer = window.Peer;\n if (Peer && this.lastPeerConfig) {\n this.createPeer(Peer, this.lastPeerConfig);\n }\n }, delay);\n } else {\n console.error('[P2PTransport] Peer ID already taken, max retries reached');\n this.onError?.(error);\n }\n } else {\n this.onError?.(error);\n }\n });\n\n this.peer.on('close', () => {\n console.log('[P2PTransport] Peer connection closed');\n this.connected = false;\n this.notifyConnectionStatus(false);\n });\n }\n\n private retryCount = 0;\n private maxRetries = 6; // Increased from 3 to handle host reconnection\n private retryDelayMs = 3000; // Delay between retries\n\n private connectToHost(): void {\n if (!this.peer) return;\n\n console.log(\n `[P2PTransport] Attempting connection to host (attempt ${\n this.retryCount + 1\n }/${this.maxRetries + 1})`,\n );\n\n // Connect with reliable data channel\n this.connection = this.peer.connect(this.hostID, {\n reliable: true,\n serialization: 'json',\n });\n\n // Connection timeout - retry if not connected within 10 seconds\n const connectionTimeout = setTimeout(() => {\n if (!this.connected && this.connection) {\n this.retryCount++;\n if (this.retryCount <= this.maxRetries) {\n console.warn(\n `[P2PTransport] Connection timeout, retrying in ${this.retryDelayMs}ms (${this.retryCount}/${this.maxRetries})...`,\n );\n this.connection.close();\n // Delay before retry to give host time to reconnect\n setTimeout(() => this.connectToHost(), this.retryDelayMs);\n } else {\n console.error(\n '[P2PTransport] Max retries reached. Could not connect to host.',\n );\n this.onError?.(\n new Error(\n 'Could not connect to host after multiple attempts. Is the host online?',\n ),\n );\n }\n }\n }, 10000);\n\n this.connection.on('open', () => {\n clearTimeout(connectionTimeout);\n console.log('[P2PTransport] Connected to host successfully!');\n this.retryCount = 0;\n this.connection?.send({ type: 'sync', metadata: this.metadata });\n // Now we're actually connected to the host\n this.onConnect();\n });\n\n this.connection.on('data', (data) => {\n this.notifyClient(data as TransportData);\n });\n\n this.connection.on('close', () => {\n clearTimeout(connectionTimeout);\n console.log('[P2PTransport] Connection to host closed');\n this.connected = false;\n this.notifyConnectionStatus(false);\n });\n\n this.connection.on('error', (err) => {\n clearTimeout(connectionTimeout);\n const error = err as Error & { type?: string };\n console.error(\n '[P2PTransport] Connection error:',\n error.type,\n error.message,\n );\n this.onError?.(error);\n });\n }\n\n private onConnect(): void {\n this.connected = true;\n this.notifyConnectionStatus(true);\n this.requestSync();\n }\n\n private notifyConnectionStatus(connected: boolean): void {\n for (const callback of this.connectionStatusCallbacks) {\n callback(connected);\n }\n }\n\n private notifyClient(data: TransportData): void {\n this.transportDataCallback(data);\n }\n\n public disconnect(): void {\n this.connection?.close();\n this.peer?.destroy();\n this.peer = null;\n this.connection = null;\n this.host = null;\n this.connected = false;\n this.notifyConnectionStatus(false);\n }\n\n public requestSync(): void {\n if (this.isHost && this.host) {\n this.host.processAction(\n {\n metadata: this.metadata,\n send: (d) => this.notifyClient(d as TransportData),\n },\n { type: 'sync' },\n );\n } else {\n this.connection?.send({ type: 'sync', metadata: this.metadata });\n }\n }\n\n public sendAction(state: State, action: CredentialedActionShape.Any): void {\n const msg: ClientAction = {\n type: 'update',\n args: [this.matchID, state, action],\n };\n if (this.isHost && this.host) {\n this.host.processAction(\n {\n metadata: this.metadata,\n send: (d) => this.notifyClient(d as TransportData),\n },\n msg,\n );\n } else {\n this.connection?.send(msg);\n }\n }\n\n public sendChatMessage(matchID: string, chatMessage: ChatMessage): void {\n const msg: ClientAction = {\n type: 'chat',\n args: [matchID, chatMessage, this.credentials],\n };\n if (this.isHost && this.host) {\n this.host.processAction(\n {\n metadata: this.metadata,\n send: (d) => this.notifyClient(d as TransportData),\n },\n msg,\n );\n } else {\n this.connection?.send(msg);\n }\n }\n\n public updateMatchID(id: string): void {\n this.matchID = id;\n this.disconnect();\n this.connect();\n }\n\n public updatePlayerID(id: string): void {\n this.playerID = id;\n this.disconnect();\n this.connect();\n }\n\n public updateCredentials(credentials?: string): void {\n this.setCredentials(credentials);\n this.disconnect();\n this.connect();\n }\n\n public isConnected(): boolean {\n return this.connected;\n }\n\n public subscribeToConnectionStatus(\n callback: (connected: boolean) => void,\n ): () => void {\n this.connectionStatusCallbacks.add(callback);\n // Immediately notify current status\n callback(this.connected);\n // Return unsubscribe function\n return () => {\n this.connectionStatusCallbacks.delete(callback);\n };\n }\n}\n\nexport function createP2PTransport(opts: P2PTransportOpts = {}) {\n return (config: TransportConfig) => new P2PTransport(config, opts);\n}\n","/**\n * Bot Manager for boardgame.io widgets\n *\n * Centralizes bot execution with support for:\n * - Multiple strategies (Random, MCTS, Custom)\n * - Configurable delays with variable timing\n * - Difficulty presets with mistake rates\n * - Observable thinking state\n */\n\nimport type { BoardgameGame } from './mount.js';\n\n// Type for boardgame.io state\nexport interface BoardgameState {\n G: unknown;\n ctx: {\n currentPlayer: string;\n gameover?: unknown;\n turn?: number;\n phase?: string;\n };\n}\n\nexport type BotStrategy = 'random' | 'mcts' | 'custom';\nexport type BotDifficulty = 'easy' | 'medium' | 'hard' | 'custom';\n\nexport interface DifficultyPreset {\n strategy: BotStrategy;\n mctsIterations: number;\n delay: number;\n minDelay: number;\n mistakeRate: number;\n}\n\nexport const DIFFICULTY_PRESETS: Record<BotDifficulty, DifficultyPreset> = {\n easy: {\n strategy: 'random',\n mctsIterations: 0,\n delay: 400,\n minDelay: 200,\n mistakeRate: 0.3,\n },\n medium: {\n strategy: 'mcts',\n mctsIterations: 500,\n delay: 800,\n minDelay: 400,\n mistakeRate: 0.1,\n },\n hard: {\n strategy: 'mcts',\n mctsIterations: 2000,\n delay: 1200,\n minDelay: 600,\n mistakeRate: 0,\n },\n custom: {\n strategy: 'mcts',\n mctsIterations: 1000,\n delay: 800,\n minDelay: 0,\n mistakeRate: 0,\n },\n};\n\nexport interface BotConfig {\n /** Difficulty preset: 'easy', 'medium', 'hard', or 'custom' */\n difficulty?: BotDifficulty;\n\n /** Bot strategy: 'random' or 'mcts' (overrides difficulty preset) */\n strategy?: BotStrategy;\n\n /** Delay before bot makes move (ms). Default from difficulty. */\n delay?: number;\n\n /** Min delay for variable timing (ms). If set, delay becomes max. */\n minDelay?: number;\n\n /** MCTS iterations (only for strategy: 'mcts'). Default from difficulty. */\n mctsIterations?: number;\n\n /** Chance bot picks suboptimal move (0-1). Default from difficulty. */\n mistakeRate?: number;\n\n /** Custom bot factory (only for strategy: 'custom') */\n customBot?: () => BotInstance;\n}\n\nexport interface ResolvedBotConfig {\n strategy: BotStrategy;\n delay: number;\n minDelay: number;\n mctsIterations: number;\n mistakeRate: number;\n customBot?: () => BotInstance;\n}\n\nexport interface BotState {\n /** Whether a bot is currently \"thinking\" */\n isThinking: boolean;\n\n /** Which player ID is thinking (null if none) */\n thinkingPlayer: string | null;\n\n /** Estimated time remaining for bot move (ms, if calculable) */\n estimatedTime?: number;\n}\n\n// Interface for bot instances (RandomBot, MCTSBot)\ninterface BotInstance {\n play(\n state: BoardgameState,\n playerID: string,\n ): Promise<BotAction | null> | BotAction | null;\n}\n\ninterface BotAction {\n action?: {\n payload: {\n type: string;\n args: unknown[];\n };\n };\n}\n\n// Move definition from game.ai.enumerate\ninterface EnumeratedMove {\n move: string;\n args: unknown[];\n}\n\n// Augment window type for boardgame.io AI module\ndeclare global {\n interface Window {\n BoardgameAI?: {\n RandomBot?: new (opts: {\n game: BoardgameGame;\n enumerate: (G: unknown, ctx: unknown) => EnumeratedMove[];\n }) => BotInstance;\n MCTSBot?: new (opts: {\n game: BoardgameGame;\n enumerate: (G: unknown, ctx: unknown) => EnumeratedMove[];\n iterations?: number;\n playoutDepth?: number;\n }) => BotInstance;\n };\n }\n}\n\n/**\n * Resolves bot configuration from inputs, game-specific overrides, and defaults.\n *\n * Resolution order:\n * 1. Explicit input values (e.g., inputs.mctsIterations)\n * 2. Game-specific difficulty preset (game.ai.difficulty[level])\n * 3. Global difficulty preset (DIFFICULTY_PRESETS[level])\n */\nexport function resolveBotConfig(\n inputs: Record<string, unknown>,\n gameAiDifficulty?: Partial<Record<BotDifficulty, Partial<DifficultyPreset>>>,\n): ResolvedBotConfig {\n const difficulty = (inputs.botDifficulty as BotDifficulty) ?? 'medium';\n const globalPreset = DIFFICULTY_PRESETS[difficulty];\n const gamePreset = gameAiDifficulty?.[difficulty] ?? {};\n\n // Merge: global -> game-specific -> explicit inputs\n return {\n strategy:\n (inputs.botStrategy as BotStrategy) ??\n gamePreset.strategy ??\n globalPreset.strategy,\n mctsIterations:\n (inputs.mctsIterations as number) ??\n gamePreset.mctsIterations ??\n globalPreset.mctsIterations,\n delay:\n (inputs.botDelay as number) ?? gamePreset.delay ?? globalPreset.delay,\n minDelay:\n (inputs.botMinDelay as number) ??\n gamePreset.minDelay ??\n globalPreset.minDelay,\n mistakeRate:\n (inputs.botMistakeRate as number) ??\n gamePreset.mistakeRate ??\n globalPreset.mistakeRate,\n customBot: inputs.customBot as (() => BotInstance) | undefined,\n };\n}\n\n/**\n * Computes which player IDs should be controlled by bots.\n *\n * Bots fill all slots except the human player's slot.\n */\nexport function computeBotPlayerIDs(\n numPlayers: number,\n botCount: number,\n humanPlayerID: string,\n): string[] {\n const botIDs: string[] = [];\n for (let i = 0; i < numPlayers && botIDs.length < botCount; i++) {\n const id = String(i);\n if (id !== humanPlayerID) {\n botIDs.push(id);\n }\n }\n return botIDs;\n}\n\nexport class BotManager {\n private config: ResolvedBotConfig;\n private bot: BotInstance | null = null;\n private pendingMove: ReturnType<typeof setTimeout> | null = null;\n private stateListeners: Set<(state: BotState) => void> = new Set();\n private currentState: BotState = { isThinking: false, thinkingPlayer: null };\n private moveInProgress = false;\n\n constructor(\n private game: BoardgameGame,\n config: ResolvedBotConfig,\n ) {\n this.config = config;\n this.initializeBot();\n }\n\n private initializeBot(): void {\n const { BoardgameAI } = window;\n const enumerate = this.game.ai?.enumerate;\n\n if (!enumerate) {\n console.warn(\n '[BotManager] game.ai.enumerate is required for bot support',\n );\n return;\n }\n\n switch (this.config.strategy) {\n case 'mcts':\n if (BoardgameAI?.MCTSBot) {\n this.bot = new BoardgameAI.MCTSBot({\n game: this.game,\n enumerate: enumerate as (\n G: unknown,\n ctx: unknown,\n ) => EnumeratedMove[],\n iterations: this.config.mctsIterations,\n });\n } else {\n // Fall back to RandomBot if MCTS not available\n console.warn(\n '[BotManager] MCTSBot not available, falling back to RandomBot',\n );\n if (BoardgameAI?.RandomBot) {\n this.bot = new BoardgameAI.RandomBot({\n game: this.game,\n enumerate: enumerate as (\n G: unknown,\n ctx: unknown,\n ) => EnumeratedMove[],\n });\n }\n }\n break;\n\n case 'random':\n if (BoardgameAI?.RandomBot) {\n this.bot = new BoardgameAI.RandomBot({\n game: this.game,\n enumerate: enumerate as (\n G: unknown,\n ctx: unknown,\n ) => EnumeratedMove[],\n });\n }\n break;\n\n case 'custom':\n if (this.config.customBot) {\n this.bot = this.config.customBot();\n } else {\n console.error(\n '[BotManager] Custom strategy requires customBot factory',\n );\n }\n break;\n }\n\n if (!this.bot) {\n console.warn(\n '[BotManager] Could not initialize bot. AI module may not be loaded.',\n );\n }\n }\n\n /**\n * Select a move, potentially with mistakes for easier difficulties.\n */\n private async selectMove(\n state: BoardgameState,\n playerID: string,\n ): Promise<BotAction | null> {\n const enumerate = this.game.ai?.enumerate;\n if (!enumerate) {\n console.log('[BotManager] No enumerate function');\n return null;\n }\n\n const legalMoves = (\n enumerate as (G: unknown, ctx: unknown) => EnumeratedMove[]\n )(state.G, state.ctx);\n\n console.log('[BotManager] Legal moves:', legalMoves);\n\n if (legalMoves.length === 0) {\n console.log('[BotManager] No legal moves available');\n return null;\n }\n\n // Check if bot should make a \"mistake\"\n if (this.config.mistakeRate > 0 && Math.random() < this.config.mistakeRate) {\n const randomIndex = Math.floor(Math.random() * legalMoves.length);\n const randomMove = legalMoves[randomIndex];\n return {\n action: {\n payload: {\n type: randomMove.move,\n args: randomMove.args,\n },\n },\n };\n }\n\n // Use the bot's calculated best move\n if (!this.bot) {\n // Fallback to random if no bot\n const randomIndex = Math.floor(Math.random() * legalMoves.length);\n const randomMove = legalMoves[randomIndex];\n return {\n action: {\n payload: {\n type: randomMove.move,\n args: randomMove.args,\n },\n },\n };\n }\n\n const result = await this.bot.play(state, playerID);\n\n // If bot returns null action but we have legal moves, fall back to random\n // This can happen when games use manual turn management (G.current instead of ctx.currentPlayer)\n // which prevents MCTS from properly simulating future game states\n if (!result?.action && legalMoves.length > 0) {\n console.log('[BotManager] Bot returned null action, falling back to random selection');\n const randomIndex = Math.floor(Math.random() * legalMoves.length);\n const randomMove = legalMoves[randomIndex];\n return {\n action: {\n payload: {\n type: randomMove.move,\n args: randomMove.args,\n },\n },\n };\n }\n\n return result;\n}\n\n /**\n * Check if it's a bot's turn and schedule a move if so.\n * Call this after every state update.\n */\n async maybePlayBot(\n state: BoardgameState,\n botPlayerIDs: string[],\n makeMove: (type: string, ...args: unknown[]) => void,\n ): Promise<void> {\n const { ctx } = state;\n\n console.log('[BotManager] maybePlayBot called:', {\n currentPlayer: ctx.currentPlayer,\n botPlayerIDs,\n moveInProgress: this.moveInProgress,\n gameover: ctx.gameover,\n });\n\n // Prevent concurrent move processing\n if (this.moveInProgress) {\n console.log('[BotManager] Skipping - move already in progress');\n return;\n }\n\n // Check if current player is a bot\n const isBotTurn = botPlayerIDs.includes(ctx.currentPlayer);\n if (!isBotTurn || ctx.gameover) {\n console.log('[BotManager] Not bot turn or gameover:', { isBotTurn, gameover: ctx.gameover });\n this.cancelPendingMove();\n this.notifyState({ isThinking: false, thinkingPlayer: null });\n return;\n }\n\n // Mark move in progress\n this.moveInProgress = true;\n\n // Notify thinking state\n this.notifyState({ isThinking: true, thinkingPlayer: ctx.currentPlayer });\n\n // Calculate delay (optionally variable)\n const delay =\n this.config.minDelay > 0\n ? this.config.minDelay +\n Math.random() * (this.config.delay - this.config.minDelay)\n : this.config.delay;\n\n try {\n // Get bot move\n console.log('[BotManager] Selecting move for player:', ctx.currentPlayer);\n const action = await this.selectMove(state, ctx.currentPlayer);\n console.log('[BotManager] Selected action:', action);\n if (!action?.action) {\n console.log('[BotManager] No action returned, skipping');\n this.notifyState({ isThinking: false, thinkingPlayer: null });\n this.moveInProgress = false;\n return;\n }\n\n // Schedule move after delay\n console.log('[BotManager] Scheduling move after delay:', delay);\n this.pendingMove = setTimeout(() => {\n const { type, args } = action.action!.payload;\n console.log('[BotManager] Executing move:', { type, args });\n makeMove(type, ...args);\n this.notifyState({ isThinking: false, thinkingPlayer: null });\n this.moveInProgress = false;\n }, delay);\n } catch (error) {\n console.error('[BotManager] Error computing move:', error);\n this.notifyState({ isThinking: false, thinkingPlayer: null });\n this.moveInProgress = false;\n }\n }\n\n /** Subscribe to bot state changes */\n subscribe(listener: (state: BotState) => void): () => void {\n this.stateListeners.add(listener);\n // Immediately notify current state\n listener(this.currentState);\n return () => this.stateListeners.delete(listener);\n }\n\n /** Get current bot state */\n getState(): BotState {\n return this.currentState;\n }\n\n private notifyState(state: BotState): void {\n this.currentState = state;\n this.stateListeners.forEach((fn) => fn(state));\n }\n\n private cancelPendingMove(): void {\n if (this.pendingMove) {\n clearTimeout(this.pendingMove);\n this.pendingMove = null;\n }\n this.moveInProgress = false;\n }\n\n dispose(): void {\n this.cancelPendingMove();\n this.stateListeners.clear();\n }\n}\n","/**\n * Mount helper for boardgame.io widgets\n *\n * Handles mounting automatically when widgets export `game` and `app` (or default).\n * The image detects these exports and creates the mount - widgets don't need to\n * reference image internals.\n *\n * Uses window.React at runtime (preloaded by the compiler).\n */\n\nimport { SettingsProvider, useSettings, type GameSettings } from './context.js';\nimport { createP2PTransport } from './p2p/index.js';\nimport { ensurePeerJS } from './setup.js';\nimport {\n BotManager,\n resolveBotConfig,\n computeBotPlayerIDs,\n type BotState,\n type BotDifficulty,\n type DifficultyPreset,\n} from './bot-manager.js';\n\n// Re-export for convenience\nexport { SettingsProvider, useSettings, type GameSettings } from './context.js';\n\n/** Multiplayer configuration passed via inputs */\nexport interface MultiplayerInput {\n matchID: string;\n playerID: string;\n credentials?: string;\n isHost?: boolean;\n}\n\nexport interface GameMountOptions {\n /** Number of players for the game */\n numPlayers?: number;\n /** Default player ID */\n defaultPlayerID?: string;\n}\n\nexport interface BoardgameGame {\n name?: string;\n minPlayers?: number;\n maxPlayers?: number;\n setup: (context: unknown) => unknown;\n ai?: {\n /** Enumerate legal moves for bot players */\n enumerate: (G: unknown, ctx: unknown) => Array<{ move: string; args: unknown[] }>;\n /** Optional game-specific difficulty presets */\n difficulty?: Partial<Record<BotDifficulty, Partial<DifficultyPreset>>>;\n };\n}\n\n/** Manifest structure for player count */\nexport interface WidgetManifest {\n players?: {\n min?: number;\n max?: number;\n };\n [key: string]: unknown;\n}\n\n// Generic component type (avoids importing React types)\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype BoardComponent = (props: any) => unknown;\n\ndeclare global {\n interface Window {\n BoardgameReact?: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Client: (opts: {\n game: BoardgameGame;\n board: BoardComponent;\n numPlayers?: number;\n multiplayer?: unknown;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) => any;\n };\n BoardgameMultiplayer?: {\n Local?: () => unknown;\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n React?: any;\n ReactDOM?: {\n createRoot: (el: HTMLElement) => {\n render: (el: unknown) => void;\n unmount: () => void;\n };\n };\n }\n}\n\n/**\n * Creates a mount function for a boardgame.io widget.\n *\n * @param game - The boardgame.io game definition\n * @param Board - The React board component\n * @param options - Mount options (numPlayers, defaultPlayerID)\n * @returns A mount function compatible with the patchwork runtime\n *\n * @example\n * ```tsx\n * export const game = { name: 'my-game', setup: () => ({}) };\n * export default function MyBoard({ G, ctx, moves }) { ... }\n * export const mount = createGameMount(game, MyBoard, { numPlayers: 2 });\n * ```\n */\nexport function createGameMount(\n game: BoardgameGame,\n Board: BoardComponent,\n options: GameMountOptions = {},\n): (container: HTMLElement, inputs?: GameSettings) => () => void {\n const {\n numPlayers: defaultNumPlayers = game.maxPlayers ?? game.minPlayers ?? 2,\n defaultPlayerID = '0',\n } = options;\n\n return (container: HTMLElement, inputs: GameSettings = {}): (() => void) => {\n const { BoardgameReact, BoardgameMultiplayer, React: R, ReactDOM } = window;\n\n if (!BoardgameReact || !R || !ReactDOM) {\n console.error(\n '[boardgameio] Missing globals: BoardgameReact, React, or ReactDOM',\n );\n container.innerHTML =\n '<div style=\"color: red; padding: 16px;\">Missing required dependencies</div>';\n return () => {};\n }\n\n // Player configuration\n const numPlayers =\n (inputs.numPlayers as number | undefined) ?? defaultNumPlayers;\n\n // Check for multiplayer configuration first (needed for bot count default)\n const multiplayerConfig = inputs.multiplayer as\n | MultiplayerInput\n | undefined;\n const isMultiplayer = !!multiplayerConfig?.matchID;\n\n // Bot count: explicit input, or default to numPlayers - 1 (0 for multiplayer)\n const botCountInput = inputs['bot-count'] ?? inputs.botCount;\n const botCount =\n typeof botCountInput === 'number'\n ? botCountInput\n : isMultiplayer\n ? 0\n : numPlayers - 1;\n const playerID =\n (inputs.playerID as string) ??\n multiplayerConfig?.playerID ??\n defaultPlayerID;\n\n // Compute which players are bots\n const botPlayerIDs = computeBotPlayerIDs(numPlayers, botCount, playerID);\n\n console.log('[boardgameio] Bot configuration:', {\n numPlayers,\n botCount,\n botCountInput,\n isMultiplayer,\n playerID,\n botPlayerIDs,\n hasBotEnumerate: !!game.ai?.enumerate,\n inputsBotCount: inputs['bot-count'],\n hasMultiplayerConfig: !!multiplayerConfig,\n });\n\n // Resolve bot configuration from difficulty presets + explicit overrides\n const botDifficulty = (inputs.botDifficulty as BotDifficulty) ?? 'medium';\n const botConfig = resolveBotConfig(inputs, game.ai?.difficulty);\n\n // Create bot manager (only if game has ai.enumerate and we have bots)\n const hasBots = botCount > 0 && game.ai?.enumerate;\n let botManager: BotManager | null = null;\n if (hasBots) {\n botManager = new BotManager(game, botConfig);\n }\n\n // Use P2P transport for multiplayer only\n // Don't use Local() for single-player/local-pass-and-play - it restricts moves\n // to the client's playerID, preventing bots and turn-taking from working\n let multiplayer: unknown;\n if (isMultiplayer && multiplayerConfig) {\n const isHost = multiplayerConfig.isHost ?? playerID === '0';\n multiplayer = createP2PTransport({\n isHost,\n });\n }\n\n // Wrap board component to inject bot state and additional props\n const WrappedBoard: BoardComponent = (props: Record<string, unknown>) => {\n const [botState, setBotState] = R.useState({\n isThinking: false,\n thinkingPlayer: null,\n }) as [BotState, (state: BotState) => void];\n\n // Subscribe to bot state changes\n R.useEffect(() => {\n if (!botManager) return;\n return botManager.subscribe(setBotState);\n }, []);\n\n // Trigger bot moves on state changes\n R.useEffect(() => {\n // Never run bots in multiplayer mode - all players are human\n if (isMultiplayer) {\n return;\n }\n\n if (!botManager || !props.G) {\n console.log('[boardgameio] Bot effect skipped:', {\n hasBotManager: !!botManager,\n hasG: !!props.G,\n });\n return;\n }\n\n // Detect current player from G.current (internal tracking) or ctx.currentPlayer\n // Many games use G.current (number) instead of boardgame.io's ctx.currentPlayer\n const gState = props.G as { current?: number; winner?: unknown };\n const ctxState = props.ctx as {\n currentPlayer: string;\n gameover?: unknown;\n };\n const currentPlayer =\n gState.current !== undefined\n ? String(gState.current)\n : ctxState.currentPlayer;\n\n // Detect gameover from G.winner (internal tracking) or ctx.gameover\n const gameover =\n gState.winner !== undefined ? gState.winner !== null : ctxState.gameover;\n\n const isBotTurn = botPlayerIDs.includes(currentPlayer);\n console.log('[boardgameio] Bot effect running:', {\n currentPlayer,\n botPlayerIDs,\n isBotTurn,\n gameover,\n });\n\n const state = {\n G: props.G,\n ctx: {\n ...ctxState,\n currentPlayer,\n gameover,\n },\n };\n\n botManager.maybePlayBot(state, botPlayerIDs, (type, ...args) => {\n console.log('[boardgameio] Bot making move:', { type, args });\n const moves = props.moves as Record<\n string,\n (...a: unknown[]) => void\n >;\n moves[type]?.(...args);\n });\n }, [\n props.G,\n (props.ctx as { currentPlayer?: string })?.currentPlayer,\n (props.ctx as { turn?: number })?.turn,\n (props.G as { current?: number })?.current,\n ]);\n\n return R.createElement(Board, {\n ...props,\n isMultiplayer,\n botState,\n botPlayerIDs,\n botCount,\n botDifficulty,\n });\n };\n\n // Create the boardgame.io Client\n const GameClient = BoardgameReact.Client({\n game,\n board: WrappedBoard,\n numPlayers,\n ...(multiplayer ? { multiplayer } : {}),\n });\n\n // Wrapper that provides settings via context\n const GameWithSettings = () => {\n const clientProps: Record<string, unknown> = { playerID };\n if (isMultiplayer && multiplayerConfig) {\n clientProps.matchID = multiplayerConfig.matchID;\n clientProps.credentials = multiplayerConfig.credentials;\n console.log('[boardgameio] Multiplayer props:', {\n matchID: multiplayerConfig.matchID,\n playerID,\n isHost: multiplayerConfig.isHost ?? playerID === '0',\n });\n }\n\n return R.createElement(\n SettingsProvider,\n { settings: inputs },\n R.createElement(GameClient, clientProps),\n );\n };\n\n const root = ReactDOM.createRoot(container);\n root.render(R.createElement(GameWithSettings));\n\n return () => {\n botManager?.dispose();\n root.unmount();\n };\n };\n}\n\n/**\n * Widget module structure expected by the boardgameio image.\n * Widgets should export `game` and either `app` or `default`.\n */\nexport interface BoardgameWidgetModule {\n game: BoardgameGame;\n app?: BoardComponent;\n default?: BoardComponent;\n}\n\n/**\n * Creates a mount function from a widget module's exports.\n * Called automatically by the runtime when it detects a boardgameio widget.\n *\n * @param module - The widget module with game and app/default exports\n * @param manifest - The widget manifest (for player count settings)\n * @returns A mount function or null if not a valid boardgameio widget\n */\nexport function createMountFromExports(\n module: BoardgameWidgetModule,\n manifest?: WidgetManifest,\n): ((container: HTMLElement, inputs?: GameSettings) => () => void) | null {\n const { game, app, default: defaultExport } = module;\n\n if (!game || typeof game.setup !== 'function') {\n return null;\n }\n\n const Board = app || defaultExport;\n if (!Board || typeof Board !== 'function') {\n return null;\n }\n\n // Get numPlayers from manifest, falling back to game definition\n const numPlayers =\n manifest?.players?.max ??\n manifest?.players?.min ??\n game.maxPlayers ??\n game.minPlayers ??\n 2;\n\n return createGameMount(game, Board, { numPlayers });\n}\n\n/**\n * Injects the mount helpers onto the window for runtime access.\n * Called during image setup.\n */\nexport function injectMountHelper(): void {\n const win = window as unknown as Record<string, unknown>;\n win.createGameMount = createGameMount;\n win.createMountFromExports = createMountFromExports;\n win.useSettings = useSettings;\n win.SettingsProvider = SettingsProvider;\n}\n\n/**\n * Image mount function - handles mounting widgets for the boardgameio image.\n *\n * Supports three patterns:\n * 1. BoardGame.io widgets: exports `game` and `app`/`default`\n * 2. Custom mount: exports a `mount` function\n * 3. React component: exports `default` as a React component\n *\n * @param module - The widget module exports\n * @param container - The DOM element to mount into\n * @param inputs - Props/settings to pass to the widget\n * @returns A cleanup function to unmount, or void\n */\nexport async function mount(\n module: Record<string, unknown>,\n container: HTMLElement,\n inputs: Record<string, unknown>,\n): Promise<void | (() => void)> {\n const { React: R, ReactDOM } = window;\n\n // 1. Custom mount function takes priority\n if (typeof module.mount === 'function') {\n const mountFn = module.mount as (\n el: HTMLElement,\n inp: Record<string, unknown>,\n ) => unknown;\n const result = await mountFn(container, inputs);\n if (typeof result === 'function') {\n return result as () => void;\n }\n return;\n }\n\n // 2. Boardgame.io widget: exports `game` and `app`/`default`\n const game = module.game as BoardgameGame | undefined;\n if (game && typeof game.setup === 'function') {\n const Board = (module.app || module.default) as BoardComponent | undefined;\n if (Board && typeof Board === 'function') {\n // Get numPlayers from inputs or game definition\n const numPlayers =\n (inputs.numPlayers as number | undefined) ??\n game.maxPlayers ??\n game.minPlayers ??\n 2;\n\n // Load PeerJS if multiplayer is requested\n const multiplayerInput = inputs.multiplayer as\n | MultiplayerInput\n | undefined;\n if (multiplayerInput?.matchID) {\n await ensurePeerJS();\n }\n\n const gameMount = createGameMount(game, Board, { numPlayers });\n return gameMount(container, inputs as GameSettings);\n }\n }\n\n // 3. Default export React component\n if (typeof module.default === 'function' && R && ReactDOM) {\n const Component = module.default as BoardComponent;\n const root = ReactDOM.createRoot(container);\n root.render(R.createElement(Component, inputs));\n return () => root.unmount();\n }\n\n console.warn(\n '[boardgameio] Widget does not export a recognized entry point (mount, game+app, or default)',\n );\n}\n","/**\n * @aprovan/patchwork-image-boardgameio\n *\n * Setup function for the Boardgame.io image.\n * Uses Tailwind Play CDN for runtime CSS generation.\n */\n\nimport { injectMountHelper } from './mount.js';\n\nexport interface SetupOptions {\n darkMode?: boolean | 'system';\n cssRuntime?: boolean;\n multiplayer?: boolean;\n}\n\nlet tailwindLoadPromise: Promise<void> | null = null;\nlet peerJSLoadPromise: Promise<void> | null = null;\nlet mountHelperInjected = false;\n\ndeclare global {\n interface Window {\n tailwind?: {\n config?: Record<string, unknown>;\n };\n // Peer type is defined in p2p/transport.ts\n }\n}\n\nexport async function setup(\n container: HTMLElement,\n options: SetupOptions = {},\n): Promise<void> {\n const { cssRuntime = true, multiplayer = false } = options;\n\n // Inject the mount helper for games to use\n if (!mountHelperInjected) {\n injectMountHelper();\n mountHelperInjected = true;\n }\n\n if (cssRuntime && !tailwindLoadPromise) {\n tailwindLoadPromise = loadTailwindPlayCDN();\n }\n\n // Load PeerJS for multiplayer support\n if (multiplayer && !peerJSLoadPromise) {\n peerJSLoadPromise = loadPeerJS();\n }\n\n await Promise.all([tailwindLoadPromise, peerJSLoadPromise].filter(Boolean));\n}\n\nexport function cleanup(container: HTMLElement): void {\n // No-op for now\n}\n\nasync function loadTailwindPlayCDN(): Promise<void> {\n if (document.querySelector('script[src*=\"tailwindcss.com/play\"]')) {\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://cdn.tailwindcss.com';\n script.async = true;\n\n return new Promise((resolve, reject) => {\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load Tailwind CDN'));\n document.head.appendChild(script);\n });\n}\n\nasync function loadPeerJS(): Promise<void> {\n if ((window as { Peer?: unknown }).Peer) {\n return;\n }\n\n if (document.querySelector('script[src*=\"peerjs\"]')) {\n // Wait for existing script to load\n return new Promise((resolve) => {\n const check = () => {\n if ((window as { Peer?: unknown }).Peer) resolve();\n else setTimeout(check, 50);\n };\n check();\n });\n }\n\n const script = document.createElement('script');\n script.src = 'https://unpkg.com/peerjs@1.5.4/dist/peerjs.min.js';\n script.async = true;\n\n return new Promise((resolve, reject) => {\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load PeerJS'));\n document.head.appendChild(script);\n });\n}\n\n/**\n * Ensures PeerJS is loaded (can be called directly by mount if needed)\n */\nexport async function ensurePeerJS(): Promise<void> {\n if (!peerJSLoadPromise) {\n peerJSLoadPromise = loadPeerJS();\n }\n await peerJSLoadPromise;\n}\n","import type { Game } from 'boardgame.io';\n\nexport interface MultiplayerConfig {\n isMultiplayer: boolean;\n isHost?: boolean;\n appId: string;\n matchID?: string;\n botCount?: number;\n}\n\nexport function getMultiplayer(config: MultiplayerConfig, game?: Game) {\n const BoardgameMultiplayer = (\n window as {\n BoardgameMultiplayer?: {\n Local?: (opts?: {\n bots?: Record<string, unknown>;\n storageKey?: string;\n }) => unknown;\n };\n }\n ).BoardgameMultiplayer;\n\n const BoardgameAI = (\n window as {\n BoardgameAI?: {\n MCTSBot?: unknown;\n RandomBot?: unknown;\n };\n }\n ).BoardgameAI;\n\n if (config.isMultiplayer) {\n return undefined;\n }\n\n const bots: Record<string, unknown> = {};\n const botCount = config.botCount ?? 0;\n\n if (botCount > 0 && game?.ai && BoardgameAI?.MCTSBot) {\n for (let i = 1; i <= botCount; i++) {\n bots[String(i)] = BoardgameAI.MCTSBot;\n }\n }\n\n if (!BoardgameMultiplayer?.Local) {\n return undefined;\n }\n\n return BoardgameMultiplayer.Local({\n storageKey: `zolvery:bgio:${config.appId}`,\n ...(Object.keys(bots).length > 0 ? { bots } : {}),\n });\n}\n\nexport function generateMatchID(): string {\n const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\n return Array.from(\n { length: 6 },\n () => chars[Math.floor(Math.random() * chars.length)],\n ).join('');\n}\n"],"mappings":"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","names":["Peer"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aprovan/patchwork-image-boardgameio",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Patchwork image: Boardgame.io with React",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"patchwork": {
|
|
23
|
+
"platform": "browser",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"react": "^18.0.0",
|
|
26
|
+
"react-dom": "^18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"esbuild": {
|
|
29
|
+
"target": "es2020",
|
|
30
|
+
"format": "esm",
|
|
31
|
+
"jsx": "transform",
|
|
32
|
+
"jsxFactory": "React.createElement",
|
|
33
|
+
"jsxFragment": "React.Fragment"
|
|
34
|
+
},
|
|
35
|
+
"framework": {
|
|
36
|
+
"globals": {
|
|
37
|
+
"react": "React",
|
|
38
|
+
"react-dom": "ReactDOM",
|
|
39
|
+
"boardgame.io/client": "BoardgameClient",
|
|
40
|
+
"boardgame.io/react": "BoardgameReact",
|
|
41
|
+
"boardgame.io/ai": "BoardgameAI",
|
|
42
|
+
"boardgame.io/multiplayer": "BoardgameMultiplayer"
|
|
43
|
+
},
|
|
44
|
+
"preload": [
|
|
45
|
+
"https://esm.sh/react@18",
|
|
46
|
+
"https://esm.sh/react-dom@18/client",
|
|
47
|
+
"https://esm.sh/boardgame.io@0.50.2/client?external=react,react-dom",
|
|
48
|
+
"https://esm.sh/boardgame.io@0.50.2/react?external=react,react-dom",
|
|
49
|
+
"https://esm.sh/boardgame.io@0.50.2/ai?external=react,react-dom",
|
|
50
|
+
"https://esm.sh/boardgame.io@0.50.2/multiplayer?external=react,react-dom"
|
|
51
|
+
],
|
|
52
|
+
"deps": {
|
|
53
|
+
"react": "18",
|
|
54
|
+
"react-dom": "18",
|
|
55
|
+
"boardgame.io": "0.50.2"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"aliases": {
|
|
59
|
+
"@/components/ui/*": "@packagedcn/react",
|
|
60
|
+
"@/lib/utils": "@packagedcn/react"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"react": "^18.0.0",
|
|
65
|
+
"react-dom": "^18.0.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/react": "^18.0.0",
|
|
69
|
+
"@types/react-dom": "^18.0.0",
|
|
70
|
+
"boardgame.io": "^0.50.2",
|
|
71
|
+
"tsup": "^8.0.0",
|
|
72
|
+
"typescript": "^5.0.0"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsup",
|
|
76
|
+
"dev": "tsup --watch",
|
|
77
|
+
"typecheck": "tsc --noEmit"
|
|
78
|
+
}
|
|
79
|
+
}
|