diff --git a/CHANGELOG.md b/CHANGELOG.md index 52ad37bb..3cfd29d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- RTT and packet loss information for `RepliconClient` and `ConnectedClients`. + ### Changed +- `ConnectedClients` now store `ConnectedClient` instead of `ClientId` with more information about the client. - All `TestFnsEntityExt` now accept `FnsId`. - Move replication-related modules from `core` module under `core::replication`. - Move `Replicated` to the `replication` module. diff --git a/src/core/connected_clients.rs b/src/core/connected_clients.rs index fe1bbc4b..58dbca6c 100644 --- a/src/core/connected_clients.rs +++ b/src/core/connected_clients.rs @@ -7,14 +7,14 @@ use crate::core::ClientId; /// Inserted as resource by [`ServerPlugin`](crate::server::ServerPlugin). /// /// See also [ReplicatedClients](super::replication::replicated_clients::ReplicatedClients). -#[derive(Resource, Default, Deref)] -pub struct ConnectedClients(Vec); +#[derive(Resource, Default, Debug, Deref)] +pub struct ConnectedClients(Vec); impl ConnectedClients { pub(crate) fn add(&mut self, client_id: ClientId) { debug!("adding connected `{client_id:?}`"); - self.0.push(client_id); + self.0.push(ConnectedClient::new(client_id)); } pub(crate) fn remove(&mut self, client_id: ClientId) { @@ -22,8 +22,70 @@ impl ConnectedClients { let index = self .iter() - .position(|test_id| *test_id == client_id) + .position(|client| client.id == client_id) .unwrap_or_else(|| panic!("{client_id:?} should be added before removal")); self.0.remove(index); } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.0.iter_mut() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct ConnectedClient { + id: ClientId, + rtt: f64, + packet_loss: f64, +} + +impl ConnectedClient { + pub fn new(id: ClientId) -> Self { + Self { + id, + rtt: 0.0, + packet_loss: 0.0, + } + } + + /// Returns the associated ID. + pub fn id(&self) -> ClientId { + self.id + } + + /// Returns the round-time trip for the connection. + /// + /// Returns zero if not provided by the backend. + pub fn rtt(&self) -> f64 { + self.rtt + } + + /// Sets the round-time trip for the connection. + /// + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
+ pub fn set_rtt(&mut self, rtt: f64) { + self.rtt = rtt; + } + + /// Returns the packet loss for the connection. + /// + /// Returns zero if not provided by the backend. + pub fn packet_loss(&self) -> f64 { + self.packet_loss + } + + /// Sets the packet loss for the connection. + /// + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
+ pub fn set_packet_loss(&mut self, packet_loss: f64) { + self.packet_loss = packet_loss; + } } diff --git a/src/core/event_registry/server_event.rs b/src/core/event_registry/server_event.rs index 60016b72..d95eb33d 100644 --- a/src/core/event_registry/server_event.rs +++ b/src/core/event_registry/server_event.rs @@ -637,14 +637,14 @@ unsafe fn send_independent_event( match *mode { SendMode::Broadcast => { - for &client_id in connected_clients.iter() { - server.send(client_id, event_data.channel_id, message.clone()); + for client in connected_clients.iter() { + server.send(client.id(), event_data.channel_id, message.clone()); } } SendMode::BroadcastExcept(id) => { - for &client_id in connected_clients.iter() { - if client_id != id { - server.send(client_id, event_data.channel_id, message.clone()); + for client in connected_clients.iter() { + if client.id() != id { + server.send(client.id(), event_data.channel_id, message.clone()); } } } diff --git a/src/core/replicon_client.rs b/src/core/replicon_client.rs index dc4b32ba..d0fe10a7 100644 --- a/src/core/replicon_client.rs +++ b/src/core/replicon_client.rs @@ -29,6 +29,9 @@ pub struct RepliconClient { /// List of sent messages and their channels since the last tick. sent_messages: Vec<(u8, Bytes)>, + + rtt: f64, + packet_loss: f64, } impl RepliconClient { @@ -153,14 +156,22 @@ impl RepliconClient { /// Removes all sent messages, returning them as an iterator with channel. /// - /// Should be called only from the messaging backend. + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
pub fn drain_sent(&mut self) -> impl Iterator + '_ { self.sent_messages.drain(..) } /// Adds a message from the server to the list of received messages. /// - /// Should be called only from the messaging backend. + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
pub fn insert_received, B: Into>(&mut self, channel_id: I, message: B) { if !self.is_connected() { warn!("trying to insert a received message when the client is not connected"); @@ -175,6 +186,42 @@ impl RepliconClient { channel_messages.push(message.into()); } + + /// Returns the round-time trip for the connection. + /// + /// Returns zero if not provided by the backend. + pub fn rtt(&self) -> f64 { + self.rtt + } + + /// Sets the round-time trip for the connection. + /// + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
+ pub fn set_rtt(&mut self, rtt: f64) { + self.rtt = rtt; + } + + /// Returns the packet loss for the connection. + /// + /// Returns zero if not provided by the backend. + pub fn packet_loss(&self) -> f64 { + self.packet_loss + } + + /// Sets the packet loss for the connection. + /// + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
+ pub fn set_packet_loss(&mut self, packet_loss: f64) { + self.packet_loss = packet_loss; + } } /// Connection status of the [`RepliconClient`]. diff --git a/src/core/replicon_server.rs b/src/core/replicon_server.rs index fda865ea..0ab52add 100644 --- a/src/core/replicon_server.rs +++ b/src/core/replicon_server.rs @@ -125,14 +125,22 @@ impl RepliconServer { /// Removes all sent messages, returning them as an iterator with client ID and channel. /// - /// Should be called only from the messaging backend. + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
pub fn drain_sent(&mut self) -> impl Iterator + '_ { self.sent_messages.drain(..) } /// Adds a message from a client to the list of received messages. /// - /// Should be called only from the messaging backend. + ///
+ /// + /// Should only be called from the messaging backend. + /// + ///
pub fn insert_received, B: Into>( &mut self, client_id: ClientId,