diff --git a/README.md b/README.md index 6f7568e4..31b1890f 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ cargo run --bin move_box_server cargo run --bin move_box_client ``` +- [`lightyear`]: high-level server-authoritative networking library, using `aeronet` as the underlying IO library + # Overview ## Quickstart @@ -127,11 +129,11 @@ cargo run --bin move_box_client - Allows receiving acknowledgement of sent message acknowledgements - Technically user-swappable, but most code above this layer relies on [`aeronet_transport`] specifically - Component replication, rollback, etc. - - This is not provided as part of `aeronet`, but you can use a crate which integrates `aeronet` with one of these e.g. [`aeronet_replicon`] + - This is not provided as part of `aeronet`, but you can use a crate which integrates `aeronet` with one of these e.g. [`aeronet_replicon`], [`lightyear`] ## Writing an IO layer -If none of the first-party or third-party IO layer implementations don't suit your needs, you can write your own IO layer implementation for your needs. `aeronet_io` is designed to be as minimal as possible, to make writing your own IO layers simple, and allow them to integrate with higher levels of the stack seamlessly. +If none of the first-party or third-party IO layer implementations suit your needs, you can write your own IO layer implementation for your needs. `aeronet_io` is designed to be as minimal as possible, to make writing your own IO layers simple, and allow them to integrate with higher levels of the stack seamlessly. You can use [`aeronet_channel`] as a simple reference implementation of an IO layer - it's [a single file](https://github.com/aecsocket/aeronet/blob/main/crates/aeronet_channel/src/lib.rs). It demonstrates how to poll a channel synchronously from the Bevy event loop, which is useful if your underlying IO layer is not async. @@ -177,7 +179,7 @@ Some example tools you may use are: - Windows - [`clumsy`](https://github.com/jagt/clumsy) -`aeronet` does not provide support for conditioning within the networking crate itself, since conditioning testing is more effective (and representative of real-world results) when the conditioning is applied at the lowest level possible. +`aeronet` does not provide support for conditioning within the networking crate itself, since conditioning testing is more effective (and representative of real-world results) when the conditioning is applied at the lowest level possible (the OS layer). # Contributing @@ -213,6 +215,7 @@ When submitting a pull request, make sure that all continuous integration (CI) c [`aeronet_steam`]: https://docs.rs/aeronet_steam [`aeronet_replicon`]: https://docs.rs/aeronet_replicon [`bevy_replicon`]: https://docs.rs/bevy_replicon +[`lightyear`]: https://docs.rs/lightyear [`aeronet_transport`]: https://docs.rs/aeronet_transport [`Session`]: io::Session [`echo_client.rs`]: ./examples/src/bin/echo_client.rs diff --git a/crates/aeronet_steam/src/client.rs b/crates/aeronet_steam/src/client.rs index 2a92c2bc..3e127e4e 100644 --- a/crates/aeronet_steam/src/client.rs +++ b/crates/aeronet_steam/src/client.rs @@ -17,6 +17,11 @@ use { }; /// Allows using [`SteamNetClient`]. +/// +/// This does not perform Steam initialization when the plugin is built; +/// instead, it defers initialization to when a [`SteamNetClient`] is added +/// to the world. This allows you to always add this plugin, but choose at +/// runtime whether you want to use Steam or not. pub struct SteamNetClientPlugin { _phantom: PhantomData, } @@ -86,6 +91,9 @@ impl SteamNetClient { /// Creates an [`EntityCommand`] to set up a session and connect it to the /// `target`. /// + /// [`SteamworksClient`] must be present in the world before this command is + /// applied. + /// /// # Examples /// /// ``` diff --git a/crates/aeronet_steam/src/server.rs b/crates/aeronet_steam/src/server.rs index 4c2cae75..45e1bc01 100644 --- a/crates/aeronet_steam/src/server.rs +++ b/crates/aeronet_steam/src/server.rs @@ -31,6 +31,11 @@ use { }; /// Allows using [`SteamNetServer`]. +/// +/// This does not perform Steam initialization when the plugin is built; +/// instead, it defers initialization to when a [`SteamNetServer`] is added +/// to the world. This allows you to always add this plugin, but choose at +/// runtime whether you want to use Steam or not. pub struct SteamNetServerPlugin { _phantom: PhantomData, } @@ -123,6 +128,9 @@ impl SteamNetServer { /// Creates an [`EntityCommand`] to set up a server and have it start /// listening for connections. /// + /// [`SteamworksClient`] must be present in the world before this command is + /// applied. + /// /// # Examples /// /// ``` diff --git a/crates/aeronet_steam/src/session.rs b/crates/aeronet_steam/src/session.rs index 44e806fa..eeaea4b8 100644 --- a/crates/aeronet_steam/src/session.rs +++ b/crates/aeronet_steam/src/session.rs @@ -40,21 +40,21 @@ impl Plugin for SteamNetSessionPlugin { app.add_plugins(AeronetIoPlugin); } - let steam = app.world().resource::>(); - // https://github.com/cBournhonesque/lightyear/issues/243 - steam - .networking_sockets() - .init_authentication() - .expect("failed to initialize steamworks authentication"); + // We don't do Steam init like `init_authentication` or poll groups here + // because this plugin just adds the *capability* of using Steam IO, + // but we don't know for sure at runtime if we'll be using Steam here. + // We leave that up to the user: if they decide to spawn an entity with + // a `SteamNetIo`, only *then* do we do Steam init (and thus assume that + // a Steam client is running on the user's machine). - let poll_group = steam.networking_sockets().create_poll_group(); - app.insert_resource(PollGroup(poll_group)) - .add_systems( - PreUpdate, - (poll_io::, poll_messages::).in_set(IoSet::Poll), - ) - .add_systems(PostUpdate, flush::.in_set(IoSet::Flush)) - .add_observer(add_connection_to_poll_group::); + app.add_systems( + PreUpdate, + (poll_io::, poll_messages::) + .in_set(IoSet::Poll) + .run_if(resource_exists::>), + ) + .add_systems(PostUpdate, flush::.in_set(IoSet::Flush)) + .add_observer(init_io::); } } @@ -96,16 +96,45 @@ pub enum SessionError { #[derive(Deref, DerefMut, Resource)] struct PollGroup(NetPollGroup); -fn add_connection_to_poll_group( +fn init_io( trigger: Trigger>, + steam: Option>>, io: Query<&SteamNetIo>, - poll_group: Res>, + poll_group: Option>>, + mut commands: Commands, ) { + let steam = steam.unwrap_or_else(|| { + panic!( + "`{}` must be present before creating a Steam IO layer", + type_name::>>() + ) + }); + let entity = trigger.target(); let io = io .get(entity) .expect("we are adding this component to this entity"); - io.conn.set_poll_group(&poll_group); + + if let Some(poll_group) = poll_group { + io.conn.set_poll_group(&poll_group); + } else { + // we combine 2 steps into one "global steam init" step: + // - init authentication + // - create poll group + // + // this means that if the poll group is removed, we re-init authentication + // but this should be fine, I think + + // https://github.com/cBournhonesque/lightyear/issues/243 + steam + .networking_sockets() + .init_authentication() + .expect("failed to initialize steamworks authentication"); + + let poll_group = steam.networking_sockets().create_poll_group(); + io.conn.set_poll_group(&poll_group); + commands.insert_resource(PollGroup(poll_group)); + } } fn poll_io( diff --git a/crates/aeronet_transport/src/rtt.rs b/crates/aeronet_transport/src/rtt.rs index 571e65f9..53d85e31 100644 --- a/crates/aeronet_transport/src/rtt.rs +++ b/crates/aeronet_transport/src/rtt.rs @@ -76,11 +76,7 @@ impl RttEstimator { self.latest = rtt; self.min = self.min.min(rtt); - let var_sample = if self.smoothed > rtt { - self.smoothed - rtt - } else { - rtt - self.smoothed - }; + let var_sample = self.smoothed.abs_diff(rtt); self.var = (3 * self.var + var_sample) / 4; self.smoothed = (7 * self.smoothed + rtt) / 8; } diff --git a/docs/changelog.md b/docs/changelog.md index a0db5d97..be702bb8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,7 +1,7 @@ # 0.15 -- Add documentation to `aeronet::_doc` module, accessible in `docs.rs` -- Move changelog to `aeronet::_doc` +- Add documentation to `aeronet::_docs` module, accessible in `docs.rs` +- Move changelog to `aeronet::_docs` - Update to `bevy_replicon 0.34.0` # 0.14.0