From 5a94beb2391a12c13d022ad5bcb89e132061ec74 Mon Sep 17 00:00:00 2001 From: JaySpruce Date: Mon, 16 Dec 2024 13:37:32 -0600 Subject: [PATCH] Extend cloning functionality and add convenience methods to `EntityWorldMut` and `EntityCommands` (#16826) ## Objective Thanks to @eugineerd's work on entity cloning (#16132), we now have a robust way to copy components between entities. We can extend this to implement some useful functionality that would have been more complicated before. Closes #15350. ## Solution `EntityCloneBuilder` now automatically includes required components alongside any component added/removed from the component filter. Added the following methods to `EntityCloneBuilder`: - `move_components` - `without_required_components` Added the following methods to `EntityWorldMut` and `EntityCommands`: - `clone_with` - `clone_components` - `move_components` Also added `clone_and_spawn` and `clone_and_spawn_with` to `EntityWorldMut` (`EntityCommands` already had them). ## Showcase ``` assert_eq!(world.entity(entity_a).get::(), Some(&B)); assert_eq!(world.entity(entity_b).get::(), None); world.entity_mut(entity_a).clone_components::(entity_b); assert_eq!(world.entity(entity_a).get::(), Some(&B)); assert_eq!(world.entity(entity_b).get::(), Some(&B)); assert_eq!(world.entity(entity_a).get::(), Some(&C(5))); assert_eq!(world.entity(entity_b).get::(), None); world.entity_mut(entity_a).move_components::(entity_b); assert_eq!(world.entity(entity_a).get::(), None); assert_eq!(world.entity(entity_b).get::(), Some(&C(5))); ``` --- crates/bevy_ecs/src/entity/clone_entities.rs | 191 +++++++++----- crates/bevy_ecs/src/system/commands/mod.rs | 115 ++++++++- crates/bevy_ecs/src/world/entity_ref.rs | 253 ++++++++++++++++++- 3 files changed, 489 insertions(+), 70 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index da0e51c25fcf6..13029aa8a7423 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -18,6 +18,7 @@ pub struct EntityCloner { filter_allows_components: bool, filter: Arc>, clone_handlers_overrides: Arc>, + move_components: bool, } impl EntityCloner { @@ -35,17 +36,21 @@ impl EntityCloner { .filter(|id| self.is_cloning_allowed(id)), ); - for component in components { + for component in &components { let global_handlers = world.components().get_component_clone_handlers(); - let handler = match self.clone_handlers_overrides.get(&component) { - None => global_handlers.get_handler(component), + let handler = match self.clone_handlers_overrides.get(component) { + None => global_handlers.get_handler(*component), Some(ComponentCloneHandler::Default) => global_handlers.get_default_handler(), Some(ComponentCloneHandler::Ignore) => component_clone_ignore, Some(ComponentCloneHandler::Custom(handler)) => *handler, }; - self.component_id = Some(component); + self.component_id = Some(*component); (handler)(&mut world.into(), self); } + + if self.move_components { + world.entity_mut(self.source).remove_by_ids(&components); + } } fn is_cloning_allowed(&self, component: &ComponentId) -> bool { @@ -145,6 +150,8 @@ pub struct EntityCloneBuilder<'w> { filter_allows_components: bool, filter: HashSet, clone_handlers_overrides: HashMap, + attach_required_components: bool, + move_components: bool, } impl<'w> EntityCloneBuilder<'w> { @@ -155,6 +162,8 @@ impl<'w> EntityCloneBuilder<'w> { filter_allows_components: false, filter: Default::default(), clone_handlers_overrides: Default::default(), + attach_required_components: true, + move_components: false, } } @@ -165,6 +174,7 @@ impl<'w> EntityCloneBuilder<'w> { filter_allows_components, filter, clone_handlers_overrides, + move_components, .. } = self; @@ -175,29 +185,49 @@ impl<'w> EntityCloneBuilder<'w> { filter_allows_components, filter: Arc::new(filter), clone_handlers_overrides: Arc::new(clone_handlers_overrides), + move_components, } .clone_entity(world); world.flush_commands(); } + /// By default, any components allowed/denied through the filter will automatically + /// allow/deny all of their required components. + /// + /// This method allows for a scoped mode where any changes to the filter + /// will not involve required components. + pub fn without_required_components( + &mut self, + builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> &mut Self { + self.attach_required_components = false; + builder(self); + self.attach_required_components = true; + self + } + + /// Sets whether the cloner should remove any components that were cloned, + /// effectively moving them from the source entity to the target. + /// + /// This is disabled by default. + /// + /// The setting only applies to components that are allowed through the filter + /// at the time [`EntityCloneBuilder::clone_entity`] is called. + pub fn move_components(&mut self, enable: bool) -> &mut Self { + self.move_components = enable; + self + } + /// Adds all components of the bundle to the list of components to clone. /// /// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call /// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods. pub fn allow(&mut self) -> &mut Self { - if self.filter_allows_components { - T::get_component_ids(self.world.components(), &mut |id| { - if let Some(id) = id { - self.filter.insert(id); - } - }); - } else { - T::get_component_ids(self.world.components(), &mut |id| { - if let Some(id) = id { - self.filter.remove(&id); - } - }); + let bundle = self.world.register_bundle::(); + let ids = bundle.explicit_components().to_owned(); + for id in ids { + self.filter_allow(id); } self } @@ -207,12 +237,8 @@ impl<'w> EntityCloneBuilder<'w> { /// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call /// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods. pub fn allow_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - if self.filter_allows_components { - self.filter.extend(ids); - } else { - ids.into_iter().for_each(|id| { - self.filter.remove(&id); - }); + for id in ids { + self.filter_allow(id); } self } @@ -222,15 +248,10 @@ impl<'w> EntityCloneBuilder<'w> { /// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call /// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods. pub fn allow_by_type_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - let ids = ids - .into_iter() - .filter_map(|id| self.world.components().get_id(id)); - if self.filter_allows_components { - self.filter.extend(ids); - } else { - ids.into_iter().for_each(|id| { - self.filter.remove(&id); - }); + for type_id in ids { + if let Some(id) = self.world.components().get_id(type_id) { + self.filter_allow(id); + } } self } @@ -244,45 +265,28 @@ impl<'w> EntityCloneBuilder<'w> { /// Disallows all components of the bundle from being cloned. pub fn deny(&mut self) -> &mut Self { - if self.filter_allows_components { - T::get_component_ids(self.world.components(), &mut |id| { - if let Some(id) = id { - self.filter.remove(&id); - } - }); - } else { - T::get_component_ids(self.world.components(), &mut |id| { - if let Some(id) = id { - self.filter.insert(id); - } - }); + let bundle = self.world.register_bundle::(); + let ids = bundle.explicit_components().to_owned(); + for id in ids { + self.filter_deny(id); } self } /// Extends the list of components that shouldn't be cloned. pub fn deny_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - if self.filter_allows_components { - ids.into_iter().for_each(|id| { - self.filter.remove(&id); - }); - } else { - self.filter.extend(ids); + for id in ids { + self.filter_deny(id); } self } /// Extends the list of components that shouldn't be cloned by type ids. pub fn deny_by_type_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - let ids = ids - .into_iter() - .filter_map(|id| self.world.components().get_id(id)); - if self.filter_allows_components { - ids.into_iter().for_each(|id| { - self.filter.remove(&id); - }); - } else { - self.filter.extend(ids); + for type_id in ids { + if let Some(id) = self.world.components().get_id(type_id) { + self.filter_deny(id); + } } self } @@ -315,11 +319,52 @@ impl<'w> EntityCloneBuilder<'w> { } self } + + /// Helper function that allows a component through the filter. + fn filter_allow(&mut self, id: ComponentId) { + if self.filter_allows_components { + self.filter.insert(id); + } else { + self.filter.remove(&id); + } + if self.attach_required_components { + if let Some(info) = self.world.components().get_info(id) { + for required_id in info.required_components().iter_ids() { + if self.filter_allows_components { + self.filter.insert(required_id); + } else { + self.filter.remove(&required_id); + } + } + } + } + } + + /// Helper function that disallows a component through the filter. + fn filter_deny(&mut self, id: ComponentId) { + if self.filter_allows_components { + self.filter.remove(&id); + } else { + self.filter.insert(id); + } + if self.attach_required_components { + if let Some(info) = self.world.components().get_info(id) { + for required_id in info.required_components().iter_ids() { + if self.filter_allows_components { + self.filter.remove(&required_id); + } else { + self.filter.insert(required_id); + } + } + } + } + } } #[cfg(test)] mod tests { use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World}; + use bevy_ecs_macros::require; #[cfg(feature = "bevy_reflect")] #[test] @@ -520,4 +565,34 @@ mod tests { assert!(world.get::(e_clone).is_none()); assert!(world.get::(e_clone).is_none()); } + + #[test] + fn clone_entity_with_required_components() { + #[derive(Component, Clone, PartialEq, Debug)] + #[require(B)] + struct A; + + #[derive(Component, Clone, PartialEq, Debug, Default)] + #[require(C(|| C(5)))] + struct B; + + #[derive(Component, Clone, PartialEq, Debug)] + struct C(u32); + + let mut world = World::default(); + + let e = world.spawn(A).id(); + let e_clone = world.spawn_empty().id(); + + let mut builder = EntityCloneBuilder::new(&mut world); + builder.deny_all(); + builder.without_required_components(|builder| { + builder.allow::(); + }); + builder.clone_entity(e, e_clone); + + assert_eq!(world.entity(e_clone).get::(), None); + assert_eq!(world.entity(e_clone).get::(), Some(&B)); + assert_eq!(world.entity(e_clone).get::(), Some(&C(5))); + } } diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 015152d61502b..f2d578bef3c9a 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1738,7 +1738,53 @@ impl<'a> EntityCommands<'a> { self.queue(observe(system)) } - /// Clones an entity and returns the [`EntityCommands`] of the clone. + /// Clones parts of an entity (components, observers, etc.) onto another entity, + /// configured through [`EntityCloneBuilder`]. + /// + /// By default, the other entity will receive all the components of the original that implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// Configure through [`EntityCloneBuilder`] as follows: + /// ``` + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Component, Clone)] + /// struct ComponentA(u32); + /// #[derive(Component, Clone)] + /// struct ComponentB(u32); + /// + /// fn example_system(mut commands: Commands) { + /// // Create an empty entity + /// let target = commands.spawn_empty().id(); + /// + /// // Create a new entity and keep its EntityCommands + /// let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); + /// + /// // Clone only ComponentA onto the target + /// entity.clone_with(target, |builder| { + /// builder.deny::(); + /// }); + /// } + /// # bevy_ecs::system::assert_is_system(example_system); + /// ``` + /// + /// See the following for more options: + /// - [`EntityCloneBuilder`] + /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) + /// - `CloneEntityHierarchyExt` + /// + /// # Panics + /// + /// The command will panic when applied if either of the entities do not exist. + pub fn clone_with( + &mut self, + target: Entity, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> &mut Self { + self.queue(clone_with(target, config)) + } + + /// Spawns a clone of this entity and returns the [`EntityCommands`] of the clone. /// /// The clone will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). @@ -1772,8 +1818,8 @@ impl<'a> EntityCommands<'a> { self.clone_and_spawn_with(|_| {}) } - /// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], - /// returning the [`EntityCommands`] of the clone. + /// Spawns a clone of this entity and allows configuring cloning behavior + /// using [`EntityCloneBuilder`], returning the [`EntityCommands`] of the clone. /// /// By default, the clone will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). @@ -1782,6 +1828,8 @@ impl<'a> EntityCommands<'a> { /// To only include specific components, use [`EntityCloneBuilder::deny_all`] /// followed by [`EntityCloneBuilder::allow`]. /// + /// See the methods on [`EntityCloneBuilder`] for more options. + /// /// # Panics /// /// The command will panic when applied if the original entity does not exist. @@ -1808,15 +1856,40 @@ impl<'a> EntityCommands<'a> { /// # bevy_ecs::system::assert_is_system(example_system); pub fn clone_and_spawn_with( &mut self, - f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, ) -> EntityCommands<'_> { let entity_clone = self.commands().spawn_empty().id(); - self.queue(clone_and_spawn_with(entity_clone, f)); + self.queue(clone_with(entity_clone, config)); EntityCommands { commands: self.commands_mut().reborrow(), entity: entity_clone, } } + + /// Clones the specified components of this entity and inserts them into another entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// # Panics + /// + /// The command will panic when applied if either of the entities do not exist. + pub fn clone_components(&mut self, target: Entity) -> &mut Self { + self.queue(clone_components::(target)) + } + + /// Clones the specified components of this entity and inserts them into another entity, + /// then removes the components from this entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// # Panics + /// + /// The command will panic when applied if either of the entities do not exist. + pub fn move_components(&mut self, target: Entity) -> &mut Self { + self.queue(move_components::(target)) + } } /// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type. @@ -2291,14 +2364,34 @@ fn observe( } } -fn clone_and_spawn_with( - entity_clone: Entity, - f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, +/// An [`EntityCommand`] that clones an entity with configurable cloning behavior. +fn clone_with( + target: Entity, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, ) -> impl EntityCommand { move |entity: Entity, world: &mut World| { - let mut builder = EntityCloneBuilder::new(world); - f(&mut builder); - builder.clone_entity(entity, entity_clone); + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.clone_with(target, config); + } + } +} + +/// An [`EntityCommand`] that clones the specified components into another entity. +fn clone_components(target: Entity) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.clone_components::(target); + } + } +} + +/// An [`EntityCommand`] that clones the specified components into another entity +/// and removes them from the original entity. +fn move_components(target: Entity) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.move_components::(target); + } } } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 4315f80fa5ad9..697dd3455427a 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -3,7 +3,7 @@ use crate::{ bundle::{Bundle, BundleId, BundleInfo, BundleInserter, DynamicBundle, InsertMode}, change_detection::MutUntyped, component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType}, - entity::{Entities, Entity, EntityLocation}, + entity::{Entities, Entity, EntityCloneBuilder, EntityLocation}, event::Event, observer::Observer, query::{Access, ReadOnlyQueryData}, @@ -1819,6 +1819,31 @@ impl<'w> EntityWorldMut<'w> { self } + /// Removes a dynamic bundle from the entity if it exists. + /// + /// You should prefer to use the typed API [`EntityWorldMut::remove`] where possible. + /// + /// # Panics + /// + /// Panics if any of the provided [`ComponentId`]s do not exist in the [`World`] or if the + /// entity has been despawned while this `EntityWorldMut` is still alive. + pub fn remove_by_ids(&mut self, component_ids: &[ComponentId]) -> &mut Self { + self.assert_not_despawned(); + let components = &mut self.world.components; + + let bundle_id = self + .world + .bundles + .init_dynamic_info(components, component_ids); + + // SAFETY: the `BundleInfo` for this `bundle_id` is initialized above + unsafe { self.remove_bundle(bundle_id) }; + + self.world.flush(); + self.update_location(); + self + } + /// Removes all components associated with the entity. /// /// # Panics @@ -2107,6 +2132,161 @@ impl<'w> EntityWorldMut<'w> { self.update_location(); self } + + /// Clones parts of an entity (components, observers, etc.) onto another entity, + /// configured through [`EntityCloneBuilder`]. + /// + /// By default, the other entity will receive all the components of the original that implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// Configure through [`EntityCloneBuilder`] as follows: + /// ``` + /// # use bevy_ecs::prelude::*; + /// # #[derive(Component, Clone, PartialEq, Debug)] + /// # struct ComponentA; + /// # #[derive(Component, Clone, PartialEq, Debug)] + /// # struct ComponentB; + /// # let mut world = World::new(); + /// # let entity = world.spawn((ComponentA, ComponentB)).id(); + /// # let target = world.spawn_empty().id(); + /// world.entity_mut(entity).clone_with(target, |builder| { + /// builder.deny::(); + /// }); + /// # assert_eq!(world.get::(target), Some(&ComponentA)); + /// # assert_eq!(world.get::(target), None); + /// ``` + /// + /// See the following for more options: + /// - [`EntityCloneBuilder`] + /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) + /// - `CloneEntityHierarchyExt` + /// + /// # Panics + /// + /// - If this entity has been despawned while this `EntityWorldMut` is still alive. + /// - If the target entity does not exist. + pub fn clone_with( + &mut self, + target: Entity, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> &mut Self { + self.assert_not_despawned(); + + let mut builder = EntityCloneBuilder::new(self.world); + config(&mut builder); + builder.clone_entity(self.entity, target); + + self.world.flush(); + self.update_location(); + self + } + + /// Spawns a clone of this entity and returns the [`Entity`] of the clone. + /// + /// The clone will receive all the components of the original that implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// To configure cloning behavior (such as only cloning certain components), + /// use [`EntityWorldMut::clone_and_spawn_with`]. + /// + /// # Panics + /// + /// If this entity has been despawned while this `EntityWorldMut` is still alive. + pub fn clone_and_spawn(&mut self) -> Entity { + self.clone_and_spawn_with(|_| {}) + } + + /// Spawns a clone of this entity and allows configuring cloning behavior + /// using [`EntityCloneBuilder`], returning the [`Entity`] of the clone. + /// + /// By default, the clone will receive all the components of the original that implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// Configure through [`EntityCloneBuilder`] as follows: + /// ``` + /// # use bevy_ecs::prelude::*; + /// # #[derive(Component, Clone, PartialEq, Debug)] + /// # struct ComponentA; + /// # #[derive(Component, Clone, PartialEq, Debug)] + /// # struct ComponentB; + /// # let mut world = World::new(); + /// # let entity = world.spawn((ComponentA, ComponentB)).id(); + /// let entity_clone = world.entity_mut(entity).clone_and_spawn_with(|builder| { + /// builder.deny::(); + /// }); + /// # assert_eq!(world.get::(entity_clone), Some(&ComponentA)); + /// # assert_eq!(world.get::(entity_clone), None); + /// ``` + /// + /// See the following for more options: + /// - [`EntityCloneBuilder`] + /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) + /// - `CloneEntityHierarchyExt` + /// + /// # Panics + /// + /// If this entity has been despawned while this `EntityWorldMut` is still alive. + pub fn clone_and_spawn_with( + &mut self, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> Entity { + self.assert_not_despawned(); + + let entity_clone = self.world.entities.reserve_entity(); + self.world.flush(); + + let mut builder = EntityCloneBuilder::new(self.world); + config(&mut builder); + builder.clone_entity(self.entity, entity_clone); + + self.world.flush(); + self.update_location(); + entity_clone + } + + /// Clones the specified components of this entity and inserts them into another entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// # Panics + /// + /// - If this entity has been despawned while this `EntityWorldMut` is still alive. + /// - If the target entity does not exist. + pub fn clone_components(&mut self, target: Entity) -> &mut Self { + self.assert_not_despawned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow::(); + builder.clone_entity(self.entity, target); + + self.world.flush(); + self.update_location(); + self + } + + /// Clones the specified components of this entity and inserts them into another entity, + /// then removes the components from this entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// # Panics + /// + /// - If this entity has been despawned while this `EntityWorldMut` is still alive. + /// - If the target entity does not exist. + pub fn move_components(&mut self, target: Entity) -> &mut Self { + self.assert_not_despawned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow::(); + builder.move_components(true); + builder.clone_entity(self.entity, target); + + self.world.flush(); + self.update_location(); + self + } } /// # Safety @@ -4776,4 +4956,75 @@ mod tests { world.flush(); assert_eq!(world.resource_mut::().0.as_slice(), &expected[..]); } + + #[test] + fn entity_world_mut_clone_and_move_components() { + #[derive(Component, Clone, PartialEq, Debug)] + struct A; + + #[derive(Component, Clone, PartialEq, Debug)] + struct B; + + #[derive(Component, Clone, PartialEq, Debug)] + struct C(u32); + + #[derive(Component, Clone, PartialEq, Debug, Default)] + struct D; + + let mut world = World::new(); + let entity_a = world.spawn((A, B, C(5))).id(); + let entity_b = world.spawn((A, C(4))).id(); + + world.entity_mut(entity_a).clone_components::(entity_b); + assert_eq!(world.entity(entity_a).get::(), Some(&B)); + assert_eq!(world.entity(entity_b).get::(), Some(&B)); + + world.entity_mut(entity_a).move_components::(entity_b); + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&C(5))); + + assert_eq!(world.entity(entity_a).get::(), Some(&A)); + assert_eq!(world.entity(entity_b).get::(), Some(&A)); + } + + #[test] + fn entity_world_mut_clone_with_move_and_require() { + #[derive(Component, Clone, PartialEq, Debug)] + #[require(B)] + struct A; + + #[derive(Component, Clone, PartialEq, Debug, Default)] + #[require(C(|| C(3)))] + struct B; + + #[derive(Component, Clone, PartialEq, Debug, Default)] + #[require(D)] + struct C(u32); + + #[derive(Component, Clone, PartialEq, Debug, Default)] + struct D; + + let mut world = World::new(); + let entity_a = world.spawn(A).id(); + let entity_b = world.spawn_empty().id(); + + world.entity_mut(entity_a).clone_with(entity_b, |builder| { + builder.move_components(true); + builder.without_required_components(|builder| { + builder.deny::(); + }); + }); + + assert_eq!(world.entity(entity_a).get::(), Some(&A)); + assert_eq!(world.entity(entity_b).get::(), None); + + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&B)); + + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&C(3))); + + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&D)); + } }