Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
feat: smooth latency tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
simbleau committed Jan 11, 2024
1 parent 539236d commit 48867d7
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"

[workspace.package]
edition = "2021"
version = "0.8.4"
version = "0.8.5"
license = "MIT/Apache-2.0"

[workspace.dependencies]
Expand Down
2 changes: 1 addition & 1 deletion bevy-silk/src/client/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl Plugin for SilkClientPlugin {
)
.add_systems(
First,
systems::update_state_latency
systems::calculate_latency
.after(systems::client_event_writer)
.run_if(state_exists_and_equals(
SilkClientStatus::Connected,
Expand Down
2 changes: 2 additions & 0 deletions bevy-silk/src/client/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ pub struct SilkState {
pub id: Option<PeerId>,
/// The latency to the server
pub latency: Option<Duration>,
/// The smooth latency to the server
pub smoothed_latency: Option<Duration>,
}
14 changes: 12 additions & 2 deletions bevy-silk/src/client/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub(crate) fn reset_socket(
host_id: None,
id: None,
latency: None,
smoothed_latency: None,
};
}

Expand Down Expand Up @@ -197,11 +198,20 @@ pub fn read_latency_tracers(
}
}

pub fn update_state_latency(
pub fn calculate_latency(
time: Res<Time>,
mut state: ResMut<SilkState>,
mut tracer: Query<&mut LatencyTracer>,
) {
let mut tracer = tracer.single_mut();
tracer.update_latency();
state.latency = Some(Duration::from_secs_f32(tracer.last_latency));
let last_latency = Duration::from_secs_f32(tracer.last_latency);
state.latency.replace(last_latency);
// Calculate smooth latency
let current_smoothed = state.smoothed_latency.get_or_insert(last_latency);
const AVG_SECS: f32 = 1.0; // 1 second average
let alpha = 1.0 - f32::exp(-time.delta_seconds() / AVG_SECS);
let current_f32 = current_smoothed.as_secs_f32() * (1.0 - alpha);
let delta = tracer.last_latency * alpha;
*current_smoothed = Duration::from_secs_f32(current_f32 + delta);
}
2 changes: 1 addition & 1 deletion bevy-silk/src/server/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Plugin for SilkServerPlugin {
(
common_socket_reader,
systems::server_event_writer,
systems::update_state_latency,
systems::calculate_latency,
)
.chain()
.run_if(resource_exists::<SilkSocket>()),
Expand Down
21 changes: 20 additions & 1 deletion bevy-silk/src/server/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct SilkState {

/// A map of user latencies
pub(crate) latencies: HashMap<PeerId, Duration>,

/// A map of smoothed user latencies
pub(crate) smoothed_latencies: HashMap<PeerId, Duration>,
}

impl SilkState {
Expand All @@ -39,6 +42,7 @@ impl SilkState {
id: None,
peers: HashSet::new(),
latencies: HashMap::new(),
smoothed_latencies: HashMap::new(),
}
}

Expand All @@ -47,15 +51,30 @@ impl SilkState {
self.peers.iter().copied()
}

/// Return the latency for a peer if they exist
/// Return the instantaneous latencies for all peers
pub fn iter_latencies(
&self,
) -> impl Iterator<Item = (PeerId, Duration)> + '_ {
self.latencies.iter().map(|(p, l)| (*p, *l))
}

/// Return the smoothed latencies for all peers
pub fn iter_smoothed_latencies(
&self,
) -> impl Iterator<Item = (PeerId, Duration)> + '_ {
self.smoothed_latencies.iter().map(|(p, l)| (*p, *l))
}

/// Return the latency for a peer if they exist
pub fn get_latency_for(&self, peer_id: PeerId) -> Option<Duration> {
self.latencies.get(&peer_id).copied()
}

/// Return the smoothed latency for a peer if they exist
pub fn get_smoothed_latency_for(
&self,
peer_id: PeerId,
) -> Option<Duration> {
self.smoothed_latencies.get(&peer_id).copied()
}
}
23 changes: 20 additions & 3 deletions bevy-silk/src/server/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,34 @@ pub fn read_latency_tracers(
}
}

pub fn update_state_latency(
pub fn calculate_latency(
time: Res<Time>,
mut state: ResMut<SilkState>,
mut tracers: Query<&mut LatencyTracer>,
) {
state.latencies.clear();
// Update & Prune
// Set latencies
for mut tracer in tracers.iter_mut() {
if !state.peers.contains(&tracer.peer_id) {
state.latencies.remove(&tracer.peer_id);
state.smoothed_latencies.remove(&tracer.peer_id);
continue;
}
tracer.update_latency();
state.latencies.insert(
tracer.peer_id,
Duration::from_secs_f32(tracer.last_latency),
);
// Calculate smooth latency
state
.smoothed_latencies
.entry(tracer.peer_id)
.and_modify(|current| {
const AVG_SECS: f32 = 1.0; // 1 second average
let alpha = 1.0 - f32::exp(-time.delta_seconds() / AVG_SECS);
let current_f32 = current.as_secs_f32() * (1.0 - alpha);
let delta = tracer.last_latency * alpha;
*current = Duration::from_secs_f32(current_f32 + delta);
})
.or_insert(Duration::from_secs_f32(tracer.last_latency));
}
}
5 changes: 3 additions & 2 deletions demo/client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,9 @@ fn app_ui(
}
ui.label(format!("Connected as {}", state.id.unwrap()));
ui.label(format!(
"Latency: {:.0?}",
state.latency.unwrap_or_default()
"Latency: {:.0?} (smoothed = {:.0?})",
state.latency.unwrap_or_default(),
state.smoothed_latency.unwrap_or_default()
));

ui.separator();
Expand Down
10 changes: 7 additions & 3 deletions demo/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,17 @@ fn print_latencies(
mut throttle: Local<Option<Timer>>,
) {
let timer = throttle.get_or_insert(Timer::new(
Duration::from_secs(1),
Duration::from_millis(100),
TimerMode::Repeating,
));
timer.tick(time.delta());
if timer.just_finished() {
for (peer, latency) in state.iter_latencies() {
info!("Latency to {peer}: {latency:.0?}");
for ((peer, latency), (_peer, smoothed)) in
state.iter_latencies().zip(state.iter_smoothed_latencies())
{
info!(
"Latency to {peer}: {latency:.0?} (smoothed = {smoothed:.0?})"
);
}
}
}

0 comments on commit 48867d7

Please sign in to comment.