From 448c6ee8e63b9540bf9efa2371418bd687f4ba4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Thu, 12 Mar 2026 11:15:31 +0100 Subject: [PATCH] add WS handlers for predictions, dishes game messages Co-Authored-By: Claude Opus 4.6 --- packages/server/src/ws/handler.ts | 143 ++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/packages/server/src/ws/handler.ts b/packages/server/src/ws/handler.ts index 82322e6..5b12a64 100644 --- a/packages/server/src/ws/handler.ts +++ b/packages/server/src/ws/handler.ts @@ -41,6 +41,25 @@ function sendError(ws: WSContext, message: string) { sendTo(ws, { type: "error", message }) } +function sendGameState(ws: WSContext, roomCode: string, sessionId: string) { + const gm = roomManager.getGameManager(roomCode) + const playerId = roomManager.getPlayerIdBySession(roomCode, sessionId) + if (!gm || !playerId) return + + const playerLookup = roomManager.getPlayerLookup(roomCode) + const gameState = gm.getGameStateForPlayer(playerId, playerLookup) + sendTo(ws, { type: "game_state", gameState }) +} + +function sendDisplayGameState(ws: WSContext, roomCode: string) { + const gm = roomManager.getGameManager(roomCode) + if (!gm) return + + const playerLookup = roomManager.getPlayerLookup(roomCode) + const gameState = gm.getGameStateForDisplay(playerLookup) + sendTo(ws, { type: "game_state", gameState }) +} + let registered = false export function registerWebSocketRoutes() { @@ -79,6 +98,7 @@ export function registerWebSocketRoutes() { type: "room_state", room: roomManager.getRoom(roomCode)!, }) + sendGameState(ws, roomCode, sessionId) broadcast(roomCode, { type: "player_reconnected", playerId: result.playerId, @@ -90,6 +110,7 @@ export function registerWebSocketRoutes() { type: "room_state", room: roomManager.getRoom(roomCode)!, }) + sendDisplayGameState(ws, roomCode) } }, @@ -131,6 +152,7 @@ export function registerWebSocketRoutes() { room: roomManager.getRoom(roomCode)!, sessionId: result.sessionId, }) + sendGameState(ws, roomCode, result.sessionId) // Broadcast player joined to everyone const room = roomManager.getRoom(roomCode)! @@ -155,6 +177,7 @@ export function registerWebSocketRoutes() { type: "room_state", room: roomManager.getRoom(roomCode)!, }) + sendGameState(ws, roomCode, msg.sessionId) broadcast(roomCode, { type: "player_reconnected", playerId: result.playerId, @@ -176,6 +199,13 @@ export function registerWebSocketRoutes() { type: "act_changed", newAct: result.newAct, }) + if (result.newAct === "act2") { + const gm = roomManager.getGameManager(roomCode) + if (gm) { + gm.lockPredictions() + broadcast(roomCode, { type: "predictions_locked" }) + } + } break } @@ -192,6 +222,119 @@ export function registerWebSocketRoutes() { broadcast(roomCode, { type: "room_ended" }) break } + + case "submit_prediction": { + if (!sessionId) { + sendError(ws, "Not joined") + return + } + const playerId = roomManager.getPlayerIdBySession(roomCode, sessionId) + const gm = roomManager.getGameManager(roomCode) + if (!playerId || !gm) { + sendError(ws, "Room not found") + return + } + + const result = gm.submitPrediction(playerId, msg.predictedWinner, msg.top3, msg.nulPointsPick) + if ("error" in result) { + sendError(ws, result.error) + return + } + + // Send updated game state back to this player only + sendGameState(ws, roomCode, sessionId) + break + } + + case "add_dish": { + if (!sessionId) { + sendError(ws, "Not joined") + return + } + if (!roomManager.isHost(roomCode, sessionId)) { + sendError(ws, "Only the host can add dishes") + return + } + const room = roomManager.getRoom(roomCode) + if (room && room.currentAct !== "lobby" && room.currentAct !== "act1") { + sendError(ws, "Dishes can only be added during lobby or Act 1") + return + } + const gm = roomManager.getGameManager(roomCode) + if (!gm) { + sendError(ws, "Room not found") + return + } + + const result = gm.addDish(msg.name, msg.correctCountry) + if ("error" in result) { + sendError(ws, result.error) + return + } + + broadcast(roomCode, { + type: "dish_added", + dish: { + id: result.dish.id, + name: result.dish.name, + correctCountry: "", + revealed: false, + }, + }) + break + } + + case "submit_dish_guess": { + if (!sessionId) { + sendError(ws, "Not joined") + return + } + const playerId = roomManager.getPlayerIdBySession(roomCode, sessionId) + const gm = roomManager.getGameManager(roomCode) + if (!playerId || !gm) { + sendError(ws, "Room not found") + return + } + + const result = gm.submitDishGuess(playerId, msg.dishId, msg.guessedCountry) + if ("error" in result) { + sendError(ws, result.error) + return + } + + sendTo(ws, { + type: "dish_guess_recorded", + dishId: msg.dishId, + guessedCountry: msg.guessedCountry, + }) + break + } + + case "reveal_dishes": { + if (!sessionId) { + sendError(ws, "Not joined") + return + } + if (!roomManager.isHost(roomCode, sessionId)) { + sendError(ws, "Only the host can reveal dishes") + return + } + const gm = roomManager.getGameManager(roomCode) + if (!gm) { + sendError(ws, "Room not found") + return + } + + gm.revealDishes() + const playerLookup = roomManager.getPlayerLookup(roomCode) + const results = gm.getDishResults(playerLookup) + + broadcast(roomCode, { + type: "dishes_revealed", + results, + }) + break + } } },