From a8a6e11350bd8f63cf5abfe7c3e1e1b8f0284d54 Mon Sep 17 00:00:00 2001 From: szymon Date: Mon, 17 Aug 2026 19:39:46 +0200 Subject: [PATCH] feat: add broadcasting current turn and board view --- src/bin/client.rs | 36 ++++++++++++++++++++++++++------- src/logic/board/board_view.rs | 6 +++--- src/logic/game_master.rs | 4 ++++ src/network/client.rs | 16 ++++++++++++--- src/network/message.rs | 5 ++--- src/network/mod.rs | 2 +- src/network/server.rs | 38 +++++++++++++++++++++++++++++++++-- 7 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/bin/client.rs b/src/bin/client.rs index 1ebdc0b..448fb69 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -3,9 +3,13 @@ use std::{ io::{self, Write}, }; -use checkers::network::client::Client; +use checkers::network::{ + client::{Client, ClientData}, + network_identity::NetworkIdentity, +}; use log::{debug, error, info, warn}; use tokio::sync::mpsc; +use uuid::Uuid; const QUIT_COMMAND: &str = "/quit"; const HELP_COMMAND: &str = "/help"; @@ -58,6 +62,9 @@ async fn main() -> Result<(), Box> { let (stdin_sender, mut stdin_receiver) = mpsc::channel(1024); + let mut indentity: Option = None; + let mut is_my_turn: bool = false; + tokio::task::spawn_blocking(move || { let stdin = std::io::stdin(); @@ -87,7 +94,7 @@ async fn main() -> Result<(), Box> { tokio::select! { msg = client_receiver.recv() => { match msg { - Some(checkers::network::client::ClientData::GameStart { + Some(ClientData::GameStart { identity, board_view, }) => { @@ -95,20 +102,35 @@ async fn main() -> Result<(), Box> { "Got new identity: {} and board_view. Game is about to start!", identity ); - println!("{}", board_view.to_string(identity.id)); + println!("{}", board_view.to_string(&identity.id)); + indentity = Some(identity); } - Some(checkers::network::client::ClientData::AvailableCaptures(capture_paths)) => { + Some(ClientData::BoardView(view)) => { + info!("New board state:\n{}", view.to_string(&indentity.as_ref().unwrap().id)); + + } + + Some(ClientData::CurrentTurn(current_turn)) => { + is_my_turn = current_turn == indentity.as_ref().expect("Server sent us new current turn without telling us who we are!").id; + if is_my_turn { + info!("It's our turn!"); + } else { + info!("It's the turn of our enemy!"); + } + } + + Some(ClientData::AvailableCaptures(capture_paths)) => { todo!() } - Some(checkers::network::client::ClientData::AvailableMoves(move_paths)) => todo!(), + Some(ClientData::AvailableMoves(move_paths)) => todo!(), - Some(checkers::network::client::ClientData::TextMessage(content)) => { + Some(ClientData::TextMessage(content)) => { info!("Got message from the server: {}", content); } - Some(checkers::network::client::ClientData::GameEnd(game_result)) => { + Some(ClientData::GameEnd(game_result)) => { info!("Game has ended with the result: {:?}", game_result); } diff --git a/src/logic/board/board_view.rs b/src/logic/board/board_view.rs index 86a092f..8e0e14b 100644 --- a/src/logic/board/board_view.rs +++ b/src/logic/board/board_view.rs @@ -32,21 +32,21 @@ impl Index for BoardView { } impl BoardView { - pub fn to_string(&self, owner: Uuid) -> String { + pub fn to_string(&self, owner: &Uuid) -> String { (0..self.size) .map(move |row| { (0..self.size) .map(move |column| match &self[row][column].pawn { Some(pawn) => match pawn.state { super::pawn::PawnState::Man => { - if pawn.owner == owner { + if pawn.owner == *owner { 'm' } else { 'M' } } super::pawn::PawnState::Dame => { - if pawn.owner == owner { + if pawn.owner == *owner { 'd' } else { 'D' diff --git a/src/logic/game_master.rs b/src/logic/game_master.rs index e2c07b1..bbcd997 100644 --- a/src/logic/game_master.rs +++ b/src/logic/game_master.rs @@ -74,6 +74,10 @@ impl GameMaster { BoardView::from(self.board.clone()) } + pub fn get_current_turn(&self) -> Uuid { + self.players.get_current_turn().id + } + pub fn get_current_player_captures(&self) -> Vec { self.board .get_player_pawns_positions(self.players.get_current_turn()) diff --git a/src/network/client.rs b/src/network/client.rs index b674a8c..361d597 100644 --- a/src/network/client.rs +++ b/src/network/client.rs @@ -2,6 +2,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use log::{debug, error, info, warn}; use tokio::sync::mpsc::{self, Receiver, Sender}; +use uuid::Uuid; use crate::{ logic::{ @@ -42,10 +43,15 @@ pub enum ClientData { identity: NetworkIdentity, board_view: BoardView, }, + GameEnd(GameResult), + AvailableCaptures(Vec), AvailableMoves(Vec), + + BoardView(BoardView), + CurrentTurn(Uuid), + TextMessage(String), - GameEnd(GameResult), } pub struct Client { @@ -169,8 +175,12 @@ impl Client { ServerMessage::AvailableCaptures { captures } => todo!(), ServerMessage::AvailableMoves { moves } => todo!(), - ServerMessage::BroadcastBoardState { board } => todo!(), - ServerMessage::BroadcastCurrentTurn { active_player } => todo!(), + ServerMessage::BroadcastBoardState { board } => { + let _ = update_sender.send(ClientData::BoardView(board)).await; + }, + ServerMessage::BroadcastCurrentTurn { active_player } => { + let _ = update_sender.send(ClientData::CurrentTurn(active_player)).await; + }, ServerMessage::GameEnd { result } => todo!(), } } else { diff --git a/src/network/message.rs b/src/network/message.rs index 4a76441..ff9dbf9 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -51,8 +51,6 @@ pub enum ServerMessage { board_view: BoardView, }, - BoardState(BoardView), - AvailableCaptures { captures: Vec, }, @@ -61,8 +59,9 @@ pub enum ServerMessage { }, BroadcastBoardState { - board: Board, + board: BoardView, }, + BroadcastCurrentTurn { active_player: Uuid, }, diff --git a/src/network/mod.rs b/src/network/mod.rs index 3c74f01..47fa7b1 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -1,5 +1,5 @@ pub mod client; pub mod connection; pub mod message; -mod network_identity; +pub mod network_identity; pub mod server; diff --git a/src/network/server.rs b/src/network/server.rs index 38151e4..4c31a09 100644 --- a/src/network/server.rs +++ b/src/network/server.rs @@ -257,6 +257,7 @@ impl Server { } self.game_master = Some(game_master); + self.on_player_action().await; } /* ======= Processing messages :D helper functions ======== */ @@ -290,20 +291,22 @@ impl Server { identity.identity.name = fixed_name } - fn process_capture(&mut self, capture_path: CapturePath) { + async fn process_capture(&mut self, capture_path: CapturePath) { if let Some(gm) = self.game_master.as_mut() { if gm.capture(&capture_path) { debug!("Capture valid!"); + self.on_player_action().await; } else { debug!("Invalid capture!"); } } } - fn process_move(&mut self, from: Position, to: Position) { + async fn process_move(&mut self, from: Position, to: Position) { if let Some(gm) = self.game_master.as_mut() { if gm.move_pawn(from, to) { debug!("Move was valid!"); + self.on_player_action().await; } else { debug!("Move was invalid!"); } @@ -330,6 +333,37 @@ impl Server { } } + /* === Sending messages === */ + + /// Wrapper around broadcast_current_turn and broadcast_board_view. Should be used after every + /// players move + async fn on_player_action(&mut self) { + self.broadcast_board_view().await; + self.broadcast_current_turn().await; + } + + async fn broadcast_board_view(&mut self) { + if let Some(gm) = &self.game_master { + let msg = Message::new(ServerMessage::BroadcastBoardState { + board: gm.get_board_view(), + }); + self.broadcast_message(msg).await; + } else { + error!("Cannot broadcast board view as game manager is not set!"); + } + } + + async fn broadcast_current_turn(&mut self) { + if let Some(gm) = &self.game_master { + let msg = Message::new(ServerMessage::BroadcastCurrentTurn { + active_player: gm.get_current_turn(), + }); + self.broadcast_message(msg).await; + } else { + error!("Cannot broadcast current turn as game master doesn't exists!!!!!!!!!"); + } + } + /* === Helper methods === */ async fn send_message(