use crate::user_handler::*; use crate::AppState; use crate::GameHandlerMessage::*; use crate::UserHandlerMessage::*; use axum::extract::ws::{CloseFrame, Message}; use lib::*; use serde_json::{from_str, to_string}; use std::net::SocketAddr; use std::sync::Arc; /// Handles incoming messages pub struct MessageHandler { /// Global state pointer state: Arc, } impl MessageHandler { /// Returns new MessageHandler object pub fn new(state: Arc) -> Self { MessageHandler { state } } /// Handles incoming messages pub async fn handle(&self, addr: SocketAddr, message: Message) { match message { Message::Text(text) => match text { _chat_message if let Ok(chat_message) = from_str::(&text) => { // TODO: This should be delegated to user handler and an outgoing message and/or chat handler let msg = format! {"{0}: {1}", self.state.online_users.read().unwrap().get(&addr).unwrap().read().unwrap().name, chat_message.text}; tracing::debug!("{msg}"); self.state .broadcast_tx .send(to_string::(&ChatMessage { text: msg }).unwrap()) .unwrap(); } _user_log_in_request if let Ok(user_log_in) = from_str::(&text) => { self.state .users_tx .send(UserLogIn { username: user_log_in.username, addr, }) .await .unwrap(); tracing::debug!("passed login to user handler"); } _new_game_request if let Ok(new_game) = from_str::(&text) => { self.state .games_tx .send(NewGame { addr, new_game }) .await .unwrap(); } _join_game_request if let Ok(join_request) = from_str::(&text) => { self.state .games_tx .send(JoinGame { addr, id: join_request.id, }) .await .unwrap(); } _player_move_request if let Ok(move_request) = from_str::(&text) => { if move_request.card_ids.is_empty() { tracing::error!("Move request card_ids is empty! Ignoring..."); return; } if move_request.game_id == "".to_string() { tracing::error!("Move request game_id is empty! Ignoring..."); return; } else { self.state .games_tx .send(MoveRequest(move_request, addr)) .await .unwrap(); } } _ => tracing::debug!("Unhandled text from {}", addr), }, Message::Binary(data) => tracing::debug!("{} sent binary: {:?}", addr, data), Message::Close(close_frame) => { self.handle_close(close_frame, addr); } Message::Ping(ping) => { tracing::debug!("Pong received with: {:?}", ping); } Message::Pong(pong) => { tracing::debug!("Pong received with: {:?}", pong); } } } /// This runs when a connection closes fn handle_close(&self, close_frame: Option, addr: SocketAddr) { let user_name = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .read() .unwrap() .name .clone(); // Send client updates self.state .broadcast_tx .send(meta_server_summary_update(&self.state)) .unwrap(); self.state .broadcast_tx .send(meta_chat_update(&self.state)) .unwrap(); // Announce User left in chat let msg = ChatMessage { text: format!("{0} left.", &user_name), }; tracing::debug!("{}", msg.text); self.state .broadcast_tx .send(to_string::(&msg).unwrap()) .unwrap(); // Process close frame if let Some(cf) = close_frame { tracing::debug!( "Close received from {0} with code: {1} and reason: {2}", &user_name, cf.code, cf.reason ) } else { tracing::debug!("close received without close frame") } // Move user to offline // This should probably happen first/immediately but moving down here avoids a clone self.state.offline_users.write().unwrap().insert( user_name, self.state .online_users .write() .unwrap() .remove(&addr) .unwrap(), ); } }