From 4c588248400834f4db3511b88a24d44a0f071a02 Mon Sep 17 00:00:00 2001 From: Sofia Date: Fri, 17 Jul 2026 19:59:06 +0300 Subject: [PATCH] Add more proper player listings --- godot/scenes/lobby.tscn | 42 +++++++++++++++++--------------- godot/scenes/player_listing.tscn | 19 +++++++++++++++ rust/src/game_manager.rs | 17 ++++++++++++- rust/src/net/network_manager.rs | 36 ++++++++++++++++++++++++++- rust/src/ui/cli.rs | 1 - rust/src/ui/lobby.rs | 40 ++++++++++++++++++++---------- rust/src/ui/mod.rs | 1 + rust/src/ui/player_listing.rs | 39 +++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 36 deletions(-) create mode 100644 godot/scenes/player_listing.tscn create mode 100644 rust/src/ui/player_listing.rs diff --git a/godot/scenes/lobby.tscn b/godot/scenes/lobby.tscn index c12a7d5..b451453 100644 --- a/godot/scenes/lobby.tscn +++ b/godot/scenes/lobby.tscn @@ -1,11 +1,14 @@ [gd_scene format=3 uid="uid://bwd6bj21x310s"] +[ext_resource type="PackedScene" uid="uid://ccd0wj2usx5os" path="res://scenes/player_listing.tscn" id="1_o1atq"] + [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")] players_list = NodePath("v_box_container/players_list") -name_field = NodePath("h_box_container/text_edit") +name_field = NodePath("v_box_container/h_box_container/text_edit") +player_listing_prefab = ExtResource("1_o1atq") anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -25,29 +28,28 @@ horizontal_alignment = 1 [node name="v_box_container" type="VBoxContainer" parent="." unique_id=336946640] layout_mode = 0 -offset_left = 178.0 -offset_top = 189.0 -offset_right = 239.0 -offset_bottom = 229.0 +offset_left = 129.0 +offset_top = 146.0 +offset_right = 334.0 +offset_bottom = 200.0 + +[node name="h_box_container" type="HBoxContainer" parent="v_box_container" unique_id=1789302658] +layout_mode = 2 +metadata/_edit_lock_ = true + +[node name="label" type="Label" parent="v_box_container/h_box_container" unique_id=626633696] +layout_mode = 2 +text = "Name:" + +[node name="text_edit" type="TextEdit" parent="v_box_container/h_box_container" unique_id=683714912] +custom_minimum_size = Vector2(150, 0) +layout_mode = 2 [node name="players_label" type="Label" parent="v_box_container" unique_id=1537542117] layout_mode = 2 text = "Players:" +metadata/_edit_lock_ = true [node name="players_list" type="VBoxContainer" parent="v_box_container" unique_id=1834737379] layout_mode = 2 - -[node name="h_box_container" type="HBoxContainer" parent="." unique_id=1789302658] -layout_mode = 0 -offset_left = 221.0 -offset_top = 512.0 -offset_right = 261.0 -offset_bottom = 552.0 - -[node name="label" type="Label" parent="h_box_container" unique_id=626633696] -layout_mode = 2 -text = "Name:" - -[node name="text_edit" type="TextEdit" parent="h_box_container" unique_id=683714912] -custom_minimum_size = Vector2(150, 0) -layout_mode = 2 +metadata/_edit_lock_ = true diff --git a/godot/scenes/player_listing.tscn b/godot/scenes/player_listing.tscn new file mode 100644 index 0000000..7fedbfe --- /dev/null +++ b/godot/scenes/player_listing.tscn @@ -0,0 +1,19 @@ +[gd_scene format=3 uid="uid://ccd0wj2usx5os"] + +[node name="player_listing" type="PlayerListing" unique_id=772938280 node_paths=PackedStringArray("name_label", "ping_label")] +name_label = NodePath("name") +ping_label = NodePath("ping") +offset_right = 265.0 +offset_bottom = 23.0 +columns = 2 + +[node name="name" type="Label" parent="." unique_id=635700663] +custom_minimum_size = Vector2(200, 0) +custom_maximum_size = Vector2(200, -1) +layout_mode = 2 +text = "Name" + +[node name="ping" type="Label" parent="." unique_id=1935412601] +layout_mode = 2 +text = "(200ms)" +horizontal_alignment = 2 diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index fbc1e11..a67b370 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -89,6 +89,7 @@ impl Game { id, connection_addr: addr, name, + ping: 0, }); self.signals().on_new_player().emit(id); id @@ -96,13 +97,19 @@ impl Game { pub fn set_players(&mut self, players: Vec) { self.players = players; - self.signals().on_players_updated().emit(); + for player in self.players.clone() { + self.signals().on_new_player().emit(player.id); + } } pub fn find_player_by_addr(&self, addr: &SocketAddr) -> Option<&Player> { self.players.iter().find(|p| p.connection_addr == *addr) } + pub fn get_player(&self, player_id: u16) -> Option<&Player> { + self.players.iter().find(|p| p.id == player_id) + } + pub fn remove_player(&mut self, id: u16) { self.signals().on_player_disconnected().emit(id); self.players.retain(|p| p.id != id); @@ -114,6 +121,13 @@ impl Game { } self.run_deferred(|s| s.signals().on_players_updated().emit()); } + + pub fn update_player_ping(&mut self, player_id: u16, ping: u128) { + if let Some(player) = self.players.iter_mut().find(|p| p.id == player_id) { + player.ping = ping; + } + self.run_deferred(|s| s.signals().on_players_updated().emit()); + } } #[derive(Clone, Deserialize, Serialize, Debug)] @@ -121,4 +135,5 @@ pub struct Player { pub id: u16, pub connection_addr: SocketAddr, pub name: String, + pub ping: u128, } diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index 8fa43ff..05c564d 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -1,11 +1,12 @@ use std::{ + collections::HashMap, net::SocketAddr, time::{Duration, Instant}, }; use godot::{prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; -use teanet::{Peer, PeerConfig, PeerMessage}; +use teanet::{Peer, PeerConfig, PeerMessage, connections::Connection}; use crate::{ game_manager::{Game, GameManager, Player}, @@ -23,6 +24,7 @@ pub struct NetworkManager { last_hello: Instant, last_netstat_update: Instant, + last_player_update: Instant, base: Base, } @@ -39,6 +41,7 @@ impl INode for NetworkManager { last_hello: Instant::now(), last_netstat_update: Instant::now(), + last_player_update: Instant::now(), base, } @@ -74,6 +77,29 @@ impl INode for NetworkManager { self.last_netstat_update = now; } + if now - self.last_player_update > Duration::from_millis(500) { + self.last_player_update = now; + if let PeerKind::Server(peer, _) = peer { + let mut pings = HashMap::new(); + + for connection in peer.connections() { + if let Some(mut game) = Game::singleton() { + let player = game + .bind() + .find_player_by_addr(&connection.address) + .cloned(); + if let Some(player) = player { + game.bind_mut() + .update_player_ping(player.id, connection.ping.as_millis()); + pings.insert(player.id, connection.ping.as_millis()); + } + } + } + + peer.broadcast_reliable(Package::UpdatePings(pings)); + } + } + match peer { PeerKind::Client(..) => {} PeerKind::Server(peer, _) => { @@ -137,6 +163,13 @@ impl INode for NetworkManager { } } } + Package::UpdatePings(pings) => { + if let Some(game) = &mut Game::singleton() { + for (player_id, ping) in pings { + game.bind_mut().update_player_ping(player_id, ping); + } + } + } _ => {} }, } @@ -320,6 +353,7 @@ pub enum Package { Players(Vec, u16), SetNick(u16, String), SetSelfNick(String), + UpdatePings(HashMap), } pub enum PeerKind { diff --git a/rust/src/ui/cli.rs b/rust/src/ui/cli.rs index 4a688c1..7a59aad 100644 --- a/rust/src/ui/cli.rs +++ b/rust/src/ui/cli.rs @@ -108,7 +108,6 @@ impl IPanel for CommandLinePanel { fn input(&mut self, event: Gd) { if event.is_action_pressed("toggle_cli") { - godot_print!("hello?"); self.opened = !self.opened; if let Some(input_field) = &mut self.input_field { if self.opened { diff --git a/rust/src/ui/lobby.rs b/rust/src/ui/lobby.rs index d60ddd6..27fdef2 100644 --- a/rust/src/ui/lobby.rs +++ b/rust/src/ui/lobby.rs @@ -6,6 +6,7 @@ use godot::{ use crate::{ game_manager::{Game, GameManager}, net::network_manager::NetworkManager, + ui::player_listing::PlayerListing, }; #[derive(GodotClass)] @@ -15,6 +16,8 @@ pub struct LobbyPanel { players_list: Option>, #[export] name_field: Option>, + #[export] + player_listing_prefab: Option>, base: Base, } @@ -25,15 +28,15 @@ impl IPanel for LobbyPanel { GameManager::singleton() .signals() .on_new_player() - .connect_other(self, |s, _| s.update_players_list()); + .connect_other(self, |s, _| s.update_players_list(true)); GameManager::singleton() .signals() .on_player_disconnected() - .connect_other(self, |s, _| s.update_players_list()); + .connect_other(self, |s, _| s.update_players_list(true)); GameManager::singleton() .signals() .on_players_updated() - .connect_other(self, |s| s.update_players_list()); + .connect_other(self, |s| s.update_players_list(false)); if let Some(name_field) = &self.name_field { name_field @@ -58,21 +61,32 @@ impl IPanel for LobbyPanel { }); } - self.update_players_list(); + self.update_players_list(true); } } impl LobbyPanel { - pub fn update_players_list(&mut self) { + pub fn update_players_list(&mut self, replace: bool) { if let Some(list) = &mut self.players_list { - for child in list.get_children().iter_shared() { - list.remove_child(&child); - } - if let Some(game) = &GameManager::singleton().bind().game { - for player in &game.bind().players { - let mut label = Label::new_alloc(); - label.set_text(&format!("{} ({})", player.name, player.connection_addr)); - list.add_child(&label); + if replace { + for child in list.get_children().iter_shared() { + list.remove_child(&child); + } + if let Some(game) = &GameManager::singleton().bind().game { + if let Some(prefab) = &self.player_listing_prefab { + for player in &game.bind().players { + if let Some(node) = prefab.instantiate() { + let mut listing = node.cast::(); + listing.bind_mut().player_id = Some(player.id); + listing.bind_mut().update(); + list.add_child(&listing); + } + } + } + } + } else { + for child in list.get_children().iter_shared() { + child.cast::().bind_mut().update(); } } } diff --git a/rust/src/ui/mod.rs b/rust/src/ui/mod.rs index 324513a..d65354e 100644 --- a/rust/src/ui/mod.rs +++ b/rust/src/ui/mod.rs @@ -1,3 +1,4 @@ pub mod cli; pub mod lobby; pub mod net_stats; +pub mod player_listing; diff --git a/rust/src/ui/player_listing.rs b/rust/src/ui/player_listing.rs new file mode 100644 index 0000000..08b1b00 --- /dev/null +++ b/rust/src/ui/player_listing.rs @@ -0,0 +1,39 @@ +use godot::{ + classes::{GridContainer, IGridContainer, Label, Panel, TextEdit, VBoxContainer}, + prelude::*, +}; + +use crate::{ + game_manager::{Game, Player}, + net::network_manager::NetworkManager, +}; + +#[derive(GodotClass)] +#[class(base=GridContainer, init)] +pub struct PlayerListing { + #[export] + pub name_label: Option>, + #[export] + pub ping_label: Option>, + + pub player_id: Option, + + base: Base, +} + +impl PlayerListing { + pub fn update(&mut self) { + if let Some(player_id) = &self.player_id + && let Some(game) = Game::singleton() + && let Some(player) = game.bind().get_player(*player_id) + { + if let Some(player_label) = &mut self.name_label { + player_label.set_text(&player.name); + } + + if let Some(ping_label) = &mut self.ping_label { + ping_label.set_text(&format!("({}ms)", player.ping)); + } + } + } +}