use std::io::Cursor; use bevy::prelude::*; use bevy_replicon::{ core::{ command_markers::MarkerConfig, ctx::{DespawnCtx, WriteCtx}, deferred_entity::DeferredEntity, replication_registry::{ command_fns, rule_fns::RuleFns, test_fns::TestFnsEntityExt, ReplicationRegistry, }, replicon_tick::RepliconTick, }, prelude::*, }; use serde::{Deserialize, Serialize}; #[test] #[should_panic] fn serialize_missing_component() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn_empty(); let _ = entity.serialize(fns_info, tick); } #[test] fn write() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(OriginalComponent); let data = entity.serialize(fns_info, tick); entity.remove::(); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } #[test] fn remove() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(OriginalComponent); entity.apply_remove(fns_info, tick); assert!(!entity.contains::()); } #[test] fn write_with_command() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(OriginalComponent); let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } #[test] fn remove_with_command() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .set_command_fns(replace, command_fns::default_remove::); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(ReplacedComponent); entity.apply_remove(fns_info, tick); assert!(!entity.contains::()); } #[test] fn write_without_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(OriginalComponent); let data = entity.serialize(fns_info, tick); entity.remove::(); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } #[test] fn remove_without_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn(OriginalComponent); entity.apply_remove(fns_info, tick); assert!(!entity.contains::()); } #[test] fn write_with_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn((OriginalComponent, ReplaceMarker)); let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } #[test] fn remove_with_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app.world_mut().spawn((ReplacedComponent, ReplaceMarker)); entity.apply_remove(fns_info, tick); assert!(!entity.contains::()); } #[test] fn write_with_multiple_markers() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ) .set_marker_fns::( command_fns::default_write::, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app .world_mut() .spawn((OriginalComponent, ReplaceMarker, DummyMarker)); let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!( entity.contains::(), "last marker should take priority" ); } #[test] fn remove_with_mutltiple_markers() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker::() .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ) .set_marker_fns::( command_fns::default_write::, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app .world_mut() .spawn((ReplacedComponent, ReplaceMarker, DummyMarker)); entity.apply_remove(fns_info, tick); assert!( !entity.contains::(), "last marker should take priority" ); } #[test] fn write_with_priority_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker_with::(MarkerConfig { priority: 1, ..Default::default() }) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ) .set_marker_fns::( command_fns::default_write::, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app .world_mut() .spawn((OriginalComponent, ReplaceMarker, DummyMarker)); let data = entity.serialize(fns_info, tick); entity.apply_write(&data, fns_info, tick); assert!(entity.contains::()); } #[test] fn remove_with_priority_marker() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)) .register_marker_with::(MarkerConfig { priority: 1, ..Default::default() }) .register_marker::() .set_marker_fns::( replace, command_fns::default_remove::, ) .set_marker_fns::( command_fns::default_write::, command_fns::default_remove::, ); let tick = RepliconTick::default(); let fns_info = app.world_mut() .resource_scope(|world, mut registry: Mut| { registry.register_rule_fns(world, RuleFns::::default()) }); let mut entity = app .world_mut() .spawn((ReplacedComponent, ReplaceMarker, DummyMarker)); entity.apply_remove(fns_info, tick); assert!(!entity.contains::()); } #[test] fn despawn() { let mut app = App::new(); app.add_plugins((MinimalPlugins, RepliconPlugins)); let mut registry = app.world_mut().resource_mut::(); registry.despawn = mark_despawned; let tick = RepliconTick::default(); let entity = app.world_mut().spawn_empty(); let id = entity.id(); // Take ID since despawn function consumes entity. entity.apply_despawn(tick); assert!(app.world().get::(id).is_some()); } #[derive(Component, Deserialize, Serialize)] struct OriginalComponent; #[derive(Component, Deserialize, Serialize)] struct ReplacedComponent; #[derive(Component)] struct Despawned; #[derive(Component, Deserialize, Serialize)] struct ReplaceMarker; #[derive(Component, Deserialize, Serialize)] struct DummyMarker; /// 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(()) } /// Adds special [`Despawned`] marker instead of despawning an entity. fn mark_despawned(_ctx: &DespawnCtx, mut entity: EntityWorldMut) { entity.insert(Despawned); }