@kossabos/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 +334 -0
- package/dist/index.js +903 -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/host.ts","../src/p2p/transport.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 {\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';\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 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 this.db.createMatch(matchID, {\n initialState,\n metadata: {\n gameName: game.name ?? 'unknown',\n players,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n },\n });\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 console.log('[P2PHost] handleUpdate:', { moveName, moveArgs, playerID });\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 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';\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\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 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 console.log(\n `[P2PTransport] Connecting as ${\n this.isHost ? 'HOST' : 'CLIENT'\n }, hostID: ${this.hostID}`,\n );\n\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 } else if (error.type === 'unavailable-id') {\n console.error('[P2PTransport] Peer ID already taken');\n }\n\n this.onError?.(error);\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 = 3;\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 (${this.retryCount}/${this.maxRetries})...`,\n );\n this.connection.close();\n this.connectToHost();\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 * 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';\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: (...args: unknown[]) => unknown;\n };\n // ... other boardgame.io game properties\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 ai?: unknown;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) => any;\n };\n BoardgameAI?: {\n RandomBot?: () => unknown;\n };\n BoardgameMultiplayer?: {\n Local?: (opts?: { bots?: Record<string, unknown> }) => 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 = game.maxPlayers ?? game.minPlayers ?? 2,\n defaultPlayerID = '0',\n } = options;\n\n return (container: HTMLElement, inputs: GameSettings = {}): (() => void) => {\n const {\n BoardgameReact,\n BoardgameAI,\n BoardgameMultiplayer,\n React: R,\n ReactDOM,\n } = 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 const botCount =\n typeof inputs['bot-count'] === 'number' ? inputs['bot-count'] : 0;\n const canUseBots = botCount > 0 && !!game.ai;\n const botFactory = canUseBots ? BoardgameAI?.RandomBot : undefined;\n\n if (canUseBots && !botFactory) {\n console.warn(\n '[boardgameio] Bot settings enabled, but AI module is unavailable.',\n );\n }\n\n const enumerate =\n typeof game.ai?.enumerate === 'function' ? game.ai.enumerate : undefined;\n\n if (canUseBots && !enumerate) {\n console.warn(\n '[boardgameio] Bot settings enabled, but game.ai.enumerate is missing.',\n );\n }\n\n const botPlayers: Record<string, unknown> = {};\n if (botFactory && enumerate) {\n let added = 0;\n for (let pid = 0; pid < numPlayers && added < botCount; pid += 1) {\n const pidStr = String(pid);\n if (pidStr === defaultPlayerID) continue;\n botPlayers[pidStr] = botFactory;\n added += 1;\n }\n }\n\n // Check for multiplayer configuration\n const multiplayerConfig = inputs.multiplayer as\n | MultiplayerInput\n | undefined;\n const isMultiplayer = !!multiplayerConfig?.matchID;\n const playerID = multiplayerConfig?.playerID ?? defaultPlayerID;\n\n // Use P2P transport for multiplayer, Local for single-player\n let multiplayer: unknown;\n if (isMultiplayer && multiplayerConfig) {\n const isHost = multiplayerConfig.isHost ?? playerID === '0';\n multiplayer = createP2PTransport({\n isHost,\n });\n } else if (BoardgameMultiplayer?.Local) {\n multiplayer = BoardgameMultiplayer.Local(\n Object.keys(botPlayers).length > 0 ? { bots: botPlayers } : undefined,\n );\n }\n\n // Wrap board component to inject isMultiplayer prop\n const WrappedBoard: BoardComponent = (props: Record<string, unknown>) => {\n return R.createElement(Board, { ...props, isMultiplayer });\n };\n\n // Create the boardgame.io Client\n const GameClient = BoardgameReact.Client({\n game,\n board: WrappedBoard,\n numPlayers,\n ...(multiplayer ? { multiplayer } : {}),\n // Bots are configured via Local transport when enabled.\n });\n\n // Wrapper that provides settings via context\n const GameWithSettings = () => {\n // matchID and credentials are passed as props to the rendered component\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 () => root.unmount();\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 * @kossabos/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: `kossabos: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":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kossabos/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
|
+
}
|