From fb691891c8229aeb0304118f8f748e7a82dff507 Mon Sep 17 00:00:00 2001 From: Adam <24621027+WhiteDopeOnPunk@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:02:14 -0400 Subject: [PATCH] add player submitted indicator --- client/src/components/game.rs | 2 +- client/src/components/game/header.rs | 6 +- client/src/components/game/views/judging.rs | 2 +- client/src/components/game/views/playing.rs | 53 +++++---- client/src/components/websocket.rs | 13 ++- lib/src/lib.rs | 12 +- server/src/game_handler.rs | 121 ++++++++++++++++---- server/src/user_handler.rs | 2 +- 8 files changed, 149 insertions(+), 62 deletions(-) diff --git a/client/src/components/game.rs b/client/src/components/game.rs index ab28161..db61238 100644 --- a/client/src/components/game.rs +++ b/client/src/components/game.rs @@ -16,7 +16,7 @@ pub fn Game() -> impl IntoView { let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; // Incoming - let game_meta = expect_context::>>(); + let game_meta = expect_context::>>(); let user_update = expect_context::>>(); // Signals // diff --git a/client/src/components/game/header.rs b/client/src/components/game/header.rs index 89f92ba..e82a553 100644 --- a/client/src/components/game/header.rs +++ b/client/src/components/game/header.rs @@ -3,7 +3,7 @@ use lib::*; #[component] fn Meta() -> impl IntoView { - let game_meta = expect_context::>>(); + let game_meta = expect_context::>>(); view! { @@ -17,7 +17,7 @@ fn Meta() -> impl IntoView { #[component] fn Scoreboard() -> impl IntoView { - let game_meta = expect_context::>>(); + let game_meta = expect_context::>>(); view! { @@ -28,6 +28,7 @@ fn Scoreboard() -> impl IntoView { "Score" + {game_meta() @@ -52,6 +53,7 @@ fn Scoreboard() -> impl IntoView { }} {player.name} {player.score} + {if player.submitted { "🟢" } else { "⚫" }} } }) diff --git a/client/src/components/game/views/judging.rs b/client/src/components/game/views/judging.rs index fc25152..e7b7d93 100644 --- a/client/src/components/game/views/judging.rs +++ b/client/src/components/game/views/judging.rs @@ -8,7 +8,7 @@ use serde_json::to_string; pub fn JudgingView() -> impl IntoView { // Incoming let websocket = expect_context::(); - let game_meta = expect_context::>>(); + let game_meta = expect_context::>>(); let judge_round = expect_context::>>(); let set_judge_round = expect_context::>>(); diff --git a/client/src/components/game/views/playing.rs b/client/src/components/game/views/playing.rs index 7890eef..d084480 100644 --- a/client/src/components/game/views/playing.rs +++ b/client/src/components/game/views/playing.rs @@ -9,7 +9,8 @@ use std::collections::{HashMap, HashSet}; pub fn PlayingView() -> impl IntoView { // Incoming let websocket = expect_context::(); - let game_meta = expect_context::>>(); + let game_meta = expect_context::>>(); + let game_state = expect_context::>>(); // Signals let (selected_cards, set_selected_cards) = create_signal::>(vec![]); @@ -27,8 +28,8 @@ pub fn PlayingView() -> impl IntoView { // Put cards in a map for easier lookup // The server sends a vec to preserve ordering create_effect(move |_| { - if game_meta().is_some() { - for card in game_meta().unwrap().white { + if game_state().is_some() { + for card in game_state().unwrap().white { set_player_hand.update(|map| { map.insert(card.uuid.clone(), card.clone()); }); @@ -48,7 +49,7 @@ pub fn PlayingView() -> impl IntoView { create_effect(move |_| { if card_clicked() != "".to_string() && !submitted() - && submitted_cards().len() < game_meta().unwrap().black.1.into() + && submitted_cards().len() < game_state().unwrap().black.1.into() { if let Some(clicked_card) = player_hand .get_untracked() @@ -86,7 +87,7 @@ pub fn PlayingView() -> impl IntoView { } }); - if submitted_cards().len() < game_meta().unwrap().black.1.into() { + if submitted_cards().len() < game_state().unwrap().black.1.into() { return; } @@ -129,28 +130,30 @@ pub fn PlayingView() -> impl IntoView { // Player hand
- + - - + }> + + + } } - } - /> + /> +
} diff --git a/client/src/components/websocket.rs b/client/src/components/websocket.rs index 50db1f2..d027c7b 100644 --- a/client/src/components/websocket.rs +++ b/client/src/components/websocket.rs @@ -74,7 +74,8 @@ pub fn Websocket() -> impl IntoView { let (chat_update, set_chat_update) = create_signal::>(Option::None); let (judge_round, set_judge_round) = create_signal::>(Option::None); let (chat_message, set_chat_message) = create_signal::>(Option::None); - let (current_game, set_current_game) = create_signal::>(Option::None); + let (game_meta, set_game_meta) = create_signal::>(Option::None); + let (game_state, set_game_state) = create_signal::>(Option::None); let (card_packs_meta, set_card_packs_meta) = create_signal::(CardPacksMeta { official_meta: vec![], unofficial_meta: vec![], @@ -87,7 +88,8 @@ pub fn Websocket() -> impl IntoView { set_chat_update.set_untracked(None); set_judge_round.set_untracked(None); set_chat_message.set_untracked(None); - set_current_game.set_untracked(None); + set_game_meta.set_untracked(None); + set_game_state.set_untracked(None); } }); @@ -98,7 +100,8 @@ pub fn Websocket() -> impl IntoView { provide_context::>>(set_judge_round); provide_context::>>(chat_message); provide_context::>>(active_games); - provide_context::>>(current_game); + provide_context::>>(game_meta); + provide_context::>>(game_state); provide_context::>>(state_summary); // Message handler @@ -115,8 +118,10 @@ pub fn Websocket() -> impl IntoView { set_chat_update(Some(chat_update)); } else if let Ok(games_update) = from_str::(message) { set_active_games(games_update.games); + } else if let Ok(game_meta) = from_str::(message) { + set_game_meta(Some(game_meta)); } else if let Ok(game_update) = from_str::(message) { - set_current_game(Some(game_update)); + set_game_state(Some(game_update)); } else if let Ok(packs_meta_update) = from_str::(message) { set_card_packs_meta(packs_meta_update); } else if let Ok(judge_update) = from_str::(message) { diff --git a/lib/src/lib.rs b/lib/src/lib.rs index c912607..8d9bc81 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -44,24 +44,30 @@ pub struct WhiteCardMeta { pub struct GamePlayerMeta { pub name: String, pub score: usize, + pub submitted: bool, } /// Game meta #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct GameStateMeta { +pub struct GameMeta { pub uuid: String, pub name: String, pub host: String, pub players: Vec, pub czar: String, - pub black: (String, u8), - pub white: Vec, pub packs: Vec, pub white_count: usize, pub black_count: usize, pub white_discard_count: usize, } +/// Game state meta +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct GameStateMeta { + pub black: (String, u8), + pub white: Vec, +} + /// Game browser meta #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GameBrowserMeta { diff --git a/server/src/game_handler.rs b/server/src/game_handler.rs index 0fb87f4..fca6d99 100644 --- a/server/src/game_handler.rs +++ b/server/src/game_handler.rs @@ -19,7 +19,8 @@ pub enum GameHandlerMessage { MoveRequest(PlayerMoveRequest, SocketAddr), JudgeDecision(JudgeDecisionRequest, SocketAddr), DeleteGame(GameDeleteRequest), - SendGameUpdate(Vec), + SendGameStateUpdate(Vec), + SendGameMetaUpdate(Vec), } /// Handles game stuff @@ -42,7 +43,8 @@ impl GameHandler { MoveRequest(request, addr) => self.handle_player_move(request, addr).await, JudgeDecision(request, addr) => self.handle_judging(request, addr).await, DeleteGame(request) => self.delete_game(request).await, - SendGameUpdate(game_ids) => self.send_game_state_update(game_ids), + SendGameStateUpdate(game_ids) => self.send_game_state_update_all(game_ids), + SendGameMetaUpdate(game_ids) => self.send_game_meta_update(game_ids), } } @@ -78,15 +80,20 @@ impl GameHandler { this_game .write() .unwrap() - .judge_round(request, player_user_id); + .judge_round(request.clone(), player_user_id); - self.send_game_state_update(vec![game_id]); + self.send_game_state_update_all(vec![game_id.clone()]); + self.send_game_meta_update(vec![game_id]); } else { tracing::error!("Received judge request for nonexistent judge player!"); } } else { tracing::error!("Received judge request for nonexistent game!"); } + + // Send updates for all players + self.send_game_state_update_all(vec![request.game_id.clone()]); + self.send_game_meta_update(vec![request.game_id]); } /// Process player move request @@ -99,7 +106,7 @@ impl GameHandler { match this_game .write() .unwrap() - .player_move(request, player_user_id) + .player_move(request.clone(), player_user_id) { Err(err) => { let message = ChatMessage { text: err }; @@ -125,6 +132,7 @@ impl GameHandler { } else { tracing::error!("Player tried to submit move for nonexistent game!"); } + self.send_game_meta_update(vec![request.game_id]); } // Ties game ids to user for easier lookup @@ -159,16 +167,19 @@ impl GameHandler { } /// Puts a user in a game - async fn join_game(&self, id: String, addr: SocketAddr) { - if let Some(this_game) = self.state.games.read().unwrap().get(&id) { + async fn join_game(&self, game_id: String, addr: SocketAddr) { + if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { if let Some(this_user) = self.state.online_users.read().unwrap().get(&addr) { let this_user_id = this_user.read().unwrap().uuid.clone(); // Register game to user - self.register_user_in_game(id.clone(), this_user_id); + self.register_user_in_game(game_id.clone(), this_user_id.clone()); // Create player this_game.write().unwrap().create_player(this_user.clone()); + + // Send cards + self.send_game_state_update_single(this_user_id, game_id.clone()) } else { tracing::error!("Tried to add a nonexistent user to game!"); return; @@ -180,7 +191,7 @@ impl GameHandler { tracing::debug!("{:#?}", self.state.games_by_user.read().unwrap()); // Send updates for all players - self.send_game_state_update(vec![id]); + self.send_game_meta_update(vec![game_id.clone()]); // Broadcast game browser update self.state @@ -195,8 +206,8 @@ impl GameHandler { .unwrap(); } - /// Send game state update for all players of a game - fn send_game_state_update(&self, game_ids: Vec) { + /// Send game meta update for all players of a game + fn send_game_meta_update(&self, game_ids: Vec) { for game_id in game_ids { if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { let players = this_game @@ -207,29 +218,24 @@ impl GameHandler { .map(|player| GamePlayerMeta { name: player.user.read().unwrap().name.clone(), score: player.black.len(), + submitted: this_game + .read() + .unwrap() + .judge_pile_meta + .values() + .collect::>() + .contains(&&player.user.read().unwrap().uuid), }) .collect::>(); for player in this_game.read().unwrap().players.values() { // Create update for user's game view - let meta = GameStateMeta { + let meta = GameMeta { uuid: game_id.clone(), name: this_game.read().unwrap().name.clone(), host: this_game.read().unwrap().host.read().unwrap().name.clone(), players: players.clone(), czar: this_game.read().unwrap().czar.read().unwrap().name.clone(), - black: ( - this_game.read().unwrap().current_black.text.clone(), - this_game.read().unwrap().current_black.pick, - ), - white: player - .white - .iter() - .map(|card| WhiteCardMeta { - uuid: card.uuid.to_string(), - text: card.text.clone(), - }) - .collect(), packs: this_game.read().unwrap().packs.clone(), white_count: this_game.read().unwrap().white.len(), black_count: this_game.read().unwrap().black.len(), @@ -242,11 +248,75 @@ impl GameHandler { tokio::spawn(async move { user_tx.send(msg).await }); } } else { + tracing::error!("Attempted to create game meta update for nonexistent game!"); + } + } + } + + /// Send game state update for all players of a game + fn send_game_state_update_all(&self, game_ids: Vec) { + for game_id in game_ids { + if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { + for player in this_game.read().unwrap().players.values() { + // Create update for user's game view + let meta = GameStateMeta { + black: ( + this_game.read().unwrap().current_black.text.clone(), + this_game.read().unwrap().current_black.pick, + ), + white: player + .white + .iter() + .map(|card| WhiteCardMeta { + uuid: card.uuid.to_string(), + text: card.text.clone(), + }) + .collect(), + }; + + // Send user's update + let msg = serde_json::to_string(&meta).unwrap(); + let user_tx = player.user.read().unwrap().tx.clone(); + tokio::spawn(async move { user_tx.send(msg).await }); + } + } else { tracing::error!("Attempted to create game state update for nonexistent game!"); } } } + /// Send game state update for a single user + fn send_game_state_update_single(&self, user_id: String, game_id: String) { + if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { + if let Some(player) = this_game.read().unwrap().players.get(&user_id) { + // Create update for user's game view + let meta = GameStateMeta { + black: ( + this_game.read().unwrap().current_black.text.clone(), + this_game.read().unwrap().current_black.pick, + ), + white: player + .white + .iter() + .map(|card| WhiteCardMeta { + uuid: card.uuid.to_string(), + text: card.text.clone(), + }) + .collect(), + }; + + // Send user's update + let msg = serde_json::to_string(&meta).unwrap(); + let user_tx = player.user.read().unwrap().tx.clone(); + tokio::spawn(async move { user_tx.send(msg).await }); + } else { + tracing::error!("Attempted to create game state update for nonexistent player!"); + } + } else { + tracing::error!("Attempted to create game state update for nonexistent game!"); + } + } + /// Creates a new game async fn create_new_game(&self, new_game: NewGameRequest, addr: SocketAddr) { if new_game.packs.is_empty() { @@ -282,7 +352,8 @@ impl GameHandler { // Register game to user self.register_user_in_game(game_id.clone(), host.read().unwrap().uuid.clone()); - self.send_game_state_update(vec![game_id]); + self.send_game_state_update_all(vec![game_id.clone()]); + self.send_game_meta_update(vec![game_id]); // Broadcast game browser update self.state diff --git a/server/src/user_handler.rs b/server/src/user_handler.rs index 5ceb548..1a2ad87 100644 --- a/server/src/user_handler.rs +++ b/server/src/user_handler.rs @@ -256,7 +256,7 @@ impl UserHandler { .unwrap() .contains_key(&user_id) { - let msg = GameHandlerMessage::SendGameUpdate( + let msg = GameHandlerMessage::SendGameMetaUpdate( self.state .games_by_user .read()