use std::io::Cursor; use bevy::{ecs::entity::MapEntities, prelude::*}; use bevy_replicon::{ core::{ ctx::WriteCtx, deferred_entity::DeferredEntity, replication_registry::{command_fns, rule_fns::RuleFns}, server_entity_map::ServerEntityMap, }, prelude::*, test_app::ServerTestAppExt, }; use serde::{Deserialize, Serialize}; #[test] fn table_storage() { 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).id(); 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) .insert(DummyComponent); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); client_app .world_mut() .query_filtered::<(), With>() .single(client_app.world()); } #[test] fn sparse_set_storage() { 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).id(); 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) .insert(SparseSetComponent); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); client_app .world_mut() .query_filtered::<(), With>() .single(client_app.world()); } #[test] fn mapped_existing_entity() { 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_mapped::(); } server_app.connect_client(&mut client_app); // Make client and server have different entity IDs. server_app.world_mut().spawn_empty(); let server_entity = server_app.world_mut().spawn(Replicated).id(); let server_map_entity = server_app.world_mut().spawn_empty().id(); let client_map_entity = client_app.world_mut().spawn_empty().id(); client_app .world_mut() .resource_mut::() .insert(server_map_entity, client_map_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) .insert(MappedComponent(server_map_entity)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let mapped_component = client_app .world_mut() .query::<&MappedComponent>() .single(client_app.world()); assert_eq!(mapped_component.0, client_map_entity); } #[test] fn mapped_new_entity() { 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_mapped::(); } server_app.connect_client(&mut client_app); // Make client and server have different entity IDs. server_app.world_mut().spawn_empty(); let server_entity = server_app.world_mut().spawn(Replicated).id(); let server_map_entity = server_app.world_mut().spawn_empty().id(); 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) .insert(MappedComponent(server_map_entity)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let mapped_component = client_app .world_mut() .query::<&MappedComponent>() .single(client_app.world()); assert!(client_app.world().get_entity(mapped_component.0).is_ok()); assert_eq!(client_app.world().entities().len(), 2); } #[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).id(); 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) .insert(OriginalComponent); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); client_app .world_mut() .query_filtered::<(), (With, Without)>() .single(client_app.world()); } #[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).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) .insert(OriginalComponent); 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 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).id(); 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) .insert((GroupComponentA, GroupComponentB)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); client_app .world_mut() .query_filtered::<(), (With, With)>() .single(client_app.world()); } #[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).id(); 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) .insert(DummyComponent); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); let components = client_app .world_mut() .query_filtered::<(), With>() .iter(client_app.world()) .count(); assert_eq!(components, 0); } #[test] fn after_removal() { 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); // Insert and remove at the same time. server_app .world_mut() .entity_mut(server_entity) .remove::() .insert(DummyComponent); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); client_app .world_mut() .query_filtered::<(), With>() .single(client_app.world()); } #[test] fn before_started_replication() { let mut server_app = App::new(); let mut client_app = App::new(); for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, ..Default::default() }), )) .replicate::(); } server_app.connect_client(&mut client_app); server_app.world_mut().spawn((Replicated, DummyComponent)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); let replicated_components = client_app .world_mut() .query_filtered::<(), With>() .iter(client_app.world()) .count(); assert_eq!( replicated_components, 0, "no entities should have been sent to the client" ); let client = client_app.world().resource::(); let client_id = client.id().unwrap(); server_app .world_mut() .send_event(StartReplication(client_id)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); client_app .world_mut() .query_filtered::<(), With>() .single(client_app.world()); } #[test] fn after_started_replication() { let mut server_app = App::new(); let mut client_app = App::new(); for app in [&mut server_app, &mut client_app] { app.add_plugins(( MinimalPlugins, RepliconPlugins.set(ServerPlugin { tick_policy: TickPolicy::EveryFrame, replicate_after_connect: false, ..Default::default() }), )) .replicate::(); } server_app.connect_client(&mut client_app); let client = client_app.world().resource::(); let client_id = client.id().unwrap(); server_app .world_mut() .send_event(StartReplication(client_id)); 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().spawn((Replicated, DummyComponent)); server_app.update(); server_app.exchange_with_client(&mut client_app); client_app.update(); server_app.exchange_with_client(&mut client_app); client_app .world_mut() .query_filtered::<(), With>() .single(client_app.world()); } #[derive(Component, Deserialize, Serialize)] struct MappedComponent(Entity); impl MapEntities for MappedComponent { fn map_entities(&mut self, entity_mapper: &mut M) { self.0 = entity_mapper.map_entity(self.0); } } #[derive(Component, Deserialize, Serialize)] struct DummyComponent; #[derive(Component, Deserialize, Serialize)] #[component(storage = "SparseSet")] struct SparseSetComponent; #[derive(Component, Deserialize, Serialize)] struct GroupComponentA; #[derive(Component, Deserialize, Serialize)] struct GroupComponentB; #[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(()) }