From 4f89ed3082dd526c621836337772fe22aeae784c Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Sun, 5 Apr 2026 17:30:45 +0200 Subject: [PATCH] fix(games): stop spectator broadcast from overwriting player state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Players subscribe to the room pub/sub channel and also receive direct GAME_STATE messages. The GAME_STARTED and GAME_UPDATE broadcasts carry the spectator view (no myColor/legalMoves), and were blindly overwriting gameState — making isPlayerView() return false and disabling all interaction. Now these broadcast handlers only update gameState for spectators; players rely exclusively on the direct GAME_STATE message. Co-Authored-By: Claude Opus 4.6 (1M context) --- panel/src/lib/useGameRoom.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/panel/src/lib/useGameRoom.ts b/panel/src/lib/useGameRoom.ts index 1941ebe..b9aef8e 100644 --- a/panel/src/lib/useGameRoom.ts +++ b/panel/src/lib/useGameRoom.ts @@ -60,6 +60,7 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe break; case "GAME_STATE": + // Authoritative player view — sent directly to this player setState(prev => ({ ...prev, gameState: msg.state, @@ -68,11 +69,18 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe break; case "GAME_STARTED": - setState(prev => ({ ...prev, gameState: msg.state, roomStatus: "playing" })); + // Broadcast with spectator view — only use for state if we're a spectator + // (players get their own GAME_STATE via direct send) + setState(prev => ({ + ...prev, + gameState: prev.isSpectator ? msg.state : prev.gameState, + roomStatus: "playing", + })); break; case "GAME_UPDATE": - setState(prev => ({ ...prev, gameState: msg.state })); + // Broadcast with spectator view — only update state for spectators + setState(prev => prev.isSpectator ? { ...prev, gameState: msg.state } : prev); break; case "PLAYER_JOINED":