From 82d3a221930ceb300c4d230a9be951424129e066 Mon Sep 17 00:00:00 2001 From: Sofia Date: Thu, 16 Jul 2026 05:50:20 +0300 Subject: [PATCH] Implement net stats --- godot/scenes/main.tscn | 10 +++++ rust/src/lib.rs | 2 +- rust/src/net/mod.rs | 2 + rust/src/net/net_stats.rs | 57 +++++++++++++++++++++++++++ rust/src/{ => net}/network_manager.rs | 21 ++++++++-- rust/src/ui/cli.rs | 2 +- rust/src/ui/mod.rs | 1 + rust/src/ui/net_stats.rs | 40 +++++++++++++++++++ teanet-rust | 2 +- 9 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 rust/src/net/mod.rs create mode 100644 rust/src/net/net_stats.rs rename rust/src/{ => net}/network_manager.rs (93%) create mode 100644 rust/src/ui/net_stats.rs diff --git a/godot/scenes/main.tscn b/godot/scenes/main.tscn index f2e01ec..8aec83c 100644 --- a/godot/scenes/main.tscn +++ b/godot/scenes/main.tscn @@ -12,3 +12,13 @@ mesh = SubResource("BoxMesh_m77kh") [node name="directional_light_3d" type="DirectionalLight3D" parent="." unique_id=75522463] transform = Transform3D(0.61237246, -0.5, 0.61237246, 0.3535534, 0.8660254, 0.3535534, -0.7071068, 0, 0.7071068, 0, 1, 2) + +[node name="netstats" type="NetStatsLabel" parent="." unique_id=1366169487] +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -144.0 +grow_horizontal = 2 +grow_vertical = 0 +vertical_alignment = 2 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 60be319..5c5b6bc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use godot::prelude::*; -pub mod network_manager; +pub mod net; pub mod ui; struct MyExtension; diff --git a/rust/src/net/mod.rs b/rust/src/net/mod.rs new file mode 100644 index 0000000..c305f1e --- /dev/null +++ b/rust/src/net/mod.rs @@ -0,0 +1,2 @@ +pub mod net_stats; +pub mod network_manager; diff --git a/rust/src/net/net_stats.rs b/rust/src/net/net_stats.rs new file mode 100644 index 0000000..8857d22 --- /dev/null +++ b/rust/src/net/net_stats.rs @@ -0,0 +1,57 @@ +use std::time::Instant; + +use godot::prelude::*; +use teanet::stats::NetStats; + +#[derive(GodotClass)] +#[class(base=Object, singleton)] +pub struct Stats { + pub statistics: Statistics, + last_update: Instant, + + base: Base, +} + +#[godot_api] +impl IObject for Stats { + fn init(base: Base) -> Self { + Stats { + statistics: Default::default(), + last_update: Instant::now(), + base, + } + } +} + +impl Stats { + pub fn update_stats(&mut self, stats: NetStats) { + let now = Instant::now(); + + let tx_diff = + stats.bytes_tx.max(self.statistics.total_bytes_tx) - self.statistics.total_bytes_tx; + let rx_diff = + stats.bytes_rx.max(self.statistics.total_bytes_rx) - self.statistics.total_bytes_rx; + + self.statistics.total_bytes_rx = stats.bytes_rx; + self.statistics.total_bytes_tx = stats.bytes_tx; + self.statistics.packet_loss = + 1. - ((stats.messages_received as f64) / (stats.messages_expected as f64)); + + let duration = now - self.last_update; + let tx_bps = tx_diff as f64 / duration.as_secs_f64(); + let rx_bps = rx_diff as f64 / duration.as_secs_f64(); + self.statistics.tx_bps = tx_bps as usize; + self.statistics.rx_bps = rx_bps as usize; + + self.last_update = now; + } +} + +#[derive(Default)] +pub struct Statistics { + pub total_bytes_tx: usize, + pub total_bytes_rx: usize, + pub tx_bps: usize, + pub rx_bps: usize, + pub packet_loss: f64, +} diff --git a/rust/src/network_manager.rs b/rust/src/net/network_manager.rs similarity index 93% rename from rust/src/network_manager.rs rename to rust/src/net/network_manager.rs index 94813b5..644ae1f 100644 --- a/rust/src/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -7,7 +7,10 @@ use godot::{prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use teanet::{Peer, PeerConfig}; -use crate::ui::cli::{CLI_GLOBAL_NAME, CliColor, CommandLinePanel}; +use crate::{ + net::net_stats::Stats, + ui::cli::{CLI_GLOBAL_NAME, CliColor, CommandLinePanel}, +}; #[derive(GodotClass)] #[class(base=Node)] @@ -15,6 +18,7 @@ pub struct NetworkManager { peer: Option, last_hello: Instant, + last_netstat_update: Instant, base: Base, } @@ -25,8 +29,11 @@ pub const NETWORK_SINGLETON_NAME: &str = "NetworkManagerGlob"; impl INode for NetworkManager { fn init(base: Base) -> Self { NetworkManager { - last_hello: Instant::now(), peer: None, + + last_hello: Instant::now(), + last_netstat_update: Instant::now(), + base, } } @@ -52,10 +59,18 @@ impl INode for NetworkManager { if let Some(peer) = &mut self.peer { let mut cli = get_autoload_by_name::(CLI_GLOBAL_NAME); + let now = Instant::now(); + if now - self.last_netstat_update > Duration::from_millis(1000) { + Stats::singleton().bind_mut().update_stats(match peer { + PeerKind::Client(peer, _) => peer.statistics(), + PeerKind::Server(peer, _) => peer.statistics(), + }); + self.last_netstat_update = now; + } + match peer { PeerKind::Client(..) => {} PeerKind::Server(peer, _) => { - let now = Instant::now(); if now - self.last_hello > Duration::from_millis(2000) { self.last_hello = now; peer.broadcast_reliable(Package::Hello); diff --git a/rust/src/ui/cli.rs b/rust/src/ui/cli.rs index b05e4fe..40ca474 100644 --- a/rust/src/ui/cli.rs +++ b/rust/src/ui/cli.rs @@ -7,7 +7,7 @@ use godot::classes::{Engine, IPanel, Panel, RichTextLabel, ScrollContainer, Text use godot::prelude::*; use godot::tools::get_autoload_by_name; -use crate::network_manager::{NETWORK_SINGLETON_NAME, NetworkManager}; +use crate::net::network_manager::{NETWORK_SINGLETON_NAME, NetworkManager}; pub enum CliColor { Command, diff --git a/rust/src/ui/mod.rs b/rust/src/ui/mod.rs index 4f77372..68b43db 100644 --- a/rust/src/ui/mod.rs +++ b/rust/src/ui/mod.rs @@ -1 +1,2 @@ pub mod cli; +pub mod net_stats; diff --git a/rust/src/ui/net_stats.rs b/rust/src/ui/net_stats.rs new file mode 100644 index 0000000..8e874a3 --- /dev/null +++ b/rust/src/ui/net_stats.rs @@ -0,0 +1,40 @@ +use godot::{ + classes::{ILabel, Label}, + prelude::*, +}; + +use crate::net::net_stats::Stats; + +#[derive(GodotClass)] +#[class(base=Label)] +pub struct NetStatsLabel { + since_update: f64, + base: Base