feat: add broadcasting current turn and board view

This commit is contained in:
2026-08-17 19:39:46 +02:00
parent a096c990a9
commit a8a6e11350
7 changed files with 88 additions and 19 deletions

View File

@@ -3,9 +3,13 @@ use std::{
io::{self, Write}, io::{self, Write},
}; };
use checkers::network::client::Client; use checkers::network::{
client::{Client, ClientData},
network_identity::NetworkIdentity,
};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use uuid::Uuid;
const QUIT_COMMAND: &str = "/quit"; const QUIT_COMMAND: &str = "/quit";
const HELP_COMMAND: &str = "/help"; const HELP_COMMAND: &str = "/help";
@@ -58,6 +62,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (stdin_sender, mut stdin_receiver) = mpsc::channel(1024); let (stdin_sender, mut stdin_receiver) = mpsc::channel(1024);
let mut indentity: Option<NetworkIdentity> = None;
let mut is_my_turn: bool = false;
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let stdin = std::io::stdin(); let stdin = std::io::stdin();
@@ -87,7 +94,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tokio::select! { tokio::select! {
msg = client_receiver.recv() => { msg = client_receiver.recv() => {
match msg { match msg {
Some(checkers::network::client::ClientData::GameStart { Some(ClientData::GameStart {
identity, identity,
board_view, board_view,
}) => { }) => {
@@ -95,20 +102,35 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
"Got new identity: {} and board_view. Game is about to start!", "Got new identity: {} and board_view. Game is about to start!",
identity 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!() 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); 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); info!("Game has ended with the result: {:?}", game_result);
} }

View File

@@ -32,21 +32,21 @@ impl Index<usize> for BoardView {
} }
impl BoardView { impl BoardView {
pub fn to_string(&self, owner: Uuid) -> String { pub fn to_string(&self, owner: &Uuid) -> String {
(0..self.size) (0..self.size)
.map(move |row| { .map(move |row| {
(0..self.size) (0..self.size)
.map(move |column| match &self[row][column].pawn { .map(move |column| match &self[row][column].pawn {
Some(pawn) => match pawn.state { Some(pawn) => match pawn.state {
super::pawn::PawnState::Man => { super::pawn::PawnState::Man => {
if pawn.owner == owner { if pawn.owner == *owner {
'm' 'm'
} else { } else {
'M' 'M'
} }
} }
super::pawn::PawnState::Dame => { super::pawn::PawnState::Dame => {
if pawn.owner == owner { if pawn.owner == *owner {
'd' 'd'
} else { } else {
'D' 'D'

View File

@@ -74,6 +74,10 @@ impl GameMaster {
BoardView::from(self.board.clone()) 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<CapturePath> { pub fn get_current_player_captures(&self) -> Vec<CapturePath> {
self.board self.board
.get_player_pawns_positions(self.players.get_current_turn()) .get_player_pawns_positions(self.players.get_current_turn())

View File

@@ -2,6 +2,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
use uuid::Uuid;
use crate::{ use crate::{
logic::{ logic::{
@@ -42,10 +43,15 @@ pub enum ClientData {
identity: NetworkIdentity, identity: NetworkIdentity,
board_view: BoardView, board_view: BoardView,
}, },
GameEnd(GameResult),
AvailableCaptures(Vec<CapturePath>), AvailableCaptures(Vec<CapturePath>),
AvailableMoves(Vec<MovePath>), AvailableMoves(Vec<MovePath>),
BoardView(BoardView),
CurrentTurn(Uuid),
TextMessage(String), TextMessage(String),
GameEnd(GameResult),
} }
pub struct Client { pub struct Client {
@@ -169,8 +175,12 @@ impl Client {
ServerMessage::AvailableCaptures { captures } => todo!(), ServerMessage::AvailableCaptures { captures } => todo!(),
ServerMessage::AvailableMoves { moves } => todo!(), ServerMessage::AvailableMoves { moves } => todo!(),
ServerMessage::BroadcastBoardState { board } => todo!(), ServerMessage::BroadcastBoardState { board } => {
ServerMessage::BroadcastCurrentTurn { active_player } => todo!(), 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!(), ServerMessage::GameEnd { result } => todo!(),
} }
} else { } else {

View File

@@ -51,8 +51,6 @@ pub enum ServerMessage {
board_view: BoardView, board_view: BoardView,
}, },
BoardState(BoardView),
AvailableCaptures { AvailableCaptures {
captures: Vec<CapturePath>, captures: Vec<CapturePath>,
}, },
@@ -61,8 +59,9 @@ pub enum ServerMessage {
}, },
BroadcastBoardState { BroadcastBoardState {
board: Board, board: BoardView,
}, },
BroadcastCurrentTurn { BroadcastCurrentTurn {
active_player: Uuid, active_player: Uuid,
}, },

View File

@@ -1,5 +1,5 @@
pub mod client; pub mod client;
pub mod connection; pub mod connection;
pub mod message; pub mod message;
mod network_identity; pub mod network_identity;
pub mod server; pub mod server;

View File

@@ -257,6 +257,7 @@ impl Server {
} }
self.game_master = Some(game_master); self.game_master = Some(game_master);
self.on_player_action().await;
} }
/* ======= Processing messages :D helper functions ======== */ /* ======= Processing messages :D helper functions ======== */
@@ -290,20 +291,22 @@ impl Server {
identity.identity.name = fixed_name 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 let Some(gm) = self.game_master.as_mut() {
if gm.capture(&capture_path) { if gm.capture(&capture_path) {
debug!("Capture valid!"); debug!("Capture valid!");
self.on_player_action().await;
} else { } else {
debug!("Invalid capture!"); 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 let Some(gm) = self.game_master.as_mut() {
if gm.move_pawn(from, to) { if gm.move_pawn(from, to) {
debug!("Move was valid!"); debug!("Move was valid!");
self.on_player_action().await;
} else { } else {
debug!("Move was invalid!"); 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 === */ /* === Helper methods === */
async fn send_message( async fn send_message(