Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion backend/app/api/routers/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ async def broadcast_filtered_states(room_id: str, service: GameService):
# Fetch presence for all players once
presence_map = await service.get_all_player_presence(room_id, list(game.players.keys()))

# Optimize: Pre-calculate full schema once
full_schema = game.to_schema()

async def get_view(player_id: str):
return await service.get_player_view(game, player_id, presence_map)
return await service.get_player_view(game, player_id, presence_map, full_schema=full_schema)

await websocket_manager.broadcast_filtered_game_states(room_id, get_view)

Expand Down
15 changes: 8 additions & 7 deletions backend/app/models/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,16 @@ def to_schema(self) -> GameStateSchema:
)

# ===== View Logic =====
def get_view_for_player(self, viewer_id: str) -> GameStateSchema:
def get_view_for_player(
self, viewer_id: str, full_schema: GameStateSchema | None = None
) -> GameStateSchema:
"""
Create a filtered view of the game state for a specific player.
Hides roles and actions based on game rules.
"""
full_schema = self.to_schema()
if full_schema is None:
full_schema = self.to_schema()

is_game_over = full_schema.phase == GamePhase.GAME_OVER

viewer = self.players.get(viewer_id)
Expand Down Expand Up @@ -258,11 +262,8 @@ def get_view_for_player(self, viewer_id: str) -> GameStateSchema:
self._state, pid
)

full_schema.players = filtered_players
# Never expose the raw seer reveal map
full_schema.seer_reveals = {}

return full_schema
# Create a copy with filtered players and clean sensitive data
return full_schema.model_copy(update={"players": filtered_players, "seer_reveals": {}})

def auto_balance_roles(self):
"""Automatically set default role distribution based on player count."""
Expand Down
8 changes: 6 additions & 2 deletions backend/app/services/game_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ async def get_all_player_presence(self, room_id: str, player_ids: list[str]) ->
return {pid: presence_results[i] is not None for i, pid in enumerate(player_ids)}

async def get_player_view(
self, game: Game, player_id: str, presence_map: dict[str, bool] | None = None
self,
game: Game,
player_id: str,
presence_map: dict[str, bool] | None = None,
full_schema: GameStateSchema | None = None,
) -> GameStateSchema:
"""Return game state with other players' roles hidden unless revealed."""
# Delegate logic to model
view = game.get_view_for_player(player_id)
view = game.get_view_for_player(player_id, full_schema=full_schema)
player_ids = list(view.players.keys())

# Optimize: if no players, skip redis
Expand Down