use crate::game::*; use crate::user_handler::*; use crate::AppState; use crate::DmUserMethod::*; use crate::GameHandlerMessage::*; use crate::SendUserMessage::*; use crate::UserHandlerMessage::*; use lib::*; use std::{ net::SocketAddr, sync::{Arc, RwLock}, }; /// For interacting with the game handler pub enum GameHandlerMessage { NewGame(NewGameRequest, SocketAddr), JoinGame(String, SocketAddr), MoveRequest(PlayerMoveRequest, SocketAddr), JudgeDecision(JudgeDecisionRequest, SocketAddr), DeleteGame(GameDeleteRequest), } /// Handles game stuff pub struct GameHandler { /// Global state pointer state: Arc, } impl GameHandler { /// Returns a new game handler pub fn new(state: Arc) -> Self { GameHandler { state } } /// Handles incoming messages pub async fn handle(&self, message: GameHandlerMessage) { match message { NewGame(request, addr) => self.create_new_game(request, addr).await, JoinGame(request, addr) => self.join_game(request, addr).await, 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, } } /// Delete game async fn delete_game(&self, request: GameDeleteRequest) { // TODO: add auth lol let _ = self .state .games .write() .unwrap() .remove(&request.delete_game_id) .unwrap(); // Broadcast game browser update self.state .broadcast_tx .send(meta_games_browser_update(&self.state)) .unwrap(); } /// Process judging async fn handle_judging(&self, request: JudgeDecisionRequest, addr: SocketAddr) { // Get pointers let player_user_id = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .read() .unwrap() .uuid .to_string(); let this_game = self .state .games .read() .unwrap() .get(&request.game_id) .unwrap() .clone(); // Send to game this_game .write() .unwrap() .judge_round(request.clone(), player_user_id.clone()); let this_game_id = this_game.read().unwrap().uuid.to_string(); self.send_game_state_update(this_game_id).await } /// Process player move request async fn handle_player_move(&self, request: PlayerMoveRequest, addr: SocketAddr) { // Get pointers let player_user_id = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .read() .unwrap() .uuid .to_string(); let this_game = self .state .games .read() .unwrap() .get(&request.game_id) .unwrap() .clone(); // Do the stuff match this_game .write() .unwrap() .player_move(request, player_user_id) { Err(err) => { let message = ChatMessage { text: err }; let tx = self.state.users_tx.clone(); tokio::spawn(async move { let _ = tx.send(DmUser(SendChatMessage(message), Addr(addr))).await; }) } Ok(None) => tokio::spawn(async move { tracing::debug!("None") }), Ok(Some((judge_round, czar_id))) => { let tx = self.state.users_tx.clone(); tokio::spawn(async move { let _ = tx .send(DmUser(SendJudgeRound(judge_round), Id(czar_id))) .await; }) } }; } /// Puts a user in a game async fn join_game(&self, id: String, addr: SocketAddr) { // Get pointers let this_game = self.state.games.read().unwrap().get(&id).unwrap().clone(); let this_user = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .clone(); // Create player this_game.write().unwrap().create_player(this_user); // Send updates for all players self.send_game_state_update(id).await; // Broadcast game browser update self.state .broadcast_tx .send(meta_games_browser_update(&self.state)) .unwrap(); // Broadcast server meta update self.state .broadcast_tx .send(meta_server_summary_update(&self.state)) .unwrap(); } /// Send game state update for all players of a game async fn send_game_state_update(&self, game_id: String) { let this_game = self .state .games .read() .unwrap() .get(&game_id) .unwrap() .clone(); 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(), }; // 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 }); } } /// Creates a new game async fn create_new_game(&self, new_game: NewGameRequest, addr: SocketAddr) { if new_game.packs.is_empty() { tracing::error!("New game cards are empty!"); return; } else if new_game.name.is_empty() { tracing::error!("New game name is empty!"); return; } // Get host pointer let host = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .clone(); // Create manifest let manifest = NewGameManifest { name: new_game.name, host: host.clone(), packs: new_game.packs, }; // Create game using manifest let mut new_game_object = Game::new(self.state.clone(), manifest); // Don't forget to create the host player!!! new_game_object.create_player(host.clone()); let game_id = new_game_object.uuid.to_string(); // Add game to active list self.state.games.write().unwrap().insert( new_game_object.uuid.to_string(), Arc::new(RwLock::new(new_game_object)), ); self.send_game_state_update(game_id).await; // Broadcast game browser update self.state .broadcast_tx .send(meta_games_browser_update(&self.state)) .unwrap(); // Broadcast server meta update self.state .broadcast_tx .send(meta_server_summary_update(&self.state)) .unwrap(); } }