diff --git a/server/src/game_handler.rs b/server/src/game_handler.rs index f735689..0fb87f4 100644 --- a/server/src/game_handler.rs +++ b/server/src/game_handler.rs @@ -12,12 +12,14 @@ use std::{ }; /// For interacting with the game handler +#[derive(Debug)] pub enum GameHandlerMessage { NewGame(NewGameRequest, SocketAddr), JoinGame(String, SocketAddr), MoveRequest(PlayerMoveRequest, SocketAddr), JudgeDecision(JudgeDecisionRequest, SocketAddr), DeleteGame(GameDeleteRequest), + SendGameUpdate(Vec), } /// Handles game stuff @@ -40,6 +42,7 @@ 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), } } @@ -77,7 +80,7 @@ impl GameHandler { .unwrap() .judge_round(request, player_user_id); - self.send_game_state_update(game_id); + self.send_game_state_update(vec![game_id]); } else { tracing::error!("Received judge request for nonexistent judge player!"); } @@ -124,10 +127,46 @@ impl GameHandler { } } + // Ties game ids to user for easier lookup + fn register_user_in_game(&self, game_id: String, user_id: String) { + if !self + .state + .games_by_user + .read() + .unwrap() + .contains_key(&user_id) + { + self.state + .games_by_user + .write() + .unwrap() + .insert(user_id, vec![game_id]); + } else if self + .state + .games_by_user + .read() + .unwrap() + .contains_key(&user_id) + { + self.state + .games_by_user + .write() + .unwrap() + .get_mut(&user_id) + .unwrap() + .extend(vec![game_id]); + } + } + /// 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) { 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); + // Create player this_game.write().unwrap().create_player(this_user.clone()); } else { @@ -138,9 +177,10 @@ impl GameHandler { tracing::error!("User tried to join a nonexistent game!"); return; } + tracing::debug!("{:#?}", self.state.games_by_user.read().unwrap()); // Send updates for all players - self.send_game_state_update(id); + self.send_game_state_update(vec![id]); // Broadcast game browser update self.state @@ -156,52 +196,54 @@ impl GameHandler { } /// Send game state update for all players of a game - fn send_game_state_update(&self, game_id: String) { - if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { - let players = this_game - .read() - .unwrap() - .players - .values() - .map(|player| GamePlayerMeta { - name: player.user.read().unwrap().name.clone(), - score: player.black.len(), - }) - .collect::>(); + fn send_game_state_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 + .read() + .unwrap() + .players + .values() + .map(|player| GamePlayerMeta { + name: player.user.read().unwrap().name.clone(), + score: player.black.len(), + }) + .collect::>(); - for player in this_game.read().unwrap().players.values() { - // Create update for user's game view - let meta = GameStateMeta { - 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(), - white_discard_count: this_game.read().unwrap().white_discard.len(), - }; + for player in this_game.read().unwrap().players.values() { + // Create update for user's game view + let meta = GameStateMeta { + 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(), + white_discard_count: this_game.read().unwrap().white_discard.len(), + }; - // 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 }); + // 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!"); } - } else { - tracing::error!("Attempted to create game state update for nonexistent game!"); } } @@ -237,7 +279,10 @@ impl GameHandler { Arc::new(RwLock::new(new_game_object)), ); - self.send_game_state_update(game_id); + // 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]); // Broadcast game browser update self.state diff --git a/server/src/lib.rs b/server/src/lib.rs index 938f508..8a3df2a 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -71,6 +71,7 @@ pub struct AppState { pub last_names: Vec, pub reserved_names: RwLock>, pub users_by_id: RwLock>>>, + pub games_by_user: RwLock>>, pub online_users: RwLock>>>, pub offline_users: RwLock>>>, pub packs: CardPacks, diff --git a/server/src/main.rs b/server/src/main.rs index 3423e73..c586a69 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -35,6 +35,7 @@ async fn main() -> Result<()> { let last_names = load_names("data/last.txt")?; let reserved_names = RwLock::new(HashSet::::new()); let users_by_id = RwLock::new(HashMap::>>::new()); + let games_by_user = RwLock::new(HashMap::>::new()); let online_users = RwLock::new(HashMap::>>::new()); let offline_users = RwLock::new(HashMap::>>::new()); let (packs, packs_meta, white_cards_by_id) = load_cards_from_json("data/cah-cards-full.json")?; @@ -50,6 +51,7 @@ async fn main() -> Result<()> { last_names, reserved_names, users_by_id, + games_by_user, online_users, offline_users, packs, diff --git a/server/src/user_handler.rs b/server/src/user_handler.rs index 7b6668b..5ceb548 100644 --- a/server/src/user_handler.rs +++ b/server/src/user_handler.rs @@ -3,7 +3,7 @@ use crate::DmUserMethod::*; use crate::SendUserMessage::*; use crate::User; use crate::UserHandlerMessage::*; -use lib::*; +use crate::*; use serde_json::to_string; use std::net::SocketAddr; use std::sync::{Arc, RwLock}; @@ -245,6 +245,35 @@ impl UserHandler { Addr(addr), ) .await; + // Update games this user is in + if let Some(user) = &self.state.online_users.read().unwrap().get(&addr) { + let user_id = user.read().unwrap().uuid.to_string(); + { + if self + .state + .games_by_user + .read() + .unwrap() + .contains_key(&user_id) + { + let msg = GameHandlerMessage::SendGameUpdate( + self.state + .games_by_user + .read() + .unwrap() + .get(&user_id) + .unwrap() + .to_vec(), + ); + let tx = self.state.games_tx.clone(); + + tokio::spawn(async move { + tracing::debug!("msg: {:#?}", &msg); + let _ = tx.send(msg).await; + }); + } + } + } // Send client updates let _ = broadcast_tx.send(meta_games_browser_update(&self.state)); let _ = broadcast_tx.send(meta_chat_update(&self.state)); @@ -275,6 +304,20 @@ pub fn meta_chat_update(state: &Arc) -> String { .unwrap() } +/// Generate chatroom join announcement +pub fn meta_announce_user_join(state: &Arc, addr: &SocketAddr) -> String { + let msg = format!("{} joined.", { + if let Some(user) = state.online_users.read().unwrap().get(addr) { + user.read().unwrap().name.clone() + } else { + return Default::default(); + } + }); + + tracing::debug!("{}", &msg); + to_string::(&ChatMessage { text: msg }).unwrap() +} + /// Generage cards meta message pub fn meta_new_game_card_packs(state: &Arc) -> String { to_string::(&state.packs_meta).unwrap() @@ -316,17 +359,3 @@ pub fn meta_games_browser_update(state: &Arc) -> String { to_string::(&GamesUpdate { games }).unwrap() } - -/// Generate chatroom join announcement -pub fn meta_announce_user_join(state: &Arc, addr: &SocketAddr) -> String { - let msg = format!("{} joined.", { - if let Some(user) = state.online_users.read().unwrap().get(addr) { - user.read().unwrap().name.clone() - } else { - return Default::default(); - } - }); - - tracing::debug!("{}", &msg); - to_string::(&ChatMessage { text: msg }).unwrap() -}