diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8871a30a..fa793623 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,6 +68,27 @@ jobs: - name: Test doc run: cargo test --all-features --doc + feature-combinations: + name: Feature combinations + runs-on: ubuntu-latest + steps: + - name: Clone repo + uses: actions/checkout@v4 + + - name: Instal stable toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache crates + uses: Swatinem/rust-cache@v2 + + - name: Install Cargo Hack + run: cargo install cargo-hack + + - name: Check feature combinations + run: cargo hack check --feature-powerset + env: + RUSTFLAGS: -Aunused -Dwarnings + test: name: Test runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce852cc..bd745585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `ServerEventsPlugin` and `ClientEventsPlugin` can be disabled on client-only and server-only apps respectively. -- Put `ClientDiagnosticsPlugin` under `diagnostics` feature (disabled by default) and make it part of the `RepliconPlugins` group. +- Put `ClientDiagnosticsPlugin` under `client_diagnostics` feature (disabled by default) and make it part of the `RepliconPlugins` group. - Put `scene` module under `scene` feature (enabled by default). - Put `parent_sync` module under `parent_sync` feature (enabled by default). +- Put `client` module under `client` feature (enabled by default). +- Put `server` module under `server` feature (enabled by default). +- `TestFnsEntityExt::serialize` now accepts `RepliconTick` for server tick instead of using `ServerTick` resource internally. +- Move `replicon_client`, `server_entity_map`, `replicon_server`, `connected_clients` under `core` module. These modules are needed for both client and server. +- Move `VisibilityPolicy` to `connected_clients` module. - Do not divide values per seconds by the number of messages for `ClientDiagnosticsPlugin`. - Move `server::events::event_data` module to `core::event_registry::server_event`. - Move `client::events::event_data` module to `core::event_registry::client_event`. diff --git a/Cargo.toml b/Cargo.toml index de5b108a..88574c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,10 +46,16 @@ criterion = { version = "0.5", default-features = false, features = [ ] } [features] -default = ["scene", "parent_sync"] +default = ["scene", "parent_sync", "client", "server"] -# Integration with Bevy diagnostics. -diagnostics = [] +# Client-related logic. +client = [] + +# Server-related logic. +server = [] + +# Integration with Bevy diagnostics for client. +client_diagnostics = ["client"] # Replication into a scene. scene = ["bevy/bevy_scene"] @@ -62,13 +68,53 @@ name = "replication" harness = false [[test]] -name = "stats" -required-features = ["diagnostics"] +name = "changes" +required-features = ["client", "server"] + +[[test]] +name = "client_event" +required-features = ["client", "server"] + +[[test]] +name = "connection" +required-features = ["client", "server"] + +[[test]] +name = "despawn" +required-features = ["client", "server"] + +[[test]] +name = "fns" +required-features = ["client"] + +[[test]] +name = "insertion" +required-features = ["client", "server"] + +[[test]] +name = "removal" +required-features = ["client", "server"] [[test]] name = "scene" required-features = ["scene"] +[[test]] +name = "server_event" +required-features = ["client", "server"] + +[[test]] +name = "spawn" +required-features = ["client", "server"] + +[[test]] +name = "stats" +required-features = ["client_diagnostics", "client", "server"] + +[[test]] +name = "visibility" +required-features = ["client", "server"] + [lints.clippy] type_complexity = "allow" too_many_arguments = "allow" diff --git a/src/client.rs b/src/client.rs index 9488008e..d87e25eb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,7 @@ pub mod confirm_history; -#[cfg(feature = "diagnostics")] +#[cfg(feature = "client_diagnostics")] pub mod diagnostics; pub mod events; -pub mod replicon_client; -pub mod server_entity_map; use std::{io::Cursor, mem}; @@ -18,12 +16,12 @@ use crate::core::{ common_conditions::{client_connected, client_just_connected, client_just_disconnected}, ctx::{DespawnCtx, RemoveCtx, WriteCtx}, replication_registry::ReplicationRegistry, + replicon_client::RepliconClient, replicon_tick::RepliconTick, + server_entity_map::ServerEntityMap, Replicated, }; use confirm_history::ConfirmHistory; -use replicon_client::RepliconClient; -use server_entity_map::ServerEntityMap; /// Client functionality and replication receiving. /// diff --git a/src/client/events.rs b/src/client/events.rs index 9cf59616..841f9854 100644 --- a/src/client/events.rs +++ b/src/client/events.rs @@ -1,11 +1,10 @@ -use super::{ - replicon_client::RepliconClient, server_entity_map::ServerEntityMap, ClientPlugin, ClientSet, - ServerInitTick, -}; +use super::{ClientPlugin, ClientSet, ServerInitTick}; use crate::core::{ common_conditions::*, ctx::{ClientReceiveCtx, ClientSendCtx}, event_registry::EventRegistry, + replicon_client::RepliconClient, + server_entity_map::ServerEntityMap, }; use bevy::prelude::*; diff --git a/src/core.rs b/src/core.rs index bd694a46..e23a941c 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,11 +1,15 @@ pub mod channels; pub mod command_markers; pub mod common_conditions; +pub mod connected_clients; pub mod ctx; pub mod event_registry; pub mod replication_registry; pub mod replication_rules; +pub mod replicon_client; +pub mod replicon_server; pub mod replicon_tick; +pub mod server_entity_map; use bevy::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/src/core/common_conditions.rs b/src/core/common_conditions.rs index 364e8745..5768f65d 100644 --- a/src/core/common_conditions.rs +++ b/src/core/common_conditions.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use crate::{client::replicon_client::RepliconClient, server::replicon_server::RepliconServer}; +use super::{replicon_client::RepliconClient, replicon_server::RepliconServer}; /// Returns `true` if the server is running. pub fn server_running(server: Option>) -> bool { diff --git a/src/server/connected_clients.rs b/src/core/connected_clients.rs similarity index 90% rename from src/server/connected_clients.rs rename to src/core/connected_clients.rs index 9b61095c..043eb742 100644 --- a/src/server/connected_clients.rs +++ b/src/core/connected_clients.rs @@ -8,13 +8,12 @@ use bevy::{ utils::{Duration, HashMap}, }; -use crate::{ - core::{replicon_tick::RepliconTick, ClientId}, - server::VisibilityPolicy, -}; +use crate::core::{replicon_tick::RepliconTick, ClientId}; use client_visibility::ClientVisibility; /// Stores information about connected clients. +/// +/// Inserted as resource by [`ServerPlugin`](crate::server::ServerPlugin). #[derive(Resource, Default)] pub struct ConnectedClients { clients: Vec, @@ -22,7 +21,7 @@ pub struct ConnectedClients { } impl ConnectedClients { - pub(super) fn new(policy: VisibilityPolicy) -> Self { + pub(crate) fn new(policy: VisibilityPolicy) -> Self { Self { clients: Default::default(), policy, @@ -106,7 +105,7 @@ impl ConnectedClients { /// Initializes a new [`ConnectedClient`] for this client. /// /// Reuses the memory from the buffers if available. - pub(super) fn add(&mut self, client_buffers: &mut ClientBuffers, client_id: ClientId) { + pub(crate) fn add(&mut self, client_buffers: &mut ClientBuffers, client_id: ClientId) { debug!("adding connected `{client_id:?}`"); let client = if let Some(mut client) = client_buffers.clients.pop() { @@ -122,7 +121,7 @@ impl ConnectedClients { /// Removes a connected client. /// /// Keeps allocated memory in the buffers for reuse. - pub(super) fn remove(&mut self, client_buffers: &mut ClientBuffers, client_id: ClientId) { + pub(crate) fn remove(&mut self, client_buffers: &mut ClientBuffers, client_id: ClientId) { debug!("removing disconnected `{client_id:?}`"); let index = self @@ -138,7 +137,7 @@ impl ConnectedClients { /// Clears all clients. /// /// Keeps allocated memory in the buffers for reuse. - pub(super) fn clear(&mut self, client_buffers: &mut ClientBuffers) { + pub(crate) fn clear(&mut self, client_buffers: &mut ClientBuffers) { for mut client in self.clients.drain(..) { client_buffers.entities.extend(client.drain_entities()); client_buffers.clients.push(client); @@ -200,7 +199,7 @@ impl ConnectedClient { } /// Sets the client's init tick. - pub(super) fn set_init_tick(&mut self, tick: RepliconTick) { + pub(crate) fn set_init_tick(&mut self, tick: RepliconTick) { self.init_tick = tick; } @@ -234,7 +233,7 @@ impl ConnectedClient { /// /// Used later to acknowledge updated entities. #[must_use] - pub(super) fn register_update( + pub(crate) fn register_update( &mut self, client_buffers: &mut ClientBuffers, tick: Tick, @@ -263,7 +262,7 @@ impl ConnectedClient { /// /// The change tick is the reference point for determining if components on an entity have changed and /// need to be replicated. Component changes older than the change limit are assumed to be acked by the client. - pub(super) fn set_change_tick(&mut self, entity: Entity, tick: Tick) { + pub(crate) fn set_change_tick(&mut self, entity: Entity, tick: Tick) { self.change_ticks.insert(entity, tick); } @@ -277,7 +276,7 @@ impl ConnectedClient { /// Change limits for all entities from this update will be set to the update's tick if it's higher. /// /// Keeps allocated memory in the buffers for reuse. - pub(super) fn acknowledge( + pub(crate) fn acknowledge( &mut self, client_buffers: &mut ClientBuffers, tick: Tick, @@ -323,7 +322,7 @@ impl ConnectedClient { /// Drains all entities for which visibility was lost during this tick. /// /// Internal cleanup happens lazily during the iteration. - pub(super) fn drain_lost_visibility(&mut self) -> impl Iterator + '_ { + pub(crate) fn drain_lost_visibility(&mut self) -> impl Iterator + '_ { self.visibility.drain_lost_visibility().inspect(|entity| { self.change_ticks.remove(entity); }) @@ -332,7 +331,7 @@ impl ConnectedClient { /// Removes all updates older then `min_timestamp`. /// /// Keeps allocated memory in the buffers for reuse. - pub(super) fn remove_older_updates( + pub(crate) fn remove_older_updates( &mut self, client_buffers: &mut ClientBuffers, min_timestamp: Duration, @@ -369,3 +368,15 @@ struct UpdateInfo { timestamp: Duration, entities: Vec, } + +/// Controls how visibility will be managed via [`ClientVisibility`]. +#[derive(Default, Debug, Clone, Copy)] +pub enum VisibilityPolicy { + /// All entities are visible by default and visibility can't be changed. + #[default] + All, + /// All entities are visible by default and should be explicitly registered to be hidden. + Blacklist, + /// All entities are hidden by default and should be explicitly registered to be visible. + Whitelist, +} diff --git a/src/server/connected_clients/client_visibility.rs b/src/core/connected_clients/client_visibility.rs similarity index 100% rename from src/server/connected_clients/client_visibility.rs rename to src/core/connected_clients/client_visibility.rs diff --git a/src/core/ctx.rs b/src/core/ctx.rs index 7d5e30b8..02dce0de 100644 --- a/src/core/ctx.rs +++ b/src/core/ctx.rs @@ -1,8 +1,6 @@ use bevy::{prelude::*, reflect::TypeRegistry}; -use crate::{ - client::server_entity_map::ServerEntityMap, core::replicon_tick::RepliconTick, Replicated, -}; +use super::{replicon_tick::RepliconTick, server_entity_map::ServerEntityMap, Replicated}; /// Replication context for serialization function. #[non_exhaustive] diff --git a/src/core/event_registry/client_event.rs b/src/core/event_registry/client_event.rs index 68def4ad..4be6137f 100644 --- a/src/core/event_registry/client_event.rs +++ b/src/core/event_registry/client_event.rs @@ -17,14 +17,12 @@ use bincode::{DefaultOptions, Options}; use serde::{de::DeserializeOwned, Serialize}; use super::EventRegistry; -use crate::{ - client::replicon_client::RepliconClient, - core::{ - channels::{RepliconChannel, RepliconChannels}, - ctx::{ClientSendCtx, ServerReceiveCtx}, - ClientId, - }, - server::replicon_server::RepliconServer, +use crate::core::{ + channels::{RepliconChannel, RepliconChannels}, + ctx::{ClientSendCtx, ServerReceiveCtx}, + replicon_client::RepliconClient, + replicon_server::RepliconServer, + ClientId, }; /// An extension trait for [`App`] for creating client events. diff --git a/src/core/event_registry/server_event.rs b/src/core/event_registry/server_event.rs index 163f32f6..ad0433c0 100644 --- a/src/core/event_registry/server_event.rs +++ b/src/core/event_registry/server_event.rs @@ -18,18 +18,14 @@ use ordered_multimap::ListOrderedMultimap; use serde::{de::DeserializeOwned, Serialize}; use super::EventRegistry; -use crate::{ - client::replicon_client::RepliconClient, - core::{ - channels::{RepliconChannel, RepliconChannels}, - ctx::{ClientReceiveCtx, ServerSendCtx}, - replicon_tick::RepliconTick, - ClientId, - }, - server::{ - connected_clients::{ConnectedClient, ConnectedClients}, - replicon_server::RepliconServer, - }, +use crate::core::{ + channels::{RepliconChannel, RepliconChannels}, + connected_clients::{ConnectedClient, ConnectedClients}, + ctx::{ClientReceiveCtx, ServerSendCtx}, + replicon_client::RepliconClient, + replicon_server::RepliconServer, + replicon_tick::RepliconTick, + ClientId, }; /// An extension trait for [`App`] for creating client events. diff --git a/src/core/replication_registry/test_fns.rs b/src/core/replication_registry/test_fns.rs index b498749a..44a02378 100644 --- a/src/core/replication_registry/test_fns.rs +++ b/src/core/replication_registry/test_fns.rs @@ -3,14 +3,11 @@ use std::io::Cursor; use bevy::{ecs::world::CommandQueue, prelude::*}; use super::{FnsInfo, ReplicationRegistry}; -use crate::{ - client::server_entity_map::ServerEntityMap, - core::{ - command_markers::{CommandMarkers, EntityMarkers}, - ctx::{DespawnCtx, RemoveCtx, SerializeCtx, WriteCtx}, - replicon_tick::RepliconTick, - }, - server::server_tick::ServerTick, +use crate::core::{ + command_markers::{CommandMarkers, EntityMarkers}, + ctx::{DespawnCtx, RemoveCtx, SerializeCtx, WriteCtx}, + replicon_tick::RepliconTick, + server_entity_map::ServerEntityMap, }; /** @@ -25,16 +22,20 @@ This example shows how to call registered functions on an entity: ``` use bevy::prelude::*; use bevy_replicon::{ - core::replication_registry::{rule_fns::RuleFns, test_fns::TestFnsEntityExt, ReplicationRegistry}, + core::{ + replication_registry::{ + rule_fns::RuleFns, test_fns::TestFnsEntityExt, ReplicationRegistry, + }, + replicon_tick::RepliconTick, + }, prelude::*, - server::server_tick::ServerTick, }; use serde::{Deserialize, Serialize}; let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); -let tick = **app.world().resource::(); +let tick = RepliconTick::default(); // Register rule functions manually to obtain `FnsInfo`. let fns_info = app @@ -44,7 +45,7 @@ let fns_info = app }); let mut entity = app.world_mut().spawn(DummyComponent); -let data = entity.serialize(fns_info); +let data = entity.serialize(fns_info, tick); entity.remove::(); entity.apply_write(&data, fns_info, tick); @@ -65,7 +66,7 @@ pub trait TestFnsEntityExt { /// /// See also [`ReplicationRegistry::register_rule_fns`]. #[must_use] - fn serialize(&mut self, fns_info: FnsInfo) -> Vec; + fn serialize(&mut self, fns_info: FnsInfo, server_tick: RepliconTick) -> Vec; /// Deserializes a component using a registered function for it and /// writes it into an entity using a write function based on markers. @@ -88,10 +89,9 @@ pub trait TestFnsEntityExt { } impl TestFnsEntityExt for EntityWorldMut<'_> { - fn serialize(&mut self, fns_info: FnsInfo) -> Vec { + fn serialize(&mut self, fns_info: FnsInfo, server_tick: RepliconTick) -> Vec { let registry = self.world().resource::(); let (component_fns, rule_fns) = registry.get(fns_info.fns_id()); - let server_tick = **self.world().resource::(); let mut cursor = Cursor::default(); let ctx = SerializeCtx { server_tick }; let ptr = self.get_by_id(fns_info.component_id()).unwrap_or_else(|| { diff --git a/src/client/replicon_client.rs b/src/core/replicon_client.rs similarity index 95% rename from src/client/replicon_client.rs rename to src/core/replicon_client.rs index 43db8beb..74d98f45 100644 --- a/src/client/replicon_client.rs +++ b/src/core/replicon_client.rs @@ -10,10 +10,12 @@ use crate::core::ClientId; /// [`Self::set_status`] should be used to reflect this. /// - For receiving messages, [`Self::insert_received`] should be to used. /// A system to forward backend messages to Replicon should run in -/// [`ClientSet::ReceivePackets`](super::ClientSet::ReceivePackets). +/// [`ClientSet::ReceivePackets`](crate::client::ClientSet::ReceivePackets). /// - For sending messages, [`Self::drain_sent`] should be used to drain all sent messages. /// A system to forward Replicon messages to the backend should run in -/// [`ClientSet::SendPackets`](super::ClientSet::SendPackets). +/// [`ClientSet::SendPackets`](crate::client::ClientSet::SendPackets). +/// +/// Inserted as resource by [`ClientPlugin`](crate::client::ClientPlugin). #[derive(Resource, Default)] pub struct RepliconClient { /// Client connection status. @@ -31,7 +33,7 @@ pub struct RepliconClient { impl RepliconClient { /// Changes the size of the receive messages storage according to the number of server channels. - pub(super) fn setup_server_channels(&mut self, channels_count: usize) { + pub(crate) fn setup_server_channels(&mut self, channels_count: usize) { self.received_messages.resize(channels_count, Vec::new()); } diff --git a/src/server/replicon_server.rs b/src/core/replicon_server.rs similarity index 93% rename from src/server/replicon_server.rs rename to src/core/replicon_server.rs index 88d46e58..6cad95d6 100644 --- a/src/server/replicon_server.rs +++ b/src/core/replicon_server.rs @@ -8,9 +8,11 @@ use crate::core::ClientId; /// The messaging backend is responsible for updating this resource: /// - When the server is started or stopped, [`Self::set_running`] should be used to reflect this. /// - For receiving messages, [`Self::insert_received`] should be used. -/// A system to forward messages from the backend to Replicon should run in [`ServerSet::ReceivePackets`](super::ServerSet::ReceivePackets). +/// A system to forward messages from the backend to Replicon should run in [`ServerSet::ReceivePackets`](crate::server::ServerSet::ReceivePackets). /// - For sending messages, [`Self::drain_sent`] should be used to drain all sent messages. -/// A system to forward messages from Replicon to the backend should run in [`ServerSet::SendPackets`](super::ServerSet::SendPackets). +/// A system to forward messages from Replicon to the backend should run in [`ServerSet::SendPackets`](crate::server::ServerSet::SendPackets). +/// +/// Inserted as resource by [`ServerPlugin`](crate::server::ServerPlugin). #[derive(Resource, Default)] pub struct RepliconServer { /// Indicates if the server is open for connections. @@ -30,12 +32,12 @@ pub struct RepliconServer { impl RepliconServer { /// Changes the size of the receive messages storage according to the number of client channels. - pub(super) fn setup_client_channels(&mut self, channels_count: usize) { + pub(crate) fn setup_client_channels(&mut self, channels_count: usize) { self.received_messages.resize(channels_count, Vec::new()); } /// Removes a disconnected client. - pub(super) fn remove_client(&mut self, client_id: ClientId) { + pub(crate) fn remove_client(&mut self, client_id: ClientId) { for receive_channel in &mut self.received_messages { receive_channel.retain(|&(sender_id, _)| sender_id != client_id); } diff --git a/src/client/server_entity_map.rs b/src/core/server_entity_map.rs similarity index 92% rename from src/client/server_entity_map.rs rename to src/core/server_entity_map.rs index 334842a6..2b6a38ae 100644 --- a/src/client/server_entity_map.rs +++ b/src/core/server_entity_map.rs @@ -4,6 +4,8 @@ use bevy::{ecs::entity::EntityHashMap, prelude::*, utils::hashbrown::hash_map::E /// /// If [`ClientSet::Reset`](crate::client::ClientSet) is disabled, then this needs to be cleaned up manually /// via [`Self::remove_by_client`] or [`Self::clear`]. +/// +/// Inserted as resource by [`ClientPlugin`](crate::client::ClientPlugin). #[derive(Default, Resource)] pub struct ServerEntityMap { server_to_client: EntityHashMap, @@ -34,7 +36,7 @@ impl ServerEntityMap { /// /// ``` /// # use bevy::{ecs::world::CommandQueue, prelude::*}; - /// # use bevy_replicon::{client::server_entity_map::ServerEntityMap, prelude::*}; + /// # use bevy_replicon::{core::server_entity_map::ServerEntityMap, prelude::*}; /// # let mut entity_map = ServerEntityMap::default(); /// # let mut queue = CommandQueue::default(); /// # let world = World::default(); @@ -58,11 +60,11 @@ impl ServerEntityMap { } } - pub(super) fn get_by_server(&mut self, server_entity: Entity) -> Option { + pub(crate) fn get_by_server(&mut self, server_entity: Entity) -> Option { self.server_to_client.get(&server_entity).copied() } - pub(super) fn remove_by_server(&mut self, server_entity: Entity) -> Option { + pub(crate) fn remove_by_server(&mut self, server_entity: Entity) -> Option { let client_entity = self.server_to_client.remove(&server_entity); if let Some(client_entity) = client_entity { self.client_to_server.remove(&client_entity); diff --git a/src/lib.rs b/src/lib.rs index 8564c575..923b75ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,8 @@ If you are planning to separate client and server you can use [`PluginGroupBuilder::disable()`] to disable [`ClientPlugin`] or [`ServerPlugin`] on [`RepliconPlugins`]. You will need to disable similar plugins on your messaing library of choice too. +You can also use `client` or `server` features to control these plugins at compile time. + Typically updates are not sent every frame. Instead, they are sent at a certain interval to save traffic. You can change the defaults with [`TickPolicy`] in the [`ServerPlugin`]: @@ -441,13 +443,16 @@ To reduce packet size there are the following limits per replication update: */ #![cfg_attr(docsrs, feature(doc_auto_cfg))] +#[cfg(feature = "client")] pub mod client; pub mod core; #[cfg(feature = "parent_sync")] pub mod parent_sync; #[cfg(feature = "scene")] pub mod scene; +#[cfg(feature = "server")] pub mod server; +#[cfg(all(feature = "server", feature = "client"))] pub mod test_app; pub mod prelude { @@ -455,35 +460,37 @@ pub mod prelude { pub use super::core::Replication; pub use super::{ - client::{ - events::ClientEventsPlugin, - replicon_client::{RepliconClient, RepliconClientStatus}, - ClientPlugin, ClientSet, ClientStats, - }, core::{ channels::{ChannelKind, RepliconChannel, RepliconChannels}, command_markers::AppMarkerExt, common_conditions::*, + connected_clients::{ + client_visibility::ClientVisibility, ConnectedClient, ConnectedClients, + VisibilityPolicy, + }, event_registry::{ client_event::{ClientEventAppExt, FromClient}, server_event::{SendMode, ServerEventAppExt, ToClients}, }, replication_rules::AppRuleExt, - ClientId, Replicated, RepliconCorePlugin, - }, - server::{ - client_entity_map::{ClientEntityMap, ClientMapping}, - connected_clients::{ - client_visibility::ClientVisibility, ConnectedClient, ConnectedClients, - }, - events::ServerEventsPlugin, + replicon_client::{RepliconClient, RepliconClientStatus}, replicon_server::RepliconServer, - ServerEvent, ServerPlugin, ServerSet, TickPolicy, VisibilityPolicy, + ClientId, Replicated, RepliconCorePlugin, }, RepliconPlugins, }; - #[cfg(feature = "diagnostics")] + #[cfg(feature = "client")] + pub use super::client::{events::ClientEventsPlugin, ClientPlugin, ClientSet, ClientStats}; + + #[cfg(feature = "server")] + pub use super::server::{ + client_entity_map::{ClientEntityMap, ClientMapping}, + events::ServerEventsPlugin, + ServerEvent, ServerPlugin, ServerSet, TickPolicy, + }; + + #[cfg(feature = "client_diagnostics")] pub use super::client::diagnostics::ClientDiagnosticsPlugin; #[cfg(feature = "parent_sync")] pub use super::parent_sync::{ParentSync, ParentSyncPlugin}; @@ -500,19 +507,24 @@ pub struct RepliconPlugins; impl PluginGroup for RepliconPlugins { fn build(self) -> PluginGroupBuilder { let mut group = PluginGroupBuilder::start::(); - group = group - .add(RepliconCorePlugin) - .add(ClientPlugin) - .add(ServerPlugin::default()) - .add(ClientEventsPlugin) - .add(ServerEventsPlugin); + group = group.add(RepliconCorePlugin); + + #[cfg(feature = "server")] + { + group = group.add(ServerPlugin::default()).add(ServerEventsPlugin); + } + + #[cfg(feature = "client")] + { + group = group.add(ClientPlugin).add(ClientEventsPlugin); + } #[cfg(feature = "parent_sync")] { group = group.add(ParentSyncPlugin); } - #[cfg(feature = "diagnostics")] + #[cfg(feature = "client_diagnostics")] { group = group.add(ClientDiagnosticsPlugin); } diff --git a/src/parent_sync.rs b/src/parent_sync.rs index 40e934f9..bc087b26 100644 --- a/src/parent_sync.rs +++ b/src/parent_sync.rs @@ -7,11 +7,11 @@ use bevy::{ }; use serde::{Deserialize, Serialize}; -use crate::{ - client::ClientSet, - core::{common_conditions::has_authority, replication_rules::AppRuleExt}, - server::ServerSet, -}; +#[cfg(feature = "client")] +use crate::client::ClientSet; +use crate::core::{common_conditions::has_authority, replication_rules::AppRuleExt}; +#[cfg(feature = "server")] +use crate::server::ServerSet; pub struct ParentSyncPlugin; @@ -27,17 +27,21 @@ pub struct ParentSyncPlugin; impl Plugin for ParentSyncPlugin { fn build(&self, app: &mut App) { app.register_type::() - .replicate_mapped::() - .add_systems( - PreUpdate, - Self::sync_hierarchy.in_set(ClientSet::SyncHierarchy), - ) - .add_systems( - PostUpdate, - (Self::store_changes, Self::store_removals) - .run_if(has_authority) - .in_set(ServerSet::StoreHierarchy), - ); + .replicate_mapped::(); + + #[cfg(feature = "client")] + app.add_systems( + PreUpdate, + Self::sync_hierarchy.in_set(ClientSet::SyncHierarchy), + ); + + #[cfg(feature = "server")] + app.add_systems( + PostUpdate, + (Self::store_changes, Self::store_removals) + .run_if(has_authority) + .in_set(ServerSet::StoreHierarchy), + ); } } @@ -45,6 +49,7 @@ impl ParentSyncPlugin { /// Synchronizes hierarchy if [`ParentSync`] changes. /// /// Runs not only on clients, but also on server in order to update the hierarchy when the server state is deserialized. + #[cfg(feature = "client")] fn sync_hierarchy( mut commands: Commands, hierarchy: Query<(Entity, &ParentSync, Option<&Parent>), Changed>, @@ -60,12 +65,14 @@ impl ParentSyncPlugin { } } + #[cfg(feature = "server")] fn store_changes(mut hierarchy: Query<(&Parent, &mut ParentSync), Changed>) { for (parent, mut parent_sync) in &mut hierarchy { parent_sync.0 = Some(**parent); } } + #[cfg(feature = "server")] fn store_removals( mut removed_parents: RemovedComponents, mut hierarchy: Query<&mut ParentSync>, @@ -109,7 +116,7 @@ impl MapEntities for ParentSync { } } -#[cfg(test)] +#[cfg(all(test, feature = "server", feature = "client"))] mod tests { use bevy::scene::ScenePlugin; diff --git a/src/server.rs b/src/server.rs index 297c952e..1c189fc3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,11 +1,9 @@ pub mod client_entity_map; -pub mod connected_clients; pub(super) mod despawn_buffer; pub mod events; pub(super) mod removal_buffer; pub(super) mod replicated_archetypes; pub(super) mod replication_messages; -pub mod replicon_server; pub mod server_tick; use std::{io::Cursor, mem, time::Duration}; @@ -26,21 +24,22 @@ use bevy::{ use crate::core::{ channels::{ReplicationChannel, RepliconChannels}, common_conditions::{server_just_stopped, server_running}, + connected_clients::{ + client_visibility::Visibility, ClientBuffers, ConnectedClient, ConnectedClients, + VisibilityPolicy, + }, ctx::SerializeCtx, replication_registry::ReplicationRegistry, replication_rules::ReplicationRules, + replicon_server::RepliconServer, replicon_tick::RepliconTick, ClientId, }; use client_entity_map::ClientEntityMap; -use connected_clients::{ - client_visibility::Visibility, ClientBuffers, ConnectedClient, ConnectedClients, -}; use despawn_buffer::{DespawnBuffer, DespawnBufferPlugin}; use removal_buffer::{RemovalBuffer, RemovalBufferPlugin}; use replicated_archetypes::ReplicatedArchetypes; use replication_messages::ReplicationMessages; -use replicon_server::RepliconServer; use server_tick::ServerTick; pub struct ServerPlugin { @@ -586,18 +585,6 @@ pub enum TickPolicy { Manual, } -/// Controls how visibility will be managed via [`ClientVisibility`](connected_clients::client_visibility::ClientVisibility). -#[derive(Default, Debug, Clone, Copy)] -pub enum VisibilityPolicy { - /// All entities are visible by default and visibility can't be changed. - #[default] - All, - /// All entities are visible by default and should be explicitly registered to be hidden. - Blacklist, - /// All entities are hidden by default and should be explicitly registered to be visible. - Whitelist, -} - /// Connection and disconnection events on the server. /// /// The messaging backend is responsible for emitting these in [`ServerSet::SendEvents`]. diff --git a/src/server/client_entity_map.rs b/src/server/client_entity_map.rs index baf00c10..e07c258e 100644 --- a/src/server/client_entity_map.rs +++ b/src/server/client_entity_map.rs @@ -5,7 +5,7 @@ use crate::core::ClientId; /** A resource that exists on the server for mapping server entities to entities that clients have already spawned. The mappings are sent to clients as part of replication -and injected into the client's [`ServerEntityMap`](crate::client::server_entity_map::ServerEntityMap). +and injected into the client's [`ServerEntityMap`](crate::core::server_entity_map::ServerEntityMap). Sometimes you don't want to wait for the server to spawn something before it appears on the client – when a client performs an action, they can immediately simulate it on the client, @@ -16,9 +16,9 @@ In this situation, the client can send the server its pre-spawned entity id, the and inject the [`ClientMapping`] into its [`ClientEntityMap`]. Replication packets will send a list of such mappings to clients, which will -be inserted into the client's [`ServerEntityMap`](crate::client::server_entity_map::ServerEntityMap). Using replication +be inserted into the client's [`ServerEntityMap`](crate::core::server_entity_map::ServerEntityMap). Using replication to propagate the mappings ensures any replication messages related to the pre-mapped -server entities will synchronize with updating the client's [`ServerEntityMap`](crate::client::server_entity_map::ServerEntityMap). +server entities will synchronize with updating the client's [`ServerEntityMap`](crate::core::server_entity_map::ServerEntityMap). ### Example: @@ -76,7 +76,7 @@ impl ClientEntityMap { /// Registers `mapping` for a client entity pre-spawned by the specified client. /// /// This will be sent as part of replication data and added to the client's - /// [`ServerEntityMap`](crate::client::server_entity_map::ServerEntityMap). + /// [`ServerEntityMap`](crate::core::server_entity_map::ServerEntityMap). pub fn insert(&mut self, client_id: ClientId, mapping: ClientMapping) { self.0.entry(client_id).or_default().push(mapping); } diff --git a/src/server/despawn_buffer.rs b/src/server/despawn_buffer.rs index 65a6b5d7..4b3c6f8e 100644 --- a/src/server/despawn_buffer.rs +++ b/src/server/despawn_buffer.rs @@ -40,7 +40,7 @@ pub(crate) struct DespawnBuffer(Vec); #[cfg(test)] mod tests { use super::*; - use crate::server::replicon_server::RepliconServer; + use crate::core::replicon_server::RepliconServer; #[test] fn despawns() { diff --git a/src/server/events.rs b/src/server/events.rs index d2aa8cc3..716f5216 100644 --- a/src/server/events.rs +++ b/src/server/events.rs @@ -1,12 +1,12 @@ use bevy::prelude::*; -use super::{ - connected_clients::ConnectedClients, replicon_server::RepliconServer, ServerPlugin, ServerSet, -}; +use super::{ServerPlugin, ServerSet}; use crate::core::{ common_conditions::*, + connected_clients::ConnectedClients, ctx::{ServerReceiveCtx, ServerSendCtx}, event_registry::EventRegistry, + replicon_server::RepliconServer, }; /// Sending events from the server to clients. diff --git a/src/server/removal_buffer.rs b/src/server/removal_buffer.rs index 70a5aa33..888189fe 100644 --- a/src/server/removal_buffer.rs +++ b/src/server/removal_buffer.rs @@ -209,11 +209,9 @@ mod tests { use serde::{Deserialize, Serialize}; use super::*; - use crate::{ - core::{ - replication_registry::ReplicationRegistry, replication_rules::AppRuleExt, Replicated, - }, - server::replicon_server::RepliconServer, + use crate::core::{ + replication_registry::ReplicationRegistry, replication_rules::AppRuleExt, + replicon_server::RepliconServer, Replicated, }; #[test] diff --git a/src/server/replication_messages.rs b/src/server/replication_messages.rs index b3e2081c..6030d878 100644 --- a/src/server/replication_messages.rs +++ b/src/server/replication_messages.rs @@ -9,16 +9,13 @@ use bincode::{DefaultOptions, Options}; use bytes::Bytes; use varint_rs::VarintWriter; -use super::{ - client_entity_map::ClientMapping, - connected_clients::{ClientBuffers, ConnectedClients}, - replicon_server::RepliconServer, - ConnectedClient, -}; +use super::{client_entity_map::ClientMapping, ConnectedClient}; use crate::core::{ channels::ReplicationChannel, + connected_clients::{ClientBuffers, ConnectedClients}, ctx::SerializeCtx, replication_registry::{component_fns::ComponentFns, rule_fns::UntypedRuleFns, FnsId}, + replicon_server::RepliconServer, replicon_tick::RepliconTick, }; diff --git a/src/test_app.rs b/src/test_app.rs index cebcbe35..91bcdfb7 100644 --- a/src/test_app.rs +++ b/src/test_app.rs @@ -1,9 +1,13 @@ use bevy::prelude::*; use crate::{ - client::replicon_client::{RepliconClient, RepliconClientStatus}, - core::ClientId, - server::{connected_clients::ConnectedClients, replicon_server::RepliconServer, ServerEvent}, + core::{ + connected_clients::ConnectedClients, + replicon_client::{RepliconClient, RepliconClientStatus}, + replicon_server::RepliconServer, + ClientId, + }, + server::ServerEvent, }; /** diff --git a/tests/changes.rs b/tests/changes.rs index ccc17372..3cae9732 100644 --- a/tests/changes.rs +++ b/tests/changes.rs @@ -2,11 +2,12 @@ use std::io::Cursor; use bevy::{ecs::entity::MapEntities, prelude::*, utils::Duration}; use bevy_replicon::{ - client::{confirm_history::ConfirmHistory, server_entity_map::ServerEntityMap, ServerInitTick}, + client::{confirm_history::ConfirmHistory, ServerInitTick}, core::{ command_markers::MarkerConfig, ctx::WriteCtx, replication_registry::{command_fns, rule_fns::RuleFns}, + server_entity_map::ServerEntityMap, }, prelude::*, server::server_tick::ServerTick, diff --git a/tests/client_event.rs b/tests/client_event.rs index 8667f52e..aed6e42a 100644 --- a/tests/client_event.rs +++ b/tests/client_event.rs @@ -4,7 +4,7 @@ use bevy::{ time::TimePlugin, }; use bevy_replicon::{ - client::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, + core::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize}; diff --git a/tests/despawn.rs b/tests/despawn.rs index aacf82e7..1d6e29fd 100644 --- a/tests/despawn.rs +++ b/tests/despawn.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; use bevy_replicon::{ - client::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, + core::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize}; diff --git a/tests/fns.rs b/tests/fns.rs index 45eb4891..7c9c3bfe 100644 --- a/tests/fns.rs +++ b/tests/fns.rs @@ -8,9 +8,9 @@ use bevy_replicon::{ replication_registry::{ command_fns, rule_fns::RuleFns, test_fns::TestFnsEntityExt, ReplicationRegistry, }, + replicon_tick::RepliconTick, }, prelude::*, - server::server_tick::ServerTick, }; use serde::{Deserialize, Serialize}; @@ -20,6 +20,7 @@ fn serialize_missing_component() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -27,7 +28,7 @@ fn serialize_missing_component() { }); let mut entity = app.world_mut().spawn_empty(); - let _ = entity.serialize(fns_info); + let _ = entity.serialize(fns_info, tick); } #[test] @@ -35,7 +36,7 @@ fn write() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -43,7 +44,7 @@ fn write() { }); let mut entity = app.world_mut().spawn(OriginalComponent); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.remove::(); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); @@ -54,7 +55,7 @@ fn remove() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -72,7 +73,7 @@ fn write_with_command() { app.add_plugins((MinimalPlugins, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -80,7 +81,7 @@ fn write_with_command() { }); let mut entity = app.world_mut().spawn(OriginalComponent); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } @@ -91,7 +92,7 @@ fn remove_with_command() { app.add_plugins((MinimalPlugins, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -113,7 +114,7 @@ fn write_without_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -121,7 +122,7 @@ fn write_without_marker() { }); let mut entity = app.world_mut().spawn(OriginalComponent); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.remove::(); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); @@ -137,7 +138,7 @@ fn remove_without_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -159,7 +160,7 @@ fn write_with_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -167,7 +168,7 @@ fn write_with_marker() { }); let mut entity = app.world_mut().spawn((OriginalComponent, ReplaceMarker)); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } @@ -182,7 +183,7 @@ fn remove_with_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -209,7 +210,7 @@ fn write_with_multiple_markers() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -219,7 +220,7 @@ fn write_with_multiple_markers() { let mut entity = app .world_mut() .spawn((OriginalComponent, ReplaceMarker, DummyMarker)); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!( entity.contains::(), @@ -242,7 +243,7 @@ fn remove_with_mutltiple_markers() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -277,7 +278,7 @@ fn write_with_priority_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -287,7 +288,7 @@ fn write_with_priority_marker() { let mut entity = app .world_mut() .spawn((OriginalComponent, ReplaceMarker, DummyMarker)); - let data = entity.serialize(fns_info); + let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } @@ -310,7 +311,7 @@ fn remove_with_priority_marker() { command_fns::default_remove::, ); - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { @@ -332,7 +333,7 @@ fn despawn() { let mut registry = app.world_mut().resource_mut::(); registry.despawn = mark_despawned; - let tick = **app.world().resource::(); + let tick = RepliconTick::default(); let entity = app.world_mut().spawn_empty(); let id = entity.id(); // Take ID since despawn function consumes entity. entity.apply_despawn(tick); diff --git a/tests/insertion.rs b/tests/insertion.rs index 8013274f..52d6db74 100644 --- a/tests/insertion.rs +++ b/tests/insertion.rs @@ -2,7 +2,7 @@ use std::io::Cursor; use bevy::{ecs::entity::MapEntities, prelude::*}; use bevy_replicon::{ - client::server_entity_map::ServerEntityMap, + core::server_entity_map::ServerEntityMap, core::{ ctx::WriteCtx, replication_registry::{command_fns, rule_fns::RuleFns}, diff --git a/tests/server_event.rs b/tests/server_event.rs index d0f6e9cd..d34e8f67 100644 --- a/tests/server_event.rs +++ b/tests/server_event.rs @@ -4,8 +4,7 @@ use bevy::{ time::TimePlugin, }; use bevy_replicon::{ - client::{server_entity_map::ServerEntityMap, ServerInitTick}, - prelude::*, + client::ServerInitTick, core::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize}; diff --git a/tests/spawn.rs b/tests/spawn.rs index 4414a772..3ca9b4b3 100644 --- a/tests/spawn.rs +++ b/tests/spawn.rs @@ -1,7 +1,6 @@ use bevy::prelude::*; use bevy_replicon::{ - client::{confirm_history::ConfirmHistory, server_entity_map::ServerEntityMap}, - prelude::*, + client::confirm_history::ConfirmHistory, core::server_entity_map::ServerEntityMap, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize};