Add readiness

This commit is contained in:
Sofia 2026-07-17 21:36:29 +03:00
parent b01ecbfe13
commit c594af3bd1
6 changed files with 139 additions and 14 deletions

View File

@ -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"

View File

@ -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)"

View File

@ -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<Player>) {
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,
}

View File

@ -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<u16, u128>),
NewPlayer(Player),
PlayerLeft(Player),
SetReady(u16, bool),
}
pub enum PeerKind {
Client(Peer<Package>, SocketAddr),
Server(Peer<Package>, u16),
}
impl PeerKind {
pub fn is_host(&self) -> bool {
match self {
PeerKind::Client(_, _) => false,
PeerKind::Server(_, _) => true,
}
}
}

View File

@ -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<Gd<TextEdit>>,
#[export]
player_listing_prefab: Option<Gd<PackedScene>>,
#[export]
ready_button: Option<Gd<Button>>,
is_ready: bool,
base: Base<Panel>,
}
@ -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!");
}
}
}
}
}

View File

@ -15,6 +15,8 @@ pub struct PlayerListing {
pub name_label: Option<Gd<Label>>,
#[export]
pub ping_label: Option<Gd<Label>>,
#[export]
pub ready_label: Option<Gd<Label>>,
pub player_id: Option<u16>,
@ -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));
}