From 1d6e8d184beece34038cf90318e06f87a0aabc8d Mon Sep 17 00:00:00 2001 From: JaySpruce Date: Sat, 14 Dec 2024 20:38:01 -0600 Subject: [PATCH 1/3] add `clone_components`, `move_components`, and variants --- crates/bevy_ecs/src/system/commands/mod.rs | 162 ++++++++++- crates/bevy_ecs/src/world/entity_ref.rs | 309 ++++++++++++++++++++- 2 files changed, 467 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 015152d61502b..97f8583e1eef3 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1738,7 +1738,7 @@ impl<'a> EntityCommands<'a> { self.queue(observe(system)) } - /// Clones an entity and returns the [`EntityCommands`] of the clone. + /// 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 +1772,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 +1782,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. @@ -1817,6 +1819,95 @@ impl<'a> EntityCommands<'a> { 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 with those components' required components + /// 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_with_requires(&mut self, target: Entity) -> &mut Self { + self.queue(clone_components_with_requires::(target)) + } + + /// Clones the given components of this entity and inserts them into another entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// You should prefer to use the typed API [`EntityCommands::clone_components`] where possible. + /// + /// # Panics + /// + /// The command will panic when applied if either of the entities do not exist. + pub fn clone_components_by_id( + &mut self, + target: Entity, + ids: impl IntoIterator + Send + 'static, + ) -> &mut Self { + self.queue(clone_components_by_id(target, ids)) + } + + /// 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)) + } + + /// Clones the specified components of this entity with those components' required components, + /// 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_with_requires(&mut self, target: Entity) -> &mut Self { + self.queue(move_components_with_requires::(target)) + } + + /// Clones the given 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). + /// + /// You should prefer to use the typed API [`EntityCommands::clone_components`] where possible. + /// + /// # Panics + /// + /// The command will panic when applied if either of the entities do not exist, + /// or if any of the provided [`ComponentId`]s do not exist. + pub fn move_components_by_id( + &mut self, + target: Entity, + ids: impl IntoIterator + Clone + Send + 'static, + ) -> &mut Self { + self.queue(move_components_by_id(target, ids)) + } } /// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type. @@ -2291,6 +2382,7 @@ fn observe( } } +/// An [`EntityCommand`] that clones an entity and allows configuring cloning behavior. fn clone_and_spawn_with( entity_clone: Entity, f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, @@ -2302,6 +2394,70 @@ fn clone_and_spawn_with( } } +/// 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 with their required components +/// into another entity. +fn clone_components_with_requires(target: Entity) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.clone_components_with_requires::(target); + } + } +} + +/// An [`EntityCommand`] that clones the given components into another entity. +fn clone_components_by_id( + target: Entity, + ids: impl IntoIterator + Send + 'static, +) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.clone_components_by_id(target, ids); + } + } +} + +/// 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); + } + } +} + +/// An [`EntityCommand`] that clones the specified components with their required components +/// into another entity and removes them from the original entity. +fn move_components_with_requires(target: Entity) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.move_components_with_requires::(target); + } + } +} + +/// An [`EntityCommand`] that clones the given components into another entity +/// and removes them from the original entity. +fn move_components_by_id( + target: Entity, + ids: impl IntoIterator + Clone + Send + 'static, +) -> impl EntityCommand { + move |entity: Entity, world: &mut World| { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.move_components_by_id(target, ids); + } + } +} + #[cfg(test)] #[allow(clippy::float_cmp, clippy::approx_constant)] mod tests { diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 4315f80fa5ad9..d10cb9ff72b9e 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}, @@ -2107,6 +2107,248 @@ impl<'w> EntityWorldMut<'w> { 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). + /// + /// To exclude specific components, use [`EntityCloneBuilder::deny`]: + /// ```ignore + /// world.entity_mut(entity).clone_and_spawn_with(|builder| { + /// builder.deny::(); + /// }); + /// ``` + /// + /// To only include specific components, use [`EntityCloneBuilder::deny_all`] + /// followed by [`EntityCloneBuilder::allow`]: + /// ```ignore + /// world.entity_mut(entity).clone_and_spawn_with(|builder| { + /// builder.deny_all().allow::(); + /// }); + /// ``` + /// + /// See the methods on [`EntityCloneBuilder`] for more options. + /// + /// # Panics + /// + /// If this entity has been despawned while this `EntityWorldMut` is still alive. + pub fn clone_and_spawn_with( + &mut self, + f: 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); + f(&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 with those components' required components + /// 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_with_requires(&mut self, target: Entity) -> &mut Self { + self.assert_not_despawned(); + + let storages = &mut self.world.storages; + let components = &mut self.world.components; + let bundles = &mut self.world.bundles; + + let bundle_id = bundles.register_contributed_bundle_info::(components, storages); + // SAFETY: the `BundleInfo` for this `BundleId` is initialized above + let bundle_info = unsafe { bundles.get_unchecked(bundle_id) }; + let component_ids = bundle_info.contributed_components().to_owned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow_by_ids(component_ids); + builder.clone_entity(self.entity, target); + + self.world.flush(); + self.update_location(); + self + } + + /// Clones the given components of this entity and inserts them into another entity. + /// + /// Components can only be cloned if they implement + /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). + /// + /// You should prefer to use the typed API [`EntityWorldMut::clone_components`] where possible. + /// + /// # Panics + /// + /// - If this entity has been despawned while this `EntityWorldMut` is still alive. + /// - If the target entity does not exist. + pub fn clone_components_by_id( + &mut self, + target: Entity, + ids: impl IntoIterator, + ) -> &mut Self { + self.assert_not_despawned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow_by_ids(ids); + 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.clone_entity(self.entity, target); + + let storages = &mut self.world.storages; + let components = &mut self.world.components; + let bundles = &mut self.world.bundles; + let bundle_id = bundles.register_info::(components, storages); + + // SAFETY: the `BundleInfo` for this `BundleId` is initialized above + unsafe { self.remove_bundle(bundle_id) }; + + self.world.flush(); + self.update_location(); + self + } + + /// Clones the specified components of this entity with those components' required components, + /// 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_with_requires(&mut self, target: Entity) -> &mut Self { + self.assert_not_despawned(); + + let storages = &mut self.world.storages; + let components = &mut self.world.components; + let bundles = &mut self.world.bundles; + let bundle_id = bundles.register_contributed_bundle_info::(components, storages); + // SAFETY: the `BundleInfo` for this `BundleId` is initialized above + let bundle_info = unsafe { bundles.get_unchecked(bundle_id) }; + let component_ids = bundle_info.contributed_components().to_owned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow_by_ids(component_ids); + builder.clone_entity(self.entity, target); + + // SAFETY: the `BundleInfo` for this `BundleId` is initialized above + unsafe { self.remove_bundle(bundle_id) }; + + self.world.flush(); + self.update_location(); + self + } + + /// Clones the given 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). + /// + /// You should prefer to use the typed API [`EntityWorldMut::move_components`] where possible. + /// + /// # Panics + /// + /// - If this entity has been despawned while this `EntityWorldMut` is still alive. + /// - If the target entity does not exist. + /// - If any of the provided [`ComponentId`]s do not exist. + pub fn move_components_by_id( + &mut self, + target: Entity, + ids: impl IntoIterator + Clone, + ) -> &mut Self { + self.assert_not_despawned(); + + let mut builder = EntityCloneBuilder::new(self.world); + builder.deny_all().allow_by_ids(ids.clone()); + builder.clone_entity(self.entity, target); + + let ids: Vec = ids.into_iter().collect(); + let components = &mut self.world.components; + let bundles = &mut self.world.bundles; + let bundle_id = if ids.len() == 1 { + bundles.init_component_info(components, ids[0]) + } else { + bundles.init_dynamic_info(components, &ids) + }; + + // SAFETY: the `BundleInfo` for this `BundleId` is initialized above + unsafe { self.remove_bundle(bundle_id) }; + + self.world.flush(); + self.update_location(); + self + } } /// # Safety @@ -4776,4 +5018,69 @@ 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)] + #[require(E(|| E(10)))] + struct D; + + #[derive(Component, Clone, PartialEq, Debug)] + #[require(F)] + struct E(u32); + + #[derive(Component, Clone, PartialEq, Debug, Default)] + struct F; + + #[derive(Component, Clone, PartialEq, Debug)] + struct G(u32); + + #[derive(Component, Clone, PartialEq, Debug, Default)] + struct H; + + let mut world = World::new(); + let entity_a = world.spawn((A, B, C(5), D, G(22), H)).id(); + let entity_b = world.spawn((A, G(44))).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))); + + world + .entity_mut(entity_a) + .move_components_with_requires::(entity_b); + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&D)); + assert_eq!(world.entity(entity_b).get::(), Some(&E(10))); + assert_eq!(world.entity(entity_b).get::(), Some(&F)); + + 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(&G(22))); + + let id = world.register_component::(); + world + .entity_mut(entity_a) + .move_components_by_id(entity_b, [id]); + assert_eq!(world.entity(entity_a).get::(), None); + assert_eq!(world.entity(entity_b).get::(), Some(&H)); + + assert_eq!(world.entity(entity_a).get::(), Some(&A)); + assert_eq!(world.entity(entity_b).get::(), Some(&A)); + } } From 4e6abafcc7a3b0cf28b5b570798c7a5581993aec Mon Sep 17 00:00:00 2001 From: JaySpruce Date: Sun, 15 Dec 2024 18:23:53 -0600 Subject: [PATCH 2/3] move variant functionality to builder, add `clone_with` --- crates/bevy_ecs/src/entity/clone_entities.rs | 193 ++++++++---- crates/bevy_ecs/src/system/commands/mod.rs | 152 +++------- crates/bevy_ecs/src/world/entity_ref.rs | 290 +++++++------------ 3 files changed, 281 insertions(+), 354 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index da0e51c25fcf6..117904a1ebcd5 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: false, + move_components: false, } } @@ -165,6 +174,7 @@ impl<'w> EntityCloneBuilder<'w> { filter_allows_components, filter, clone_handlers_overrides, + move_components, .. } = self; @@ -175,29 +185,51 @@ 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(); } + /// Allows for a scoped mode where any changes to the filter that allow/deny components + /// will also allow/deny those components' required components, recursively. + pub fn with_required_components( + &mut self, + builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + ) -> &mut Self { + self.attach_required_components = true; + builder(self); + self.attach_required_components = false; + self + } + + /// Sets the cloner to remove any components that were cloned, + /// effectively moving them from the source entity to the target. + /// + /// This only applies to components that are allowed through the filter + /// at the time [`EntityCloneBuilder::clone_entity`] is called. + pub fn enable_move_on_clone(&mut self) -> &mut Self { + self.move_components = true; + self + } + + /// The default setting for the cloner. The source entity will keep + /// all components that are cloned. + pub fn disable_move_on_clone(&mut self) -> &mut Self { + self.move_components = false; + 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 +239,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 +250,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 +267,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 +321,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 +567,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.with_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 97f8583e1eef3..dce91c1d99158 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1738,6 +1738,31 @@ impl<'a> EntityCommands<'a> { self.queue(observe(system)) } + /// 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: + /// ```ignore + /// commands.entity(entity).clone_with(|builder| { + /// builder.deny::(); + /// }); + /// ``` + /// + /// See the following for more options: + /// - [`EntityCloneBuilder`] + /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) + /// - `CloneEntityHierarchyExt` + 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 @@ -1810,10 +1835,10 @@ 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, @@ -1832,37 +1857,6 @@ impl<'a> EntityCommands<'a> { self.queue(clone_components::(target)) } - /// Clones the specified components of this entity with those components' required components - /// 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_with_requires(&mut self, target: Entity) -> &mut Self { - self.queue(clone_components_with_requires::(target)) - } - - /// Clones the given components of this entity and inserts them into another entity. - /// - /// Components can only be cloned if they implement - /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). - /// - /// You should prefer to use the typed API [`EntityCommands::clone_components`] where possible. - /// - /// # Panics - /// - /// The command will panic when applied if either of the entities do not exist. - pub fn clone_components_by_id( - &mut self, - target: Entity, - ids: impl IntoIterator + Send + 'static, - ) -> &mut Self { - self.queue(clone_components_by_id(target, ids)) - } - /// Clones the specified components of this entity and inserts them into another entity, /// then removes the components from this entity. /// @@ -1875,39 +1869,6 @@ impl<'a> EntityCommands<'a> { pub fn move_components(&mut self, target: Entity) -> &mut Self { self.queue(move_components::(target)) } - - /// Clones the specified components of this entity with those components' required components, - /// 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_with_requires(&mut self, target: Entity) -> &mut Self { - self.queue(move_components_with_requires::(target)) - } - - /// Clones the given 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). - /// - /// You should prefer to use the typed API [`EntityCommands::clone_components`] where possible. - /// - /// # Panics - /// - /// The command will panic when applied if either of the entities do not exist, - /// or if any of the provided [`ComponentId`]s do not exist. - pub fn move_components_by_id( - &mut self, - target: Entity, - ids: impl IntoIterator + Clone + Send + 'static, - ) -> &mut Self { - self.queue(move_components_by_id(target, ids)) - } } /// A wrapper around [`EntityCommands`] with convenience methods for working with a specified component type. @@ -2382,15 +2343,15 @@ fn observe( } } -/// An [`EntityCommand`] that clones an entity and allows configuring cloning behavior. -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); + } } } @@ -2403,28 +2364,6 @@ fn clone_components(target: Entity) -> impl EntityCommand { } } -/// An [`EntityCommand`] that clones the specified components with their required components -/// into another entity. -fn clone_components_with_requires(target: Entity) -> impl EntityCommand { - move |entity: Entity, world: &mut World| { - if let Ok(mut entity) = world.get_entity_mut(entity) { - entity.clone_components_with_requires::(target); - } - } -} - -/// An [`EntityCommand`] that clones the given components into another entity. -fn clone_components_by_id( - target: Entity, - ids: impl IntoIterator + Send + 'static, -) -> impl EntityCommand { - move |entity: Entity, world: &mut World| { - if let Ok(mut entity) = world.get_entity_mut(entity) { - entity.clone_components_by_id(target, ids); - } - } -} - /// An [`EntityCommand`] that clones the specified components into another entity /// and removes them from the original entity. fn move_components(target: Entity) -> impl EntityCommand { @@ -2435,29 +2374,6 @@ fn move_components(target: Entity) -> impl EntityCommand { } } -/// An [`EntityCommand`] that clones the specified components with their required components -/// into another entity and removes them from the original entity. -fn move_components_with_requires(target: Entity) -> impl EntityCommand { - move |entity: Entity, world: &mut World| { - if let Ok(mut entity) = world.get_entity_mut(entity) { - entity.move_components_with_requires::(target); - } - } -} - -/// An [`EntityCommand`] that clones the given components into another entity -/// and removes them from the original entity. -fn move_components_by_id( - target: Entity, - ids: impl IntoIterator + Clone + Send + 'static, -) -> impl EntityCommand { - move |entity: Entity, world: &mut World| { - if let Ok(mut entity) = world.get_entity_mut(entity) { - entity.move_components_by_id(target, ids); - } - } -} - #[cfg(test)] #[allow(clippy::float_cmp, clippy::approx_constant)] mod tests { diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index d10cb9ff72b9e..584e21cc4948c 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -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 @@ -2108,6 +2133,44 @@ impl<'w> EntityWorldMut<'w> { 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: + /// ```ignore + /// world.entity_mut(entity).clone_with(|builder| { + /// builder.deny::(); + /// }); + /// ``` + /// + /// 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 @@ -2144,14 +2207,17 @@ impl<'w> EntityWorldMut<'w> { /// }); /// ``` /// - /// See the methods on [`EntityCloneBuilder`] for more options. + /// 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, - f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, ) -> Entity { self.assert_not_despawned(); @@ -2159,7 +2225,7 @@ impl<'w> EntityWorldMut<'w> { self.world.flush(); let mut builder = EntityCloneBuilder::new(self.world); - f(&mut builder); + config(&mut builder); builder.clone_entity(self.entity, entity_clone); self.world.flush(); @@ -2188,64 +2254,6 @@ impl<'w> EntityWorldMut<'w> { self } - /// Clones the specified components of this entity with those components' required components - /// 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_with_requires(&mut self, target: Entity) -> &mut Self { - self.assert_not_despawned(); - - let storages = &mut self.world.storages; - let components = &mut self.world.components; - let bundles = &mut self.world.bundles; - - let bundle_id = bundles.register_contributed_bundle_info::(components, storages); - // SAFETY: the `BundleInfo` for this `BundleId` is initialized above - let bundle_info = unsafe { bundles.get_unchecked(bundle_id) }; - let component_ids = bundle_info.contributed_components().to_owned(); - - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow_by_ids(component_ids); - builder.clone_entity(self.entity, target); - - self.world.flush(); - self.update_location(); - self - } - - /// Clones the given components of this entity and inserts them into another entity. - /// - /// Components can only be cloned if they implement - /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). - /// - /// You should prefer to use the typed API [`EntityWorldMut::clone_components`] where possible. - /// - /// # Panics - /// - /// - If this entity has been despawned while this `EntityWorldMut` is still alive. - /// - If the target entity does not exist. - pub fn clone_components_by_id( - &mut self, - target: Entity, - ids: impl IntoIterator, - ) -> &mut Self { - self.assert_not_despawned(); - - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow_by_ids(ids); - 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. /// @@ -2261,90 +2269,9 @@ impl<'w> EntityWorldMut<'w> { let mut builder = EntityCloneBuilder::new(self.world); builder.deny_all().allow::(); + builder.enable_move_on_clone(); builder.clone_entity(self.entity, target); - let storages = &mut self.world.storages; - let components = &mut self.world.components; - let bundles = &mut self.world.bundles; - let bundle_id = bundles.register_info::(components, storages); - - // SAFETY: the `BundleInfo` for this `BundleId` is initialized above - unsafe { self.remove_bundle(bundle_id) }; - - self.world.flush(); - self.update_location(); - self - } - - /// Clones the specified components of this entity with those components' required components, - /// 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_with_requires(&mut self, target: Entity) -> &mut Self { - self.assert_not_despawned(); - - let storages = &mut self.world.storages; - let components = &mut self.world.components; - let bundles = &mut self.world.bundles; - let bundle_id = bundles.register_contributed_bundle_info::(components, storages); - // SAFETY: the `BundleInfo` for this `BundleId` is initialized above - let bundle_info = unsafe { bundles.get_unchecked(bundle_id) }; - let component_ids = bundle_info.contributed_components().to_owned(); - - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow_by_ids(component_ids); - builder.clone_entity(self.entity, target); - - // SAFETY: the `BundleInfo` for this `BundleId` is initialized above - unsafe { self.remove_bundle(bundle_id) }; - - self.world.flush(); - self.update_location(); - self - } - - /// Clones the given 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). - /// - /// You should prefer to use the typed API [`EntityWorldMut::move_components`] where possible. - /// - /// # Panics - /// - /// - If this entity has been despawned while this `EntityWorldMut` is still alive. - /// - If the target entity does not exist. - /// - If any of the provided [`ComponentId`]s do not exist. - pub fn move_components_by_id( - &mut self, - target: Entity, - ids: impl IntoIterator + Clone, - ) -> &mut Self { - self.assert_not_despawned(); - - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow_by_ids(ids.clone()); - builder.clone_entity(self.entity, target); - - let ids: Vec = ids.into_iter().collect(); - let components = &mut self.world.components; - let bundles = &mut self.world.bundles; - let bundle_id = if ids.len() == 1 { - bundles.init_component_info(components, ids[0]) - } else { - bundles.init_dynamic_info(components, &ids) - }; - - // SAFETY: the `BundleInfo` for this `BundleId` is initialized above - unsafe { self.remove_bundle(bundle_id) }; - self.world.flush(); self.update_location(); self @@ -5030,57 +4957,64 @@ mod tests { #[derive(Component, Clone, PartialEq, Debug)] struct C(u32); - #[derive(Component, Clone, PartialEq, Debug)] - #[require(E(|| E(10)))] + #[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(F)] - struct E(u32); + #[require(B)] + struct A; #[derive(Component, Clone, PartialEq, Debug, Default)] - struct F; + #[require(C(|| C(3)))] + struct B; - #[derive(Component, Clone, PartialEq, Debug)] - struct G(u32); + #[derive(Component, Clone, PartialEq, Debug, Default)] + #[require(D)] + struct C(u32); #[derive(Component, Clone, PartialEq, Debug, Default)] - struct H; + struct D; let mut world = World::new(); - let entity_a = world.spawn((A, B, C(5), D, G(22), H)).id(); - let entity_b = world.spawn((A, G(44))).id(); + 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.deny_all(); + builder.enable_move_on_clone(); + builder.with_required_components(|builder| { + builder.allow::(); + }); + }); - world.entity_mut(entity_a).clone_components::(entity_b); - assert_eq!(world.entity(entity_a).get::(), Some(&B)); + 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)); - 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_b).get::(), Some(&C(3))); - world - .entity_mut(entity_a) - .move_components_with_requires::(entity_b); assert_eq!(world.entity(entity_a).get::(), None); - assert_eq!(world.entity(entity_a).get::(), None); - assert_eq!(world.entity(entity_a).get::(), None); assert_eq!(world.entity(entity_b).get::(), Some(&D)); - assert_eq!(world.entity(entity_b).get::(), Some(&E(10))); - assert_eq!(world.entity(entity_b).get::(), Some(&F)); - - 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(&G(22))); - - let id = world.register_component::(); - world - .entity_mut(entity_a) - .move_components_by_id(entity_b, [id]); - assert_eq!(world.entity(entity_a).get::(), None); - assert_eq!(world.entity(entity_b).get::(), Some(&H)); - - assert_eq!(world.entity(entity_a).get::(), Some(&A)); - assert_eq!(world.entity(entity_b).get::(), Some(&A)); } } From 97642e8def9e4d5054dc0076f96fb01bb95678fd Mon Sep 17 00:00:00 2001 From: JaySpruce Date: Mon, 16 Dec 2024 12:04:20 -0600 Subject: [PATCH 3/3] implement suggestions --- crates/bevy_ecs/src/entity/clone_entities.rs | 34 +++++++-------- crates/bevy_ecs/src/system/commands/mod.rs | 29 ++++++++++-- crates/bevy_ecs/src/world/entity_ref.rs | 46 ++++++++++++-------- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 117904a1ebcd5..13029aa8a7423 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -162,7 +162,7 @@ impl<'w> EntityCloneBuilder<'w> { filter_allows_components: false, filter: Default::default(), clone_handlers_overrides: Default::default(), - attach_required_components: false, + attach_required_components: true, move_components: false, } } @@ -192,32 +192,30 @@ impl<'w> EntityCloneBuilder<'w> { world.flush_commands(); } - /// Allows for a scoped mode where any changes to the filter that allow/deny components - /// will also allow/deny those components' required components, recursively. - pub fn with_required_components( + /// 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 = true; - builder(self); self.attach_required_components = false; + builder(self); + self.attach_required_components = true; self } - /// Sets the cloner to remove any components that were cloned, + /// Sets whether the cloner should remove any components that were cloned, /// effectively moving them from the source entity to the target. /// - /// This only applies to components that are allowed through the filter + /// 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 enable_move_on_clone(&mut self) -> &mut Self { - self.move_components = true; - self - } - - /// The default setting for the cloner. The source entity will keep - /// all components that are cloned. - pub fn disable_move_on_clone(&mut self) -> &mut Self { - self.move_components = false; + pub fn move_components(&mut self, enable: bool) -> &mut Self { + self.move_components = enable; self } @@ -588,7 +586,7 @@ mod tests { let mut builder = EntityCloneBuilder::new(&mut world); builder.deny_all(); - builder.with_required_components(|builder| { + builder.without_required_components(|builder| { builder.allow::(); }); builder.clone_entity(e, e_clone); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index dce91c1d99158..f2d578bef3c9a 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1745,16 +1745,37 @@ impl<'a> EntityCommands<'a> { /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// /// Configure through [`EntityCloneBuilder`] as follows: - /// ```ignore - /// commands.entity(entity).clone_with(|builder| { - /// builder.deny::(); - /// }); + /// ``` + /// # 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, diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 584e21cc4948c..697dd3455427a 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -2140,10 +2140,20 @@ impl<'w> EntityWorldMut<'w> { /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// /// Configure through [`EntityCloneBuilder`] as follows: - /// ```ignore - /// world.entity_mut(entity).clone_with(|builder| { + /// ``` + /// # 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: @@ -2192,19 +2202,20 @@ impl<'w> EntityWorldMut<'w> { /// By default, the clone will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// - /// To exclude specific components, use [`EntityCloneBuilder::deny`]: - /// ```ignore - /// world.entity_mut(entity).clone_and_spawn_with(|builder| { - /// builder.deny::(); - /// }); + /// Configure through [`EntityCloneBuilder`] as follows: /// ``` - /// - /// To only include specific components, use [`EntityCloneBuilder::deny_all`] - /// followed by [`EntityCloneBuilder::allow`]: - /// ```ignore - /// world.entity_mut(entity).clone_and_spawn_with(|builder| { - /// builder.deny_all().allow::(); + /// # 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: @@ -2269,7 +2280,7 @@ impl<'w> EntityWorldMut<'w> { let mut builder = EntityCloneBuilder::new(self.world); builder.deny_all().allow::(); - builder.enable_move_on_clone(); + builder.move_components(true); builder.clone_entity(self.entity, target); self.world.flush(); @@ -4998,10 +5009,9 @@ mod tests { let entity_b = world.spawn_empty().id(); world.entity_mut(entity_a).clone_with(entity_b, |builder| { - builder.deny_all(); - builder.enable_move_on_clone(); - builder.with_required_components(|builder| { - builder.allow::(); + builder.move_components(true); + builder.without_required_components(|builder| { + builder.deny::(); }); });