use std::io::Cursor; use bevy::prelude::*; use bevy_replicon::{ core::{ ctx::WriteCtx, deferred_entity::DeferredEntity, replication_registry::{command_fns, rule_fns::RuleFns}, }, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize}; #[test] fn single() { 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() }), )) .replicate::(); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, DummyComponent)) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let client_entity = client_app .world_mut() .query_filtered::>() .single(client_app.world()); server_app .world_mut() .entity_mut(server_entity) .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(!client_entity.contains::()); } #[test] fn command_fns() { 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() }), )) .replicate::() .set_command_fns(replace, command_fns::default_remove::); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, OriginalComponent)) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let client_entity = client_app .world_mut() .query_filtered::>() .single(client_app.world()); server_app .world_mut() .entity_mut(server_entity) .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(!client_entity.contains::()); } #[test] fn marker() { 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() }), )) .register_marker::() .replicate::() .set_marker_fns::( replace, command_fns::default_remove::, ); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, OriginalComponent)) .id(); let client_entity = client_app.world_mut().spawn(ReplaceMarker).id(); let client = client_app.world().resource::(); let client_id = client.id().unwrap(); let mut entity_map = server_app.world_mut().resource_mut::(); entity_map.insert( client_id, ClientMapping { server_entity, client_entity, }, ); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); server_app .world_mut() .entity_mut(server_entity) .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(!client_entity.contains::()); } #[test] fn group() { 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() }), )) .replicate_group::<(GroupComponentA, GroupComponentB)>(); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, (GroupComponentA, GroupComponentB))) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let client_entity = client_app .world_mut() .query_filtered::, With)>() .single(client_app.world()); server_app .world_mut() .entity_mut(server_entity) .remove::<(GroupComponentA, GroupComponentB)>(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(!client_entity.contains::()); assert!(!client_entity.contains::()); } #[test] fn not_replicated() { 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() }), )); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, NotReplicatedComponent)) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let client_entity = client_app .world_mut() .query_filtered::, Without)>() .single(client_app.world()); client_app .world_mut() .entity_mut(client_entity) .insert(NotReplicatedComponent); server_app .world_mut() .entity_mut(server_entity) .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(client_entity.contains::()); } #[test] fn after_insertion() { 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() }), )) .replicate::(); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, DummyComponent)) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let client_entity = client_app .world_mut() .query_filtered::>() .single(client_app.world()); // Insert and remove at the same time. server_app .world_mut() .entity_mut(server_entity) .insert(DummyComponent) .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let client_entity = client_app.world().entity(client_entity); assert!(!client_entity.contains::()); } #[test] fn with_despawn() { 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() }), )) .replicate::(); } server_app.connect_client(&mut client_app); let server_entity = server_app .world_mut() .spawn((Replicated, DummyComponent)) .id(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); assert_eq!(client_app.world().entities().len(), 1); // Un-replicate and remove at the same time. server_app .world_mut() .entity_mut(server_entity) .remove::() .remove::(); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); assert!(client_app.world().entities().is_empty()); } #[derive(Component, Deserialize, Serialize)] struct DummyComponent; #[derive(Component, Deserialize, Serialize)] struct GroupComponentA; #[derive(Component, Deserialize, Serialize)] struct GroupComponentB; #[derive(Component, Deserialize, Serialize)] struct NotReplicatedComponent; #[derive(Component)] struct ReplaceMarker; #[derive(Component, Deserialize, Serialize)] struct OriginalComponent; #[derive(Component, Deserialize, Serialize)] struct ReplacedComponent; /// Deserializes [`OriginalComponent`], but ignores it and inserts [`ReplacedComponent`]. fn replace( ctx: &mut WriteCtx, rule_fns: &RuleFns, entity: &mut DeferredEntity, cursor: &mut Cursor<&[u8]>, ) -> bincode::Result<()> { rule_fns.deserialize(ctx, cursor)?; ctx.commands.entity(entity.id()).insert(ReplacedComponent); Ok(()) }