diff --git a/benches/replication.rs b/benches/replication.rs index 1dc71e49..8f270ccd 100644 --- a/benches/replication.rs +++ b/benches/replication.rs @@ -157,7 +157,7 @@ fn replication(c: &mut Criterion) { fn setup_app(app: &mut App) { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .replicate::(); diff --git a/src/client.rs b/src/client.rs index 8a63e320..eac5b650 100644 --- a/src/client.rs +++ b/src/client.rs @@ -57,9 +57,9 @@ impl ClientPlugin { let end_pos: u64 = message.len().try_into().unwrap(); let mut cursor = Cursor::new(message); - if !deserialize_tick(&mut cursor, world)? { + let Some(network_tick) = deserialize_tick(&mut cursor, world)? else { continue; - } + }; if cursor.position() == end_pos { continue; } @@ -70,6 +70,7 @@ impl ClientPlugin { &mut entity_map, &replication_rules, DiffKind::Change, + network_tick, )?; if cursor.position() == end_pos { continue; @@ -81,12 +82,19 @@ impl ClientPlugin { &mut entity_map, &replication_rules, DiffKind::Removal, + network_tick, )?; if cursor.position() == end_pos { continue; } - deserialize_despawns(&mut cursor, world, &mut entity_map)?; + deserialize_despawns( + &mut cursor, + world, + &mut entity_map, + &replication_rules, + network_tick, + )?; } Ok(()) @@ -109,16 +117,19 @@ impl ClientPlugin { /// Deserializes server tick and applies it to [`LastTick`] if it is newer. /// -/// Returns true if [`LastTick`] has been updated. -fn deserialize_tick(cursor: &mut Cursor, world: &mut World) -> Result { +/// Returns the tick if [`LastTick`] has been updated. +fn deserialize_tick( + cursor: &mut Cursor, + world: &mut World, +) -> Result, bincode::Error> { let tick = bincode::deserialize_from(cursor)?; let mut last_tick = world.resource_mut::(); if last_tick.0 < tick { last_tick.0 = tick; - Ok(true) + Ok(Some(tick)) } else { - Ok(false) + Ok(None) } } @@ -129,6 +140,7 @@ fn deserialize_component_diffs( entity_map: &mut NetworkEntityMap, replication_rules: &ReplicationRules, diff_kind: DiffKind, + tick: NetworkTick, ) -> Result<(), bincode::Error> { let entities_count: u16 = bincode::deserialize_from(&mut *cursor)?; for _ in 0..entities_count { @@ -141,9 +153,9 @@ fn deserialize_component_diffs( let replication_info = unsafe { replication_rules.get_info_unchecked(replication_id) }; match diff_kind { DiffKind::Change => { - (replication_info.deserialize)(&mut entity, entity_map, cursor)? + (replication_info.deserialize)(&mut entity, entity_map, cursor, tick)? } - DiffKind::Removal => (replication_info.remove)(&mut entity), + DiffKind::Removal => (replication_info.remove)(&mut entity, tick), } } } @@ -156,6 +168,8 @@ fn deserialize_despawns( cursor: &mut Cursor, world: &mut World, entity_map: &mut NetworkEntityMap, + replication_rules: &ReplicationRules, + tick: NetworkTick, ) -> Result<(), bincode::Error> { let entities_count: u16 = bincode::deserialize_from(&mut *cursor)?; for _ in 0..entities_count { @@ -167,7 +181,7 @@ fn deserialize_despawns( .remove_by_server(server_entity) .and_then(|entity| world.get_entity_mut(entity)) { - client_entity.despawn_recursive(); + (replication_rules.despawn_fn)(client_entity, tick); } } diff --git a/src/lib.rs b/src/lib.rs index 8c1c9d51..73ab5cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,10 @@ app.add_plugins(MinimalPlugins) .add_plugins(ReplicationPlugins); ``` -This group contains necessary replication stuff and setups server and client -plugins to let you host and join games from the same application. If you -planning to separate client and server you can use +This group contains necessary replication stuff and sets up the server and client +plugins to let you host and join games from the same application. + +If you are planning to separate client and server you can use `disable()` to disable [`ClientPlugin`] or [`ServerPlugin`]. You can also configure how often updates are sent from server to clients with [`ServerPlugin`]'s [`TickPolicy`].: @@ -94,11 +95,11 @@ you can use [`AppReplicationExt::replicate_with`]: ```rust # use std::io::Cursor; # use bevy::{ecs::world::EntityMut, prelude::*, ptr::Ptr, utils::HashMap}; -# use bevy_replicon::{prelude::*, renet::Bytes}; +# use bevy_replicon::{prelude::*, renet::Bytes, replicon_core::replication_rules}; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); # app.add_plugins(ReplicationPlugins); -app.replicate_with::(serialize_transform, deserialize_transform); +app.replicate_with::(serialize_transform, deserialize_transform, replication_rules::remove_component::); /// Serializes only translation. fn serialize_transform( @@ -115,6 +116,7 @@ fn deserialize_transform( entity: &mut EntityMut, _entity_map: &mut NetworkEntityMap, cursor: &mut Cursor, + _tick: NetworkTick, ) -> Result<(), bincode::Error> { let translation: Vec3 = bincode::deserialize_from(cursor)?; entity.insert(Transform::from_translation(translation)); @@ -131,6 +133,18 @@ will be replicated. If you need to disable replication for specific component for specific entity, you can insert [`Ignored`] component and replication will be skipped for `T`. +### NetworkTick, and fixed timestep games. + +The [`ServerPlugin`] sends replication data in `PostUpdate` any time the [`NetworkTick`] resource +changes. By default, its incremented in `PostUpdate` per the [`TickPolicy`]. + +If you set [`TickPolicy::Manual`], you can increment [`NetworkTick`] at the start of your +`FixedTimestep` game loop. This value can represent your simulation step, and is made available +to the client in the custom deserialization, despawn and component removal functions. + +One use for this is rollback networking: you may want to rollback time and apply the update +for the tick frame, which is in the past, then resimulate. + ### "Blueprints" pattern The idea was borrowed from [iyes_scene_tools](https://github.com/IyesGames/iyes_scene_tools#blueprints-pattern). @@ -147,11 +161,11 @@ your initialization systems to [`ClientSet::Receive`]: ```rust # use std::io::Cursor; # use bevy::{ecs::world::EntityMut, prelude::*, ptr::Ptr, utils::HashMap}; -# use bevy_replicon::{prelude::*, renet::Bytes}; +# use bevy_replicon::{prelude::*, renet::Bytes, replicon_core::replication_rules}; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); # app.add_plugins(ReplicationPlugins); -app.replicate_with::(serialize_transform, deserialize_transform) +app.replicate_with::(serialize_transform, deserialize_transform, replication_rules::remove_component::) .replicate::() .add_systems(PreUpdate, player_init_system.after(ClientSet::Receive)); @@ -175,7 +189,7 @@ fn player_init_system( #[derive(Component, Deserialize, Serialize)] struct Player; # fn serialize_transform(_: Ptr, _: &mut Cursor>) -> Result<(), bincode::Error> { unimplemented!() } -# fn deserialize_transform(_: &mut EntityMut, _: &mut NetworkEntityMap, _: &mut Cursor) -> Result<(), bincode::Error> { unimplemented!() } +# fn deserialize_transform(_: &mut EntityMut, _: &mut NetworkEntityMap, _: &mut Cursor, _: NetworkTick) -> Result<(), bincode::Error> { unimplemented!() } ``` This pairs nicely with server state serialization and keeps saves clean. @@ -380,10 +394,11 @@ pub mod prelude { replicon_core::{ replication_rules::{ AppReplicationExt, Ignored, MapNetworkEntities, Mapper, Replication, + ReplicationRules, }, NetworkChannels, NetworkTick, RepliconCorePlugin, }, - server::{has_authority, ServerPlugin, ServerSet, ServerTicks, TickPolicy, SERVER_ID}, + server::{has_authority, AckedTicks, ServerPlugin, ServerSet, TickPolicy, SERVER_ID}, ReplicationPlugins, }; } @@ -393,6 +408,7 @@ pub use bevy_renet::*; pub use bincode; use prelude::*; +/// Plugin Group for all replicon plugins. pub struct ReplicationPlugins; impl PluginGroup for ReplicationPlugins { diff --git a/src/replicon_core.rs b/src/replicon_core.rs index 5bb8073c..ac592f8b 100644 --- a/src/replicon_core.rs +++ b/src/replicon_core.rs @@ -74,10 +74,11 @@ fn channel_configs(channels: &[SendType]) -> Vec { channel_configs } -/// Corresponds to the number of server update. +/// A tick that increments each time we need the server to compute and send an update. +/// This is mapped to the bevy Tick in [`crate::server::AckedTicks`]. /// /// See also [`crate::server::TickPolicy`]. -#[derive(Clone, Copy, Default, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Resource, Serialize)] pub struct NetworkTick(u32); impl NetworkTick { diff --git a/src/replicon_core/replication_rules.rs b/src/replicon_core/replication_rules.rs index 79a3c157..72b81ee9 100644 --- a/src/replicon_core/replication_rules.rs +++ b/src/replicon_core/replication_rules.rs @@ -10,6 +10,7 @@ use bevy_renet::renet::Bytes; use bincode::{DefaultOptions, Options}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use super::NetworkTick; use crate::client::{ClientMapper, NetworkEntityMap}; pub trait AppReplicationExt { @@ -28,11 +29,12 @@ pub trait AppReplicationExt { where C: Component + Serialize + DeserializeOwned + MapNetworkEntities; - /// Same as [`Self::replicate`], but uses the specified functions for serialization and deserialization. + /// Same as [`Self::replicate`], but uses the specified functions for serialization, deserialization, and removal. fn replicate_with( &mut self, serialize: SerializeFn, deserialize: DeserializeFn, + remove: RemoveComponentFn, ) -> &mut Self where C: Component; @@ -43,17 +45,30 @@ impl AppReplicationExt for App { where C: Component + Serialize + DeserializeOwned, { - self.replicate_with::(serialize_component::, deserialize_component::) + self.replicate_with::( + serialize_component::, + deserialize_component::, + remove_component::, + ) } fn replicate_mapped(&mut self) -> &mut Self where C: Component + Serialize + DeserializeOwned + MapNetworkEntities, { - self.replicate_with::(serialize_component::, deserialize_mapped_component::) + self.replicate_with::( + serialize_component::, + deserialize_mapped_component::, + remove_component::, + ) } - fn replicate_with(&mut self, serialize: SerializeFn, deserialize: DeserializeFn) -> &mut Self + fn replicate_with( + &mut self, + serialize: SerializeFn, + deserialize: DeserializeFn, + remove: RemoveComponentFn, + ) -> &mut Self where C: Component, { @@ -63,7 +78,7 @@ impl AppReplicationExt for App { ignored_id, serialize, deserialize, - remove: remove_component::, + remove, }; let mut replication_rules = self.world.resource_mut::(); @@ -77,10 +92,14 @@ impl AppReplicationExt for App { } /// Stores information about which components will be serialized and how. -/// -/// See also [`replicate_into_scene`]. #[derive(Resource)] -pub(crate) struct ReplicationRules { +pub struct ReplicationRules { + /// Custom function to handle entity despawning. + /// + /// By default uses [`despawn_recursive`]. + /// Useful if you need to intercept despawns and handle them in a special way. + pub despawn_fn: EntityDespawnFn, + /// Maps component IDs to their replication IDs. ids: HashMap, @@ -133,6 +152,7 @@ impl FromWorld for ReplicationRules { infos: Default::default(), ids: Default::default(), marker_id: world.init_component::(), + despawn_fn: despawn_recursive, } } } @@ -141,8 +161,18 @@ impl FromWorld for ReplicationRules { pub type SerializeFn = fn(Ptr, &mut Cursor>) -> Result<(), bincode::Error>; /// Signature of component deserialization functions. -pub type DeserializeFn = - fn(&mut EntityMut, &mut NetworkEntityMap, &mut Cursor) -> Result<(), bincode::Error>; +pub type DeserializeFn = fn( + &mut EntityMut, + &mut NetworkEntityMap, + &mut Cursor, + NetworkTick, +) -> Result<(), bincode::Error>; + +/// Signature of component removal functions. +pub type RemoveComponentFn = fn(&mut EntityMut, NetworkTick); + +/// Signature of the entity despawn function. +pub type EntityDespawnFn = fn(EntityMut, NetworkTick); /// Stores meta information about replicated component. pub(crate) struct ReplicationInfo { @@ -156,7 +186,7 @@ pub(crate) struct ReplicationInfo { pub(crate) deserialize: DeserializeFn, /// Function that removes specific component from [`EntityMut`]. - pub(crate) remove: fn(&mut EntityMut), + pub(crate) remove: RemoveComponentFn, } /// Marks entity for replication. @@ -192,7 +222,7 @@ pub trait Mapper { } /// Default serialization function. -fn serialize_component( +pub fn serialize_component( component: Ptr, cursor: &mut Cursor>, ) -> Result<(), bincode::Error> { @@ -202,10 +232,11 @@ fn serialize_component( } /// Default deserialization function. -fn deserialize_component( +pub fn deserialize_component( entity: &mut EntityMut, _entity_map: &mut NetworkEntityMap, cursor: &mut Cursor, + _tick: NetworkTick, ) -> Result<(), bincode::Error> { let component: C = DefaultOptions::new().deserialize_from(cursor)?; entity.insert(component); @@ -214,10 +245,11 @@ fn deserialize_component( } /// Like [`deserialize_component`], but also maps entities before insertion. -fn deserialize_mapped_component( +pub fn deserialize_mapped_component( entity: &mut EntityMut, entity_map: &mut NetworkEntityMap, cursor: &mut Cursor, + _tick: NetworkTick, ) -> Result<(), bincode::Error> { let mut component: C = DefaultOptions::new().deserialize_from(cursor)?; @@ -230,7 +262,12 @@ fn deserialize_mapped_component(entity: &mut EntityMut) { +/// Default component removal function. +pub fn remove_component(entity: &mut EntityMut, _tick: NetworkTick) { entity.remove::(); } + +/// Default entity despawn function. +pub fn despawn_recursive(entity: EntityMut, _tick: NetworkTick) { + entity.despawn_recursive(); +} diff --git a/src/server.rs b/src/server.rs index 651ff63c..f00b00c5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -51,14 +51,17 @@ impl Plugin for ServerPlugin { RemovalTrackerPlugin, DespawnTrackerPlugin, )) - .init_resource::() + .init_resource::() + .init_resource::() .configure_set( PreUpdate, ServerSet::Receive.after(NetcodeServerPlugin::update_system), ) .configure_set( PostUpdate, - ServerSet::Send.before(NetcodeServerPlugin::send_packets), + ServerSet::Send + .before(NetcodeServerPlugin::send_packets) + .run_if(resource_changed::()), ) .add_systems( PreUpdate, @@ -77,9 +80,23 @@ impl Plugin for ServerPlugin { ), ); - if let TickPolicy::MaxTickRate(max_tick_rate) = self.tick_policy { - let tick_time = Duration::from_millis(1000 / max_tick_rate as u64); - app.configure_set(PostUpdate, ServerSet::Send.run_if(on_timer(tick_time))); + match self.tick_policy { + TickPolicy::MaxTickRate(max_tick_rate) => { + let tick_time = Duration::from_millis(1000 / max_tick_rate as u64); + app.add_systems( + PostUpdate, + Self::increment_tick + .before(Self::diffs_sending_system) + .run_if(on_timer(tick_time)), + ); + } + TickPolicy::EveryFrame => { + app.add_systems( + PostUpdate, + Self::increment_tick.before(Self::diffs_sending_system), + ); + } + TickPolicy::Manual => (), } } } @@ -89,15 +106,17 @@ impl ServerPlugin { Self { tick_policy } } - fn acks_receiving_system( - mut server_ticks: ResMut, - mut server: ResMut, - ) { + /// Increments current server tick which causes the server to send a diff packet this frame. + pub fn increment_tick(mut network_tick: ResMut) { + network_tick.increment(); + } + + fn acks_receiving_system(mut acked_ticks: ResMut, mut server: ResMut) { for client_id in server.clients_id() { while let Some(message) = server.receive_message(client_id, REPLICATION_CHANNEL_ID) { match bincode::deserialize::(&message) { Ok(tick) => { - let acked_tick = server_ticks.acked_ticks.entry(client_id).or_default(); + let acked_tick = acked_ticks.clients.entry(client_id).or_default(); if *acked_tick < tick { *acked_tick = tick; } @@ -107,20 +126,20 @@ impl ServerPlugin { } } - server_ticks.cleanup_system_ticks(); + acked_ticks.cleanup_system_ticks(); } fn acks_cleanup_system( mut server_events: EventReader, - mut server_ticks: ResMut, + mut acked_ticks: ResMut, ) { for event in &mut server_events { match event { ServerEvent::ClientDisconnected { client_id, .. } => { - server_ticks.acked_ticks.remove(client_id); + acked_ticks.clients.remove(client_id); } ServerEvent::ClientConnected { client_id } => { - server_ticks.acked_ticks.entry(*client_id).or_default(); + acked_ticks.clients.entry(*client_id).or_default(); } } } @@ -129,15 +148,16 @@ impl ServerPlugin { fn diffs_sending_system( mut buffers: Local>, change_tick: SystemChangeTick, - mut set: ParamSet<(&World, ResMut, ResMut)>, + mut set: ParamSet<(&World, ResMut, ResMut)>, replication_rules: Res, despawn_tracker: Res, + network_tick: Res, removal_trackers: Query<(Entity, &RemovalTracker)>, ) -> Result<(), bincode::Error> { - let mut server_ticks = set.p2(); - server_ticks.increment(change_tick.this_run()); + let mut acked_ticks = set.p2(); + acked_ticks.register_network_tick(*network_tick, change_tick.this_run()); - let buffers = prepare_buffers(&mut buffers, &server_ticks)?; + let buffers = prepare_buffers(&mut buffers, &acked_ticks, *network_tick)?; collect_changes( buffers, set.p0(), @@ -165,9 +185,9 @@ impl ServerPlugin { Ok(()) } - fn reset_system(mut server_ticks: ResMut) { - server_ticks.acked_ticks.clear(); - server_ticks.system_ticks.clear(); + fn reset_system(mut acked_ticks: ResMut) { + acked_ticks.clients.clear(); + acked_ticks.system_ticks.clear(); } } @@ -179,27 +199,25 @@ impl ServerPlugin { /// and the returned slice will not include them. fn prepare_buffers<'a>( buffers: &'a mut Vec, - server_ticks: &ServerTicks, + acked_ticks: &AckedTicks, + network_tick: NetworkTick, ) -> Result<&'a mut [ReplicationBuffer], bincode::Error> { - buffers.reserve(server_ticks.acked_ticks.len()); - for (index, (&client_id, &tick)) in server_ticks.acked_ticks.iter().enumerate() { - let system_tick = *server_ticks - .system_ticks - .get(&tick) - .unwrap_or(&Tick::new(0)); + buffers.reserve(acked_ticks.clients.len()); + for (index, (&client_id, &tick)) in acked_ticks.clients.iter().enumerate() { + let system_tick = *acked_ticks.system_ticks.get(&tick).unwrap_or(&Tick::new(0)); if let Some(buffer) = buffers.get_mut(index) { - buffer.reset(client_id, system_tick, server_ticks.current_tick)?; + buffer.reset(client_id, system_tick, network_tick)?; } else { buffers.push(ReplicationBuffer::new( client_id, system_tick, - server_ticks.current_tick, + network_tick, )?); } } - Ok(&mut buffers[..server_ticks.acked_ticks.len()]) + Ok(&mut buffers[..acked_ticks.clients.len()]) } /// Collect component changes into buffers based on last acknowledged tick. @@ -377,6 +395,8 @@ pub enum TickPolicy { /// /// By default it's 30 updates per second. MaxTickRate(u16), + /// Send updates from server every frame. + EveryFrame, /// [`ServerSet::Send`] should be manually configured. Manual, } @@ -385,43 +405,30 @@ pub enum TickPolicy { /// /// Used only on server. #[derive(Resource, Default)] -pub struct ServerTicks { - /// Current server tick. - current_tick: NetworkTick, - +pub struct AckedTicks { /// Last acknowledged server ticks for all clients. - acked_ticks: HashMap, + clients: HashMap, /// Stores mapping from server ticks to system change ticks. system_ticks: HashMap, } -impl ServerTicks { - /// Increments current tick by 1 and makes corresponding system tick mapping for it. - fn increment(&mut self, system_tick: Tick) { - self.current_tick.increment(); - self.system_ticks.insert(self.current_tick, system_tick); +impl AckedTicks { + /// Stores mapping between `network_tick` and the current `system_tick`. + fn register_network_tick(&mut self, network_tick: NetworkTick, system_tick: Tick) { + self.system_ticks.insert(network_tick, system_tick); } /// Removes system tick mappings for acks that was acknowledged by everyone. fn cleanup_system_ticks(&mut self) { - self.system_ticks.retain(|tick, _| { - self.acked_ticks - .values() - .any(|acked_tick| acked_tick <= tick) - }) - } - - /// Returns current server tick. - #[inline] - pub fn current_tick(&self) -> NetworkTick { - self.current_tick + self.system_ticks + .retain(|tick, _| self.clients.values().any(|acked_tick| acked_tick <= tick)); } /// Returns last acknowledged server ticks for all clients. #[inline] pub fn acked_ticks(&self) -> &HashMap { - &self.acked_ticks + &self.clients } } @@ -468,10 +475,10 @@ impl ReplicationBuffer { fn new( client_id: u64, system_tick: Tick, - current_tick: NetworkTick, + network_tick: NetworkTick, ) -> Result { let mut message = Default::default(); - bincode::serialize_into(&mut message, ¤t_tick)?; + bincode::serialize_into(&mut message, &network_tick)?; Ok(Self { client_id, system_tick, @@ -494,14 +501,14 @@ impl ReplicationBuffer { &mut self, client_id: u64, system_tick: Tick, - current_tick: NetworkTick, + network_tick: NetworkTick, ) -> Result<(), bincode::Error> { self.client_id = client_id; self.system_tick = system_tick; self.message.set_position(0); self.message.get_mut().clear(); self.arrays_with_data = 0; - bincode::serialize_into(&mut self.message, ¤t_tick)?; + bincode::serialize_into(&mut self.message, &network_tick)?; Ok(()) } diff --git a/src/server/despawn_tracker.rs b/src/server/despawn_tracker.rs index 2e58899d..e543a383 100644 --- a/src/server/despawn_tracker.rs +++ b/src/server/despawn_tracker.rs @@ -5,7 +5,7 @@ use bevy::{ }; use bevy_renet::renet::RenetServer; -use super::{ServerSet, ServerTicks}; +use super::{AckedTicks, ServerSet}; use crate::replicon_core::replication_rules::Replication; /// Tracks entity despawns of entities with [`Replication`] component in [`DespawnTracker`] resource. @@ -44,11 +44,11 @@ impl DespawnTrackerPlugin { fn cleanup_system( change_tick: SystemChangeTick, mut despawn_tracker: ResMut, - server_ticks: Res, + acked_ticks: Res, ) { despawn_tracker.despawns.retain(|(_, tick)| { - server_ticks.acked_ticks.values().any(|acked_tick| { - let system_tick = *server_ticks + acked_ticks.clients.values().any(|acked_tick| { + let system_tick = *acked_ticks .system_ticks .get(acked_tick) .unwrap_or(&Tick::new(0)); @@ -95,15 +95,15 @@ mod tests { let mut app = App::new(); app.add_plugins(DespawnTrackerPlugin) .insert_resource(RenetServer::new(Default::default())) - .init_resource::(); + .init_resource::(); app.update(); // To avoid cleanup. const DUMMY_CLIENT_ID: u64 = 0; app.world - .resource_mut::() - .acked_ticks + .resource_mut::() + .clients .insert(DUMMY_CLIENT_ID, NetworkTick::new(0)); let replicated_entity = app.world.spawn(Replication).id(); diff --git a/src/server/removal_tracker.rs b/src/server/removal_tracker.rs index e8ffef5e..5a1790c0 100644 --- a/src/server/removal_tracker.rs +++ b/src/server/removal_tracker.rs @@ -5,7 +5,7 @@ use bevy::{ }; use bevy_renet::renet::RenetServer; -use super::{ServerSet, ServerTicks}; +use super::{AckedTicks, ServerSet}; use crate::replicon_core::replication_rules::{Replication, ReplicationId, ReplicationRules}; /// Stores component removals in [`RemovalTracker`] component to make them persistent across ticks. @@ -41,13 +41,13 @@ impl RemovalTrackerPlugin { /// Cleanups all acknowledged despawns. fn cleanup_system( change_tick: SystemChangeTick, - server_ticks: Res, + acked_ticks: Res, mut removal_trackers: Query<&mut RemovalTracker>, ) { for mut removal_tracker in &mut removal_trackers { removal_tracker.retain(|_, tick| { - server_ticks.acked_ticks.values().any(|acked_tick| { - let system_tick = *server_ticks + acked_ticks.clients.values().any(|acked_tick| { + let system_tick = *acked_ticks .system_ticks .get(acked_tick) .unwrap_or(&Tick::new(0)); @@ -94,7 +94,7 @@ mod tests { let mut app = App::new(); app.add_plugins(RemovalTrackerPlugin) .insert_resource(RenetServer::new(Default::default())) - .init_resource::() + .init_resource::() .init_resource::() .replicate::(); @@ -103,8 +103,8 @@ mod tests { // To avoid cleanup. const DUMMY_CLIENT_ID: u64 = 0; app.world - .resource_mut::() - .acked_ticks + .resource_mut::() + .clients .insert(DUMMY_CLIENT_ID, NetworkTick::new(0)); let replicated_entity = app.world.spawn((DummyComponent, Replication)).id(); diff --git a/tests/replication.rs b/tests/replication.rs index 237060ff..27273c4d 100644 --- a/tests/replication.rs +++ b/tests/replication.rs @@ -13,7 +13,7 @@ fn acked_ticks_cleanup() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )); } @@ -27,8 +27,8 @@ fn acked_ticks_cleanup() { server_app.update(); server_app.update(); - let server_ticks = server_app.world.resource::(); - assert!(!server_ticks.acked_ticks().contains_key(&client_id)); + let acked_ticks = server_app.world.resource::(); + assert!(!acked_ticks.acked_ticks().contains_key(&client_id)); } #[test] @@ -38,7 +38,7 @@ fn tick_acks_receiving() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )); } @@ -47,7 +47,7 @@ fn tick_acks_receiving() { client_app.update(); server_app.update(); - let acked_ticks = server_app.world.resource::(); + let acked_ticks = server_app.world.resource::(); let client_id = client_app .world .resource::() @@ -63,7 +63,7 @@ fn spawn_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .replicate::(); } @@ -96,10 +96,11 @@ fn spawn_replication() { fn insert_replication() { let mut server_app = App::new(); let mut client_app = App::new(); + for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .replicate::() .replicate::() @@ -140,6 +141,7 @@ fn insert_replication() { assert!(client_entity.contains::()); assert!(client_entity.contains::()); assert!(!client_entity.contains::()); + assert!(!client_entity.contains::()); assert_eq!( client_entity.get::().unwrap().0, client_map_entity @@ -153,7 +155,7 @@ fn removal_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .replicate::(); } @@ -197,7 +199,7 @@ fn despawn_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )); } diff --git a/tests/server_event.rs b/tests/server_event.rs index 654f4541..56f42d4c 100644 --- a/tests/server_event.rs +++ b/tests/server_event.rs @@ -34,7 +34,7 @@ fn sending_receiving() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .add_server_event::(SendPolicy::Ordered); } @@ -79,7 +79,7 @@ fn sending_receiving_and_mapping() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .add_mapped_server_event::(SendPolicy::Ordered); } @@ -120,7 +120,7 @@ fn sending_receiving_reflect() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .register_type::() .add_server_reflect_event::( @@ -171,7 +171,7 @@ fn sending_receiving_and_mapping_reflect() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .register_type::() .add_mapped_server_reflect_event::(SendPolicy::Ordered); @@ -214,7 +214,7 @@ fn local_resending() { let mut app = App::new(); app.add_plugins(( TimePlugin, - ReplicationPlugins.set(ServerPlugin::new(TickPolicy::Manual)), + ReplicationPlugins.set(ServerPlugin::new(TickPolicy::EveryFrame)), )) .add_server_event::(SendPolicy::Ordered);