Add more proper player listings

This commit is contained in:
Sofia 2026-07-17 19:59:06 +03:00
parent 8893a2370d
commit 4c58824840
8 changed files with 159 additions and 36 deletions

View File

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

View File

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

View File

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

View File

@ -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<Node>,
}
@ -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<Player>, u16),
SetNick(u16, String),
SetSelfNick(String),
UpdatePings(HashMap<u16, u128>),
}
pub enum PeerKind {

View File

@ -108,7 +108,6 @@ impl IPanel for CommandLinePanel {
fn input(&mut self, event: Gd<InputEvent>) {
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 {

View File

@ -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<Gd<VBoxContainer>>,
#[export]
name_field: Option<Gd<TextEdit>>,
#[export]
player_listing_prefab: Option<Gd<PackedScene>>,
base: Base<Panel>,
}
@ -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::<PlayerListing>();
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::<PlayerListing>().bind_mut().update();
}
}
}

View File

@ -1,3 +1,4 @@
pub mod cli;
pub mod lobby;
pub mod net_stats;
pub mod player_listing;

View File

@ -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<Gd<Label>>,
#[export]
pub ping_label: Option<Gd<Label>>,
pub player_id: Option<u16>,
base: Base<GridContainer>,
}
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));
}
}
}
}