From 622bef963f8d38acec22bc44dd126fe3b2f35f89 Mon Sep 17 00:00:00 2001 From: htom Date: Mon, 24 Nov 2025 10:07:20 +0100 Subject: [PATCH] new serevr message enum for easier message handling --- engine/src/gameend.rs | 11 +++-- server/src/bin/client.rs | 38 ++++++++++++------ server/src/connection.rs | 84 +++++++++++++++++++++++++++------------ server/src/matchmaking.rs | 31 +++++++++------ 4 files changed, 109 insertions(+), 55 deletions(-) diff --git a/engine/src/gameend.rs b/engine/src/gameend.rs index 43dd158..9238ea2 100644 --- a/engine/src/gameend.rs +++ b/engine/src/gameend.rs @@ -1,6 +1,9 @@ +use serde::{Deserialize, Serialize}; +#[derive(Deserialize, Serialize)] pub enum GameEnd { - WhiteWon(String), - BlackWon(String), - Draw(String) -} \ No newline at end of file + WhiteWon(String), + BlackWon(String), + Draw(String), +} + diff --git a/server/src/bin/client.rs b/server/src/bin/client.rs index 9838611..d74f60b 100644 --- a/server/src/bin/client.rs +++ b/server/src/bin/client.rs @@ -1,9 +1,11 @@ +use engine::gameend::GameEnd; use engine::{boardsquare::BoardSquare, chessmove::ChessMove, piecetype::PieceType}; use futures_util::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use std::io::{self, Write}; use tokio_tungstenite::{connect_async, tungstenite::Message}; use url::Url; +use uuid::Uuid; #[derive(Serialize, Deserialize, Debug)] struct Step { @@ -33,6 +35,21 @@ struct ServerMessage { reason: Option, } +#[derive(Serialize, Deserialize)] +pub enum ServerMessage2 { + GameEnd { + winner: GameEnd, + }, + UIUpdate { + fen: String, + }, + MatchFound { + match_id: Uuid, + color: String, + opponent_name: String, + }, +} + #[tokio::main] async fn main() -> Result<(), Box> { println!("Knightly Chess Client"); @@ -68,23 +85,20 @@ async fn main() -> Result<(), Box> { println!("\nServer: {}", text); // Try to parse as structured message - if let Ok(parsed) = serde_json::from_str::(text) { - match parsed.message_type.as_str() { - "welcome" => { - if let Some(player_id) = parsed.player_id { - println!("Welcome! Your player ID: {}", player_id); - } - } - "match_found" => { + if let Ok(parsed) = serde_json::from_str::(text) { + match parsed { + ServerMessage2::MatchFound { + match_id, + color, + opponent_name, + } => { println!( "opponent: {}, match_id: {}, color: {}", - parsed.opponent.unwrap(), - parsed.match_id.unwrap(), - parsed.color.unwrap() + opponent_name, match_id, color ); } _ => { - println!("cucc: {:?}", parsed); + println!("cucc"); } } } diff --git a/server/src/connection.rs b/server/src/connection.rs index a38fb3b..6b7b9d7 100644 --- a/server/src/connection.rs +++ b/server/src/connection.rs @@ -1,6 +1,7 @@ use crate::connection::ClientEvent::*; use engine::chessmove::ChessMove; -use engine::get_available_moves; +use engine::gameend::GameEnd::{self, *}; +use engine::{get_available_moves, is_game_over}; use futures_util::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; @@ -35,7 +36,7 @@ pub struct Step { pub to: String, } -#[derive(Serialize, Deserialize, Debug)] +/*#[derive(Serialize, Deserialize, Debug)] struct ServerMessage { #[serde(rename = "type")] message_type: String, @@ -45,6 +46,21 @@ struct ServerMessage { color: Option, reason: Option, response: Option, +}*/ + +#[derive(Serialize, Deserialize)] +pub enum ServerMessage2 { + GameEnd { + winner: GameEnd, + }, + UIUpdate { + fen: String, + }, + MatchFound { + match_id: Uuid, + color: String, + opponent_name: String, + }, } #[derive(Serialize, Deserialize)] @@ -52,7 +68,7 @@ struct ServerMessage { enum ClientEvent { Join { username: String }, FindMatch, - Move { step: ChessMove, fen: String }, + Move { step: ChessMove }, Resign, Chat { text: String }, RequestLegalMoves { fen: String }, @@ -205,7 +221,7 @@ pub async fn handle_connection( println!("Appended {} to the waiting queue", player_id); println!("queue {:?}", wait_queue); } - Move { step, fen } => { + Move { step } => { let match_id = connections .lock() .await @@ -214,25 +230,22 @@ pub async fn handle_connection( .current_match .unwrap(); - // TODO: discuss if the fen sent by the client is before or after the move - let new_fen = engine::get_board_after_move(&fen, &step); - - let message: ServerMessage = ServerMessage { - message_type: String::from("move"), - player_id: (Some(player_id.clone())), - match_id: (Some(match_id.clone())), - opponent: (None), - color: (None), - reason: (Some(String::from( - "after player stepped we update the position for opponent board, and move history", - ))), - response: (Some(String::from( - &serde_json::to_string(&Move { - step: step, - fen: fen, - }) - .unwrap(), - ))), + { + let mut matches = matches.lock().await; + matches.get_mut(&match_id).unwrap().board_state = + engine::get_board_after_move( + &matches.get(&match_id).unwrap().board_state, + &step, + ); + } + let message = ServerMessage2::UIUpdate { + fen: matches + .lock() + .await + .get(&match_id) + .unwrap() + .board_state + .clone(), }; let _ = broadcast_to_match( @@ -243,7 +256,25 @@ pub async fn handle_connection( ) .await; - let res = engine::is_game_over(&new_fen); // TODO: discuss how to handle this + { + match engine::is_game_over( + &matches.lock().await.get(&match_id).unwrap().board_state, + ) { + Some(res) => { + let message = ServerMessage2::GameEnd { winner: res }; + let _ = broadcast_to_match( + &connections, + &matches, + match_id, + &serde_json::to_string(&message).unwrap(), + ) + .await; + } + None => { + println!("No winner match continues.") + } + } + } } RequestLegalMoves { fen } => { let moves = get_available_moves(&fen); @@ -291,6 +322,7 @@ async fn cleanup_player( mod tests { use super::*; use uuid::Uuid; + #[tokio::test] async fn test_send_message_to_nonexistent_player() { let connections = new_connection_map(); @@ -300,7 +332,7 @@ mod tests { let result = send_message_to_player_connection(None, "test message").await; assert!(result.is_err(), "Should return error for None connection"); - println!("✅ Test passed: Handles None connection correctly"); + println!("Test passed: Handles None connection correctly"); // Test 2: Try to get non-existent player from map let mut conn = connections.lock().await; @@ -313,7 +345,7 @@ mod tests { result2.is_err(), "Should return error for non-existent player" ); - println!("✅ Test passed: Handles non-existent player in map correctly"); + println!("Test passed: Handles non-existent player in map correctly"); } #[tokio::test] diff --git a/server/src/matchmaking.rs b/server/src/matchmaking.rs index 00d001e..daa5c10 100644 --- a/server/src/matchmaking.rs +++ b/server/src/matchmaking.rs @@ -1,3 +1,4 @@ +use crate::connection::ServerMessage2; use crate::connection::{ConnectionMap, GameMatch, MatchMap, WaitingQueue, broadcast_to_match}; use rand::random; use uuid::Uuid; @@ -75,34 +76,38 @@ impl MatchmakingSystem { // Notify white player if let Some(_) = conn_map.get(&white) { - let message = format!( - r#"{{"type": "match_found", "match_id": "{}", "opponent": "{}", "color": "white"}}"#, - match_id, - conn_map + let message = ServerMessage2::MatchFound { + match_id: match_id.clone(), + color: String::from("white"), + opponent_name: conn_map .get(&white) .and_then(|c| c.username.as_deref()) .unwrap_or("Opponent") - ); + .to_string(), + }; + let _ = crate::connection::send_message_to_player_connection( conn_map.get_mut(&white), - &message, + &serde_json::to_string(&message).unwrap(), ) .await; } // Notify black player if let Some(_) = conn_map.get(&black) { - let message = format!( - r#"{{"type": "match_found", "match_id": "{}", "opponent": "{}", "color": "black"}}"#, - match_id, - conn_map + let message = ServerMessage2::MatchFound { + match_id: match_id.clone(), + color: String::from("black"), + opponent_name: conn_map .get(&black) .and_then(|c| c.username.as_deref()) .unwrap_or("Opponent") - ); + .to_string(), + }; + let _ = crate::connection::send_message_to_player_connection( - conn_map.get_mut(&black), - &message, + conn_map.get_mut(&white), + &serde_json::to_string(&message).unwrap(), ) .await; }