use bevy::prelude::*;
use bevy_matchbox::matchbox_socket::PeerId;
use instant::Duration;
use serde::{Deserialize, Serialize};
mod bevy_rtc {
pub use crate::protocol;
}
#[derive(proc_macro_protocol::Protocol, Serialize, Deserialize, Debug, Clone)]
pub struct LatencyTracerPayload {
pub from: PeerId,
pub sent: f64,
}
impl LatencyTracerPayload {
pub fn new(from: PeerId) -> Self {
Self {
from,
sent: instant::now(),
}
}
pub fn age(&self) -> Duration {
let time_shift = (instant::now() - self.sent).max(0.0) as u64;
Duration::from_millis(time_shift)
}
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct LatencyTracer {
pub peer_id: PeerId,
pub(crate) latency_hist: Vec<(LatencyTracerPayload, Duration)>,
pub(crate) last_latency: Option<f32>,
}
impl LatencyTracer {
pub fn new(peer_id: PeerId) -> Self {
Self {
peer_id,
latency_hist: vec![],
last_latency: None,
}
}
pub fn process(&mut self, payload: LatencyTracerPayload) {
let latency = payload.age() / 2;
self.latency_hist.push((payload, latency));
}
pub fn update_latency(&mut self) {
const NET_HISTORY_SECS: Duration = Duration::from_secs(3);
self.latency_hist
.retain(|(p, _)| p.age() <= NET_HISTORY_SECS);
self.latency_hist.sort_by(|(a, _), (b, _)| {
a.sent
.partial_cmp(&b.sent)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mid = self.latency_hist.len() / 2;
let median = self.latency_hist.get(mid).map(|(_, lat)| lat.as_secs_f32());
self.last_latency = median;
}
}