From 4f024cae34493cdc6a70b8b3c7e412d2e12b7189 Mon Sep 17 00:00:00 2001 From: szymon Date: Mon, 17 Aug 2026 15:50:52 +0200 Subject: [PATCH] feat: add starting the game --- src/logic/board/board.rs | 38 ++++++++++++--------- src/logic/board/board_view.rs | 19 +++++++++++ src/logic/board/mod.rs | 1 + src/logic/game_master.rs | 21 ++++++++++-- src/logic/player.rs | 4 +-- src/network/client.rs | 39 +++++++++++++++++++--- src/network/message.rs | 45 ++++++++++++++++++------- src/network/network_identity.rs | 10 +++++- src/network/server.rs | 59 +++++++++++++++++++++++++++++---- 9 files changed, 190 insertions(+), 46 deletions(-) create mode 100644 src/logic/board/board_view.rs diff --git a/src/logic/board/board.rs b/src/logic/board/board.rs index 3db6114..b5eda57 100644 --- a/src/logic/board/board.rs +++ b/src/logic/board/board.rs @@ -72,6 +72,12 @@ impl Board { Board { board, size } } + /// Returns the clone of the board inner vec. Meant for `BoardView`. This vec shouldn't be + /// manipulated by hand + pub fn get_board(&self) -> Vec { + self.board.clone() + } + /// For testing fn new_empty(player1: &mut Player, player2: &mut Player, size: Option) -> Self { debug!("Creating new empty board!"); @@ -527,8 +533,8 @@ mod tests { fn new_board_test() { init_logger(); - let mut player1: Player = Player::new("Morbius".to_string()); - let mut player2: Player = Player::new("Milo".to_string()); + let mut player1: Player = Player::new("Morbius"); + let mut player2: Player = Player::new("Milo"); let test_board: Board = Board::new(&mut player1, &mut player2, None); let size: usize = test_board.size; @@ -575,8 +581,8 @@ mod tests { fn test_overloaded_operators() { init_logger(); - let mut player1: Player = Player::new("Morbius".to_string()); - let mut player2: Player = Player::new("Milo".to_string()); + let mut player1: Player = Player::new("Morbius"); + let mut player2: Player = Player::new("Milo"); let test_board: Board = Board::new(&mut player1, &mut player2, None); @@ -607,8 +613,8 @@ mod tests { fn test_moving_pawn() { init_logger(); - let mut player1: Player = Player::new("Morbius".to_string()); - let mut player2: Player = Player::new("Milo".to_string()); + let mut player1: Player = Player::new("Morbius"); + let mut player2: Player = Player::new("Milo"); let mut test_board: Board = Board::new(&mut player1, &mut player2, None); @@ -642,8 +648,8 @@ mod tests { fn test_getting_pawn_moves() { init_logger(); - let mut p1: Player = Player::new("Morbius".to_string()); - let mut p2: Player = Player::new("Milo".to_string()); + let mut p1: Player = Player::new("Morbius"); + let mut p2: Player = Player::new("Milo"); let mut board: Board = Board::new_empty(&mut p1, &mut p2, None); @@ -676,8 +682,8 @@ mod tests { fn test_getting_player_pawns_positions() { init_logger(); - let mut p1: Player = Player::new("Morbius".to_string()); - let mut p2: Player = Player::new("Milo".to_string()); + let mut p1: Player = Player::new("Morbius"); + let mut p2: Player = Player::new("Milo"); let test_board: Board = Board::new(&mut p1, &mut p2, None); let expected_positions: Vec = (0..test_board.size) @@ -707,8 +713,8 @@ mod tests { fn test_is_movable() { init_logger(); - let mut p1: Player = Player::new("Morbius".to_string()); - let mut p2: Player = Player::new("Milo".to_string()); + let mut p1: Player = Player::new("Morbius"); + let mut p2: Player = Player::new("Milo"); let test_board: Board = Board::new(&mut p1, &mut p2, None); assert!(test_board.is_pawn_movable(Position { row: 2, column: 1 }, &p1)); @@ -726,8 +732,8 @@ mod tests { fn test_getting_captures_path() { init_logger(); - let mut p1 = Player::new("Morbius".to_string()); - let mut p2 = Player::new("Milo".to_string()); + let mut p1 = Player::new("Morbius"); + let mut p2 = Player::new("Milo"); let mut board = Board::new_empty(&mut p1, &mut p2, None); @@ -758,8 +764,8 @@ mod tests { fn test_capturing() { init_logger(); - let mut p1 = Player::new("Morbius".to_string()); - let mut p2 = Player::new("Milo".to_string()); + let mut p1 = Player::new("Morbius"); + let mut p2 = Player::new("Milo"); let mut board = Board::new_empty(&mut p1, &mut p2, None); diff --git a/src/logic/board/board_view.rs b/src/logic/board/board_view.rs new file mode 100644 index 0000000..6b61ea6 --- /dev/null +++ b/src/logic/board/board_view.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +use crate::logic::board::{board::Board, field::Field, pawn::Pawn}; + +/// View of the board, without any methods. Meant for sending over network +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct BoardView { + pub board: Vec, + pub size: usize, +} + +impl From for BoardView { + fn from(value: Board) -> Self { + Self { + board: value.get_board(), + size: value.size, + } + } +} diff --git a/src/logic/board/mod.rs b/src/logic/board/mod.rs index 079d7f1..81d9a95 100644 --- a/src/logic/board/mod.rs +++ b/src/logic/board/mod.rs @@ -1,3 +1,4 @@ pub mod board; +pub mod board_view; mod field; pub mod pawn; diff --git a/src/logic/game_master.rs b/src/logic/game_master.rs index d480c6c..e2c07b1 100644 --- a/src/logic/game_master.rs +++ b/src/logic/game_master.rs @@ -1,9 +1,11 @@ use log::{info, warn}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; use crate::logic::{ board::{ board::Board, + board_view::BoardView, pawn::{CapturePath, MovePath}, }, math::position::Position, @@ -23,7 +25,7 @@ struct PlayerList { } impl PlayerList { - pub fn new(player1_name: String, player2_name: String) -> Self { + pub fn new(player1_name: &str, player2_name: &str) -> Self { PlayerList { players: [Player::new(player1_name), Player::new(player2_name)], current_turn: 0, @@ -50,7 +52,7 @@ pub struct GameMaster { } impl GameMaster { - pub fn new(player1_name: String, player2_name: String) -> Self { + pub fn new(player1_name: &str, player2_name: &str) -> Self { let mut players: PlayerList = PlayerList::new(player1_name, player2_name); let [player1, player2] = &mut players.players; @@ -59,6 +61,19 @@ impl GameMaster { GameMaster { players, board } } + pub fn get_players_names_and_ids(&self) -> Vec<(String, Uuid)> { + let mut result: Vec<(String, Uuid)> = vec![]; + for player in &self.players.players { + result.push((player.name.clone(), player.id)); + } + + result + } + + pub fn get_board_view(&self) -> BoardView { + BoardView::from(self.board.clone()) + } + pub fn get_current_player_captures(&self) -> Vec { self.board .get_player_pawns_positions(self.players.get_current_turn()) @@ -167,7 +182,7 @@ mod tests { use rand::seq::IndexedRandom; fn run_game() { - let mut master: GameMaster = GameMaster::new("Morbius".to_string(), "Milo".to_string()); + let mut master: GameMaster = GameMaster::new("Morbius", "Milo"); let mut turns = 0; let max_turns = 2000; diff --git a/src/logic/player.rs b/src/logic/player.rs index 6036a0f..307ef54 100644 --- a/src/logic/player.rs +++ b/src/logic/player.rs @@ -12,9 +12,9 @@ pub struct Player { } impl Player { - pub fn new(name: String) -> Self { + pub fn new(name: &str) -> Self { Player { - name, + name: name.to_string(), id: Uuid::new_v4(), end_row: None, vertical_direction: None, diff --git a/src/network/client.rs b/src/network/client.rs index 3667570..c40c580 100644 --- a/src/network/client.rs +++ b/src/network/client.rs @@ -3,11 +3,15 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use log::{debug, error, info}; use tokio::sync::mpsc::{self, Receiver, Sender}; -use crate::network::{ - connection::{Connection, ConnectionType}, - message::{ - ClientMessage::{self, SignalReadiness, TextMessage}, - Message, ServerMessage, +use crate::{ + logic::board::board_view::BoardView, + network::{ + connection::{Connection, ConnectionType}, + message::{ + ClientMessage::{self, SignalReadiness, TextMessage}, + Message, ServerMessage, + }, + network_identity::NetworkIdentity, }, }; @@ -21,6 +25,10 @@ pub struct Client { conn_reciever_incoming: Option)>>, conn_sender_outgoing: Sender>, settings: ClientSettings, + + identity: Option, + board_view: Option, + update_reciever: Option>>, } impl Client { @@ -51,6 +59,9 @@ impl Client { conn_reciever_incoming: Some(conn_reciever), conn_sender_outgoing, settings, + identity: None, + board_view: None, + update_reciever: None, }) } @@ -64,6 +75,9 @@ impl Client { let settings = self.settings.clone(); + let (update_sender, update_reciever) = mpsc::channel::>(1024); + self.update_reciever = Some(update_reciever); + tokio::spawn(async move { loop { if let Some(oldest_msg) = conn_reciever_incoming.recv().await { @@ -79,13 +93,28 @@ impl Client { let _ = sender.send(msg.unwrap()).await; } + ServerMessage::AcceptHandshake => { info!("Server accepted us! Yay :D"); } + ServerMessage::DeclineHandshake { reason } => { error!("Server declined connection: {}", reason); break; } + + ServerMessage::GameStart { + identity, + board_view, + } => { + info!("Server started the game!"); + info!("Our identity is: {}", identity); + /* + self.board_view = Some(board_view); + self.identity = Some(identity); + */ + } + ServerMessage::AvailableCaptures { captures } => todo!(), ServerMessage::AvailableMoves { moves } => todo!(), ServerMessage::BroadcastBoardState { board } => todo!(), diff --git a/src/network/message.rs b/src/network/message.rs index 9d23ae1..19cfe19 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -3,13 +3,17 @@ use std::net::SocketAddr; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use uuid::Uuid; -use crate::logic::{ - board::{ - board::Board, - pawn::{CapturePath, MovePath}, +use crate::{ + logic::{ + board::{ + board::Board, + board_view::BoardView, + pawn::{CapturePath, MovePath}, + }, + game_master::GameResult, + math::position::Position, }, - game_master::GameResult, - math::position::Position, + network::network_identity::NetworkIdentity, }; use postcard::{from_bytes, to_allocvec}; @@ -38,15 +42,32 @@ where pub enum ServerMessage { RequestHandshake, AcceptHandshake, - DeclineHandshake { reason: String }, + DeclineHandshake { + reason: String, + }, - AvailableCaptures { captures: Vec }, - AvailableMoves { moves: Vec }, + GameStart { + identity: NetworkIdentity, + board_view: BoardView, + }, - BroadcastBoardState { board: Board }, - BroadcastCurrentTurn { active_player: Uuid }, + AvailableCaptures { + captures: Vec, + }, + AvailableMoves { + moves: Vec, + }, - GameEnd { result: GameResult }, + BroadcastBoardState { + board: Board, + }, + BroadcastCurrentTurn { + active_player: Uuid, + }, + + GameEnd { + result: GameResult, + }, } #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] diff --git a/src/network/network_identity.rs b/src/network/network_identity.rs index 4753e00..02ace06 100644 --- a/src/network/network_identity.rs +++ b/src/network/network_identity.rs @@ -1,15 +1,23 @@ +use std::fmt::Display; + use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::Sender; use crate::network::message::{Message, ServerMessage}; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)] pub struct NetworkIdentity { pub name: String, pub id: uuid::Uuid, pub is_ready: bool, } +impl Display for NetworkIdentity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(Name: {}, id: {})", self.name, self.id) + } +} + impl Default for NetworkIdentity { fn default() -> Self { Self { diff --git a/src/network/server.rs b/src/network/server.rs index cf106b0..cb5d942 100644 --- a/src/network/server.rs +++ b/src/network/server.rs @@ -7,9 +7,14 @@ use tokio::{ net::TcpListener, sync::mpsc::{Receiver, Sender}, }; +use uuid::Uuid; use crate::{ - logic::{board::pawn::CapturePath, game_master::GameMaster, math::position::Position}, + logic::{ + board::{board_view::BoardView, pawn::CapturePath}, + game_master::GameMaster, + math::position::Position, + }, network::{ connection::{Connection, ConnectionType}, message::{ @@ -83,7 +88,7 @@ impl Server { .expect("Could not start the server"), ), // TODO! Handle this nicely, as this will // crash gui app - connections: HashMap::new(), + connections: HashMap::with_capacity(2), sender, reciever, welcoming_reciever: None, @@ -186,7 +191,7 @@ impl Server { }, ClientMessage::SignalReadiness => { - self.handle_readiness(addr) + self.handle_readiness(addr).await }, ClientMessage::RequestCapture { capture_path } => { @@ -210,8 +215,48 @@ impl Server { } } - fn start_game(&self) { - todo!(); + async fn start_game(&mut self) { + info!("Starting the game!"); + let mut names: Vec = vec![]; + for networkidentity in self.connections.values() { + if networkidentity.identity.is_ready { + names.push(networkidentity.identity.name.clone()); + } + } + + if names.len() != self.settings.max_connections { + error!("Tried starting the game, but could not gather enough ready player names"); + return; + } + + let game_master = GameMaster::new(&names[0], &names[1]); + + let names_and_ids: Vec<(String, Uuid)> = game_master.get_players_names_and_ids(); + let board_view: BoardView = game_master.get_board_view(); + + let mut msgs: Vec<(SocketAddr, Result, postcard::Error>)> = vec![]; + + for (name, id) in names_and_ids { + for (addr, networkidentity) in self.connections.iter_mut() { + if networkidentity.identity.name == name { + networkidentity.identity.id = id; + + msgs.push(( + *addr, + Message::new(ServerMessage::GameStart { + identity: networkidentity.identity.clone(), + board_view: board_view.clone(), + }), + )); + } + } + } + + for (addr, msg) in msgs { + self.send_message(addr, msg).await; + } + + self.game_master = Some(game_master); } /* ======= Processing messages :D helper functions ======== */ @@ -293,7 +338,7 @@ impl Server { }; } - fn handle_readiness(&mut self, addr: SocketAddr) { + async fn handle_readiness(&mut self, addr: SocketAddr) { self.state.ready_count += 1; info!( "{} signaled readiness. Currently ready: {}/{}", @@ -304,7 +349,7 @@ impl Server { if self.state.ready_count == self.settings.max_connections { info!("All players are ready! Changing state to ServerStage::Game"); self.state.stage = ServerStage::Game; - self.start_game(); + self.start_game().await; } }