Skip to main content

naia_shared/connection/
bandwidth_monitor.rs

1use std::time::Duration;
2
3/// Rolling time-window byte counter for measuring incoming or outgoing bandwidth.
4#[derive(Clone)]
5pub struct BandwidthMonitor {
6    time_queue: ExpiringTimeQueue<usize>,
7    total_bytes: u16,
8    to_kbps_factor: f32,
9}
10
11impl BandwidthMonitor {
12    /// Creates a monitor with the given measurement window duration.
13    pub fn new(bandwidth_measure_duration: Duration) -> Self {
14        Self {
15            time_queue: ExpiringTimeQueue::new(bandwidth_measure_duration),
16            total_bytes: 0,
17            to_kbps_factor: 0.008 / bandwidth_measure_duration.as_secs_f32(),
18        }
19    }
20
21    /// Records `bytes` bytes in the measurement window.
22    pub fn record_packet(&mut self, bytes: usize) {
23        self.clear_expired_packets();
24
25        self.total_bytes += bytes as u16;
26        self.time_queue.add_item(bytes);
27    }
28
29    /// Perform housekeeping - clear expired packets from the measurement window.
30    /// Call this during the update phase of the tick cycle.
31    pub fn tick(&mut self) {
32        self.clear_expired_packets();
33    }
34
35    /// Returns the current bandwidth in kbps.
36    /// This is a pure read-only query - call `tick()` during the update phase
37    /// to ensure expired packets are cleared.
38    pub fn bandwidth(&self) -> f32 {
39        self.total_bytes as f32 * self.to_kbps_factor
40    }
41
42    fn clear_expired_packets(&mut self) {
43        let now = Instant::now();
44        while let Some(bytes) = self.time_queue.pop_item(&now) {
45            self.total_bytes -= bytes as u16;
46        }
47    }
48}
49
50use naia_socket_shared::{Instant, TimeQueue};
51
52#[derive(Clone)]
53struct ExpiringTimeQueue<T: Eq + PartialEq> {
54    queue: TimeQueue<T>,
55    expire_time: Duration,
56}
57
58impl<T: Eq + PartialEq> ExpiringTimeQueue<T> {
59    pub fn new(duration: Duration) -> Self {
60        Self {
61            queue: TimeQueue::new(),
62            expire_time: duration,
63        }
64    }
65
66    pub fn add_item(&mut self, item: T) {
67        let mut instant = Instant::now();
68        instant.add_millis(self.expire_time.as_millis() as u32);
69        self.queue.add_item(instant, item);
70    }
71
72    pub fn pop_item(&mut self, now: &Instant) -> Option<T> {
73        self.queue.pop_item(now)
74    }
75}