From ccfa150018fd118fab67322fa68505f25984665b Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 28 Oct 2024 08:25:43 +0000 Subject: [PATCH 01/17] First implmentation of entity cloning --- crates/bevy_ecs/macros/src/component.rs | 6 + crates/bevy_ecs/src/component.rs | 197 +++++++++- crates/bevy_ecs/src/entity/clone_entities.rs | 342 ++++++++++++++++++ crates/bevy_ecs/src/entity/mod.rs | 2 + crates/bevy_ecs/src/lib.rs | 2 +- .../bevy_ecs/src/observer/entity_observer.rs | 108 +++++- crates/bevy_ecs/src/observer/mod.rs | 1 + crates/bevy_ecs/src/system/commands/mod.rs | 63 +++- crates/bevy_ecs/src/world/mod.rs | 34 +- crates/bevy_hierarchy/src/hierarchy.rs | 115 +++++- 10 files changed, 861 insertions(+), 9 deletions(-) create mode 100644 crates/bevy_ecs/src/entity/clone_entities.rs diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 08b4e73056635..ec1d6606916f8 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -163,6 +163,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream { #on_replace #on_remove } + + fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler { + use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase}; + (&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::::default()) + .get_component_clone_handler() + } } }) } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index c6a1abe75bca1..0e4375217ec3c 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -5,7 +5,7 @@ use crate::{ archetype::ArchetypeFlags, bundle::BundleInfo, change_detection::MAX_CHANGE_AGE, - entity::Entity, + entity::{Entity, EntityCloner}, query::DebugCheckedUnwrap, storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, system::{Local, Resource, SystemParam}, @@ -390,6 +390,11 @@ pub trait Component: Send + Sync + 'static { _inheritance_depth: u16, ) { } + + /// Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component. + fn get_component_clone_handler() -> ComponentCloneHandler { + ComponentCloneHandler::default() + } } /// The storage used for a specific component type. @@ -875,12 +880,86 @@ impl ComponentDescriptor { } } +/// Function type that can be used to clone an entity. +pub type ComponentCloneFn = fn(&mut World, ComponentId, &EntityCloner); + +/// An enum instructing how to clone a component. +#[derive(Debug, Default)] +pub enum ComponentCloneHandler { + #[default] + /// Use the global default function to clone the component with this handler. + Default, + /// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped. + Ignore, + /// Set a custom handler for the component. + Custom(ComponentCloneFn), +} + +/// A registry of component clone handlers. Allows to set global default and per-component clone function for all components in the world. +#[derive(Debug)] +pub struct ComponentCloneHandlers { + handlers: HashMap, + default_handler: ComponentCloneFn, +} + +impl ComponentCloneHandlers { + /// Sets the default handler for this registry. All components with [`Default`](ComponentCloneHandler::Default) handler, as well as any component that does not have an + /// explicitly registered clone function will use this handler. + pub fn set_default_handler(&mut self, handler: ComponentCloneFn) { + self.default_handler = handler; + } + + /// Sets a handler for a specific component. + pub fn set_component_handler( + &mut self, + component_id: ComponentId, + handler: ComponentCloneHandler, + ) { + match handler { + ComponentCloneHandler::Default => self.handlers.remove(&component_id), + ComponentCloneHandler::Ignore => { + self.handlers.insert(component_id, component_clone_ignore) + } + ComponentCloneHandler::Custom(handler) => self.handlers.insert(component_id, handler), + }; + } + + /// Checks if the specified component is registered. If not, the component will use the default global handler. + pub fn is_handler_registered(&self, component_id: ComponentId) -> bool { + self.handlers.contains_key(&component_id) + } + + /// Gets a handler to clone a component. This can be one of the following: + /// - Custom clone function for this specific component. + /// - Default global handler. + /// - A [`component_clone_ignore`] (no cloning). + pub fn get_handler(&self, component_id: ComponentId) -> ComponentCloneFn { + match self.handlers.get(&component_id) { + Some(handler) => *handler, + None => self.default_handler, + } + } +} + +impl Default for ComponentCloneHandlers { + fn default() -> Self { + Self { + handlers: Default::default(), + #[cfg(feature = "bevy_reflect")] + default_handler: component_clone_via_reflect, + #[cfg(not(feature = "bevy_reflect"))] + default_handler: component_clone_ignore, + } + } +} + /// Stores metadata associated with each kind of [`Component`] in a given [`World`]. #[derive(Debug, Default)] pub struct Components { components: Vec, indices: TypeIdMap, resource_indices: TypeIdMap, + component_clone_handlers: ComponentCloneHandlers, } impl Components { @@ -918,6 +997,9 @@ impl Components { let info = &mut self.components[id.index()]; T::register_component_hooks(&mut info.hooks); info.required_components = required_components; + let clone_handler = T::get_component_clone_handler(); + self.component_clone_handlers + .set_component_handler(id, clone_handler); } id } @@ -1249,6 +1331,16 @@ impl Components { .map(|info| &mut info.required_by) } + /// Retrieves the [`ComponentCloneHandlers`]. Can be used to get clone functions for components. + pub fn get_component_clone_handlers(&self) -> &ComponentCloneHandlers { + &self.component_clone_handlers + } + + /// Retrieves a mutable referene to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. + pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { + &mut self.component_clone_handlers + } + /// Type-erased equivalent of [`Components::component_id()`]. #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { @@ -1834,3 +1926,106 @@ impl RequiredComponents { } } } + +/// Component [clone handler function](ComponentCloneFn) implemented using the [`Clone`] trait. +/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for the specific component it is implemented for. +/// It will panic if set as handler for any other component. +/// +/// See [`ComponentCloneHandlers`] for more details +pub fn component_clone_via_clone( + world: &mut World, + _component_id: ComponentId, + entity_cloner: &EntityCloner, +) { + let component = world + .entity(entity_cloner.get_source()) + .get::() + .expect("Component must exists on source entity") + .clone(); + world + .entity_mut(entity_cloner.get_target()) + .insert(component); +} + +/// Component [clone handler function](ComponentCloneFn) implemented using reflect. +/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component, +/// but only reflected components will be cloned. +/// +/// See [`ComponentCloneHandlers`] for more details +#[cfg(feature = "bevy_reflect")] +pub fn component_clone_via_reflect( + world: &mut World, + component_id: ComponentId, + entity_cloner: &EntityCloner, +) { + world.resource_scope::(|world, registry| { + let registry = registry.read(); + + let component_info = world + .components() + .get_info(component_id) + .expect("Component must be registered"); + let Some(type_id) = component_info.type_id() else { + return; + }; + let Some(reflect_component) = + registry.get_type_data::(type_id) + else { + return; + }; + let source_component = reflect_component + .reflect( + world + .get_entity(entity_cloner.get_source()) + .expect("Source entity must exist"), + ) + .expect("Source entity must have reflected component") + .clone_value(); + let mut target = world + .get_entity_mut(entity_cloner.get_target()) + .expect("Target entity must exist"); + reflect_component.apply_or_insert(&mut target, &*source_component, ®istry); + }); +} + +/// Noop implementation of component clone handler function +/// +/// See [`ComponentCloneHandlers`] for more details +pub fn component_clone_ignore( + _world: &mut World, + _component_id: ComponentId, + _entity_cloner: &EntityCloner, +) { +} + +/// Wrapper for components clone specialization using autoderef +#[doc(hidden)] +pub struct ComponentCloneSpecializationWrapper(PhantomData); + +impl Default for ComponentCloneSpecializationWrapper { + fn default() -> Self { + Self(PhantomData) + } +} + +/// Base Trait for components clone specialization using autoderef +#[doc(hidden)] +pub trait ComponentCloneBase { + fn get_component_clone_handler(&self) -> ComponentCloneHandler; +} +impl ComponentCloneBase for ComponentCloneSpecializationWrapper { + fn get_component_clone_handler(&self) -> ComponentCloneHandler { + ComponentCloneHandler::default() + } +} + +/// Specialized trait for components clone specialization using autoderef +#[doc(hidden)] +pub trait ComponentCloneViaClone { + fn get_component_clone_handler(&self) -> ComponentCloneHandler; +} +impl ComponentCloneViaClone for &ComponentCloneSpecializationWrapper { + fn get_component_clone_handler(&self) -> ComponentCloneHandler { + ComponentCloneHandler::Custom(component_clone_via_clone::) + } +} diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs new file mode 100644 index 0000000000000..a523b8c3e02fa --- /dev/null +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -0,0 +1,342 @@ +use core::any::TypeId; + +use bevy_utils::{HashSet, TypeIdMap}; + +use crate::{ + component::{Component, ComponentCloneHandler, ComponentCloneHandlers, ComponentId}, + entity::Entity, + world::World, +}; + +/// A helper struct to clone an entity. Used internaly by [`EntityCloneBuilder::clone_entity`] and custom clone handlers. +pub struct EntityCloner<'a> { + source: Entity, + target: Entity, + allowed: bool, + filter: &'a HashSet, + clone_handlers_overrides: &'a ComponentCloneHandlers, +} + +impl<'a> EntityCloner<'a> { + /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. + pub fn clone_entity(&self, world: &mut World) { + let components = world + .get_entity(self.source) + .unwrap() + .archetype() + .components() + .collect::>(); + + for component in components { + if !self.is_cloning_allowed(&component) { + continue; + } + let handler = if self + .clone_handlers_overrides + .is_handler_registered(component) + { + self.clone_handlers_overrides.get_handler(component) + } else { + world + .components() + .get_component_clone_handlers() + .get_handler(component) + }; + + (handler)(world, component, self); + } + } + + fn is_cloning_allowed(&self, component: &ComponentId) -> bool { + (self.allowed && self.filter.contains(component)) + || (!self.allowed && !self.filter.contains(component)) + } + + /// Returns the current source entity. + pub fn get_source(&self) -> Entity { + self.source + } + + /// Returns the current target entity. + pub fn get_target(&self) -> Entity { + self.target + } + + /// Reuse existing [`EntityCloner`] configuration with new source and target. + pub fn with_source_and_target(&self, source: Entity, target: Entity) -> EntityCloner<'a> { + EntityCloner { + source, + target, + ..*self + } + } +} + +/// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them. +/// After configuration is complete an entity can be cloned using [`Self::clone_entity`]. +/// +///``` +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::entity::EntityCloneBuilder; +/// +/// #[derive(Component, Clone, PartialEq, Eq)] +/// struct A { +/// field: usize, +/// } +/// +/// let mut world = World::default(); +/// +/// let component = A { field: 5 }; +/// +/// let entity = world.spawn(component.clone()).id(); +/// let entity_clone = world.spawn_empty().id(); +/// +/// EntityCloneBuilder::default().clone_entity(&mut world, entity, entity_clone); +/// +/// assert!(world.get::(entity_clone).is_some_and(|c| *c == component)); +///``` +#[derive(Default)] +pub struct EntityCloneBuilder { + ignored_components: HashSet, + allowed_components: HashSet, + clone_handlers_overrides: TypeIdMap, +} + +impl EntityCloneBuilder { + /// Finish configuring the builder and clone an entity. + pub fn clone_entity(self, world: &mut World, source: Entity, target: Entity) { + let EntityCloneBuilder { + ignored_components, + allowed_components, + clone_handlers_overrides, + .. + } = self; + + let mut component_clone_handlers = ComponentCloneHandlers::default(); + for (k, v) in clone_handlers_overrides.into_iter() { + if let Some(component_id) = world.components().get_id(k) { + component_clone_handlers.set_component_handler(component_id, v); + }; + } + + let mut allowed = false; + let filter = if allowed_components.is_empty() { + ignored_components + .iter() + .flat_map(|type_id| world.components().get_id(*type_id)) + .collect::>() + } else { + allowed = true; + allowed_components + .difference(&ignored_components) + .flat_map(|type_id| world.components().get_id(*type_id)) + .collect::>() + }; + + EntityCloner { + source, + target, + allowed, + filter: &filter, + clone_handlers_overrides: &component_clone_handlers, + } + .clone_entity(world); + } + + /// Add a component to the list of allowed components to clone. + /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. + pub fn allow(&mut self) -> &mut Self { + self.allowed_components.insert(TypeId::of::()); + self + } + + /// Disallow a component from being cloned. + /// If [`Self::allow`] was called beforehand, this will remove the copmonent from allowed list. + pub fn deny(&mut self) -> &mut Self { + self.ignored_components.insert(TypeId::of::()); + self + } + + /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. + /// This handler will be used to clone component instead of the global one defined by [`ComponentCloneHandlers`] + pub fn override_component_clone_handler( + &mut self, + handler: ComponentCloneHandler, + ) -> &mut Self { + self.clone_handlers_overrides + .insert(TypeId::of::(), handler); + self + } +} + +#[cfg(test)] +mod tests { + use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World}; + + #[cfg(feature = "bevy_reflect")] + #[test] + fn clone_entity_using_reflect() { + use crate::reflect::{AppTypeRegistry, ReflectComponent}; + use bevy_reflect::Reflect; + + #[derive(Component, Reflect, Clone, PartialEq, Eq)] + #[reflect(Component)] + struct A { + field: usize, + } + + let mut world = World::default(); + world.init_resource::(); + let registry = world.get_resource::().unwrap(); + registry.write().register::(); + + let component = A { field: 5 }; + + let e = world.spawn(component.clone()).id(); + let e_clone = world.spawn_empty().id(); + + EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + } + + #[test] + fn clone_entity_using_clone() { + #[derive(Component, Clone, PartialEq, Eq)] + struct A { + field: usize, + } + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn(component.clone()).id(); + let e_clone = world.spawn_empty().id(); + + EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + } + + #[cfg(feature = "bevy_reflect")] + #[test] + fn clone_entity_specialization() { + use crate::reflect::{AppTypeRegistry, ReflectComponent}; + use bevy_reflect::Reflect; + + #[derive(Component, Reflect, PartialEq, Eq)] + #[reflect(Component)] + struct A { + field: usize, + } + + impl Clone for A { + fn clone(&self) -> Self { + Self { field: 10 } + } + } + + let mut world = World::default(); + world.init_resource::(); + let registry = world.get_resource::().unwrap(); + registry.write().register::(); + + let component = A { field: 5 }; + + let e = world.spawn(component.clone()).id(); + let e_clone = world.spawn_empty().id(); + + EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + + assert!(world + .get::(e_clone) + .is_some_and(|comp| *comp == A { field: 10 })); + } + + #[test] + fn clone_entity_with_allow_filter() { + #[derive(Component, Clone, PartialEq, Eq)] + struct A { + field: usize, + } + + #[derive(Component, Clone)] + struct B; + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn((component.clone(), B)).id(); + let e_clone = world.spawn_empty().id(); + + let mut builder = EntityCloneBuilder::default(); + builder.allow::(); + builder.clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + assert!(world.get::(e_clone).is_none()); + } + + #[test] + fn clone_entity_with_deny_filter() { + #[derive(Component, Clone, PartialEq, Eq)] + struct A { + field: usize, + } + + #[derive(Component, Clone)] + struct B; + + #[derive(Component, Clone)] + struct C; + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn((component.clone(), B, C)).id(); + let e_clone = world.spawn_empty().id(); + + let mut builder = EntityCloneBuilder::default(); + builder.deny::(); + builder.clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + assert!(world.get::(e_clone).is_none()); + assert!(world.get::(e_clone).is_some()); + } + + #[test] + fn clone_entity_with_override_allow_filter() { + #[derive(Component, Clone, PartialEq, Eq)] + struct A { + field: usize, + } + + #[derive(Component, Clone)] + struct B; + + #[derive(Component, Clone)] + struct C; + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn((component.clone(), B, C)).id(); + let e_clone = world.spawn_empty().id(); + + let mut builder = EntityCloneBuilder::default(); + builder.allow::(); + builder.allow::(); + builder.allow::(); + builder.deny::(); + builder.clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + assert!(world.get::(e_clone).is_none()); + assert!(world.get::(e_clone).is_some()); + } +} diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index c2f687af14d55..163d2775d0ed5 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -35,12 +35,14 @@ //! [`World::despawn`]: crate::world::World::despawn //! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert //! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove +mod clone_entities; mod map_entities; mod visit_entities; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; #[cfg(all(feature = "bevy_reflect", feature = "serialize"))] use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; +pub use clone_entities::*; pub use map_entities::*; pub use visit_entities::*; diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 67254d6298f11..8e05ec8887fc7 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -49,7 +49,7 @@ pub mod prelude { component::Component, entity::{Entity, EntityMapper}, event::{Event, EventMutator, EventReader, EventWriter, Events}, - observer::{Observer, Trigger}, + observer::{CloneEntityWithObserversExt, Observer, Trigger}, query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without}, removal_detection::RemovedComponents, schedule::{ diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index a5f332e6f3b8c..19c33e668af12 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -1,7 +1,8 @@ use crate::{ - component::{Component, ComponentHooks, StorageType}, - entity::Entity, + component::{Component, ComponentCloneHandler, ComponentHooks, ComponentId, StorageType}, + entity::{Entity, EntityCloneBuilder, EntityCloner}, observer::ObserverState, + world::World, }; /// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to. @@ -40,3 +41,106 @@ impl Component for ObservedBy { }); } } + +/// Trait that holds functions for configuring interaction with observers during entity cloning. +pub trait CloneEntityWithObserversExt { + /// Sets the option to automatically add cloned entities to the obsevers targeting source entity. + fn add_observers(&mut self, add_observers: bool) -> &mut EntityCloneBuilder; +} + +impl CloneEntityWithObserversExt for EntityCloneBuilder { + fn add_observers(&mut self, add_observers: bool) -> &mut EntityCloneBuilder { + if add_observers { + self.override_component_clone_handler::(ComponentCloneHandler::Custom( + component_clone_observed_by, + )) + } else { + self.override_component_clone_handler::(ComponentCloneHandler::Ignore) + } + } +} + +fn component_clone_observed_by( + world: &mut World, + _component_id: ComponentId, + entity_cloner: &EntityCloner, +) { + let target = entity_cloner.get_target(); + let source = entity_cloner.get_source(); + + let observed_by = world + .get::(source) + .map(|observed_by| observed_by.0.clone()) + .expect("Source entity must have ObservedBy"); + + world + .entity_mut(target) + .insert(ObservedBy(observed_by.clone())); + + for observer in &observed_by { + let mut observer_state = world + .get_mut::(*observer) + .expect("Source observer entity must have ObserverState"); + observer_state.descriptor.entities.push(target); + let event_types = observer_state.descriptor.events.clone(); + let components = observer_state.descriptor.components.clone(); + for event_type in event_types { + let observers = world.observers.get_observers(event_type); + if components.is_empty() { + if let Some(map) = observers.entity_observers.get(&source).cloned() { + observers.entity_observers.insert(target, map); + } + } else { + for component in &components { + let Some(observers) = observers.component_observers.get_mut(component) else { + continue; + }; + if let Some(map) = observers.entity_map.get(&source).cloned() { + observers.entity_map.insert(target, map); + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + self as bevy_ecs, + entity::EntityCloneBuilder, + event::Event, + observer::{CloneEntityWithObserversExt, Trigger}, + system::{ResMut, Resource}, + world::World, + }; + + #[derive(Resource, Default)] + struct Num(usize); + + #[derive(Event)] + struct E; + + #[test] + fn clone_entity_with_observer() { + let mut world = World::default(); + world.init_resource::(); + + let e = world + .spawn_empty() + .observe(|_: Trigger, mut res: ResMut| res.0 += 1) + .id(); + world.flush(); + + world.trigger_targets(E, e); + + let e_clone = world.spawn_empty().id(); + let mut builder = EntityCloneBuilder::default(); + builder.add_observers(true); + builder.clone_entity(&mut world, e, e_clone); + + world.trigger_targets(E, [e, e_clone]); + + assert_eq!(world.resource::().0, 3); + } +} diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 674f98f650725..cec04b94eaf4a 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -4,6 +4,7 @@ mod entity_observer; mod runner; mod trigger_event; +pub use entity_observer::CloneEntityWithObserversExt; pub use runner::*; pub use trigger_event::*; diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 08eda1e5d59fc..fa5d7453dcca8 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -10,7 +10,7 @@ use crate::{ bundle::{Bundle, InsertMode}, change_detection::Mut, component::{Component, ComponentId, ComponentInfo}, - entity::{Entities, Entity}, + entity::{Entities, Entity, EntityCloneBuilder}, event::{Event, SendEvent}, observer::{Observer, TriggerEvent, TriggerTargets}, system::{input::SystemInput, RunSystemWithInput, SystemId}, @@ -269,6 +269,67 @@ impl<'w, 's> Commands<'w, 's> { } } + /// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], returning [`EntityCommands`] of the cloned entity. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Component, Clone)] + /// struct ComponentA(u32); + /// #[derive(Component, Clone)] + /// struct ComponentB(u32); + /// + /// fn example_system(mut commands: Commands) { + /// // Create a new entity and retrieve its id. + /// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id(); + /// + /// // Create a clone of the first entity, but without ComponentB + /// let entity_clone = commands.clone_entity_with(entity, |builder| { + /// builder.deny::() + /// }).id(); + /// } + /// # bevy_ecs::system::assert_is_system(example_system); + pub fn clone_entity_with( + &mut self, + entity: Entity, + f: impl FnOnce(&mut EntityCloneBuilder), + ) -> EntityCommands<'_> { + let mut builder = EntityCloneBuilder::default(); + f(&mut builder); + let cloned_entity = self.spawn_empty().id(); + self.queue(move |world: &mut World| builder.clone_entity(world, entity, cloned_entity)); + EntityCommands { + commands: self.reborrow(), + entity: cloned_entity, + } + } + + /// Clones an entity and returns [`EntityCommands`] of the cloned entity. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Component, Clone)] + /// struct ComponentA(u32); + /// #[derive(Component, Clone)] + /// struct ComponentB(u32); + /// + /// fn example_system(mut commands: Commands) { + /// // Create a new entity and retrieve its id. + /// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id(); + /// + /// // Create a clone of the first entity + /// let entity_clone = commands.clone_entity(entity).id(); + /// } + /// # bevy_ecs::system::assert_is_system(example_system); + pub fn clone_entity(&mut self, entity: Entity) -> EntityCommands<'_> { + self.clone_entity_with(entity, |_| {}) + } + /// Reserves a new empty [`Entity`] to be spawned, and returns its corresponding [`EntityCommands`]. /// /// See [`World::spawn_empty`] for more details. diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d50f8f421cfc2..814eec502816b 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -34,8 +34,9 @@ use crate::{ bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode}, change_detection::{MutUntyped, TicksMut}, component::{ - Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, - Components, RequiredComponents, RequiredComponentsError, Tick, + Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId, + ComponentInfo, ComponentTicks, Components, RequiredComponents, RequiredComponentsError, + Tick, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityHashSet, EntityLocation}, event::{Event, EventId, Events, SendBatchIds}, @@ -3260,6 +3261,35 @@ impl World { // SAFETY: We just initialized the bundle so its id should definitely be valid. unsafe { self.bundles.get(id).debug_checked_unwrap() } } + + /// Retrieves a mutable referene to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// use bevy_ecs::component::{ComponentId, ComponentCloneHandler}; + /// use bevy_ecs::entity::EntityCloner; + /// + /// fn custom_clone_handler( + /// _world: &mut World, + /// component_id: ComponentId, + /// _entity_cloner: &EntityCloner, + /// ) { + /// // Custom cloning logic for component + /// } + /// + /// #[derive(Component)] + /// struct ComponentA; + /// + /// let mut world = World::new(); + /// + /// let component_id = world.register_component::(); + /// + /// world.get_component_clone_handlers_mut() + /// .set_component_handler(component_id, ComponentCloneHandler::Custom(custom_clone_handler)) + /// ``` + pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { + self.components.get_component_clone_handlers_mut() + } } impl World { diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 4a9d71ace7d13..806846fe0df50 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -1,6 +1,10 @@ -use crate::components::{Children, Parent}; +use crate::{ + components::{Children, Parent}, + BuildChildren, +}; use bevy_ecs::{ - entity::Entity, + component::{ComponentCloneHandler, ComponentId}, + entity::{Entity, EntityCloneBuilder, EntityCloner}, system::EntityCommands, world::{Command, EntityWorldMut, World}, }; @@ -198,6 +202,50 @@ impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> { } } +/// Trait that holds functions for cloning entities recursively down the hierarchy +pub trait CloneEntityRecursiveExt { + /// Sets the option to recursively clone entities. + /// When set to true all children will be cloned with the same options as the parent. + fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder; +} + +impl CloneEntityRecursiveExt for EntityCloneBuilder { + fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder { + if recursive { + self.override_component_clone_handler::(ComponentCloneHandler::Custom( + component_clone_children, + )) + .override_component_clone_handler::(ComponentCloneHandler::Ignore) + } else { + self.override_component_clone_handler::(ComponentCloneHandler::Default) + .override_component_clone_handler::(ComponentCloneHandler::Default) + } + } +} + +/// Clone handler for the [`Children`] component. Allows to clone the entity recursively. +fn component_clone_children( + world: &mut World, + _component_id: ComponentId, + entity_cloner: &EntityCloner, +) { + let children = world + .get::(entity_cloner.get_source()) + .expect("Source entity must have Children component") + .iter() + .cloned() + .collect::>(); + for child in children { + let child_clone = world.spawn_empty().id(); + entity_cloner + .with_source_and_target(child, child_clone) + .clone_entity(world); + world + .entity_mut(child_clone) + .set_parent(entity_cloner.get_target()); + } +} + #[cfg(test)] mod tests { use bevy_ecs::{ @@ -210,6 +258,7 @@ mod tests { use crate::{ child_builder::{BuildChildren, ChildBuild}, components::Children, + CloneEntityRecursiveExt, }; #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)] @@ -342,4 +391,66 @@ mod tests { // The original child should be despawned. assert!(world.get_entity(child).is_err()); } + + #[test] + fn clone_entity_recursive() { + #[derive(Component, PartialEq, Eq, Clone)] + struct Component1 { + field: usize, + } + + let parent_component = Component1 { field: 10 }; + let child1_component = Component1 { field: 20 }; + let child1_1_component = Component1 { field: 30 }; + let child2_component = Component1 { field: 21 }; + let child2_1_component = Component1 { field: 31 }; + + let mut world = World::default(); + + let mut queue = CommandQueue::default(); + let e_clone = { + let mut commands = Commands::new(&mut queue, &world); + let e = commands + .spawn(parent_component.clone()) + .with_children(|children| { + children + .spawn(child1_component.clone()) + .with_children(|children| { + children.spawn(child1_1_component.clone()); + }); + children + .spawn(child2_component.clone()) + .with_children(|children| { + children.spawn(child2_1_component.clone()); + }); + }) + .id(); + let e_clone = commands + .clone_entity_with(e, |builder| { + builder.recursive(true); + }) + .id(); + e_clone + }; + queue.apply(&mut world); + + assert!(world + .get::(e_clone) + .is_some_and(|c| *c == parent_component)); + + let children = world.get::(e_clone).unwrap(); + for (child, (component1, component2)) in children.iter().zip([ + (child1_component, child1_1_component), + (child2_component, child2_1_component), + ]) { + assert!(world + .get::(*child) + .is_some_and(|c| *c == component1)); + for child2 in world.get::(*child).unwrap().iter() { + assert!(world + .get::(*child2) + .is_some_and(|c| *c == component2)); + } + } + } } From e3b85b69737931aa17116fba423b0371d65551e3 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 28 Oct 2024 11:22:14 +0000 Subject: [PATCH 02/17] fix typos --- crates/bevy_ecs/src/component.rs | 2 +- crates/bevy_ecs/src/entity/clone_entities.rs | 4 ++-- crates/bevy_ecs/src/system/commands/mod.rs | 2 +- crates/bevy_ecs/src/world/mod.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 0e4375217ec3c..f62196cf91c90 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1336,7 +1336,7 @@ impl Components { &self.component_clone_handlers } - /// Retrieves a mutable referene to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. + /// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { &mut self.component_clone_handlers } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index a523b8c3e02fa..763c689fdcd84 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -8,7 +8,7 @@ use crate::{ world::World, }; -/// A helper struct to clone an entity. Used internaly by [`EntityCloneBuilder::clone_entity`] and custom clone handlers. +/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`] and custom clone handlers. pub struct EntityCloner<'a> { source: Entity, target: Entity, @@ -151,7 +151,7 @@ impl EntityCloneBuilder { } /// Disallow a component from being cloned. - /// If [`Self::allow`] was called beforehand, this will remove the copmonent from allowed list. + /// If [`Self::allow`] was called beforehand, this will remove the component from allowed list. pub fn deny(&mut self) -> &mut Self { self.ignored_components.insert(TypeId::of::()); self diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index fa5d7453dcca8..02aea5876e275 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -287,7 +287,7 @@ impl<'w, 's> Commands<'w, 's> { /// /// // Create a clone of the first entity, but without ComponentB /// let entity_clone = commands.clone_entity_with(entity, |builder| { - /// builder.deny::() + /// builder.deny::(); /// }).id(); /// } /// # bevy_ecs::system::assert_is_system(example_system); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 814eec502816b..7d49c4a067df1 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3262,7 +3262,7 @@ impl World { unsafe { self.bundles.get(id).debug_checked_unwrap() } } - /// Retrieves a mutable referene to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. + /// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components. /// /// ``` /// # use bevy_ecs::prelude::*; From 0685c649f4afb861c200167dfb7555f06e74b3d5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 29 Oct 2024 09:55:54 +0000 Subject: [PATCH 03/17] added `allow_by_ids` and `disallow_by_ids` to `EntityCloneBuilder` --- crates/bevy_ecs/src/entity/clone_entities.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 763c689fdcd84..9af0660beb5d2 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -143,20 +143,32 @@ impl EntityCloneBuilder { .clone_entity(world); } - /// Add a component to the list of allowed components to clone. + /// Add a component to the list of components to clone. /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. pub fn allow(&mut self) -> &mut Self { self.allowed_components.insert(TypeId::of::()); self } + /// Extend the list of components to clone. + /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. + pub fn allow_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { + self.allowed_components.extend(ids); + self + } + /// Disallow a component from being cloned. - /// If [`Self::allow`] was called beforehand, this will remove the component from allowed list. pub fn deny(&mut self) -> &mut Self { self.ignored_components.insert(TypeId::of::()); self } + /// Extend the list of components that shouldn't be cloned. + pub fn deny_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { + self.ignored_components.extend(ids); + self + } + /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. /// This handler will be used to clone component instead of the global one defined by [`ComponentCloneHandlers`] pub fn override_component_clone_handler( From c1c10c11756429b7314160cc0a9890bb3f0b8f7b Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 30 Oct 2024 10:46:18 +0000 Subject: [PATCH 04/17] set `ObservedBy` clone handler to `Ignore` by default --- crates/bevy_ecs/src/observer/entity_observer.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 19c33e668af12..3b44cd8f5f6f8 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -40,6 +40,10 @@ impl Component for ObservedBy { } }); } + + fn get_component_clone_handler() -> ComponentCloneHandler { + ComponentCloneHandler::Ignore + } } /// Trait that holds functions for configuring interaction with observers during entity cloning. @@ -55,7 +59,7 @@ impl CloneEntityWithObserversExt for EntityCloneBuilder { component_clone_observed_by, )) } else { - self.override_component_clone_handler::(ComponentCloneHandler::Ignore) + self.override_component_clone_handler::(ComponentCloneHandler::Default) } } } From 5b840f75c7853450f35ead7792fd143470853e92 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 11:11:01 +0000 Subject: [PATCH 05/17] Add `as_child` to `EntityCloneBuilder` --- .../bevy_hierarchy/src/components/children.rs | 12 +++- .../bevy_hierarchy/src/components/parent.rs | 12 +++- crates/bevy_hierarchy/src/hierarchy.rs | 57 +++++++++++++++++-- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/crates/bevy_hierarchy/src/components/children.rs b/crates/bevy_hierarchy/src/components/children.rs index 18ad943ee3637..b811c1da2dd0e 100644 --- a/crates/bevy_hierarchy/src/components/children.rs +++ b/crates/bevy_hierarchy/src/components/children.rs @@ -4,7 +4,7 @@ use bevy_ecs::reflect::{ ReflectVisitEntitiesMut, }; use bevy_ecs::{ - component::Component, + component::{Component, ComponentCloneHandler, StorageType}, entity::{Entity, VisitEntitiesMut}, prelude::FromWorld, world::World, @@ -25,7 +25,7 @@ use smallvec::SmallVec; /// [`Query`]: bevy_ecs::system::Query /// [`Parent`]: crate::components::parent::Parent /// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children -#[derive(Component, Debug, VisitEntitiesMut)] +#[derive(Debug, VisitEntitiesMut)] #[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] #[cfg_attr( feature = "reflect", @@ -40,6 +40,14 @@ use smallvec::SmallVec; )] pub struct Children(pub(crate) SmallVec<[Entity; 8]>); +impl Component for Children { + const STORAGE_TYPE: StorageType = StorageType::Table; + + fn get_component_clone_handler() -> ComponentCloneHandler { + ComponentCloneHandler::Ignore + } +} + // TODO: We need to impl either FromWorld or Default so Children can be registered as Reflect. // This is because Reflect deserialize by creating an instance and apply a patch on top. // However Children should only ever be set with a real user-defined entities. Its worth looking diff --git a/crates/bevy_hierarchy/src/components/parent.rs b/crates/bevy_hierarchy/src/components/parent.rs index b369447734206..445857bb18d6f 100644 --- a/crates/bevy_hierarchy/src/components/parent.rs +++ b/crates/bevy_hierarchy/src/components/parent.rs @@ -4,7 +4,7 @@ use bevy_ecs::reflect::{ ReflectVisitEntitiesMut, }; use bevy_ecs::{ - component::Component, + component::{Component, ComponentCloneHandler, StorageType}, entity::{Entity, VisitEntities, VisitEntitiesMut}, traversal::Traversal, world::{FromWorld, World}, @@ -24,7 +24,7 @@ use core::ops::Deref; /// [`Query`]: bevy_ecs::system::Query /// [`Children`]: super::children::Children /// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children -#[derive(Component, Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)] +#[derive(Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)] #[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] #[cfg_attr( feature = "reflect", @@ -40,6 +40,14 @@ use core::ops::Deref; )] pub struct Parent(pub(crate) Entity); +impl Component for Parent { + const STORAGE_TYPE: StorageType = StorageType::Table; + + fn get_component_clone_handler() -> ComponentCloneHandler { + ComponentCloneHandler::Ignore + } +} + impl Parent { /// Gets the [`Entity`] ID of the parent. #[inline(always)] diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 806846fe0df50..b6073de507edc 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -203,22 +203,31 @@ impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> { } /// Trait that holds functions for cloning entities recursively down the hierarchy -pub trait CloneEntityRecursiveExt { +pub trait CloneEntityHierarchyExt { /// Sets the option to recursively clone entities. /// When set to true all children will be cloned with the same options as the parent. fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder; + /// Sets the option to add cloned entity as a child to the parent entity. + fn as_child(&mut self, as_child: bool) -> &mut EntityCloneBuilder; } -impl CloneEntityRecursiveExt for EntityCloneBuilder { +impl CloneEntityHierarchyExt for EntityCloneBuilder { fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder { if recursive { self.override_component_clone_handler::(ComponentCloneHandler::Custom( component_clone_children, )) - .override_component_clone_handler::(ComponentCloneHandler::Ignore) } else { self.override_component_clone_handler::(ComponentCloneHandler::Default) - .override_component_clone_handler::(ComponentCloneHandler::Default) + } + } + fn as_child(&mut self, as_child: bool) -> &mut EntityCloneBuilder { + if as_child { + self.override_component_clone_handler::(ComponentCloneHandler::Custom( + component_clone_parent, + )) + } else { + self.override_component_clone_handler::(ComponentCloneHandler::Default) } } } @@ -246,6 +255,21 @@ fn component_clone_children( } } +/// Clone handler for the [`Parent`] component. Allows to add clone as a child to the parent entity. +fn component_clone_parent( + world: &mut World, + _component_id: ComponentId, + entity_cloner: &EntityCloner, +) { + let parent = world + .get::(entity_cloner.get_source()) + .map(|p| p.0) + .expect("Source entity must have Parent component"); + world + .entity_mut(entity_cloner.get_target()) + .set_parent(parent); +} + #[cfg(test)] mod tests { use bevy_ecs::{ @@ -258,7 +282,7 @@ mod tests { use crate::{ child_builder::{BuildChildren, ChildBuild}, components::Children, - CloneEntityRecursiveExt, + CloneEntityHierarchyExt, }; #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)] @@ -453,4 +477,27 @@ mod tests { } } } + + #[test] + fn clone_entity_as_child() { + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + let child = commands.spawn_empty().id(); + let parent = commands.spawn_empty().add_child(child).id(); + + let child_clone = commands + .clone_entity_with(child, |builder| { + builder.as_child(true); + }) + .id(); + + queue.apply(&mut world); + + assert!(world + .entity(parent) + .get::() + .is_some_and(|c| c.contains(&child_clone))) + } } From c1dde50caac6d2877aacc8e54271c894f1a5c907 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 17:40:21 +0000 Subject: [PATCH 06/17] add `allow_all` and `deny_all` to `EntityCloneBuilder` --- crates/bevy_ecs/src/entity/clone_entities.rs | 31 +++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 9af0660beb5d2..b857b93f88258 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -22,7 +22,7 @@ impl<'a> EntityCloner<'a> { pub fn clone_entity(&self, world: &mut World) { let components = world .get_entity(self.source) - .unwrap() + .expect("Source entity must exist") .archetype() .components() .collect::>(); @@ -119,16 +119,15 @@ impl EntityCloneBuilder { }; } - let mut allowed = false; - let filter = if allowed_components.is_empty() { - ignored_components - .iter() + let allowed = !allowed_components.is_empty(); + let filter = if allowed { + allowed_components + .difference(&ignored_components) .flat_map(|type_id| world.components().get_id(*type_id)) .collect::>() } else { - allowed = true; - allowed_components - .difference(&ignored_components) + ignored_components + .iter() .flat_map(|type_id| world.components().get_id(*type_id)) .collect::>() }; @@ -157,6 +156,13 @@ impl EntityCloneBuilder { self } + /// Reset the filter to allow all components to be cloned + pub fn allow_all(&mut self) -> &mut Self { + self.allowed_components.clear(); + self.ignored_components.clear(); + self + } + /// Disallow a component from being cloned. pub fn deny(&mut self) -> &mut Self { self.ignored_components.insert(TypeId::of::()); @@ -169,6 +175,15 @@ impl EntityCloneBuilder { self } + /// Set the filter to deny all components + pub fn deny_all(&mut self) -> &mut Self { + struct Dummy; + self.allowed_components.clear(); + // just put some dummy type id that can't be a component to emulate "allowed" mode + self.allowed_components.insert(TypeId::of::()); + self + } + /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. /// This handler will be used to clone component instead of the global one defined by [`ComponentCloneHandlers`] pub fn override_component_clone_handler( From d94ca1822f88c337af3e69b63e5f0f02eee7e315 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 18:21:37 +0000 Subject: [PATCH 07/17] add `allow_bundle` and `deny_bundle` to `EntityCloneBuilder` --- crates/bevy_ecs/src/entity/clone_entities.rs | 88 ++++++++++++++++++-- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b857b93f88258..1928aaea37e7b 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -3,7 +3,10 @@ use core::any::TypeId; use bevy_utils::{HashSet, TypeIdMap}; use crate::{ - component::{Component, ComponentCloneHandler, ComponentCloneHandlers, ComponentId}, + bundle::Bundle, + component::{ + Component, ComponentCloneHandler, ComponentCloneHandlers, ComponentId, Components, + }, entity::Entity, world::World, }; @@ -98,7 +101,9 @@ impl<'a> EntityCloner<'a> { #[derive(Default)] pub struct EntityCloneBuilder { ignored_components: HashSet, + ignored_bundles: Vec)>, allowed_components: HashSet, + allowed_bundles: Vec)>, clone_handlers_overrides: TypeIdMap, } @@ -107,7 +112,9 @@ impl EntityCloneBuilder { pub fn clone_entity(self, world: &mut World, source: Entity, target: Entity) { let EntityCloneBuilder { ignored_components, + ignored_bundles, allowed_components, + allowed_bundles, clone_handlers_overrides, .. } = self; @@ -119,17 +126,30 @@ impl EntityCloneBuilder { }; } + let ignored_components = ignored_components + .into_iter() + .flat_map(|type_id| world.components().get_id(type_id)) + .chain(ignored_bundles.into_iter().flat_map(|f| { + let mut id = None; + (f)(world.components(), &mut id); + id + })) + .collect::>(); + let allowed = !allowed_components.is_empty(); let filter = if allowed { allowed_components - .difference(&ignored_components) - .flat_map(|type_id| world.components().get_id(*type_id)) + .into_iter() + .flat_map(|type_id| world.components().get_id(type_id)) + .chain(allowed_bundles.into_iter().flat_map(|f| { + let mut id = None; + (f)(world.components(), &mut id); + id + })) + .filter(|id| !ignored_components.contains(id)) .collect::>() } else { ignored_components - .iter() - .flat_map(|type_id| world.components().get_id(*type_id)) - .collect::>() }; EntityCloner { @@ -159,10 +179,23 @@ impl EntityCloneBuilder { /// Reset the filter to allow all components to be cloned pub fn allow_all(&mut self) -> &mut Self { self.allowed_components.clear(); + self.allowed_bundles.clear(); self.ignored_components.clear(); + self.ignored_bundles.clear(); self } + /// Add a bundle of components to the list of components to clone. + /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. + pub fn allow_bundle(&mut self) { + let bundle_ids_getter = |components: &Components, id: &mut Option| { + T::get_component_ids(components, &mut |component_id: Option| { + *id = component_id; + }); + }; + self.allowed_bundles.push(bundle_ids_getter) + } + /// Disallow a component from being cloned. pub fn deny(&mut self) -> &mut Self { self.ignored_components.insert(TypeId::of::()); @@ -177,13 +210,24 @@ impl EntityCloneBuilder { /// Set the filter to deny all components pub fn deny_all(&mut self) -> &mut Self { - struct Dummy; self.allowed_components.clear(); + self.allowed_bundles.clear(); // just put some dummy type id that can't be a component to emulate "allowed" mode + struct Dummy; self.allowed_components.insert(TypeId::of::()); self } + /// Disallow a bundle of components from being cloned. + pub fn deny_bundle(&mut self) { + let bundle_ids_getter = |components: &Components, id: &mut Option| { + T::get_component_ids(components, &mut |component_id: Option| { + *id = component_id; + }); + }; + self.ignored_bundles.push(bundle_ids_getter) + } + /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. /// This handler will be used to clone component instead of the global one defined by [`ComponentCloneHandlers`] pub fn override_component_clone_handler( @@ -366,4 +410,34 @@ mod tests { assert!(world.get::(e_clone).is_none()); assert!(world.get::(e_clone).is_some()); } + + #[test] + fn clone_entity_with_override_bundle() { + #[derive(Component, Clone, PartialEq, Eq)] + struct A { + field: usize, + } + + #[derive(Component, Clone)] + struct B; + + #[derive(Component, Clone)] + struct C; + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn((component.clone(), B, C)).id(); + let e_clone = world.spawn_empty().id(); + + let mut builder = EntityCloneBuilder::default(); + builder.allow_bundle::<(A, B, C)>(); + builder.deny_bundle::(); + builder.clone_entity(&mut world, e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); + assert!(world.get::(e_clone).is_none()); + assert!(world.get::(e_clone).is_some()); + } } From 6c4bb0656760e6f99125b9cbb54d9f667942f70b Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 21:19:18 +0000 Subject: [PATCH 08/17] clippy fixes --- crates/bevy_ecs/src/entity/clone_entities.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 1928aaea37e7b..ad1b13aa857c7 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -193,7 +193,7 @@ impl EntityCloneBuilder { *id = component_id; }); }; - self.allowed_bundles.push(bundle_ids_getter) + self.allowed_bundles.push(bundle_ids_getter); } /// Disallow a component from being cloned. @@ -225,7 +225,7 @@ impl EntityCloneBuilder { *id = component_id; }); }; - self.ignored_bundles.push(bundle_ids_getter) + self.ignored_bundles.push(bundle_ids_getter); } /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. From 0b800df29bdd2f8ba30955da7a0a4a9420a9541f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 21:30:48 +0000 Subject: [PATCH 09/17] actually ran clippy --- crates/bevy_hierarchy/src/hierarchy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index b6073de507edc..15b187d10c960 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -498,6 +498,6 @@ mod tests { assert!(world .entity(parent) .get::() - .is_some_and(|c| c.contains(&child_clone))) + .is_some_and(|c| c.contains(&child_clone))); } } From 07332486331e2eb1cf33a3f9a4dc15341ba1f523 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 31 Oct 2024 22:15:14 +0000 Subject: [PATCH 10/17] fix `allow_bundle` and `deny_bundle` not actually working as intended --- crates/bevy_ecs/src/entity/clone_entities.rs | 60 ++++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index ad1b13aa857c7..06a626f38c05e 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -101,9 +101,9 @@ impl<'a> EntityCloner<'a> { #[derive(Default)] pub struct EntityCloneBuilder { ignored_components: HashSet, - ignored_bundles: Vec)>, + ignored_bundles: Vec)>, allowed_components: HashSet, - allowed_bundles: Vec)>, + allowed_bundles: Vec, &HashSet)>, clone_handlers_overrides: TypeIdMap, } @@ -126,28 +126,29 @@ impl EntityCloneBuilder { }; } - let ignored_components = ignored_components + let mut ignored_components = ignored_components .into_iter() .flat_map(|type_id| world.components().get_id(type_id)) - .chain(ignored_bundles.into_iter().flat_map(|f| { - let mut id = None; - (f)(world.components(), &mut id); - id - })) .collect::>(); + for getter in ignored_bundles { + (getter)(world.components(), &mut ignored_components); + } let allowed = !allowed_components.is_empty(); let filter = if allowed { - allowed_components + let mut allowed_components = allowed_components .into_iter() .flat_map(|type_id| world.components().get_id(type_id)) - .chain(allowed_bundles.into_iter().flat_map(|f| { - let mut id = None; - (f)(world.components(), &mut id); - id - })) - .filter(|id| !ignored_components.contains(id)) - .collect::>() + .filter(|component_id| !ignored_components.contains(component_id)) + .collect::>(); + for getter in allowed_bundles { + (getter)( + world.components(), + &mut allowed_components, + &ignored_components, + ); + } + allowed_components } else { ignored_components }; @@ -188,11 +189,18 @@ impl EntityCloneBuilder { /// Add a bundle of components to the list of components to clone. /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. pub fn allow_bundle(&mut self) { - let bundle_ids_getter = |components: &Components, id: &mut Option| { - T::get_component_ids(components, &mut |component_id: Option| { - *id = component_id; - }); - }; + let bundle_ids_getter = + |components: &Components, + ids: &mut HashSet, + ignored_ids: &HashSet| { + T::get_component_ids(components, &mut |component_id: Option| { + if let Some(id) = component_id { + if !ignored_ids.contains(&id) { + ids.insert(id); + } + }; + }); + }; self.allowed_bundles.push(bundle_ids_getter); } @@ -220,9 +228,11 @@ impl EntityCloneBuilder { /// Disallow a bundle of components from being cloned. pub fn deny_bundle(&mut self) { - let bundle_ids_getter = |components: &Components, id: &mut Option| { + let bundle_ids_getter = |components: &Components, ids: &mut HashSet| { T::get_component_ids(components, &mut |component_id: Option| { - *id = component_id; + if let Some(id) = component_id { + ids.insert(id); + }; }); }; self.ignored_bundles.push(bundle_ids_getter); @@ -433,11 +443,11 @@ mod tests { let mut builder = EntityCloneBuilder::default(); builder.allow_bundle::<(A, B, C)>(); - builder.deny_bundle::(); + builder.deny_bundle::<(B, C)>(); builder.clone_entity(&mut world, e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); - assert!(world.get::(e_clone).is_some()); + assert!(world.get::(e_clone).is_none()); } } From 7798b9d1b4050e7916eac7d535da33f0d2ee7a51 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 1 Nov 2024 17:15:24 +0000 Subject: [PATCH 11/17] rename some `EntityCloner` items for clarity --- crates/bevy_ecs/src/component.rs | 10 ++++------ crates/bevy_ecs/src/entity/clone_entities.rs | 16 +++++++--------- crates/bevy_ecs/src/observer/entity_observer.rs | 4 ++-- crates/bevy_hierarchy/src/hierarchy.rs | 10 ++++------ 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index f62196cf91c90..07c7174779ca4 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1938,13 +1938,11 @@ pub fn component_clone_via_clone( entity_cloner: &EntityCloner, ) { let component = world - .entity(entity_cloner.get_source()) + .entity(entity_cloner.source()) .get::() .expect("Component must exists on source entity") .clone(); - world - .entity_mut(entity_cloner.get_target()) - .insert(component); + world.entity_mut(entity_cloner.target()).insert(component); } /// Component [clone handler function](ComponentCloneFn) implemented using reflect. @@ -1976,13 +1974,13 @@ pub fn component_clone_via_reflect( let source_component = reflect_component .reflect( world - .get_entity(entity_cloner.get_source()) + .get_entity(entity_cloner.source()) .expect("Source entity must exist"), ) .expect("Source entity must have reflected component") .clone_value(); let mut target = world - .get_entity_mut(entity_cloner.get_target()) + .get_entity_mut(entity_cloner.target()) .expect("Target entity must exist"); reflect_component.apply_or_insert(&mut target, &*source_component, ®istry); }); diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 06a626f38c05e..8da11a9b04e58 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -15,7 +15,7 @@ use crate::{ pub struct EntityCloner<'a> { source: Entity, target: Entity, - allowed: bool, + filter_allows_components: bool, filter: &'a HashSet, clone_handlers_overrides: &'a ComponentCloneHandlers, } @@ -28,12 +28,10 @@ impl<'a> EntityCloner<'a> { .expect("Source entity must exist") .archetype() .components() + .filter(|id| self.is_cloning_allowed(id)) .collect::>(); for component in components { - if !self.is_cloning_allowed(&component) { - continue; - } let handler = if self .clone_handlers_overrides .is_handler_registered(component) @@ -51,17 +49,17 @@ impl<'a> EntityCloner<'a> { } fn is_cloning_allowed(&self, component: &ComponentId) -> bool { - (self.allowed && self.filter.contains(component)) - || (!self.allowed && !self.filter.contains(component)) + (self.filter_allows_components && self.filter.contains(component)) + || (!self.filter_allows_components && !self.filter.contains(component)) } /// Returns the current source entity. - pub fn get_source(&self) -> Entity { + pub fn source(&self) -> Entity { self.source } /// Returns the current target entity. - pub fn get_target(&self) -> Entity { + pub fn target(&self) -> Entity { self.target } @@ -156,7 +154,7 @@ impl EntityCloneBuilder { EntityCloner { source, target, - allowed, + filter_allows_components: allowed, filter: &filter, clone_handlers_overrides: &component_clone_handlers, } diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 3b44cd8f5f6f8..be81df0f79515 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -69,8 +69,8 @@ fn component_clone_observed_by( _component_id: ComponentId, entity_cloner: &EntityCloner, ) { - let target = entity_cloner.get_target(); - let source = entity_cloner.get_source(); + let target = entity_cloner.target(); + let source = entity_cloner.source(); let observed_by = world .get::(source) diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 15b187d10c960..75564dc80d12c 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -239,7 +239,7 @@ fn component_clone_children( entity_cloner: &EntityCloner, ) { let children = world - .get::(entity_cloner.get_source()) + .get::(entity_cloner.source()) .expect("Source entity must have Children component") .iter() .cloned() @@ -251,7 +251,7 @@ fn component_clone_children( .clone_entity(world); world .entity_mut(child_clone) - .set_parent(entity_cloner.get_target()); + .set_parent(entity_cloner.target()); } } @@ -262,12 +262,10 @@ fn component_clone_parent( entity_cloner: &EntityCloner, ) { let parent = world - .get::(entity_cloner.get_source()) + .get::(entity_cloner.source()) .map(|p| p.0) .expect("Source entity must have Parent component"); - world - .entity_mut(entity_cloner.get_target()) - .set_parent(parent); + world.entity_mut(entity_cloner.target()).set_parent(parent); } #[cfg(test)] From b4a69e15afea7af01da2a664dcfc64bcf5f1ffa8 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 1 Nov 2024 19:11:06 +0000 Subject: [PATCH 12/17] rework filtering in `EntityCloneBuilder` to use `ComponentId`s --- crates/bevy_ecs/src/component.rs | 5 + crates/bevy_ecs/src/entity/clone_entities.rs | 289 +++++++++--------- .../bevy_ecs/src/observer/entity_observer.rs | 12 +- crates/bevy_ecs/src/system/commands/mod.rs | 10 +- crates/bevy_hierarchy/src/hierarchy.rs | 14 +- 5 files changed, 177 insertions(+), 153 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 07c7174779ca4..d8d419b06b2f9 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -909,6 +909,11 @@ impl ComponentCloneHandlers { self.default_handler = handler; } + /// Returns the currently registered default handler. + pub fn get_default_handler(&self) -> ComponentCloneFn { + self.default_handler + } + /// Sets a handler for a specific component. pub fn set_component_handler( &mut self, diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 8da11a9b04e58..8190fa1f458e3 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,12 +1,10 @@ use core::any::TypeId; -use bevy_utils::{HashSet, TypeIdMap}; +use bevy_utils::{HashMap, HashSet}; use crate::{ bundle::Bundle, - component::{ - Component, ComponentCloneHandler, ComponentCloneHandlers, ComponentId, Components, - }, + component::{component_clone_ignore, Component, ComponentCloneHandler, ComponentId}, entity::Entity, world::World, }; @@ -17,7 +15,7 @@ pub struct EntityCloner<'a> { target: Entity, filter_allows_components: bool, filter: &'a HashSet, - clone_handlers_overrides: &'a ComponentCloneHandlers, + clone_handlers_overrides: &'a HashMap, } impl<'a> EntityCloner<'a> { @@ -32,18 +30,13 @@ impl<'a> EntityCloner<'a> { .collect::>(); for component in components { - let handler = if self - .clone_handlers_overrides - .is_handler_registered(component) - { - self.clone_handlers_overrides.get_handler(component) - } else { - world - .components() - .get_component_clone_handlers() - .get_handler(component) + let global_handlers = world.components().get_component_clone_handlers(); + 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, }; - (handler)(world, component, self); } } @@ -96,154 +89,175 @@ impl<'a> EntityCloner<'a> { /// /// assert!(world.get::(entity_clone).is_some_and(|c| *c == component)); ///``` -#[derive(Default)] -pub struct EntityCloneBuilder { - ignored_components: HashSet, - ignored_bundles: Vec)>, - allowed_components: HashSet, - allowed_bundles: Vec, &HashSet)>, - clone_handlers_overrides: TypeIdMap, +#[derive(Debug)] +pub struct EntityCloneBuilder<'w> { + world: &'w mut World, + filter_allows_components: bool, + filter: HashSet, + clone_handlers_overrides: HashMap, } -impl EntityCloneBuilder { - /// Finish configuring the builder and clone an entity. - pub fn clone_entity(self, world: &mut World, source: Entity, target: Entity) { +impl<'w> EntityCloneBuilder<'w> { + /// Creates a new [`EntityCloneBuilder`] for world. + pub fn new(world: &'w mut World) -> Self { + Self { + world, + filter_allows_components: false, + filter: Default::default(), + clone_handlers_overrides: Default::default(), + } + } + + /// Finishes configuring the builder and clones `source` entity to `target`. + pub fn clone_entity(self, source: Entity, target: Entity) { let EntityCloneBuilder { - ignored_components, - ignored_bundles, - allowed_components, - allowed_bundles, + world, + filter_allows_components, + filter, clone_handlers_overrides, .. } = self; - let mut component_clone_handlers = ComponentCloneHandlers::default(); - for (k, v) in clone_handlers_overrides.into_iter() { - if let Some(component_id) = world.components().get_id(k) { - component_clone_handlers.set_component_handler(component_id, v); - }; - } - - let mut ignored_components = ignored_components - .into_iter() - .flat_map(|type_id| world.components().get_id(type_id)) - .collect::>(); - for getter in ignored_bundles { - (getter)(world.components(), &mut ignored_components); - } - - let allowed = !allowed_components.is_empty(); - let filter = if allowed { - let mut allowed_components = allowed_components - .into_iter() - .flat_map(|type_id| world.components().get_id(type_id)) - .filter(|component_id| !ignored_components.contains(component_id)) - .collect::>(); - for getter in allowed_bundles { - (getter)( - world.components(), - &mut allowed_components, - &ignored_components, - ); - } - allowed_components - } else { - ignored_components - }; - EntityCloner { source, target, - filter_allows_components: allowed, + filter_allows_components, filter: &filter, - clone_handlers_overrides: &component_clone_handlers, + clone_handlers_overrides: &clone_handlers_overrides, } .clone_entity(world); } - /// Add a component to the list of components to clone. - /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. - pub fn allow(&mut self) -> &mut Self { - self.allowed_components.insert(TypeId::of::()); + /// 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); + } + }); + } self } - /// Extend the list of components to clone. - /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. - pub fn allow_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - self.allowed_components.extend(ids); + /// Extends 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_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); + }); + } self } - /// Reset the filter to allow all components to be cloned - pub fn allow_all(&mut self) -> &mut Self { - self.allowed_components.clear(); - self.allowed_bundles.clear(); - self.ignored_components.clear(); - self.ignored_bundles.clear(); + /// Extends the list of components to clone using [`TypeId`]s. + /// + /// 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); + }); + } self } - /// Add a bundle of components to the list of components to clone. - /// Calling this function automatically disallows all other components, only explicitly allowed ones will be cloned. - pub fn allow_bundle(&mut self) { - let bundle_ids_getter = - |components: &Components, - ids: &mut HashSet, - ignored_ids: &HashSet| { - T::get_component_ids(components, &mut |component_id: Option| { - if let Some(id) = component_id { - if !ignored_ids.contains(&id) { - ids.insert(id); - } - }; - }); - }; - self.allowed_bundles.push(bundle_ids_getter); + /// Resets the filter to allow all components to be cloned. + pub fn allow_all(&mut self) -> &mut Self { + self.filter_allows_components = false; + self.filter.clear(); + self } - /// Disallow a component from being cloned. - pub fn deny(&mut self) -> &mut Self { - self.ignored_components.insert(TypeId::of::()); + /// 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); + } + }); + } self } - /// Extend the list of components that shouldn't be cloned. - pub fn deny_by_ids(&mut self, ids: impl IntoIterator) -> &mut Self { - self.ignored_components.extend(ids); + /// 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); + } self } - /// Set the filter to deny all components - pub fn deny_all(&mut self) -> &mut Self { - self.allowed_components.clear(); - self.allowed_bundles.clear(); - // just put some dummy type id that can't be a component to emulate "allowed" mode - struct Dummy; - self.allowed_components.insert(TypeId::of::()); + /// 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); + } self } - /// Disallow a bundle of components from being cloned. - pub fn deny_bundle(&mut self) { - let bundle_ids_getter = |components: &Components, ids: &mut HashSet| { - T::get_component_ids(components, &mut |component_id: Option| { - if let Some(id) = component_id { - ids.insert(id); - }; - }); - }; - self.ignored_bundles.push(bundle_ids_getter); + /// Sets the filter to deny all components. + pub fn deny_all(&mut self) -> &mut Self { + self.filter_allows_components = true; + self.filter.clear(); + self } - /// Overrides the [`ComponentCloneHandler`] for the specific component for this builder. - /// This handler will be used to clone component instead of the global one defined by [`ComponentCloneHandlers`] + /// Overrides the [`ComponentCloneHandler`] for a component in this builder. + /// This handler will be used to clone the component instead of the global one defined by [`ComponentCloneHandlers`] pub fn override_component_clone_handler( &mut self, handler: ComponentCloneHandler, ) -> &mut Self { - self.clone_handlers_overrides - .insert(TypeId::of::(), handler); + if let Some(id) = self.world.components().component_id::() { + self.clone_handlers_overrides.insert(id, handler); + } + self + } + + /// Removes a previously set override of [`ComponentCloneHandler`] for a component in this builder. + pub fn remove_component_clone_handler_override(&mut self) -> &mut Self { + if let Some(id) = self.world.components().component_id::() { + self.clone_handlers_overrides.remove(&id); + } self } } @@ -274,7 +288,7 @@ mod tests { let e = world.spawn(component.clone()).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } @@ -293,7 +307,7 @@ mod tests { let e = world.spawn(component.clone()).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } @@ -326,7 +340,7 @@ mod tests { let e = world.spawn(component.clone()).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::default().clone_entity(&mut world, e, e_clone); + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); assert!(world .get::(e_clone) @@ -350,9 +364,10 @@ mod tests { let e = world.spawn((component.clone(), B)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::default(); + let mut builder = EntityCloneBuilder::new(&mut world); + builder.deny_all(); builder.allow::(); - builder.clone_entity(&mut world, e, e_clone); + builder.clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -378,9 +393,9 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::default(); + let mut builder = EntityCloneBuilder::new(&mut world); builder.deny::(); - builder.clone_entity(&mut world, e, e_clone); + builder.clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -407,12 +422,13 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::default(); + let mut builder = EntityCloneBuilder::new(&mut world); + builder.deny_all(); builder.allow::(); builder.allow::(); builder.allow::(); builder.deny::(); - builder.clone_entity(&mut world, e, e_clone); + builder.clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -439,10 +455,11 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::default(); - builder.allow_bundle::<(A, B, C)>(); - builder.deny_bundle::<(B, C)>(); - builder.clone_entity(&mut world, e, e_clone); + let mut builder = EntityCloneBuilder::new(&mut world); + builder.deny_all(); + builder.allow::<(A, B, C)>(); + builder.deny::<(B, C)>(); + builder.clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index be81df0f79515..90a742fdf5733 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -49,17 +49,17 @@ impl Component for ObservedBy { /// Trait that holds functions for configuring interaction with observers during entity cloning. pub trait CloneEntityWithObserversExt { /// Sets the option to automatically add cloned entities to the obsevers targeting source entity. - fn add_observers(&mut self, add_observers: bool) -> &mut EntityCloneBuilder; + fn add_observers(&mut self, add_observers: bool) -> &mut Self; } -impl CloneEntityWithObserversExt for EntityCloneBuilder { - fn add_observers(&mut self, add_observers: bool) -> &mut EntityCloneBuilder { +impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { + fn add_observers(&mut self, add_observers: bool) -> &mut Self { if add_observers { self.override_component_clone_handler::(ComponentCloneHandler::Custom( component_clone_observed_by, )) } else { - self.override_component_clone_handler::(ComponentCloneHandler::Default) + self.remove_component_clone_handler_override::() } } } @@ -139,9 +139,9 @@ mod tests { world.trigger_targets(E, e); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::default(); + let mut builder = EntityCloneBuilder::new(&mut world); builder.add_observers(true); - builder.clone_entity(&mut world, e, e_clone); + builder.clone_entity(e, e_clone); world.trigger_targets(E, [e, e_clone]); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 02aea5876e275..28cef4ac9efcc 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -294,12 +294,14 @@ impl<'w, 's> Commands<'w, 's> { pub fn clone_entity_with( &mut self, entity: Entity, - f: impl FnOnce(&mut EntityCloneBuilder), + f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, ) -> EntityCommands<'_> { - let mut builder = EntityCloneBuilder::default(); - f(&mut builder); let cloned_entity = self.spawn_empty().id(); - self.queue(move |world: &mut World| builder.clone_entity(world, entity, cloned_entity)); + self.queue(move |world: &mut World| { + let mut builder = EntityCloneBuilder::new(world); + f(&mut builder); + builder.clone_entity(entity, cloned_entity); + }); EntityCommands { commands: self.reborrow(), entity: cloned_entity, diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 75564dc80d12c..3ddb9d0be4a00 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -206,28 +206,28 @@ impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> { pub trait CloneEntityHierarchyExt { /// Sets the option to recursively clone entities. /// When set to true all children will be cloned with the same options as the parent. - fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder; + fn recursive(&mut self, recursive: bool) -> &mut Self; /// Sets the option to add cloned entity as a child to the parent entity. - fn as_child(&mut self, as_child: bool) -> &mut EntityCloneBuilder; + fn as_child(&mut self, as_child: bool) -> &mut Self; } -impl CloneEntityHierarchyExt for EntityCloneBuilder { - fn recursive(&mut self, recursive: bool) -> &mut EntityCloneBuilder { +impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> { + fn recursive(&mut self, recursive: bool) -> &mut Self { if recursive { self.override_component_clone_handler::(ComponentCloneHandler::Custom( component_clone_children, )) } else { - self.override_component_clone_handler::(ComponentCloneHandler::Default) + self.remove_component_clone_handler_override::() } } - fn as_child(&mut self, as_child: bool) -> &mut EntityCloneBuilder { + fn as_child(&mut self, as_child: bool) -> &mut Self { if as_child { self.override_component_clone_handler::(ComponentCloneHandler::Custom( component_clone_parent, )) } else { - self.override_component_clone_handler::(ComponentCloneHandler::Default) + self.remove_component_clone_handler_override::() } } } From b5034da3ac0c55b92d47b8112415a7f68566573f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 2 Nov 2024 10:14:18 +0000 Subject: [PATCH 13/17] document component clone handler priority and default cloning strategy --- crates/bevy_ecs/src/component.rs | 20 ++++++---- crates/bevy_ecs/src/entity/clone_entities.rs | 40 +++++++++++++++++++- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index d8d419b06b2f9..65fe93723424e 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -392,6 +392,8 @@ pub trait Component: Send + Sync + 'static { } /// Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component. + /// + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. fn get_component_clone_handler() -> ComponentCloneHandler { ComponentCloneHandler::default() } @@ -905,6 +907,8 @@ pub struct ComponentCloneHandlers { impl ComponentCloneHandlers { /// Sets the default handler for this registry. All components with [`Default`](ComponentCloneHandler::Default) handler, as well as any component that does not have an /// explicitly registered clone function will use this handler. + /// + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. pub fn set_default_handler(&mut self, handler: ComponentCloneFn) { self.default_handler = handler; } @@ -915,6 +919,8 @@ impl ComponentCloneHandlers { } /// Sets a handler for a specific component. + /// + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. pub fn set_component_handler( &mut self, component_id: ComponentId, @@ -1936,7 +1942,7 @@ impl RequiredComponents { /// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for the specific component it is implemented for. /// It will panic if set as handler for any other component. /// -/// See [`ComponentCloneHandlers`] for more details +/// See [`ComponentCloneHandlers`] for more details. pub fn component_clone_via_clone( world: &mut World, _component_id: ComponentId, @@ -1954,7 +1960,7 @@ pub fn component_clone_via_clone( /// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component, /// but only reflected components will be cloned. /// -/// See [`ComponentCloneHandlers`] for more details +/// See [`ComponentCloneHandlers`] for more details. #[cfg(feature = "bevy_reflect")] pub fn component_clone_via_reflect( world: &mut World, @@ -1991,9 +1997,9 @@ pub fn component_clone_via_reflect( }); } -/// Noop implementation of component clone handler function +/// Noop implementation of component clone handler function. /// -/// See [`ComponentCloneHandlers`] for more details +/// See [`ComponentCloneHandlers`] for more details. pub fn component_clone_ignore( _world: &mut World, _component_id: ComponentId, @@ -2001,7 +2007,7 @@ pub fn component_clone_ignore( ) { } -/// Wrapper for components clone specialization using autoderef +/// Wrapper for components clone specialization using autoderef. #[doc(hidden)] pub struct ComponentCloneSpecializationWrapper(PhantomData); @@ -2011,7 +2017,7 @@ impl Default for ComponentCloneSpecializationWrapper { } } -/// Base Trait for components clone specialization using autoderef +/// Base trait for components clone specialization using autoderef. #[doc(hidden)] pub trait ComponentCloneBase { fn get_component_clone_handler(&self) -> ComponentCloneHandler; @@ -2022,7 +2028,7 @@ impl ComponentCloneBase for ComponentCloneSpecializationWrapper } } -/// Specialized trait for components clone specialization using autoderef +/// Specialized trait for components clone specialization using autoderef. #[doc(hidden)] pub trait ComponentCloneViaClone { fn get_component_clone_handler(&self) -> ComponentCloneHandler; diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 8190fa1f458e3..47d21100f9a4e 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -85,10 +85,44 @@ impl<'a> EntityCloner<'a> { /// let entity = world.spawn(component.clone()).id(); /// let entity_clone = world.spawn_empty().id(); /// -/// EntityCloneBuilder::default().clone_entity(&mut world, entity, entity_clone); +/// EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone); /// /// assert!(world.get::(entity_clone).is_some_and(|c| *c == component)); ///``` +/// +/// # Default cloning strategy +/// By default, all types that derive [`Component`] and implement either [`Clone`] or `Reflect` (with `ReflectComponent`) will be cloned +/// (with `Clone`-based implementation preferred in case component implements both). +/// +/// It should be noted that if `Component` is implemented manually or if `Clone` implementation is conditional +/// (like when deriving `Clone` for a type with a generic parameter without `Clone` bound), +/// the component will be cloned using the [default cloning strategy](crate::component::ComponentCloneHandlers::get_default_handler). +/// To use `Clone`-based handler ([`component_clone_via_clone`](crate::component::component_clone_via_clone)) in this case it should be set manually using one +/// of the methods mentioned in the [Handlers](#handlers) section +/// +/// Here's an example of how to do it using [`get_component_clone_handler`](Component::get_component_clone_handler): +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::component::{StorageType, component_clone_via_clone, ComponentCloneHandler}; +/// #[derive(Clone)] +/// struct SomeComponent; +/// +/// impl Component for SomeComponent { +/// const STORAGE_TYPE: StorageType = StorageType::Table; +/// fn get_component_clone_handler() -> ComponentCloneHandler { +/// ComponentCloneHandler::Custom(component_clone_via_clone::) +/// } +/// } +/// ``` +/// +/// # Handlers +/// `EntityCloneBuilder` clones entities by cloning components using [`handlers`](ComponentCloneHandler), and there are multiple layers +/// to decide which handler to use for which component. The overall hierarchy looks like this (priority from most to least): +/// 1. local overrides using [`override_component_clone_handler`](Self::override_component_clone_handler) +/// 2. global overrides using [`set_component_handler`](crate::component::ComponentCloneHandlers::set_component_handler) +/// 3. component-defined handler using [`get_component_clone_handler`](Component::get_component_clone_handler) +/// 4. default handler override using [`set_default_handler`](crate::component::ComponentCloneHandlers::set_default_handler) +/// 5. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not. #[derive(Debug)] pub struct EntityCloneBuilder<'w> { world: &'w mut World, @@ -242,7 +276,9 @@ impl<'w> EntityCloneBuilder<'w> { } /// Overrides the [`ComponentCloneHandler`] for a component in this builder. - /// This handler will be used to clone the component instead of the global one defined by [`ComponentCloneHandlers`] + /// This handler will be used to clone the component instead of the global one defined by [`ComponentCloneHandlers`](crate::component::ComponentCloneHandlers) + /// + /// See [Handlers section of `EntityCloneBuilder`](EntityCloneBuilder#handlers) to understand how this affects handler priority. pub fn override_component_clone_handler( &mut self, handler: ComponentCloneHandler, From a514b2f27db0d84ad72e010dfb9ad7f7f150c71a Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 2 Nov 2024 12:43:52 +0000 Subject: [PATCH 14/17] preallocate Vec for `ComponentId`s using `Archetype::component_count` --- crates/bevy_ecs/src/entity/clone_entities.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 47d21100f9a4e..ad6b880037d83 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -21,13 +21,17 @@ pub struct EntityCloner<'a> { impl<'a> EntityCloner<'a> { /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. pub fn clone_entity(&self, world: &mut World) { - let components = world + let source_entity = world .get_entity(self.source) - .expect("Source entity must exist") - .archetype() - .components() - .filter(|id| self.is_cloning_allowed(id)) - .collect::>(); + .expect("Source entity must exist"); + let archetype = source_entity.archetype(); + + let mut components = Vec::with_capacity(archetype.component_count()); + components.extend( + archetype + .components() + .filter(|id| self.is_cloning_allowed(id)), + ); for component in components { let global_handlers = world.components().get_component_clone_handlers(); From 4277e1021f593efe5e6903a7af492a08748dd7c6 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 2 Nov 2024 12:48:14 +0000 Subject: [PATCH 15/17] replace `HashMap` in `ComponentCloneHandlers` with `Vec` to speed up `get_handler` --- crates/bevy_ecs/src/component.rs | 35 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 65fe93723424e..93ee525916cf2 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -900,7 +900,7 @@ pub enum ComponentCloneHandler { /// A registry of component clone handlers. Allows to set global default and per-component clone function for all components in the world. #[derive(Debug)] pub struct ComponentCloneHandlers { - handlers: HashMap, + handlers: Vec>, default_handler: ComponentCloneFn, } @@ -921,33 +921,34 @@ impl ComponentCloneHandlers { /// Sets a handler for a specific component. /// /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. - pub fn set_component_handler( - &mut self, - component_id: ComponentId, - handler: ComponentCloneHandler, - ) { + pub fn set_component_handler(&mut self, id: ComponentId, handler: ComponentCloneHandler) { + if id.0 >= self.handlers.len() { + self.handlers.resize(id.0 + 1, None); + } match handler { - ComponentCloneHandler::Default => self.handlers.remove(&component_id), - ComponentCloneHandler::Ignore => { - self.handlers.insert(component_id, component_clone_ignore) - } - ComponentCloneHandler::Custom(handler) => self.handlers.insert(component_id, handler), + ComponentCloneHandler::Default => self.handlers[id.0] = None, + ComponentCloneHandler::Ignore => self.handlers[id.0] = Some(component_clone_ignore), + ComponentCloneHandler::Custom(handler) => self.handlers[id.0] = Some(handler), }; } /// Checks if the specified component is registered. If not, the component will use the default global handler. - pub fn is_handler_registered(&self, component_id: ComponentId) -> bool { - self.handlers.contains_key(&component_id) + /// + /// This will return an incorrect result if `id` did not come from the same world as `self`. + pub fn is_handler_registered(&self, id: ComponentId) -> bool { + self.handlers.get(id.0).is_some_and(Option::is_some) } /// Gets a handler to clone a component. This can be one of the following: /// - Custom clone function for this specific component. /// - Default global handler. /// - A [`component_clone_ignore`] (no cloning). - pub fn get_handler(&self, component_id: ComponentId) -> ComponentCloneFn { - match self.handlers.get(&component_id) { - Some(handler) => *handler, - None => self.default_handler, + /// + /// This will return an incorrect result if `id` did not come from the same world as `self`. + pub fn get_handler(&self, id: ComponentId) -> ComponentCloneFn { + match self.handlers.get(id.0) { + Some(Some(handler)) => *handler, + Some(None) | None => self.default_handler, } } } From 49a809f91cb1ae405763d92eb10d5fe192170afd Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 2 Dec 2024 17:14:55 +0000 Subject: [PATCH 16/17] use `DeferredWorld` instead of `World` to allow for more optimal entity cloning implementation later --- crates/bevy_ecs/src/component.rs | 78 +++++++++---------- crates/bevy_ecs/src/entity/clone_entities.rs | 32 +++++--- .../bevy_ecs/src/observer/entity_observer.rs | 73 +++++++++-------- crates/bevy_hierarchy/src/hierarchy.rs | 35 ++++----- 4 files changed, 110 insertions(+), 108 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index dcce3b9ab788c..acffd5b3d10fd 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -883,7 +883,7 @@ impl ComponentDescriptor { } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut World, ComponentId, &EntityCloner); +pub type ComponentCloneFn = fn(&mut DeferredWorld, &EntityCloner); /// An enum instructing how to clone a component. #[derive(Debug, Default)] @@ -1964,8 +1964,7 @@ impl RequiredComponents { /// /// See [`ComponentCloneHandlers`] for more details. pub fn component_clone_via_clone( - world: &mut World, - _component_id: ComponentId, + world: &mut DeferredWorld, entity_cloner: &EntityCloner, ) { let component = world @@ -1973,7 +1972,10 @@ pub fn component_clone_via_clone( .get::() .expect("Component must exists on source entity") .clone(); - world.entity_mut(entity_cloner.target()).insert(component); + world + .commands() + .entity(entity_cloner.target()) + .insert(component); } /// Component [clone handler function](ComponentCloneFn) implemented using reflect. @@ -1982,50 +1984,42 @@ pub fn component_clone_via_clone( /// /// See [`ComponentCloneHandlers`] for more details. #[cfg(feature = "bevy_reflect")] -pub fn component_clone_via_reflect( - world: &mut World, - component_id: ComponentId, - entity_cloner: &EntityCloner, -) { - world.resource_scope::(|world, registry| { - let registry = registry.read(); - - let component_info = world - .components() - .get_info(component_id) - .expect("Component must be registered"); - let Some(type_id) = component_info.type_id() else { - return; - }; - let Some(reflect_component) = - registry.get_type_data::(type_id) - else { - return; - }; - let source_component = reflect_component - .reflect( - world - .get_entity(entity_cloner.source()) - .expect("Source entity must exist"), - ) - .expect("Source entity must have reflected component") - .clone_value(); - let mut target = world - .get_entity_mut(entity_cloner.target()) - .expect("Target entity must exist"); - reflect_component.apply_or_insert(&mut target, &*source_component, ®istry); +pub fn component_clone_via_reflect(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { + let component_id = entity_cloner.component_id(); + let source = entity_cloner.source(); + let target = entity_cloner.target(); + world.commands().queue(move |world: &mut World| { + world.resource_scope::(|world, registry| { + let registry = registry.read(); + + let component_info = world + .components() + .get_info(component_id) + .expect("Component must be registered"); + let Some(type_id) = component_info.type_id() else { + return; + }; + let Some(reflect_component) = + registry.get_type_data::(type_id) + else { + return; + }; + let source_component = reflect_component + .reflect(world.get_entity(source).expect("Source entity must exist")) + .expect("Source entity must have reflected component") + .clone_value(); + let mut target = world + .get_entity_mut(target) + .expect("Target entity must exist"); + reflect_component.apply_or_insert(&mut target, &*source_component, ®istry); + }); }); } /// Noop implementation of component clone handler function. /// /// See [`ComponentCloneHandlers`] for more details. -pub fn component_clone_ignore( - _world: &mut World, - _component_id: ComponentId, - _entity_cloner: &EntityCloner, -) { -} +pub fn component_clone_ignore(_world: &mut DeferredWorld, _entity_cloner: &EntityCloner) {} /// Wrapper for components clone specialization using autoderef. #[doc(hidden)] diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index ad6b880037d83..710b96e5a3080 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,3 +1,4 @@ +use alloc::sync::Arc; use core::any::TypeId; use bevy_utils::{HashMap, HashSet}; @@ -10,17 +11,18 @@ use crate::{ }; /// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`] and custom clone handlers. -pub struct EntityCloner<'a> { +pub struct EntityCloner { source: Entity, target: Entity, + component_id: Option, filter_allows_components: bool, - filter: &'a HashSet, - clone_handlers_overrides: &'a HashMap, + filter: Arc>, + clone_handlers_overrides: Arc>, } -impl<'a> EntityCloner<'a> { +impl EntityCloner { /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. - pub fn clone_entity(&self, world: &mut World) { + pub fn clone_entity(&mut self, world: &mut World) { let source_entity = world .get_entity(self.source) .expect("Source entity must exist"); @@ -41,7 +43,8 @@ impl<'a> EntityCloner<'a> { Some(ComponentCloneHandler::Ignore) => component_clone_ignore, Some(ComponentCloneHandler::Custom(handler)) => *handler, }; - (handler)(world, component, self); + self.component_id = Some(component); + (handler)(&mut world.into(), self); } } @@ -60,11 +63,19 @@ impl<'a> EntityCloner<'a> { self.target } + /// Returns the [`ComponentId`] of currently cloned component. + pub fn component_id(&self) -> ComponentId { + self.component_id + .expect("ComponentId must be set in clone_entity") + } + /// Reuse existing [`EntityCloner`] configuration with new source and target. - pub fn with_source_and_target(&self, source: Entity, target: Entity) -> EntityCloner<'a> { + pub fn with_source_and_target(&self, source: Entity, target: Entity) -> EntityCloner { EntityCloner { source, target, + filter: self.filter.clone(), + clone_handlers_overrides: self.clone_handlers_overrides.clone(), ..*self } } @@ -159,11 +170,14 @@ impl<'w> EntityCloneBuilder<'w> { EntityCloner { source, target, + component_id: None, filter_allows_components, - filter: &filter, - clone_handlers_overrides: &clone_handlers_overrides, + filter: Arc::new(filter), + clone_handlers_overrides: Arc::new(clone_handlers_overrides), } .clone_entity(world); + + world.flush_commands(); } /// Adds all components of the bundle to the list of components to clone. diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 90a742fdf5733..8433d3cea358b 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -1,8 +1,8 @@ use crate::{ - component::{Component, ComponentCloneHandler, ComponentHooks, ComponentId, StorageType}, + component::{Component, ComponentCloneHandler, ComponentHooks, StorageType}, entity::{Entity, EntityCloneBuilder, EntityCloner}, observer::ObserverState, - world::World, + world::{DeferredWorld, World}, }; /// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to. @@ -64,48 +64,47 @@ impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { } } -fn component_clone_observed_by( - world: &mut World, - _component_id: ComponentId, - entity_cloner: &EntityCloner, -) { +fn component_clone_observed_by(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { let target = entity_cloner.target(); let source = entity_cloner.source(); - let observed_by = world - .get::(source) - .map(|observed_by| observed_by.0.clone()) - .expect("Source entity must have ObservedBy"); - - world - .entity_mut(target) - .insert(ObservedBy(observed_by.clone())); - - for observer in &observed_by { - let mut observer_state = world - .get_mut::(*observer) - .expect("Source observer entity must have ObserverState"); - observer_state.descriptor.entities.push(target); - let event_types = observer_state.descriptor.events.clone(); - let components = observer_state.descriptor.components.clone(); - for event_type in event_types { - let observers = world.observers.get_observers(event_type); - if components.is_empty() { - if let Some(map) = observers.entity_observers.get(&source).cloned() { - observers.entity_observers.insert(target, map); - } - } else { - for component in &components { - let Some(observers) = observers.component_observers.get_mut(component) else { - continue; - }; - if let Some(map) = observers.entity_map.get(&source).cloned() { - observers.entity_map.insert(target, map); + world.commands().queue(move |world: &mut World| { + let observed_by = world + .get::(source) + .map(|observed_by| observed_by.0.clone()) + .expect("Source entity must have ObservedBy"); + + world + .entity_mut(target) + .insert(ObservedBy(observed_by.clone())); + + for observer in &observed_by { + let mut observer_state = world + .get_mut::(*observer) + .expect("Source observer entity must have ObserverState"); + observer_state.descriptor.entities.push(target); + let event_types = observer_state.descriptor.events.clone(); + let components = observer_state.descriptor.components.clone(); + for event_type in event_types { + let observers = world.observers.get_observers(event_type); + if components.is_empty() { + if let Some(map) = observers.entity_observers.get(&source).cloned() { + observers.entity_observers.insert(target, map); + } + } else { + for component in &components { + let Some(observers) = observers.component_observers.get_mut(component) + else { + continue; + }; + if let Some(map) = observers.entity_map.get(&source).cloned() { + observers.entity_map.insert(target, map); + } } } } } - } + }); } #[cfg(test)] diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 3ddb9d0be4a00..d48e74d864e23 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -3,10 +3,10 @@ use crate::{ BuildChildren, }; use bevy_ecs::{ - component::{ComponentCloneHandler, ComponentId}, + component::ComponentCloneHandler, entity::{Entity, EntityCloneBuilder, EntityCloner}, system::EntityCommands, - world::{Command, EntityWorldMut, World}, + world::{Command, DeferredWorld, EntityWorldMut, World}, }; use bevy_utils::tracing::debug; @@ -233,39 +233,34 @@ impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> { } /// Clone handler for the [`Children`] component. Allows to clone the entity recursively. -fn component_clone_children( - world: &mut World, - _component_id: ComponentId, - entity_cloner: &EntityCloner, -) { +fn component_clone_children(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { let children = world .get::(entity_cloner.source()) .expect("Source entity must have Children component") .iter() .cloned() .collect::>(); + let parent = entity_cloner.target(); for child in children { - let child_clone = world.spawn_empty().id(); - entity_cloner - .with_source_and_target(child, child_clone) - .clone_entity(world); - world - .entity_mut(child_clone) - .set_parent(entity_cloner.target()); + let child_clone = world.commands().spawn_empty().id(); + let mut entity_cloner = entity_cloner.with_source_and_target(child, child_clone); + world.commands().queue(move |world: &mut World| { + entity_cloner.clone_entity(world); + world.entity_mut(child_clone).set_parent(parent); + }); } } /// Clone handler for the [`Parent`] component. Allows to add clone as a child to the parent entity. -fn component_clone_parent( - world: &mut World, - _component_id: ComponentId, - entity_cloner: &EntityCloner, -) { +fn component_clone_parent(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { let parent = world .get::(entity_cloner.source()) .map(|p| p.0) .expect("Source entity must have Parent component"); - world.entity_mut(entity_cloner.target()).set_parent(parent); + world + .commands() + .entity(entity_cloner.target()) + .set_parent(parent); } #[cfg(test)] From cc66e8d41b7183e40f09037a7a882f97390a804d Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 2 Dec 2024 17:37:08 +0000 Subject: [PATCH 17/17] fix `get_component_clone_handlers_mut` doctest --- crates/bevy_ecs/src/world/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 6fec72d53a26c..2d51122e24a05 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3346,10 +3346,10 @@ impl World { /// # use bevy_ecs::prelude::*; /// use bevy_ecs::component::{ComponentId, ComponentCloneHandler}; /// use bevy_ecs::entity::EntityCloner; + /// use bevy_ecs::world::DeferredWorld; /// /// fn custom_clone_handler( - /// _world: &mut World, - /// component_id: ComponentId, + /// _world: &mut DeferredWorld, /// _entity_cloner: &EntityCloner, /// ) { /// // Custom cloning logic for component