From c594af3bd15111bc3327a03e53e7b10173c446d6 Mon Sep 17 00:00:00 2001 From: Sofia Date: Fri, 17 Jul 2026 21:36:29 +0300 Subject: [PATCH] Add readiness --- godot/scenes/lobby.tscn | 11 ++++- godot/scenes/player_listing.tscn | 13 ++++-- rust/src/game_manager.rs | 15 +++++-- rust/src/net/network_manager.rs | 32 ++++++++++++++ rust/src/ui/lobby.rs | 72 ++++++++++++++++++++++++++++---- rust/src/ui/player_listing.rs | 10 +++++ 6 files changed, 139 insertions(+), 14 deletions(-) diff --git a/godot/scenes/lobby.tscn b/godot/scenes/lobby.tscn index 720b3d8..62e5e34 100644 --- a/godot/scenes/lobby.tscn +++ b/godot/scenes/lobby.tscn @@ -5,10 +5,11 @@ [sub_resource type="LabelSettings" id="LabelSettings_1cgct"] font_size = 32 -[node name="lobby" type="LobbyPanel" unique_id=915192272 node_paths=PackedStringArray("players_list", "name_field")] +[node name="lobby" type="LobbyPanel" unique_id=915192272 node_paths=PackedStringArray("players_list", "name_field", "ready_button")] players_list = NodePath("v_box_container/players_list") name_field = NodePath("v_box_container/h_box_container/text_edit") player_listing_prefab = ExtResource("1_o1atq") +ready_button = NodePath("ready_button") anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -64,3 +65,11 @@ offset_top = -144.0 grow_horizontal = 2 grow_vertical = 0 vertical_alignment = 2 + +[node name="ready_button" type="Button" parent="." unique_id=311553419] +layout_mode = 0 +offset_left = 129.0 +offset_top = 504.0 +offset_right = 278.0 +offset_bottom = 555.0 +text = "Not Ready" diff --git a/godot/scenes/player_listing.tscn b/godot/scenes/player_listing.tscn index 7fedbfe..725b78b 100644 --- a/godot/scenes/player_listing.tscn +++ b/godot/scenes/player_listing.tscn @@ -1,11 +1,12 @@ [gd_scene format=3 uid="uid://ccd0wj2usx5os"] -[node name="player_listing" type="PlayerListing" unique_id=772938280 node_paths=PackedStringArray("name_label", "ping_label")] +[node name="player_listing" type="PlayerListing" unique_id=772938280 node_paths=PackedStringArray("name_label", "ping_label", "ready_label")] name_label = NodePath("name") ping_label = NodePath("ping") -offset_right = 265.0 +ready_label = NodePath("ready") +offset_right = 319.0 offset_bottom = 23.0 -columns = 2 +columns = 3 [node name="name" type="Label" parent="." unique_id=635700663] custom_minimum_size = Vector2(200, 0) @@ -13,6 +14,12 @@ custom_maximum_size = Vector2(200, -1) layout_mode = 2 text = "Name" +[node name="ready" type="Label" parent="." unique_id=1064828735] +custom_minimum_size = Vector2(100, 0) +custom_maximum_size = Vector2(100, -1) +layout_mode = 2 +text = "Ready" + [node name="ping" type="Label" parent="." unique_id=1935412601] layout_mode = 2 text = "(200ms)" diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index 279d1d7..aebba08 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -95,15 +95,16 @@ impl Game { connection_addr: addr, name, ping: 0, + ready: false, }); - self.signals().on_new_player().emit(id); + self.run_deferred(move |s| s.signals().on_new_player().emit(id)); id } pub fn set_players(&mut self, players: Vec) { self.players = players; for player in self.players.clone() { - self.signals().on_new_player().emit(player.id); + self.run_deferred(move |s| s.signals().on_new_player().emit(player.id)); } } @@ -117,7 +118,7 @@ impl Game { pub fn remove_player(&mut self, id: u16) { self.players.retain(|p| p.id != id); - self.signals().on_player_disconnected().emit(id); + self.run_deferred(move |s| s.signals().on_player_disconnected().emit(id)); } pub fn update_player_nick(&mut self, player_id: u16, nick: String) { @@ -133,6 +134,13 @@ impl Game { } self.run_deferred(|s| s.signals().on_players_updated().emit()); } + + pub fn update_player_ready(&mut self, player_id: u16, ready: bool) { + if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) { + player.ready = ready; + } + self.run_deferred(|s| s.signals().on_players_updated().emit()); + } } #[derive(Clone, Deserialize, Serialize, Debug)] @@ -141,4 +149,5 @@ pub struct Player { pub connection_addr: SocketAddr, pub name: String, pub ping: u128, + pub ready: bool, } diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index 51b401c..5ab69a3 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -190,6 +190,12 @@ impl INode for NetworkManager { } } } + Package::SetReady(player_id, is_ready) => { + if let Some(game) = &mut Game::singleton() { + game.bind_mut() + .update_player_ready(player_id, is_ready); + } + } _ => {} }, } @@ -278,6 +284,21 @@ impl INode for NetworkManager { } } } + Package::SetReady(_, is_ready) => { + if let Some(game) = &mut Game::singleton() { + let player = game + .bind() + .find_player_by_addr(&conn.address) + .cloned(); + if let Some(player) = player { + game.bind_mut() + .update_player_ready(player.id, is_ready); + peer.broadcast_reliable(Package::SetReady( + player.id, is_ready, + )); + } + } + } _ => {} }, } @@ -319,6 +340,7 @@ impl NetworkManager { "Host".to_owned(), None, ); + game.bind_mut().update_player_ready(id, true); game.bind_mut().set_self(id); } } @@ -393,9 +415,19 @@ pub enum Package { UpdatePings(HashMap), NewPlayer(Player), PlayerLeft(Player), + SetReady(u16, bool), } pub enum PeerKind { Client(Peer, SocketAddr), Server(Peer, u16), } + +impl PeerKind { + pub fn is_host(&self) -> bool { + match self { + PeerKind::Client(_, _) => false, + PeerKind::Server(_, _) => true, + } + } +} diff --git a/rust/src/ui/lobby.rs b/rust/src/ui/lobby.rs index 27fdef2..5dd7e02 100644 --- a/rust/src/ui/lobby.rs +++ b/rust/src/ui/lobby.rs @@ -1,11 +1,11 @@ use godot::{ - classes::{IPanel, Label, Panel, TextEdit, VBoxContainer}, + classes::{Button, IPanel, Label, Panel, TextEdit, VBoxContainer}, prelude::*, }; use crate::{ game_manager::{Game, GameManager}, - net::network_manager::NetworkManager, + net::network_manager::{NetworkManager, Package, PeerKind}, ui::player_listing::PlayerListing, }; @@ -18,6 +18,10 @@ pub struct LobbyPanel { name_field: Option>, #[export] player_listing_prefab: Option>, + #[export] + ready_button: Option>, + + is_ready: bool, base: Base, } @@ -28,15 +32,22 @@ impl IPanel for LobbyPanel { GameManager::singleton() .signals() .on_new_player() - .connect_other(self, |s, _| s.update_players_list(true)); + .connect_other(self, |s, _| s.update_players(true)); GameManager::singleton() .signals() .on_player_disconnected() - .connect_other(self, |s, _| s.update_players_list(true)); + .connect_other(self, |s, _| s.update_players(true)); GameManager::singleton() .signals() .on_players_updated() - .connect_other(self, |s| s.update_players_list(false)); + .connect_other(self, |s| s.update_players(false)); + + if let Some(ready_button) = &self.ready_button { + ready_button + .signals() + .pressed() + .connect_other(self, |s| s.on_ready()); + } if let Some(name_field) = &self.name_field { name_field @@ -61,12 +72,37 @@ impl IPanel for LobbyPanel { }); } - self.update_players_list(true); + self.update_players(true); + + if let Some(peer) = &NetworkManager::singleton().bind().peer + && peer.is_host() + { + if let Some(button) = &mut self.ready_button { + button.set_text("Start Game"); + button.set_disabled(true); + } + } + self.update_ready_button(); } } impl LobbyPanel { - pub fn update_players_list(&mut self, replace: bool) { + fn update_ready_button(&mut self) { + if let Some(game) = Game::singleton() + && let Some(peer) = &NetworkManager::singleton().bind().peer + { + if let PeerKind::Server(_, _) = peer + && let Some(button) = &mut self.ready_button + { + let all_ready = game.bind().players.iter().all(|p| p.ready); + button.set_disabled(!all_ready); + } + } + } + + pub fn update_players(&mut self, replace: bool) { + self.update_ready_button(); + if let Some(list) = &mut self.players_list { if replace { for child in list.get_children().iter_shared() { @@ -91,4 +127,26 @@ impl LobbyPanel { } } } + + pub fn on_ready(&mut self) { + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { + match peer { + PeerKind::Client(peer, socket_addr) => { + self.is_ready = !self.is_ready; + if let Some(button) = &mut self.ready_button { + if self.is_ready { + button.set_text("Ready"); + } else { + button.set_text("Not Ready"); + } + } + + peer.send_reliable(socket_addr, Package::SetReady(0, self.is_ready)); + } + PeerKind::Server(_, _) => { + godot_print!("Start game!"); + } + } + } + } } diff --git a/rust/src/ui/player_listing.rs b/rust/src/ui/player_listing.rs index c80de25..a33428d 100644 --- a/rust/src/ui/player_listing.rs +++ b/rust/src/ui/player_listing.rs @@ -15,6 +15,8 @@ pub struct PlayerListing { pub name_label: Option>, #[export] pub ping_label: Option>, + #[export] + pub ready_label: Option>, pub player_id: Option, @@ -31,6 +33,14 @@ impl PlayerListing { player_label.set_text(&format!("{} ({})", player.name, player.connection_addr)); } + if let Some(ready_label) = &mut self.ready_label { + if player.ready { + ready_label.set_text("Ready"); + } else { + ready_label.set_text("Not Ready"); + } + } + if let Some(ping_label) = &mut self.ping_label { ping_label.set_text(&format!("({}ms)", player.ping)); }