diff --git a/CHANGELOG.md b/CHANGELOG.md index 3017d656..1329dce2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Rename `RepliconClientStatus` to `ClientState` and `RepliconServerStatus` to `ServerState`. They are now regular Bevy states. As result, we now require `StatesPlugin` to be added. It's present by default in `DefaultPlugins`, but with `MinimalPlugins` you have to add it manually. - `AppRuleExt::replicate_with` now accepts `IntoReplicationRule` trait that allows to define rules with multiple components. - Rename `GroupReplication` into `BundleReplication`. - Rename `AppRuleExt::replicate_group` into `AppRuleExt::replicate_bundle`. @@ -30,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `WriteCtx::commands`. You can now insert and remove components directly through `DeferredEntity`. - Deprecated methods. +- Methods in `RepliconServer` and `RepliconClient` that updated the connection state. Use Bevy's state API with `ServerState` and `ClientState` instead. +- All provided run conditions. Just use `in_state` or `OnEnter`/`OnExit` with `ServerState` and `ClientState` instead. ## [0.33.0] - 2025-04-27 diff --git a/Cargo.toml b/Cargo.toml index b05dbce3..361833aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,9 @@ all-features = true members = ["bevy_replicon_example_backend"] [dependencies] -bevy = { version = "0.16.0", default-features = false } +bevy = { version = "0.16.0", default-features = false, features = [ + "bevy_state", +] } log = "0.4" # Directly depend on `log` like other `no_std` Bevy crates, since `bevy_log` currently requires `std`. petgraph = { version = "0.8", default-features = false, features = [ "stable_graph", diff --git a/benches/related_entities.rs b/benches/related_entities.rs index 4bec624c..f9d1d51e 100644 --- a/benches/related_entities.rs +++ b/benches/related_entities.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::prelude::*; use criterion::{Criterion, criterion_group, criterion_main}; @@ -11,24 +11,25 @@ fn hierarchy_spawning(c: &mut Criterion) { group.bench_function("regular", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); b.iter(|| spawn_then_despawn(&mut app)); }); group.bench_function("related_without_server", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .sync_related_entities::(); b.iter(|| spawn_then_despawn(&mut app)); }); group.bench_function("related", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .sync_related_entities::(); - let mut server = app.world_mut().resource_mut::(); - server.set_running(true); + app.world_mut() + .resource_mut::>() + .set(ServerState::Running); b.iter(|| spawn_then_despawn(&mut app)); }); @@ -45,7 +46,7 @@ fn hierarchy_changes(c: &mut Criterion) { group.bench_function("regular", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); spawn_hierarchy(app.world_mut()); @@ -53,7 +54,7 @@ fn hierarchy_changes(c: &mut Criterion) { }); group.bench_function("related_without_server", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .sync_related_entities::(); spawn_hierarchy(app.world_mut()); @@ -62,13 +63,14 @@ fn hierarchy_changes(c: &mut Criterion) { }); group.bench_function("related", |b| { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .sync_related_entities::(); spawn_hierarchy(app.world_mut()); - let mut server = app.world_mut().resource_mut::(); - server.set_running(true); + app.world_mut() + .resource_mut::>() + .set(ServerState::Running); b.iter(|| trigger_hierarchy_change(&mut app)); }); diff --git a/benches/replication.rs b/benches/replication.rs index ffc02a8d..e3b53d3a 100644 --- a/benches/replication.rs +++ b/benches/replication.rs @@ -1,6 +1,8 @@ use core::time::Duration; -use bevy::{ecs::component::Mutable, platform::time::Instant, prelude::*}; +use bevy::{ + ecs::component::Mutable, platform::time::Instant, prelude::*, state::app::StatesPlugin, +}; use bevy_replicon::{prelude::*, test_app::ServerTestAppExt}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; @@ -184,6 +186,7 @@ fn create_app() -> App { let mut app = App::new(); app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/bevy_replicon_example_backend/Cargo.toml b/bevy_replicon_example_backend/Cargo.toml index 90510fa3..e13501d5 100644 --- a/bevy_replicon_example_backend/Cargo.toml +++ b/bevy_replicon_example_backend/Cargo.toml @@ -28,7 +28,6 @@ fastrand = "2.3" [dev-dependencies] bevy = { version = "0.16.0", default-features = false, features = [ "bevy_gizmos", - "bevy_state", "bevy_text", "bevy_ui_picking_backend", "bevy_ui", diff --git a/bevy_replicon_example_backend/examples/tic_tac_toe.rs b/bevy_replicon_example_backend/examples/tic_tac_toe.rs index fea02f66..5e279cce 100644 --- a/bevy_replicon_example_backend/examples/tic_tac_toe.rs +++ b/bevy_replicon_example_backend/examples/tic_tac_toe.rs @@ -55,19 +55,17 @@ fn main() { .add_systems(OnEnter(GameState::Winner), show_winner_text) .add_systems(OnEnter(GameState::Tie), show_tie_text) .add_systems(OnEnter(GameState::Disconnected), stop_networking) + .add_systems(OnEnter(ClientState::Connected), client_start) + .add_systems(OnEnter(ClientState::Connecting), show_connecting_text) + .add_systems(OnExit(ClientState::Connected), disconnect_by_server) + .add_systems(OnEnter(ServerState::Running), show_waiting_client_text) .add_systems( Update, ( - show_connecting_text.run_if(resource_added::), - show_waiting_client_text.run_if(resource_added::), - client_start.run_if(client_just_connected), - ( - disconnect_by_server.run_if(client_just_disconnected), - update_buttons_background.run_if(local_player_turn), - show_turn_symbol.run_if(resource_changed::), - ) - .run_if(in_state(GameState::InGame)), - ), + update_buttons_background.run_if(local_player_turn), + show_turn_symbol.run_if(resource_changed::), + ) + .run_if(in_state(GameState::InGame)), ) .run(); } diff --git a/bevy_replicon_example_backend/src/client.rs b/bevy_replicon_example_backend/src/client.rs index 9239b71d..cd1f2351 100644 --- a/bevy_replicon_example_backend/src/client.rs +++ b/bevy_replicon_example_backend/src/client.rs @@ -20,9 +20,11 @@ impl Plugin for RepliconExampleClientPlugin { app.add_systems( PreUpdate, ( - set_disconnected.run_if(resource_removed::), - set_connected.run_if(resource_added::), receive_packets.run_if(resource_exists::), + ( + set_disconnected.run_if(resource_removed::), + set_connected.run_if(resource_added::), + ), ) .chain() .in_set(ClientSet::ReceivePackets), @@ -36,12 +38,12 @@ impl Plugin for RepliconExampleClientPlugin { } } -fn set_disconnected(mut replicon_client: ResMut) { - replicon_client.set_status(RepliconClientStatus::Disconnected); +fn set_disconnected(mut state: ResMut>) { + state.set(ClientState::Disconnected); } -fn set_connected(mut replicon_client: ResMut) { - replicon_client.set_status(RepliconClientStatus::Connected); +fn set_connected(mut state: ResMut>) { + state.set(ClientState::Connected); } fn receive_packets( diff --git a/bevy_replicon_example_backend/src/server.rs b/bevy_replicon_example_backend/src/server.rs index 6836e4a6..d3e3feff 100644 --- a/bevy_replicon_example_backend/src/server.rs +++ b/bevy_replicon_example_backend/src/server.rs @@ -20,9 +20,11 @@ impl Plugin for RepliconExampleServerPlugin { app.add_systems( PreUpdate, ( - set_stopped.run_if(resource_removed::), - set_running.run_if(resource_added::), receive_packets.run_if(resource_exists::), + ( + set_stopped.run_if(resource_removed::), + set_running.run_if(resource_added::), + ), ) .chain() .in_set(ServerSet::ReceivePackets), @@ -36,12 +38,12 @@ impl Plugin for RepliconExampleServerPlugin { } } -fn set_stopped(mut server: ResMut) { - server.set_running(false); +fn set_stopped(mut state: ResMut>) { + state.set(ServerState::Stopped); } -fn set_running(mut server: ResMut) { - server.set_running(true); +fn set_running(mut state: ResMut>) { + state.set(ServerState::Running); } fn receive_packets( diff --git a/bevy_replicon_example_backend/tests/backend.rs b/bevy_replicon_example_backend/tests/backend.rs index 2803c189..793369f7 100644 --- a/bevy_replicon_example_backend/tests/backend.rs +++ b/bevy_replicon_example_backend/tests/backend.rs @@ -1,6 +1,6 @@ use std::io; -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::prelude::*; use bevy_replicon_example_backend::{ExampleClient, ExampleServer, RepliconExampleBackendPlugins}; use serde::{Deserialize, Serialize}; @@ -12,6 +12,7 @@ fn connect_disconnect() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -22,13 +23,14 @@ fn connect_disconnect() { setup(&mut server_app, &mut client_app).unwrap(); - assert!(server_app.world().resource::().is_running()); + let server_state = server_app.world().resource::>(); + assert_eq!(*server_state, ServerState::Running); let mut clients = server_app.world_mut().query::<&ConnectedClient>(); assert_eq!(clients.iter(server_app.world()).len(), 1); - let replicon_client = client_app.world().resource::(); - assert!(replicon_client.is_connected()); + let client_state = client_app.world().resource::>(); + assert_eq!(*client_state, ClientState::Connected); let renet_client = client_app.world().resource::(); assert!(renet_client.is_connected()); @@ -40,14 +42,15 @@ fn connect_disconnect() { assert_eq!(clients.iter(server_app.world()).len(), 0); - let replicon_client = client_app.world().resource::(); - assert!(replicon_client.is_disconnected()); + let client_state = client_app.world().resource::>(); + assert_eq!(*client_state, ClientState::Disconnected); server_app.world_mut().remove_resource::(); server_app.update(); - assert!(!server_app.world().resource::().is_running()); + let server_state = server_app.world().resource::>(); + assert_eq!(*server_state, ServerState::Stopped); } #[test] @@ -57,6 +60,7 @@ fn replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -83,6 +87,7 @@ fn server_event() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -114,6 +119,7 @@ fn client_event() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/src/client.rs b/src/client.rs index b7ca6a87..37423a74 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,10 +11,10 @@ use postcard::experimental::max_size::MaxSize; use crate::shared::{ backend::{ + ClientState, replicon_channels::{ReplicationChannel, RepliconChannels}, replicon_client::RepliconClient, }, - common_conditions::{client_connected, client_just_connected, client_just_disconnected}, entity_serde, postcard_utils, replication::{ Replicated, @@ -51,10 +51,6 @@ impl Plugin for ClientPlugin { PreUpdate, ( ClientSet::ReceivePackets, - ( - ClientSet::ResetEvents.run_if(client_just_connected), - ClientSet::Reset.run_if(client_just_disconnected), - ), ClientSet::Receive, ClientSet::Diagnostics, ) @@ -65,13 +61,16 @@ impl Plugin for ClientPlugin { (ClientSet::Send, ClientSet::SendPackets).chain(), ) .add_systems(Startup, setup_channels) + .add_systems( + OnExit(ClientState::Connected), + reset.in_set(ClientSet::Reset), + ) .add_systems( PreUpdate, receive_replication .in_set(ClientSet::Receive) - .run_if(client_connected), - ) - .add_systems(PreUpdate, reset.in_set(ClientSet::Reset)); + .run_if(in_state(ClientState::Connected)), + ); } fn finish(&self, app: &mut App) { @@ -156,11 +155,13 @@ pub(super) fn receive_replication( } fn reset( + mut client: ResMut, mut update_tick: ResMut, mut entity_map: ResMut, mut buffered_mutations: ResMut, stats: Option>, ) { + client.clear(); *update_tick = Default::default(); entity_map.clear(); buffered_mutations.clear(); @@ -727,7 +728,7 @@ pub enum ClientSet { SendPackets, /// Systems that reset queued server events. /// - /// Runs in [`PreUpdate`] immediately after the client connects to ensure client sessions have a fresh start. + /// Runs in [`OnEnter`] with [`ClientState::Connected`] to ensure client sessions have a fresh start. /// /// This is a separate set from [`ClientSet::Reset`] because the reset requirements for events are different /// from the replicon client internals. @@ -736,7 +737,7 @@ pub enum ClientSet { ResetEvents, /// Systems that reset the client. /// - /// Runs in [`PreUpdate`] when the client just disconnected. + /// Runs in [`OnExit`] with [`ClientState::Disconnected`] (when the client just disconnected). /// /// You may want to disable this set if you want to preserve client replication state across reconnects. /// In that case, you need to manually repair the client state (or use something like diff --git a/src/client/diagnostics.rs b/src/client/diagnostics.rs index 215b7b9e..ab2425e1 100644 --- a/src/client/diagnostics.rs +++ b/src/client/diagnostics.rs @@ -5,9 +5,7 @@ use bevy::{ }; use super::{ClientReplicationStats, ClientSet}; -use crate::shared::{ - backend::replicon_client::RepliconClient, common_conditions::client_connected, -}; +use crate::shared::backend::{ClientState, replicon_client::RepliconClient}; /// Plugin to write [`Diagnostics`] based on [`ClientReplicationStats`] every second. /// @@ -21,7 +19,7 @@ impl Plugin for ClientDiagnosticsPlugin { PreUpdate, add_measurements .in_set(ClientSet::Diagnostics) - .run_if(client_connected), + .run_if(in_state(ClientState::Connected)), ) .register_diagnostic( Diagnostic::new(RTT) diff --git a/src/client/event.rs b/src/client/event.rs index 9f2e92e7..bfac18ce 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -1,7 +1,6 @@ use super::{ClientSet, ServerUpdateTick}; use crate::shared::{ - backend::replicon_client::RepliconClient, - common_conditions::*, + backend::{ClientState, replicon_client::RepliconClient}, event::{ ctx::{ClientReceiveCtx, ClientSendCtx}, remote_event_registry::RemoteEventRegistry, @@ -114,25 +113,26 @@ impl Plugin for ClientEventPlugin { .build_system(reset); app.insert_resource(event_registry) + .add_systems( + OnEnter(ClientState::Connected), + reset.in_set(ClientSet::ResetEvents), + ) .add_systems( PreUpdate, ( - reset.in_set(ClientSet::ResetEvents), - ( - receive - .after(super::receive_replication) - .run_if(client_connected), - trigger, - ) - .chain() - .in_set(ClientSet::Receive), - ), + receive + .after(super::receive_replication) + .run_if(in_state(ClientState::Connected)), + trigger, + ) + .chain() + .in_set(ClientSet::Receive), ) .add_systems( PostUpdate, ( - send.run_if(client_connected), - resend_locally.run_if(server_or_singleplayer), + send.run_if(in_state(ClientState::Connected)), + resend_locally.run_if(in_state(ClientState::Disconnected)), ) .chain() .in_set(ClientSet::Send), diff --git a/src/lib.rs b/src/lib.rs index 73437098..80882515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,12 +16,12 @@ We provide a [`prelude`] module, which exports most of the typically used traits Add [`RepliconPlugins`] and plugins for your chosen messaging backend to your app: ``` -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::prelude::*; # use bevy::app::PluginGroupBuilder; let mut app = App::new(); -app.add_plugins((MinimalPlugins, RepliconPlugins, MyMessagingPlugins)); +app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins, MyMessagingPlugins)); # struct MyMessagingPlugins; # impl PluginGroup for MyMessagingPlugins { # fn build(self) -> PluginGroupBuilder { @@ -30,14 +30,14 @@ app.add_plugins((MinimalPlugins, RepliconPlugins, MyMessagingPlugins)); # } ``` +If you use [`MinimalPlugins`], you need to add [`StatesPlugin`](bevy::state::app::StatesPlugin) +manually. It is included by default with [`DefaultPlugins`]. + ## Server and client creation This part is specific to your messaging backend. For `bevy_replicon_renet`, see [this section](https://docs.rs/bevy_replicon_renet#server-and-client-creation). -Backends manage [`RepliconServer`] and [`RepliconClient`] resources. They can be used -to obtain things like state or statistic in backend-independent way. - On server connected clients represented as entities with [`ConnectedClient`] component. Their data represented as components, such as [`NetworkStats`]. Users can also attach their own metadata to them or even replicate these entiteis back to clients. @@ -45,6 +45,57 @@ own metadata to them or even replicate these entiteis back to clients. You can use [`Trigger`] to react to new connections, or use backend-provided events if you need the disconnect reason. +## States + +We provide [`ClientState`] and [`ServerState`], which are Bevy [`States`]. +These are managed by your messaging backend, and you can use them to control when your systems run. + +For systems that should run continuously while in a specific state, use [`IntoScheduleConfigs::run_if`] +with the [`in_state`] run condition: + +``` +# use bevy::prelude::*; +# use bevy_replicon::prelude::*; +# let mut app = App::new(); +app.add_systems( + Update, + ( + apply_damage.run_if(in_state(ServerState::Running)), // Runs every frame on the server. + display_vfx.run_if(in_state(ClientState::Connected)), // Runs every frame on the client. + ), +); +# fn apply_damage() {} +# fn display_vfx() {} +``` + +To run systems when entering or exiting a state, use the [`OnEnter`] or [`OnExit`] schedules: + +``` +# use bevy::prelude::*; +# use bevy_replicon::prelude::*; +# let mut app = App::new(); +app.add_systems(OnEnter(ClientState::Connecting), display_connection_message) // Runs when the client starts connecting. + .add_systems(OnExit(ClientState::Connected), show_disconnected_message) // Runs when the client disconnects. + .add_systems(OnEnter(ServerState::Running), initialize_match); // Runs when the server starts. +# fn display_connection_message() {} +# fn show_disconnected_message() {} +# fn initialize_match() {} +``` + +You can also use these states with [`StateScoped`], but you'll need to call +[`AppExtStates::enable_state_scoped_entities`] in your app: + +``` +# use bevy::prelude::*; +# use bevy_replicon::prelude::*; +# let mut app = App::new(); +app.enable_state_scoped_entities::() + .enable_state_scoped_entities::(); +``` + +Read more about system patterns in the [Abstracting over configurations](abstracting-over-configurations) +section. + ## Replication It's a process of exchanging data in order to keep the world in sync. Replicon @@ -73,9 +124,10 @@ You can use [`TickPolicy::Manual`] and then add the [`increment_tick`](server::i system to [`FixedUpdate`]: ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # let mut app = App::new(); +# app.add_plugins((MinimalPlugins, StatesPlugin)); app.add_plugins( RepliconPlugins .build() @@ -109,11 +161,11 @@ By default no components are replicated, you need to define rules for it. Use [`AppRuleExt::replicate`] to create a replication rule for a single component: ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.replicate::(); #[derive(Component, Deserialize, Serialize)] @@ -144,11 +196,11 @@ necessary to send over the network. Components that can be calculated on the cli be inserted using Bevy's required components feature. ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); // Replicate only transform and player marker. app.replicate::() .replicate::() @@ -246,21 +298,21 @@ These events will appear on server as [`FromClient`] wrapper event that contains sender ID and the sent event. ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_client_event::(Channel::Ordered) .add_systems( PreUpdate, receive_events .after(ServerSet::Receive) - .run_if(server_running), + .run_if(in_state(ServerState::Running)), ) .add_systems( PostUpdate, - send_events.before(ClientSet::Send).run_if(client_connected), + send_events.before(ClientSet::Send).run_if(in_state(ClientState::Connected)), ); fn send_events(mut events: EventWriter) { @@ -290,14 +342,14 @@ Alternatively, you can use triggers with a similar API. First, you need to regis using [`ClientTriggerAppExt::add_client_trigger`], and then use [`ClientTriggerExt::client_trigger`]. ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_client_trigger::(Channel::Ordered) .add_observer(receive_events) - .add_systems(Update, send_events.run_if(client_connected)); + .add_systems(Update, send_events.run_if(in_state(ClientState::Connected))); fn send_events(mut commands: Commands) { commands.client_trigger(ExampleEvent); @@ -324,21 +376,21 @@ and send it from server using [`ToClients`]. This wrapper contains send paramete and the event itself. ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_server_event::(Channel::Ordered) .add_systems( PreUpdate, receive_events .after(ClientSet::Receive) - .run_if(client_connected), + .run_if(in_state(ClientState::Connected)), ) .add_systems( PostUpdate, - send_events.before(ServerSet::Send).run_if(server_running), + send_events.before(ServerSet::Send).run_if(in_state(ServerState::Running)), ); fn send_events(mut events: EventWriter>) { @@ -365,14 +417,14 @@ Trigger-based API available for server events as well. First, you need to regist with [`ServerTriggerAppExt::add_server_trigger`] and then use [`ServerTriggerExt::server_trigger`]: ``` -# use bevy::prelude::*; +# use bevy::{prelude::*, state::app::StatesPlugin}; # use bevy_replicon::prelude::*; # use serde::{Deserialize, Serialize}; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_server_trigger::(Channel::Ordered) .add_observer(receive_events) - .add_systems(Update, send_events.run_if(server_running)); + .add_systems(Update, send_events.run_if(in_state(ServerState::Running))); fn send_events(mut commands: Commands) { commands.server_trigger(ToClients { @@ -431,31 +483,34 @@ without actually running them: - Listen server configuration runs only the server and both logics. - Singleplayer configuration doesn't run the client or server but runs both logics. -To achieve this, just use provided [run conditions](shared::common_conditions): +To achieve this, use the provided [`ClientState`] and [`ServerState`] states: -- Use [`server_or_singleplayer`] for systems that require server authority. For example, systems that - apply damage or send server events. -- Use client or server conditions like [`client_connecting`], [`client_connected`], [`server_running`], etc. +- Use [`ClientState::Disconnected`] for systems that require server authority. + For example, systems that apply damage or send server events. This basically means "run when not a client", + which applies to both server **and** singleplayer. +- Use [`ClientState::Connecting`], [`ClientState::Connected`], [`ServerState::Running`], etc. **only** for miscellaneous things, like display a connection message or a menu to kick connected players (things that actually require server or client running) -- For everything else don't use Replicon's conditions. - -We also provide [`ClientSet`] and [`ServerSet`] to schedule your system at specific time in the frame. -For example, you can run your systems right after receive using [`ClientSet::Receive`] or [`ServerSet::Receive`]. Everything else is done automatically by the crate. All provided [examples](https://github.com/projectharmonia/bevy_replicon/tree/master/bevy_replicon_example_backend/examples) use this approach. -Internally we run replication sending system only if [`server_running`] and replication receiving -only if [`client_connected`]. This way for singleplayer replication systems won't run at all and +Internally we run replication sending system only in [`ServerState::Running`] and replication receiving +only in [`ClientState::Connected`]. This way for singleplayer replication systems won't run at all and for listen server replication will only be sending (server world is already in the correct state). -For events it's a bit trickier. For all client events we internally drain events as `E` and re-emit -them as [`FromClient`] locally with a special [`SERVER`] entity if [`server_or_singleplayer`]. +For events, it's a bit trickier. For all client events, we internally drain events as `E` and re-emit +them as [`FromClient`] locally with a special [`SERVER`] entity in [`ClientState::Disconnected`]. +This emulates event receiving for both server and singleplayer without actually transmitting data +over the network. + For server events we drain [`ToClients`] and, if the [`SERVER`] entity is the recipient of the event, re-emit it as `E` locally. +We also provide [`ClientSet`] and [`ServerSet`] to schedule your system at specific time in the frame. +For example, you can run your systems right after receive using [`ClientSet::Receive`] or [`ServerSet::Receive`]. + ## Organizing your game code If you support a dedicated server, it's recommended to split your game logic into "client", "server", and "shared" @@ -619,12 +674,12 @@ pub mod prelude { shared::{ RepliconSharedPlugin, SERVER, backend::{ + ClientState, ServerState, connected_client::{ConnectedClient, NetworkStats}, replicon_channels::{Channel, RepliconChannels}, - replicon_client::{RepliconClient, RepliconClientStatus}, + replicon_client::RepliconClient, replicon_server::RepliconServer, }, - common_conditions::*, event::{ client_event::{ClientEventAppExt, FromClient}, client_trigger::{ClientTriggerAppExt, ClientTriggerExt}, diff --git a/src/scene.rs b/src/scene.rs index cb0892b5..913c53c8 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -15,11 +15,11 @@ So on deserialization you need to insert it back if you want entities to continu # Examples ``` -use bevy::{prelude::*, asset::ron, scene::serde::SceneDeserializer}; +use bevy::{asset::ron, prelude::*, scene::serde::SceneDeserializer}; use bevy_replicon::{prelude::*, scene}; use serde::de::DeserializeSeed; # let mut app = App::new(); -# app.add_plugins(RepliconPlugins); +# app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); // Serialization let registry = app.world().resource::(); diff --git a/src/server.rs b/src/server.rs index 138557e1..ec93675b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -23,11 +23,11 @@ use log::{debug, trace}; use crate::shared::{ backend::{ + ServerState, connected_client::ConnectedClient, replicon_channels::{ReplicationChannel, RepliconChannels}, replicon_server::RepliconServer, }, - common_conditions::*, event::server_event::BufferedServerEvents, postcard_utils, replication::{ @@ -111,6 +111,7 @@ impl Plugin for ServerPlugin { .add_observer(handle_disconnects) .add_observer(buffer_despawns) .add_systems(Startup, setup_channels) + .add_systems(OnExit(ServerState::Running), reset) .add_systems( PreUpdate, ( @@ -119,20 +120,17 @@ impl Plugin for ServerPlugin { ) .chain() .in_set(ServerSet::Receive) - .run_if(server_running), + .run_if(in_state(ServerState::Running)), ) .add_systems( PostUpdate, ( - ( - buffer_removals, - send_replication.run_if(resource_changed::), - ) - .chain() - .in_set(ServerSet::Send) - .run_if(server_running), - reset.run_if(server_just_stopped), - ), + buffer_removals, + send_replication.run_if(resource_changed::), + ) + .chain() + .in_set(ServerSet::Send) + .run_if(in_state(ServerState::Running)), ); match self.tick_policy { @@ -142,7 +140,7 @@ impl Plugin for ServerPlugin { PostUpdate, increment_tick .before(send_replication) - .run_if(server_running) + .run_if(in_state(ServerState::Running)) .run_if(on_timer(tick_time)), ); } @@ -151,7 +149,7 @@ impl Plugin for ServerPlugin { PostUpdate, increment_tick .before(send_replication) - .run_if(server_running), + .run_if(in_state(ServerState::Running)), ); } TickPolicy::Manual => (), @@ -247,9 +245,9 @@ fn receive_acks( fn buffer_despawns( trigger: Trigger, mut despawn_buffer: ResMut, - server: Res, + state: Res>, ) { - if server.is_running() { + if *state == ServerState::Running { despawn_buffer.push(trigger.target()); } } @@ -337,11 +335,13 @@ fn send_replication( fn reset( mut commands: Commands, + mut server: ResMut, mut server_tick: ResMut, mut related_entities: ResMut, clients: Query>, mut buffered_events: ResMut, ) { + server.clear(); *server_tick = Default::default(); buffered_events.clear(); related_entities.clear(); diff --git a/src/server/client_visibility.rs b/src/server/client_visibility.rs index a654802b..c92fd054 100644 --- a/src/server/client_visibility.rs +++ b/src/server/client_visibility.rs @@ -13,17 +13,18 @@ use bevy::{ /// # Examples /// /// ``` -/// # use bevy::prelude::*; +/// # use bevy::{prelude::*, state::app::StatesPlugin}; /// # use bevy_replicon::prelude::*; /// # let mut app = App::new(); /// app.add_plugins(( /// MinimalPlugins, +/// StatesPlugin, /// RepliconPlugins.set(ServerPlugin { /// visibility_policy: VisibilityPolicy::Whitelist, // Makes all entities invisible for clients by default. /// ..Default::default() /// }), /// )) -/// .add_systems(Update, update_visibility.run_if(server_running)); +/// .add_systems(Update, update_visibility.run_if(in_state(ServerState::Running))); /// /// /// Disables the visibility of other players' entities that are further away than the visible distance. /// fn update_visibility( diff --git a/src/server/event.rs b/src/server/event.rs index ab71cfec..1da6e553 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -5,8 +5,10 @@ use bevy::{ use super::{ServerSet, server_tick::ServerTick}; use crate::shared::{ - backend::{connected_client::ConnectedClient, replicon_server::RepliconServer}, - common_conditions::*, + backend::{ + ClientState, ServerState, connected_client::ConnectedClient, + replicon_server::RepliconServer, + }, event::{ ctx::{ServerReceiveCtx, ServerSendCtx}, remote_event_registry::RemoteEventRegistry, @@ -92,8 +94,8 @@ impl Plugin for ServerEventPlugin { .add_systems( PreUpdate, ( - receive.run_if(server_running), - trigger.run_if(server_or_singleplayer), + receive.run_if(in_state(ServerState::Running)), + trigger.run_if(in_state(ClientState::Disconnected)), ) .chain() .in_set(ServerSet::Receive), @@ -101,11 +103,13 @@ impl Plugin for ServerEventPlugin { .add_systems( PostUpdate, ( - send_or_buffer.run_if(server_running), - send_buffered - .run_if(server_running) - .run_if(resource_changed::), - resend_locally.run_if(server_or_singleplayer), + ( + send_or_buffer, + send_buffered.run_if(resource_changed::), + ) + .chain() + .run_if(in_state(ServerState::Running)), + resend_locally.run_if(in_state(ClientState::Disconnected)), ) .chain() .after(super::send_replication) diff --git a/src/server/related_entities.rs b/src/server/related_entities.rs index 5b29e0b4..62914763 100644 --- a/src/server/related_entities.rs +++ b/src/server/related_entities.rs @@ -14,12 +14,7 @@ use petgraph::{ visit::EdgeRef, }; -use crate::{ - server::ServerSet, - shared::{ - backend::replicon_server::RepliconServer, common_conditions::*, replication::Replicated, - }, -}; +use crate::shared::{backend::ServerState, replication::Replicated}; pub trait SyncRelatedAppExt { /// Ensures that entities related by `C` are replicated in sync. @@ -41,7 +36,7 @@ pub trait SyncRelatedAppExt { /// use bevy_replicon::prelude::*; /// /// # let mut app = App::new(); - /// # app.add_plugins(RepliconPlugins); + /// # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); /// app.sync_related_entities::(); /// /// // Changes to any replicated components on these @@ -62,17 +57,11 @@ impl SyncRelatedAppExt for App { where C: Relationship + Component, { - self.add_systems( - PostUpdate, - read_relations:: - .before(super::send_replication) - .in_set(ServerSet::Send) - .run_if(server_just_started), - ) - .add_observer(add_relation::) - .add_observer(remove_relation::) - .add_observer(start_replication::) - .add_observer(stop_replication::) + self.add_systems(OnEnter(ServerState::Running), read_relations::) + .add_observer(add_relation::) + .add_observer(remove_relation::) + .add_observer(start_replication::) + .add_observer(stop_replication::) } } @@ -255,11 +244,11 @@ fn read_relations( fn add_relation( trigger: Trigger, - server: Res, mut related_entities: ResMut, + state: Res>, components: Query<&C, With>, ) { - if server.is_running() { + if *state == ServerState::Running { if let Ok(relationship) = components.get(trigger.target()) { related_entities.add_relation::(trigger.target(), relationship.get()); } @@ -268,11 +257,11 @@ fn add_relation( fn remove_relation( trigger: Trigger, - server: Res, mut related_entities: ResMut, + state: Res>, relationships: Query<&C, With>, ) { - if server.is_running() { + if *state == ServerState::Running { if let Ok(relationship) = relationships.get(trigger.target()) { related_entities.remove_relation::(trigger.target(), relationship.get()); } @@ -281,11 +270,11 @@ fn remove_relation( fn start_replication( trigger: Trigger, - server: Res, mut related_entities: ResMut, + state: Res>, components: Query<&C, With>, ) { - if server.is_running() { + if *state == ServerState::Running { if let Ok(relationship) = components.get(trigger.target()) { related_entities.add_relation::(trigger.target(), relationship.get()); } @@ -294,11 +283,11 @@ fn start_replication( fn stop_replication( trigger: Trigger, - server: Res, mut related_entities: ResMut, + state: Res>, relationships: Query<&C, With>, ) { - if server.is_running() { + if *state == ServerState::Running { if let Ok(relationship) = relationships.get(trigger.target()) { related_entities.remove_relation::(trigger.target(), relationship.get()); } @@ -307,18 +296,18 @@ fn stop_replication( #[cfg(test)] mod tests { + use bevy::state::app::StatesPlugin; + use super::*; #[test] fn orphan() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let entity1 = app .world_mut() .spawn((Replicated, Children::default())) @@ -338,13 +327,11 @@ mod tests { #[test] fn single() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child1 = app.world_mut().spawn(Replicated).id(); let child2 = app.world_mut().spawn(Replicated).id(); let root = app @@ -364,13 +351,11 @@ mod tests { #[test] fn disjoint() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child1 = app.world_mut().spawn(Replicated).id(); let root1 = app.world_mut().spawn(Replicated).add_child(child1).id(); let child2 = app.world_mut().spawn(Replicated).id(); @@ -388,13 +373,11 @@ mod tests { #[test] fn nested() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let grandchild = app.world_mut().spawn(Replicated).id(); let child = app.world_mut().spawn(Replicated).add_child(grandchild).id(); let root = app.world_mut().spawn(Replicated).add_child(child).id(); @@ -410,13 +393,11 @@ mod tests { #[test] fn split() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let grandgrandchild = app.world_mut().spawn(Replicated).id(); let grandchild = app .world_mut() @@ -440,13 +421,11 @@ mod tests { #[test] fn join() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child1 = app.world_mut().spawn(Replicated).id(); let root1 = app.world_mut().spawn(Replicated).add_child(child1).id(); let child2 = app.world_mut().spawn(Replicated).id(); @@ -466,13 +445,11 @@ mod tests { #[test] fn reparent() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child1 = app.world_mut().spawn(Replicated).id(); let root1 = app.world_mut().spawn(Replicated).add_child(child1).id(); let child2 = app.world_mut().spawn(Replicated).id(); @@ -492,13 +469,11 @@ mod tests { #[test] fn orphan_after_split() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn(Replicated).id(); let root = app.world_mut().spawn(Replicated).add_child(child).id(); @@ -514,13 +489,11 @@ mod tests { #[test] fn despawn() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child1 = app.world_mut().spawn(Replicated).id(); let child2 = app.world_mut().spawn(Replicated).id(); let root = app @@ -542,14 +515,12 @@ mod tests { #[test] fn intersection() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn(Replicated).id(); let root1 = app.world_mut().spawn(Replicated).add_child(child).id(); let root2 = app @@ -569,14 +540,12 @@ mod tests { #[test] fn overlap() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn(Replicated).id(); let root = app .world_mut() @@ -595,14 +564,12 @@ mod tests { #[test] fn overlap_removal() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn(Replicated).id(); let root = app .world_mut() @@ -623,14 +590,12 @@ mod tests { #[test] fn connected() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let grandchild = app.world_mut().spawn(Replicated).id(); let child = app.world_mut().spawn(Replicated).add_child(grandchild).id(); let root = app @@ -650,14 +615,12 @@ mod tests { #[test] fn replication_start() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn_empty().id(); let root = app.world_mut().spawn_empty().add_child(child).id(); @@ -674,14 +637,12 @@ mod tests { #[test] fn replication_stop() { let mut app = App::new(); - app.init_resource::() + app.add_plugins(StatesPlugin) + .insert_state(ServerState::Running) + .init_resource::() .sync_related_entities::() .sync_related_entities::(); - let mut server = RepliconServer::default(); - server.set_running(true); - app.insert_resource(server); - let child = app.world_mut().spawn(Replicated).id(); let root = app .world_mut() @@ -702,8 +663,9 @@ mod tests { #[test] fn runs_only_with_server() { let mut app = App::new(); - app.init_resource::() - .init_resource::() + app.add_plugins(StatesPlugin) + .init_state::() + .init_resource::() .sync_related_entities::(); let child1 = app.world_mut().spawn(Replicated).id(); @@ -722,8 +684,8 @@ mod tests { assert_eq!(related.graph_index(child2), None); app.world_mut() - .resource_mut::() - .set_running(true); + .resource_mut::>() + .set(ServerState::Running); app.update(); diff --git a/src/shared.rs b/src/shared.rs index 40eb00e2..a8311cad 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,5 +1,4 @@ pub mod backend; -pub mod common_conditions; pub mod entity_serde; pub mod event; pub mod postcard_utils; @@ -10,6 +9,7 @@ pub mod server_entity_map; use bevy::prelude::*; use backend::{ + ClientState, ServerState, connected_client::{ConnectedClient, NetworkIdMap, NetworkStats}, replicon_channels::RepliconChannels, }; @@ -28,6 +28,8 @@ impl Plugin for RepliconSharedPlugin { .register_type::() .register_type::() .register_type::() + .init_state::() + .init_state::() .init_resource::() .init_resource::() .init_resource::() diff --git a/src/shared/backend.rs b/src/shared/backend.rs index e6a6c53f..1a640db0 100644 --- a/src/shared/backend.rs +++ b/src/shared/backend.rs @@ -1,11 +1,15 @@ //! API for messaging backends. //! -//! We don't provide any traits to avoid Rust's "orphan rule". Instead, backends need to create channels defined in the -//! [`RepliconChannels`](replicon_channels::RepliconChannels) resource and then manage the -//! [`RepliconServer`](replicon_server::RepliconServer) and [`RepliconClient`](replicon_client::RepliconClient) resources, -//! along with the [`ConnectedClient`](connected_client::ConnectedClient) component. This way, integrations can be provided -//! as separate crates without requiring us or crate authors to maintain them under a feature. See the documentation on -//! types in this module for details. +//! We don't provide any traits to avoid Rust's orphan rule. Instead, backends are expected to: +//! +//! - Create channels defined in the [`RepliconChannels`](replicon_channels::RepliconChannels) resource. +//! This can be done via an extension trait that provides a conversion which the user needs to call manually to get channels for the backend. +//! - Manage the [`ClientState`] and [`ServerState`] states. +//! - Update the [`RepliconServer`](replicon_server::RepliconServer) and [`RepliconClient`](replicon_client::RepliconClient) resources. +//! - Spawn and despawn entities with [`ConnectedClient`](connected_client::ConnectedClient) component. +//! +//! This way, integrations can be provided as separate crates without requiring us or crate authors to maintain them under a feature. +//! See the documentation on types in this module for details. //! //! It's also recommended to split the crate into client and server plugins, along with `server` and `client` features. //! This way, plugins can be conveniently disabled at compile time, which is useful for dedicated server or client @@ -20,3 +24,39 @@ pub mod connected_client; pub mod replicon_channels; pub mod replicon_client; pub mod replicon_server; + +use bevy::prelude::*; + +/// Connection state of the [`RepliconClient`](replicon_client::RepliconClient). +/// +///
+/// +/// Should only be changed from the messaging backend when the client changes its state. +/// +///
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] +pub enum ClientState { + /// Not connected or trying to connect. + #[default] + Disconnected, + /// Trying to connect to the server. + Connecting, + /// Connected to the server. + Connected, +} + +/// Connection state of the [`RepliconServer`](replicon_server::RepliconServer). +/// +///
+/// +/// Should only be changed from the messaging backend when the server changes its state. +/// +///
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] +pub enum ServerState { + /// Inactive. + #[default] + Stopped, + /// Accepting and handling client connections. + Running, +} diff --git a/src/shared/backend/replicon_client.rs b/src/shared/backend/replicon_client.rs index 5b4c902e..68024721 100644 --- a/src/shared/backend/replicon_client.rs +++ b/src/shared/backend/replicon_client.rs @@ -1,14 +1,12 @@ use bevy::prelude::*; use bytes::Bytes; -use log::{debug, trace, warn}; +use log::trace; use super::connected_client::NetworkStats; /// Stores information about a client independent from the messaging backend. /// /// The messaging backend is responsible for updating this resource: -/// - When the messaging client changes its status (connected, connecting and disconnected), -/// [`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`](crate::client::ClientSet::ReceivePackets). @@ -20,9 +18,6 @@ use super::connected_client::NetworkStats; /// Inserted as resource by [`ClientPlugin`](crate::client::ClientPlugin). #[derive(Resource, Default)] pub struct RepliconClient { - /// Client connection status. - status: RepliconClientStatus, - /// List of received messages for each channel. /// /// Top index is channel ID. @@ -64,11 +59,6 @@ impl RepliconClient { /// /// pub fn receive>(&mut self, channel_id: I) -> impl Iterator + '_ { - if !self.is_connected() { - // We can't return here because we need to return an empty iterator. - warn!("trying to receive a message when the client is not connected"); - } - let channel_id = channel_id.into(); let channel_messages = self .received_messages @@ -95,11 +85,6 @@ impl RepliconClient { /// /// pub fn send, B: Into>(&mut self, channel_id: I, message: B) { - if !self.is_connected() { - warn!("trying to send a message when the client is not connected"); - return; - } - let channel_id = channel_id.into(); let message: Bytes = message.into(); @@ -108,61 +93,13 @@ impl RepliconClient { self.sent_messages.push((channel_id, message)); } - /// Sets the client connection status. - /// - /// Discards all messages if the state changes from [`RepliconClientStatus::Connected`]. - /// See also [`Self::status`]. - /// - ///
- /// - /// Should only be called from the messaging backend when the client status changes. - /// - ///
- pub fn set_status(&mut self, status: RepliconClientStatus) { - debug!("changing `RepliconClient` status to `{status:?}`"); - - if self.is_connected() && !matches!(status, RepliconClientStatus::Connected) { - for channel_messages in &mut self.received_messages { - channel_messages.clear(); - } - self.sent_messages.clear(); - - self.stats = Default::default(); + pub(crate) fn clear(&mut self) { + for channel_messages in &mut self.received_messages { + channel_messages.clear(); } + self.sent_messages.clear(); - self.status = status; - } - - /// Returns the current client status. - /// - /// See also [`Self::set_status`]. - #[inline] - pub fn status(&self) -> RepliconClientStatus { - self.status - } - - /// Returns `true` if the client is disconnected. - /// - /// See also [`Self::status`]. - #[inline] - pub fn is_disconnected(&self) -> bool { - self.status == RepliconClientStatus::Disconnected - } - - /// Returns `true` if the client is connecting. - /// - /// See also [`Self::status`]. - #[inline] - pub fn is_connecting(&self) -> bool { - self.status == RepliconClientStatus::Connecting - } - - /// Returns `true` if the client is connected. - /// - /// See also [`Self::status`]. - #[inline] - pub fn is_connected(&self) -> bool { - self.status == RepliconClientStatus::Connected + self.stats = Default::default(); } /// Removes all sent messages, returning them as an iterator with channel. @@ -184,11 +121,6 @@ impl RepliconClient { /// /// 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"); - return; - } - let channel_id = channel_id.into(); let channel_messages = self .received_messages @@ -214,15 +146,3 @@ impl RepliconClient { &mut self.stats } } - -/// Connection status of the [`RepliconClient`]. -#[derive(Clone, Copy, PartialEq, Debug, Default)] -pub enum RepliconClientStatus { - /// Not connected or trying to connect. - #[default] - Disconnected, - /// Trying to connect to the server. - Connecting, - /// Connected to the server. - Connected, -} diff --git a/src/shared/backend/replicon_server.rs b/src/shared/backend/replicon_server.rs index bafbea94..b6f6323d 100644 --- a/src/shared/backend/replicon_server.rs +++ b/src/shared/backend/replicon_server.rs @@ -1,11 +1,10 @@ use bevy::prelude::*; use bytes::Bytes; -use log::{debug, trace, warn}; +use log::trace; /// Stores information about the server independent from the messaging backend. /// /// 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`](crate::server::ServerSet::ReceivePackets). /// - For sending messages, [`Self::drain_sent`] should be used to drain all sent messages. @@ -14,11 +13,6 @@ use log::{debug, trace, warn}; /// Inserted as resource by [`ServerPlugin`](crate::server::ServerPlugin). #[derive(Resource, Default)] pub struct RepliconServer { - /// Indicates if the server is open for connections. - /// - /// By default set to `false`. - running: bool, - /// List of received messages for each channel. /// /// Top index is channel ID. @@ -57,11 +51,6 @@ impl RepliconServer { &mut self, channel_id: I, ) -> impl Iterator + '_ { - if !self.running { - // We can't return here because we need to return an empty iterator. - warn!("trying to receive a message when the server is not running"); - } - let channel_id = channel_id.into(); let channel_messages = self .received_messages @@ -93,11 +82,6 @@ impl RepliconServer { channel_id: I, message: B, ) { - if !self.running { - warn!("trying to send a message when the server is not running"); - return; - } - let channel_id = channel_id.into(); let message: Bytes = message.into(); @@ -107,32 +91,6 @@ impl RepliconServer { .push((client_entity, channel_id, message)); } - /// Marks the server as running or stopped. - /// - ///
- /// - /// Should only be called from the messaging backend when the server changes its state. - /// - ///
- pub fn set_running(&mut self, running: bool) { - debug!("changing `RepliconServer` running status to `{running}`"); - - if !running { - for receive_channel in &mut self.received_messages { - receive_channel.clear(); - } - self.sent_messages.clear(); - } - - self.running = running; - } - - /// Returns `true` if the server is running. - #[inline] - pub fn is_running(&self) -> bool { - self.running - } - /// Retains only the messages specified by the predicate. /// /// Used for testing. @@ -167,11 +125,6 @@ impl RepliconServer { channel_id: I, message: B, ) { - if !self.running { - warn!("trying to insert a received message when the server is not running"); - return; - } - let channel_id = channel_id.into(); let receive_channel = self .received_messages @@ -180,4 +133,11 @@ impl RepliconServer { receive_channel.push((client_entity, message.into())); } + + pub(crate) fn clear(&mut self) { + for receive_channel in &mut self.received_messages { + receive_channel.clear(); + } + self.sent_messages.clear(); + } } diff --git a/src/shared/common_conditions.rs b/src/shared/common_conditions.rs deleted file mode 100644 index db075b09..00000000 --- a/src/shared/common_conditions.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! System conditions for [`RepliconClient`] and [`RepliconServer`] resources. - -use bevy::prelude::*; - -use super::backend::{replicon_client::RepliconClient, replicon_server::RepliconServer}; - -/// Returns `true` if the server is running. -pub fn server_running(server: Option>) -> bool { - server.is_some_and(|server| server.is_running()) -} - -/// Returns `true` if there is no client or if the existing client is disconnected. -/// -/// Can be used instead of the regular [`server_running`] to seamlessly support -/// singleplayer or listen-server mode (where server is also a player). -pub fn server_or_singleplayer(client: Option>) -> bool { - client.is_none_or(|client| client.is_disconnected()) -} - -/// Returns `true` when the client is connecting. -pub fn client_connecting(client: Option>) -> bool { - client.is_some_and(|client| client.is_connecting()) -} - -/// Returns `true` when the client is connected. -pub fn client_connected(client: Option>) -> bool { - client.is_some_and(|client| client.is_connected()) -} - -/// Returns `true` if the server stopped on this tick. -pub fn server_just_stopped( - mut last_running: Local, - server: Option>, -) -> bool { - let running = server.is_some_and(|server| server.is_running()); - - let just_stopped = *last_running && !running; - *last_running = running; - just_stopped -} - -/// Returns `true` if the server started on this tick. -pub fn server_just_started( - mut last_running: Local, - server: Option>, -) -> bool { - let running = server.is_some_and(|server| server.is_running()); - - let just_started = !*last_running && running; - *last_running = running; - just_started -} - -/// Returns `true` when the client just started connecting on this tick. -pub fn client_started_connecting( - mut last_connecting: Local, - client: Option>, -) -> bool { - let connecting = client.is_some_and(|client| client.is_connecting()); - - let started_connecting = !*last_connecting && connecting; - *last_connecting = connecting; - started_connecting -} - -/// Returns `true` when the client is connected on this tick. -pub fn client_just_connected( - mut last_connected: Local, - client: Option>, -) -> bool { - let connected = client.is_some_and(|client| client.is_connected()); - - let just_connected = !*last_connected && connected; - *last_connected = connected; - just_connected -} - -/// Returns `true` when the client is disconnected on this tick. -pub fn client_just_disconnected( - mut last_not_disconnected: Local, - client: Option>, -) -> bool { - let disconnected = client.is_some_and(|client| client.is_disconnected()); - - let just_disconnected = *last_not_disconnected && disconnected; - *last_not_disconnected = !disconnected; - just_disconnected -} diff --git a/src/shared/event/client_event.rs b/src/shared/event/client_event.rs index 0fad2ca3..301eed47 100644 --- a/src/shared/event/client_event.rs +++ b/src/shared/event/client_event.rs @@ -59,11 +59,11 @@ pub trait ClientEventAppExt { /// # Examples /// /// ``` - /// # use bevy::{prelude::*, ecs::entity::MapEntities}; + /// # use bevy::{ecs::entity::MapEntities, prelude::*, state::app::StatesPlugin}; /// # use bevy_replicon::prelude::*; /// # use serde::{Deserialize, Serialize}; /// # let mut app = App::new(); - /// # app.add_plugins(RepliconPlugins); + /// # app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); /// app.add_mapped_client_event::(Channel::Ordered); /// /// #[derive(Debug, Deserialize, Event, Serialize, Clone, MapEntities)] @@ -94,6 +94,7 @@ pub trait ClientEventAppExt { use bevy::{ prelude::*, reflect::serde::{ReflectDeserializer, ReflectSerializer}, + state::app::StatesPlugin, }; use bevy_replicon::{ bytes::Bytes, @@ -107,7 +108,7 @@ pub trait ClientEventAppExt { use serde::{de::DeserializeSeed, Serialize}; let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_client_event_with(Channel::Ordered, serialize_reflect, deserialize_reflect); fn serialize_reflect( diff --git a/src/shared/event/server_event.rs b/src/shared/event/server_event.rs index 95e0b619..eae8eb73 100644 --- a/src/shared/event/server_event.rs +++ b/src/shared/event/server_event.rs @@ -91,6 +91,7 @@ pub trait ServerEventAppExt { use bevy::{ prelude::*, reflect::serde::{ReflectDeserializer, ReflectSerializer}, + state::app::StatesPlugin, }; use bevy_replicon::{ bytes::Bytes, @@ -104,7 +105,7 @@ pub trait ServerEventAppExt { use serde::{de::DeserializeSeed, Serialize}; let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.add_server_event_with(Channel::Ordered, serialize_reflect, deserialize_reflect); fn serialize_reflect( diff --git a/src/shared/replication/command_markers.rs b/src/shared/replication/command_markers.rs index 9f4caf94..d9d60915 100644 --- a/src/shared/replication/command_markers.rs +++ b/src/shared/replication/command_markers.rs @@ -47,7 +47,10 @@ pub trait AppMarkerExt { Then `Health` updates after that will be inserted to the history. ``` - use bevy::{ecs::system::EntityCommands, ecs::component::Mutable, prelude::*, platform::collections::HashMap}; + use bevy::{ + ecs::component::Mutable, ecs::system::EntityCommands, platform::collections::HashMap, + prelude::*, + }; use bevy_replicon::{ bytes::Bytes, shared::{ @@ -66,7 +69,7 @@ pub trait AppMarkerExt { use serde::{Serialize, Deserialize}; # let mut app = App::new(); - # app.add_plugins(RepliconPlugins); + # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); app.register_marker_with::(MarkerConfig { need_history: true, // Enable writing for values that are older than the last received value. ..Default::default() diff --git a/src/shared/replication/replication_registry/test_fns.rs b/src/shared/replication/replication_registry/test_fns.rs index 3ef10045..7e09e1e0 100644 --- a/src/shared/replication/replication_registry/test_fns.rs +++ b/src/shared/replication/replication_registry/test_fns.rs @@ -24,7 +24,7 @@ See also [`ReplicationRegistry::register_rule_fns`]. This example shows how to call registered functions on an entity: ``` -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ shared::{ replication::replication_registry::{ @@ -37,7 +37,7 @@ use bevy_replicon::{ use serde::{Deserialize, Serialize}; let mut app = App::new(); -app.add_plugins((MinimalPlugins, RepliconPlugins)); +app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); let tick = RepliconTick::default(); diff --git a/src/shared/replication/replication_rules.rs b/src/shared/replication/replication_rules.rs index 947f652e..89407d95 100644 --- a/src/shared/replication/replication_rules.rs +++ b/src/shared/replication/replication_rules.rs @@ -72,7 +72,7 @@ pub trait AppRuleExt { use serde::{Deserialize, Serialize}; # let mut app = App::new(); - # app.add_plugins(RepliconPlugins); + # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); app.replicate_bundle::<(Name, City)>() // Tuple of components is also a bundle! .replicate_bundle::(); @@ -167,7 +167,7 @@ pub trait AppRuleExt { }; # let mut app = App::new(); - # app.add_plugins(RepliconPlugins); + # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); // We override in-place as well to apply only translation when the component is already inserted. app.replicate_with( RuleFns::new(serialize_translation, deserialize_translation) @@ -218,7 +218,7 @@ pub trait AppRuleExt { use serde::{Deserialize, Serialize}; # let mut app = App::new(); - # app.add_plugins(RepliconPlugins); + # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); app.replicate_with(( // You can also use `replicate_bundle` if you don't want // to tweak functions or send rate. @@ -261,7 +261,7 @@ pub trait AppRuleExt { use serde::{Deserialize, Serialize}; # let mut app = App::new(); - # app.add_plugins(RepliconPlugins); + # app.add_plugins((MinimalPlugins, bevy::state::app::StatesPlugin, RepliconPlugins)); app.replicate_with(RuleFns::new( serialize_big_component, deserialize_big_component, @@ -313,7 +313,7 @@ pub trait AppRuleExt { Custom ser/de with entity mapping: ``` - use bevy::prelude::*; + use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ bytes::Bytes, shared::{ @@ -329,7 +329,7 @@ pub trait AppRuleExt { use serde::{Deserialize, Serialize}; let mut app = App::new(); - app.add_plugins(RepliconPlugins); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.replicate_with(RuleFns::new( serialize_mapped_component, deserialize_mapped_component, @@ -372,6 +372,7 @@ pub trait AppRuleExt { ``` use bevy::{ prelude::*, + state::app::StatesPlugin, reflect::serde::{ReflectDeserializer, ReflectSerializer}, }; use bevy_replicon::{ @@ -389,7 +390,7 @@ pub trait AppRuleExt { use serde::{de::DeserializeSeed, Serialize}; let mut app = App::new(); - app.add_plugins(RepliconPlugins); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.replicate_with(RuleFns::new(serialize_reflect, deserialize_reflect)); fn serialize_reflect( diff --git a/src/test_app.rs b/src/test_app.rs index 03708b22..a46b807a 100644 --- a/src/test_app.rs +++ b/src/test_app.rs @@ -1,8 +1,7 @@ use bevy::prelude::*; use crate::shared::backend::{ - connected_client::ConnectedClient, - replicon_client::{RepliconClient, RepliconClientStatus}, + ClientState, ServerState, connected_client::ConnectedClient, replicon_client::RepliconClient, replicon_server::RepliconServer, }; @@ -12,7 +11,7 @@ Extension for [`App`] to communicate with other instances like it's a server. # Example ``` -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{prelude::*, test_app::ServerTestAppExt}; let mut server_app = App::new(); @@ -20,6 +19,7 @@ let mut client_app = App::new(); for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, // No messaging library plugin required. RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, // To tick each app update. @@ -86,19 +86,26 @@ pub trait ServerTestAppExt { impl ServerTestAppExt for App { fn connect_client(&mut self, client_app: &mut App) { - let mut server = self.world_mut().resource_mut::(); - server.set_running(true); + self.world_mut() + .resource_mut::>() + .set(ServerState::Running); + let client_entity = self .world_mut() .spawn(ConnectedClient { max_size: 1200 }) .id(); - let mut client = client_app.world_mut().resource_mut::(); - assert!( - client.is_disconnected(), + assert_eq!( + *client_app.world_mut().resource::>(), + ClientState::Disconnected, "client can't be connected multiple times" ); - client.set_status(RepliconClientStatus::Connected); + + client_app + .world_mut() + .resource_mut::>() + .set(ClientState::Connected); + client_app .world_mut() .insert_resource(TestClientEntity(client_entity)); @@ -108,8 +115,11 @@ impl ServerTestAppExt for App { } fn disconnect_client(&mut self, client_app: &mut App) { - let mut client = client_app.world_mut().resource_mut::(); - client.set_status(RepliconClientStatus::Disconnected); + client_app + .world_mut() + .resource_mut::>() + .set(ClientState::Disconnected); + let client_entity = *client_app .world_mut() .remove_resource::() diff --git a/tests/client_event.rs b/tests/client_event.rs index 4ce5a63d..b9f1ffa5 100644 --- a/tests/client_event.rs +++ b/tests/client_event.rs @@ -1,6 +1,7 @@ use bevy::{ ecs::{entity::MapEntities, event::Events}, prelude::*, + state::app::StatesPlugin, time::TimePlugin, }; use bevy_replicon::{ @@ -17,6 +18,7 @@ fn channels() { let mut app = App::new(); app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -36,7 +38,7 @@ fn sending_receiving() { 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, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .add_client_event::(Channel::Ordered) .finish(); } @@ -60,7 +62,7 @@ fn mapping_and_sending_receiving() { 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, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .add_mapped_client_event::(Channel::Ordered) .finish(); } @@ -98,6 +100,7 @@ fn sending_receiving_without_plugins() { server_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -108,6 +111,7 @@ fn sending_receiving_without_plugins() { client_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -133,7 +137,7 @@ fn sending_receiving_without_plugins() { #[test] fn local_resending() { let mut app = App::new(); - app.add_plugins((TimePlugin, RepliconPlugins)) + app.add_plugins((TimePlugin, StatesPlugin, RepliconPlugins)) .add_client_event::(Channel::Ordered) .finish(); diff --git a/tests/client_trigger.rs b/tests/client_trigger.rs index 22aeedef..070ed4f7 100644 --- a/tests/client_trigger.rs +++ b/tests/client_trigger.rs @@ -1,4 +1,4 @@ -use bevy::{ecs::entity::MapEntities, prelude::*, time::TimePlugin}; +use bevy::{ecs::entity::MapEntities, prelude::*, state::app::StatesPlugin, time::TimePlugin}; use bevy_replicon::{ prelude::*, shared::server_entity_map::ServerEntityMap, test_app::ServerTestAppExt, }; @@ -9,7 +9,7 @@ fn sending_receiving() { 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, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .add_client_trigger::(Channel::Ordered) .finish(); } @@ -32,7 +32,7 @@ fn sending_receiving_with_target() { 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, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .add_client_trigger::(Channel::Ordered) .finish(); } @@ -64,7 +64,7 @@ fn mapping_and_sending_receiving() { 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, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .add_mapped_client_trigger::(Channel::Ordered) .finish(); } @@ -99,6 +99,7 @@ fn sending_receiving_without_plugins() { server_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -109,6 +110,7 @@ fn sending_receiving_without_plugins() { client_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -133,7 +135,7 @@ fn sending_receiving_without_plugins() { #[test] fn local_resending() { let mut app = App::new(); - app.add_plugins((TimePlugin, RepliconPlugins)) + app.add_plugins((TimePlugin, StatesPlugin, RepliconPlugins)) .add_client_trigger::(Channel::Ordered) .finish(); app.init_resource::>(); diff --git a/tests/connection.rs b/tests/connection.rs index a3b8a5fc..87a7983e 100644 --- a/tests/connection.rs +++ b/tests/connection.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ prelude::*, server::server_tick::ServerTick, @@ -14,20 +14,17 @@ fn client_to_server() { 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, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.update(); } const MESSAGES: &[&[u8]] = &[&[0], &[1]]; let mut client = client_app.world_mut().resource_mut::(); - client.set_status(RepliconClientStatus::Connected); for &message in MESSAGES { client.send(ReplicationChannel::Updates, message); } let mut server = server_app.world_mut().resource_mut::(); - server.set_running(true); - for (channel_id, message) in client.drain_sent() { server.insert_received(Entity::PLACEHOLDER, channel_id, message); } @@ -44,20 +41,17 @@ fn server_to_client() { 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, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.update(); } const MESSAGES: &[&[u8]] = &[&[0], &[1]]; let mut server = server_app.world_mut().resource_mut::(); - server.set_running(true); for &message in MESSAGES { server.send(Entity::PLACEHOLDER, ReplicationChannel::Updates, message); } let mut client = client_app.world_mut().resource_mut::(); - client.set_status(RepliconClientStatus::Connected); - for (_, channel_id, message) in server.drain_sent() { client.insert_received(channel_id, message); } @@ -71,13 +65,7 @@ fn connect_disconnect() { 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, - RepliconPlugins.set(ServerPlugin { - tick_policy: TickPolicy::EveryFrame, - ..Default::default() - }), - )); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); } server_app.connect_client(&mut client_app); @@ -102,106 +90,59 @@ fn connect_disconnect() { #[test] fn client_cleanup_on_disconnect() { let mut app = App::new(); - app.add_plugins(( - MinimalPlugins, - RepliconPlugins.set(ServerPlugin { - tick_policy: TickPolicy::EveryFrame, - ..Default::default() - }), - )); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.update(); - let mut client = app.world_mut().resource_mut::(); - client.set_status(RepliconClientStatus::Connected); + app.world_mut() + .resource_mut::>() + .set(ClientState::Connected); + + // Run only state transition to avoid actually processing the messages. + app.world_mut().run_schedule(StateTransition); + let mut client = app.world_mut().resource_mut::(); client.send(ReplicationChannel::Updates, Vec::new()); client.insert_received(ReplicationChannel::Updates, Vec::new()); - client.set_status(RepliconClientStatus::Disconnected); + app.world_mut() + .resource_mut::>() + .set(ClientState::Disconnected); + + app.world_mut().run_schedule(StateTransition); + let mut client = app.world_mut().resource_mut::(); assert_eq!(client.drain_sent().count(), 0); assert_eq!(client.receive(ReplicationChannel::Updates).count(), 0); - - app.update(); } #[test] fn server_cleanup_on_stop() { let mut app = App::new(); - app.add_plugins(( - MinimalPlugins, - RepliconPlugins.set(ServerPlugin { - tick_policy: TickPolicy::EveryFrame, - ..Default::default() - }), - )); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); app.update(); - let mut server = app.world_mut().resource_mut::(); - server.set_running(true); + app.world_mut() + .resource_mut::>() + .set(ServerState::Running); + + // Run only state transition to avoid actually processing the messages. + app.world_mut().run_schedule(StateTransition); + let mut server = app.world_mut().resource_mut::(); server.send(Entity::PLACEHOLDER, ReplicationChannel::Updates, Vec::new()); server.insert_received(Entity::PLACEHOLDER, ReplicationChannel::Updates, Vec::new()); - server.set_running(false); - - assert_eq!(server.drain_sent().count(), 0); - assert_eq!(server.receive(ReplicationChannel::Updates).count(), 0); - - app.update(); - - assert_eq!(app.world().resource::().get(), 0); -} - -#[test] -fn client_disconnected() { - let mut app = App::new(); - app.add_plugins(( - MinimalPlugins, - RepliconPlugins.set(ServerPlugin { - tick_policy: TickPolicy::EveryFrame, - ..Default::default() - }), - )); - - app.update(); - - let mut client = app.world_mut().resource_mut::(); - - client.send(ReplicationChannel::Updates, Vec::new()); - client.insert_received(ReplicationChannel::Updates, Vec::new()); + app.world_mut() + .resource_mut::>() + .set(ServerState::Stopped); - assert_eq!(client.drain_sent().count(), 0); - assert_eq!(client.receive(ReplicationChannel::Updates).count(), 0); - - app.update(); -} - -#[test] -fn server_inactive() { - let mut app = App::new(); - app.add_plugins(( - MinimalPlugins, - RepliconPlugins.set(ServerPlugin { - tick_policy: TickPolicy::EveryFrame, - ..Default::default() - }), - )); - - app.update(); + app.world_mut().run_schedule(StateTransition); let mut server = app.world_mut().resource_mut::(); - - server.send(Entity::PLACEHOLDER, ReplicationChannel::Updates, Vec::new()); - server.insert_received(Entity::PLACEHOLDER, ReplicationChannel::Updates, Vec::new()); - assert_eq!(server.drain_sent().count(), 0); assert_eq!(server.receive(ReplicationChannel::Updates).count(), 0); - - app.update(); - assert_eq!(app.world().resource::().get(), 0); } @@ -212,6 +153,7 @@ fn deferred_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, diff --git a/tests/despawn.rs b/tests/despawn.rs index 3205ab81..830e18f4 100644 --- a/tests/despawn.rs +++ b/tests/despawn.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ prelude::*, shared::server_entity_map::ServerEntityMap, test_app::ServerTestAppExt, }; @@ -11,6 +11,7 @@ fn single() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -48,6 +49,7 @@ fn with_hierarchy() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -93,6 +95,7 @@ fn after_spawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -124,6 +127,7 @@ fn hidden() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Whitelist, // Hide all spawned entities by default. diff --git a/tests/fns.rs b/tests/fns.rs index ad4422d5..6d7e4f5a 100644 --- a/tests/fns.rs +++ b/tests/fns.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ prelude::*, shared::{ @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; #[should_panic] fn serialize_missing_component() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); let tick = RepliconTick::default(); let (_, fns_id) = @@ -37,7 +37,7 @@ fn serialize_missing_component() { #[test] fn write() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); let tick = RepliconTick::default(); let (_, fns_id) = @@ -56,7 +56,7 @@ fn write() { #[test] fn remove() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); let tick = RepliconTick::default(); let (_, fns_id) = @@ -73,7 +73,7 @@ fn remove() { #[test] fn write_with_command() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); let tick = RepliconTick::default(); @@ -92,7 +92,7 @@ fn write_with_command() { #[test] fn remove_with_command() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); let tick = RepliconTick::default(); @@ -110,7 +110,7 @@ fn remove_with_command() { #[test] fn write_without_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, @@ -134,7 +134,7 @@ fn write_without_marker() { #[test] fn remove_without_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, @@ -156,7 +156,7 @@ fn remove_without_marker() { #[test] fn write_with_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, @@ -179,7 +179,7 @@ fn write_with_marker() { #[test] fn remove_with_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, @@ -201,7 +201,7 @@ fn remove_with_marker() { #[test] fn write_with_multiple_markers() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .register_marker::() .set_marker_fns::( @@ -234,7 +234,7 @@ fn write_with_multiple_markers() { #[test] fn remove_with_mutltiple_markers() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker::() .register_marker::() .set_marker_fns::( @@ -266,7 +266,7 @@ fn remove_with_mutltiple_markers() { #[test] fn write_with_priority_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker_with::(MarkerConfig { priority: 1, ..Default::default() @@ -299,7 +299,7 @@ fn write_with_priority_marker() { #[test] fn remove_with_priority_marker() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)) + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)) .register_marker_with::(MarkerConfig { priority: 1, ..Default::default() @@ -331,7 +331,7 @@ fn remove_with_priority_marker() { #[test] fn despawn() { let mut app = App::new(); - app.add_plugins((MinimalPlugins, RepliconPlugins)); + app.add_plugins((MinimalPlugins, StatesPlugin, RepliconPlugins)); let mut registry = app.world_mut().resource_mut::(); registry.despawn = mark_despawned; diff --git a/tests/insertion.rs b/tests/insertion.rs index cf3a95b0..e6e8749f 100644 --- a/tests/insertion.rs +++ b/tests/insertion.rs @@ -1,4 +1,4 @@ -use bevy::{ecs::system::SystemState, prelude::*}; +use bevy::{ecs::system::SystemState, prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ client::confirm_history::{ConfirmHistory, EntityReplicated}, prelude::*, @@ -22,6 +22,7 @@ fn table_storage() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -59,6 +60,7 @@ fn sparse_set_storage() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -96,6 +98,7 @@ fn immutable() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -146,6 +149,7 @@ fn mapped_existing_entity() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -195,6 +199,7 @@ fn mapped_new_entity() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -240,6 +245,7 @@ fn multiple_components() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -285,6 +291,7 @@ fn command_fns() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -325,6 +332,7 @@ fn marker() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -377,6 +385,7 @@ fn group() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -414,6 +423,7 @@ fn not_replicated() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -450,6 +460,7 @@ fn after_removal() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -501,6 +512,7 @@ fn before_started_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, @@ -547,6 +559,7 @@ fn after_started_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, @@ -587,6 +600,7 @@ fn confirm_history() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/mutate_ticks.rs b/tests/mutate_ticks.rs index e696575e..2bf8e8d9 100644 --- a/tests/mutate_ticks.rs +++ b/tests/mutate_ticks.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ client::server_mutate_ticks::{MutateTickReceived, ServerMutateTicks}, prelude::*, @@ -15,6 +15,7 @@ fn without_changes() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -50,6 +51,7 @@ fn one_message() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -117,6 +119,7 @@ fn multiple_messages() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/mutations.rs b/tests/mutations.rs index d9fb165a..2759d75d 100644 --- a/tests/mutations.rs +++ b/tests/mutations.rs @@ -1,6 +1,6 @@ use core::time::Duration; -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ client::{ ServerUpdateTick, @@ -28,6 +28,7 @@ fn small_component() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -74,6 +75,7 @@ fn package_size_component() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -126,6 +128,7 @@ fn many_components() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -181,6 +184,7 @@ fn once() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -228,6 +232,7 @@ fn periodic() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -284,6 +289,7 @@ fn periodic_with_miss() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -347,6 +353,7 @@ fn related() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -392,6 +399,7 @@ fn command_fns() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -440,6 +448,7 @@ fn marker() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -499,6 +508,7 @@ fn marker_with_history() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -574,6 +584,7 @@ fn marker_with_history_consume() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -663,6 +674,7 @@ fn marker_with_history_old_update() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -739,6 +751,7 @@ fn many_entities() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -791,6 +804,7 @@ fn with_insertion() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -835,6 +849,7 @@ fn with_removal() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -879,6 +894,7 @@ fn with_despawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -927,6 +943,7 @@ fn buffering() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -984,6 +1001,7 @@ fn old_ignored() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -1050,6 +1068,7 @@ fn acknowledgment() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, mutations_timeout: Duration::ZERO, // Will cause dropping updates after each frame. @@ -1127,6 +1146,7 @@ fn confirm_history() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -1193,6 +1213,7 @@ fn after_disconnect() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/removal.rs b/tests/removal.rs index e46731e6..6443e5d5 100644 --- a/tests/removal.rs +++ b/tests/removal.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ client::confirm_history::{ConfirmHistory, EntityReplicated}, prelude::*, @@ -19,6 +19,7 @@ fn single() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -61,6 +62,7 @@ fn multiple() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -111,6 +113,7 @@ fn command_fns() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -154,6 +157,7 @@ fn marker() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -208,6 +212,7 @@ fn group() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -255,6 +260,7 @@ fn not_replicated() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -305,6 +311,7 @@ fn after_insertion() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -349,6 +356,7 @@ fn with_spawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -381,6 +389,7 @@ fn with_despawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -425,6 +434,7 @@ fn confirm_history() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -493,6 +503,7 @@ fn hidden() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Whitelist, // Hide all spawned entities by default. diff --git a/tests/scene.rs b/tests/scene.rs index b560f410..917cf4c7 100644 --- a/tests/scene.rs +++ b/tests/scene.rs @@ -1,11 +1,11 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{prelude::*, scene}; use serde::{Deserialize, Serialize}; #[test] fn replicated_entity() { let mut app = App::new(); - app.add_plugins(RepliconPlugins) + app.add_plugins((StatesPlugin, RepliconPlugins)) .register_type::() .register_type::() .replicate::() @@ -40,7 +40,7 @@ fn replicated_entity() { #[test] fn empty_entity() { let mut app = App::new(); - app.add_plugins(RepliconPlugins); + app.add_plugins((StatesPlugin, RepliconPlugins)); let entity = app.world_mut().spawn(Replicated).id(); @@ -59,7 +59,7 @@ fn empty_entity() { #[test] fn not_replicated_entity() { let mut app = App::new(); - app.add_plugins(RepliconPlugins) + app.add_plugins((StatesPlugin, RepliconPlugins)) .register_type::() .replicate::(); @@ -75,7 +75,7 @@ fn not_replicated_entity() { #[test] fn entity_update() { let mut app = App::new(); - app.add_plugins(RepliconPlugins) + app.add_plugins((StatesPlugin, RepliconPlugins)) .register_type::() .replicate::() .register_type::(); diff --git a/tests/server_event.rs b/tests/server_event.rs index 60da864c..b56e4a12 100644 --- a/tests/server_event.rs +++ b/tests/server_event.rs @@ -1,6 +1,7 @@ use bevy::{ ecs::{entity::MapEntities, event::Events}, prelude::*, + state::app::StatesPlugin, time::TimePlugin, }; use bevy_replicon::{ @@ -19,6 +20,7 @@ fn channels() { let mut app = App::new(); app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -40,6 +42,7 @@ fn sending_receiving() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -85,6 +88,7 @@ fn sending_receiving_and_mapping() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -128,6 +132,7 @@ fn sending_receiving_without_plugins() { server_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .set(ServerPlugin { @@ -142,6 +147,7 @@ fn sending_receiving_without_plugins() { client_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -184,6 +190,7 @@ fn local_resending() { let mut app = App::new(); app.add_plugins(( TimePlugin, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -226,6 +233,7 @@ fn event_buffering() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::Manual, // To artificially delay replication after sending. ..Default::default() @@ -272,6 +280,7 @@ fn event_queue() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -322,6 +331,7 @@ fn event_queue_and_mapping() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -385,6 +395,7 @@ fn multiple_event_queues() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -447,6 +458,7 @@ fn independent() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -508,6 +520,7 @@ fn before_started_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, @@ -548,6 +561,7 @@ fn independent_before_started_replication() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, @@ -585,6 +599,7 @@ fn different_ticks() { for app in [&mut server_app, &mut client_app1, &mut client_app2] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/server_trigger.rs b/tests/server_trigger.rs index ad2aea18..d61cc4d0 100644 --- a/tests/server_trigger.rs +++ b/tests/server_trigger.rs @@ -1,4 +1,4 @@ -use bevy::{ecs::entity::MapEntities, prelude::*, time::TimePlugin}; +use bevy::{ecs::entity::MapEntities, prelude::*, state::app::StatesPlugin, time::TimePlugin}; use bevy_replicon::{ prelude::*, shared::server_entity_map::ServerEntityMap, test_app::ServerTestAppExt, }; @@ -11,6 +11,7 @@ fn sending_receiving() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -43,6 +44,7 @@ fn sending_receiving_with_target() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -85,6 +87,7 @@ fn sending_receiving_and_mapping() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -125,6 +128,7 @@ fn sending_receiving_without_plugins() { server_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .set(ServerPlugin { @@ -139,6 +143,7 @@ fn sending_receiving_without_plugins() { client_app .add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins .build() .disable::() @@ -168,6 +173,7 @@ fn local_resending() { let mut app = App::new(); app.add_plugins(( TimePlugin, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/spawn.rs b/tests/spawn.rs index bf781bf3..57aec6f6 100644 --- a/tests/spawn.rs +++ b/tests/spawn.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ client::confirm_history::ConfirmHistory, prelude::*, @@ -14,6 +14,7 @@ fn empty() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -55,6 +56,7 @@ fn with_component() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -84,6 +86,7 @@ fn with_multiple_components() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -123,6 +126,7 @@ fn with_old_component() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -167,6 +171,7 @@ fn before_connection() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -196,6 +201,7 @@ fn pre_spawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() @@ -264,6 +270,7 @@ fn after_despawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/stats.rs b/tests/stats.rs index 8d8c0399..b110928b 100644 --- a/tests/stats.rs +++ b/tests/stats.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ prelude::*, test_app::{ServerTestAppExt, TestClientEntity}, @@ -12,6 +12,7 @@ fn client_stats() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, ..Default::default() diff --git a/tests/visibility.rs b/tests/visibility.rs index 3e147e2f..27e5ce02 100644 --- a/tests/visibility.rs +++ b/tests/visibility.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, state::app::StatesPlugin}; use bevy_replicon::{ prelude::*, test_app::{ServerTestAppExt, TestClientEntity}, @@ -12,6 +12,7 @@ fn empty_blacklist() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Blacklist, @@ -42,6 +43,7 @@ fn blacklist() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Blacklist, @@ -97,6 +99,7 @@ fn blacklist_with_despawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Blacklist, @@ -139,6 +142,7 @@ fn empty_whitelist() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Whitelist, @@ -171,6 +175,7 @@ fn whitelist() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Whitelist, @@ -229,6 +234,7 @@ fn whitelist_with_despawn() { for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, + StatesPlugin, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, visibility_policy: VisibilityPolicy::Whitelist,