diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 323dde9b17b2a..b05e8bba351f5 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -60,6 +60,11 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] } unsafe_op_in_unsafe_fn = "warn" unused_qualifications = "warn" +[[bench]] +name = "entity_cloning" +path = "benches/bevy_ecs/entity_cloning.rs" +harness = false + [[bench]] name = "ecs" path = "benches/bevy_ecs/main.rs" diff --git a/benches/benches/bevy_ecs/entity_cloning.rs b/benches/benches/bevy_ecs/entity_cloning.rs new file mode 100644 index 0000000000000..d0c71c7c2ae8e --- /dev/null +++ b/benches/benches/bevy_ecs/entity_cloning.rs @@ -0,0 +1,171 @@ +use bevy_ecs::bundle::Bundle; +use bevy_ecs::reflect::AppTypeRegistry; +use bevy_ecs::{component::Component, reflect::ReflectComponent, world::World}; +use bevy_hierarchy::{BuildChildren, CloneEntityHierarchyExt}; +use bevy_math::Mat4; +use bevy_reflect::{GetTypeRegistration, Reflect}; +use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion}; + +criterion_group!(benches, reflect_benches, clone_benches); +criterion_main!(benches); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C1(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C2(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C3(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C4(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C5(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C6(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C7(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C8(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C9(Mat4); + +#[derive(Component, Reflect, Default, Clone)] +#[reflect(Component)] +struct C10(Mat4); + +type ComplexBundle = (C1, C2, C3, C4, C5, C6, C7, C8, C9, C10); + +fn hierarchy( + b: &mut Bencher, + width: usize, + height: usize, + clone_via_reflect: bool, +) { + let mut world = World::default(); + let registry = AppTypeRegistry::default(); + { + let mut r = registry.write(); + r.register::(); + } + world.insert_resource(registry); + world.register_bundle::(); + if clone_via_reflect { + let mut components = Vec::new(); + C::get_component_ids(world.components(), &mut |id| components.push(id.unwrap())); + for component in components { + world + .get_component_clone_handlers_mut() + .set_component_handler( + component, + bevy_ecs::component::ComponentCloneHandler::reflect_handler(), + ); + } + } + + let id = world.spawn(black_box(C::default())).id(); + + let mut hierarchy_level = vec![id]; + + for _ in 0..height { + let current_hierarchy_level = hierarchy_level.clone(); + hierarchy_level.clear(); + for parent_id in current_hierarchy_level { + for _ in 0..width { + let child_id = world + .spawn(black_box(C::default())) + .set_parent(parent_id) + .id(); + hierarchy_level.push(child_id) + } + } + } + world.flush(); + + b.iter(move || { + world.commands().entity(id).clone_and_spawn_with(|builder| { + builder.recursive(true); + }); + world.flush(); + }); +} + +fn simple(b: &mut Bencher, clone_via_reflect: bool) { + let mut world = World::default(); + let registry = AppTypeRegistry::default(); + { + let mut r = registry.write(); + r.register::(); + } + world.insert_resource(registry); + world.register_bundle::(); + if clone_via_reflect { + let mut components = Vec::new(); + C::get_component_ids(world.components(), &mut |id| components.push(id.unwrap())); + for component in components { + world + .get_component_clone_handlers_mut() + .set_component_handler( + component, + bevy_ecs::component::ComponentCloneHandler::reflect_handler(), + ); + } + } + let id = world.spawn(black_box(C::default())).id(); + + b.iter(move || { + world.commands().entity(id).clone_and_spawn(); + world.flush(); + }); +} + +fn reflect_benches(c: &mut Criterion) { + c.bench_function("many components reflect", |b| { + simple::(b, true); + }); + + c.bench_function("hierarchy wide reflect", |b| { + hierarchy::(b, 10, 4, true); + }); + + c.bench_function("hierarchy tall reflect", |b| { + hierarchy::(b, 1, 50, true); + }); + + c.bench_function("hierarchy many reflect", |b| { + hierarchy::(b, 5, 5, true); + }); +} + +fn clone_benches(c: &mut Criterion) { + c.bench_function("many components clone", |b| { + simple::(b, false); + }); + + c.bench_function("hierarchy wide clone", |b| { + hierarchy::(b, 10, 4, false); + }); + + c.bench_function("hierarchy tall clone", |b| { + hierarchy::(b, 1, 50, false); + }); + + c.bench_function("hierarchy many clone", |b| { + hierarchy::(b, 5, 5, false); + }); +} diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index ba5893046a376..bb5e2424d230f 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -133,6 +133,7 @@ spin = { version = "0.9.8", default-features = false, features = [ ] } tracing = { version = "0.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } +bumpalo = "3" [dev-dependencies] rand = "0.8" diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 29b375896a197..f0cd6a6b5bcaf 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -5,12 +5,14 @@ use crate::{ archetype::ArchetypeFlags, bundle::BundleInfo, change_detection::MAX_CHANGE_AGE, - entity::{Entity, EntityCloner}, + entity::{ComponentCloneCtx, Entity}, query::DebugCheckedUnwrap, storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, system::{Local, Resource, SystemParam}, world::{DeferredWorld, FromWorld, World}, }; +#[cfg(feature = "bevy_reflect")] +use alloc::boxed::Box; use alloc::{borrow::Cow, format, vec::Vec}; pub use bevy_ecs_macros::Component; use bevy_ptr::{OwningPtr, UnsafeCellDeref}; @@ -419,7 +421,7 @@ pub trait Component: Send + Sync + 'static { /// /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::default() + ComponentCloneHandler::default_handler() } } @@ -981,18 +983,45 @@ impl ComponentDescriptor { } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut DeferredWorld, &EntityCloner); +pub type ComponentCloneFn = fn(&mut DeferredWorld, &mut ComponentCloneCtx); -/// An enum instructing how to clone a component. -#[derive(Debug, Default)] -pub enum ComponentCloneHandler { - #[default] +/// A struct instructing which clone handler to use when cloning a component. +#[derive(Debug)] +pub struct ComponentCloneHandler(Option); + +impl ComponentCloneHandler { /// Use the global default function to clone the component with this handler. - Default, + pub fn default_handler() -> Self { + Self(None) + } + /// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped. - Ignore, + pub fn ignore() -> Self { + Self(Some(component_clone_ignore)) + } + + /// Set clone handler based on `Clone` trait. + /// + /// If set as a handler for a component that is not the same as the one used to create this handler, it will panic. + pub fn clone_handler() -> Self { + Self(Some(component_clone_via_clone::)) + } + + /// Set clone handler based on `Reflect` trait. + #[cfg(feature = "bevy_reflect")] + pub fn reflect_handler() -> Self { + Self(Some(component_clone_via_reflect)) + } + /// Set a custom handler for the component. - Custom(ComponentCloneFn), + pub fn custom_handler(handler: ComponentCloneFn) -> Self { + Self(Some(handler)) + } + + /// Get [`ComponentCloneFn`] representing this handler or `None` if set to default handler. + pub fn get_handler(&self) -> Option { + self.0 + } } /// A registry of component clone handlers. Allows to set global default and per-component clone function for all components in the world. @@ -1003,7 +1032,7 @@ 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 + /// Sets the default handler for this registry. All components with [`default`](ComponentCloneHandler::default_handler) 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. @@ -1023,11 +1052,7 @@ impl ComponentCloneHandlers { if id.0 >= self.handlers.len() { self.handlers.resize(id.0 + 1, None); } - match 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), - }; + self.handlers[id.0] = handler.0; } /// Checks if the specified component is registered. If not, the component will use the default global handler. @@ -2146,62 +2171,96 @@ pub fn enforce_no_required_components_recursion( /// /// See [`ComponentCloneHandlers`] for more details. pub fn component_clone_via_clone( - world: &mut DeferredWorld, - entity_cloner: &EntityCloner, + _world: &mut DeferredWorld, + ctx: &mut ComponentCloneCtx, ) { - let component = world - .entity(entity_cloner.source()) - .get::() - .expect("Component must exists on source entity") - .clone(); - world - .commands() - .entity(entity_cloner.target()) - .insert(component); + if let Some(component) = ctx.read_source_component::() { + ctx.write_target_component(component.clone()); + } } /// 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. +/// To clone a component using this handler, the following must be true: +/// - World has [`AppTypeRegistry`](crate::reflect::AppTypeRegistry) +/// - Component has [`TypeId`] +/// - Component is registered +/// - Component has [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) registered +/// - Component has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect), +/// [`ReflectDefault`](bevy_reflect::std_traits::ReflectDefault), [`ReflectFromWorld`](crate::reflect::ReflectFromWorld) +/// +/// If any of the conditions is not satisfied, the component will be skipped. +/// +/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. #[cfg(feature = "bevy_reflect")] -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); +pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + let Some(registry) = ctx.type_registry() else { + return; + }; + let Some(source_component_reflect) = ctx.read_source_component_reflect() else { + return; + }; + let component_info = ctx.component_info(); + // checked in read_source_component_reflect + let type_id = component_info.type_id().unwrap(); + let registry = registry.read(); + + // Try to clone using ReflectFromReflect + if let Some(reflect_from_reflect) = + registry.get_type_data::(type_id) + { + if let Some(component) = + reflect_from_reflect.from_reflect(source_component_reflect.as_partial_reflect()) + { + drop(registry); + ctx.write_target_component_reflect(component); + return; + } + } + // Else, try to clone using ReflectDefault + if let Some(reflect_default) = + registry.get_type_data::(type_id) + { + let mut component = reflect_default.default(); + component.apply(source_component_reflect.as_partial_reflect()); + drop(registry); + ctx.write_target_component_reflect(component); + return; + } + // Otherwise, try to clone using ReflectFromWorld + if let Some(reflect_from_world) = + registry.get_type_data::(type_id) + { + let reflect_from_world = reflect_from_world.clone(); + let source_component_cloned = source_component_reflect.clone_value(); + let component_layout = component_info.layout(); + let target = ctx.target(); + let component_id = ctx.component_id(); + world.commands().queue(move |world: &mut World| { + let mut component = reflect_from_world.from_world(world); + assert_eq!(type_id, (*component).type_id()); + component.apply(source_component_cloned.as_partial_reflect()); + // SAFETY: + // - component_id is from the same world as target entity + // - component is a valid value represented by component_id + unsafe { + let raw_component_ptr = + core::ptr::NonNull::new_unchecked(Box::into_raw(component).cast::()); + world + .entity_mut(target) + .insert_by_id(component_id, OwningPtr::new(raw_component_ptr)); + alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout); + } }); - }); + } } /// Noop implementation of component clone handler function. /// -/// See [`ComponentCloneHandlers`] for more details. -pub fn component_clone_ignore(_world: &mut DeferredWorld, _entity_cloner: &EntityCloner) {} +/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. +pub fn component_clone_ignore(_world: &mut DeferredWorld, _ctx: &mut ComponentCloneCtx) {} /// Wrapper for components clone specialization using autoderef. #[doc(hidden)] @@ -2220,7 +2279,7 @@ pub trait ComponentCloneBase { } impl ComponentCloneBase for ComponentCloneSpecializationWrapper { fn get_component_clone_handler(&self) -> ComponentCloneHandler { - ComponentCloneHandler::default() + ComponentCloneHandler::default_handler() } } @@ -2231,6 +2290,6 @@ pub trait ComponentCloneViaClone { } impl ComponentCloneViaClone for &ComponentCloneSpecializationWrapper { fn get_component_clone_handler(&self) -> ComponentCloneHandler { - ComponentCloneHandler::Custom(component_clone_via_clone::) + ComponentCloneHandler::clone_handler::() } } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 35ecab24e4305..b15723aa57419 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,8 +1,13 @@ use alloc::{borrow::ToOwned, vec::Vec}; -use core::any::TypeId; +use bevy_ptr::{Ptr, PtrMut}; +use bumpalo::Bump; +use core::{any::TypeId, ptr::NonNull}; use bevy_utils::{HashMap, HashSet}; +#[cfg(feature = "bevy_reflect")] +use alloc::boxed::Box; + #[cfg(feature = "portable-atomic")] use portable_atomic_util::Arc; @@ -11,16 +16,261 @@ use alloc::sync::Arc; use crate::{ bundle::Bundle, - component::{component_clone_ignore, Component, ComponentCloneHandler, ComponentId}, + component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components}, entity::Entity, + query::DebugCheckedUnwrap, world::World, }; -/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`] and custom clone handlers. +/// Context for component clone handlers. +/// +/// Provides fast access to useful resources like [`AppTypeRegistry`](crate::reflect::AppTypeRegistry) +/// and allows component clone handler to get information about component being cloned. +pub struct ComponentCloneCtx<'a, 'b> { + component_id: ComponentId, + source_component_ptr: Ptr<'a>, + target_component_written: bool, + target_components_ptrs: &'a mut Vec>, + target_components_buffer: &'b Bump, + components: &'a Components, + component_info: &'a ComponentInfo, + entity_cloner: &'a EntityCloner, + #[cfg(feature = "bevy_reflect")] + type_registry: Option<&'a crate::reflect::AppTypeRegistry>, + #[cfg(not(feature = "bevy_reflect"))] + #[expect(dead_code)] + type_registry: Option<()>, +} + +impl<'a, 'b> ComponentCloneCtx<'a, 'b> { + /// Create a new instance of `ComponentCloneCtx` that can be passed to component clone handlers. + /// + /// # Safety + /// Caller must ensure that: + /// - `components` and `component_id` are from the same world. + /// - `source_component_ptr` points to a valid component of type represented by `component_id`. + unsafe fn new( + component_id: ComponentId, + source_component_ptr: Ptr<'a>, + target_components_ptrs: &'a mut Vec>, + target_components_buffer: &'b Bump, + components: &'a Components, + entity_cloner: &'a EntityCloner, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, + #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<()>, + ) -> Self { + Self { + component_id, + source_component_ptr, + target_components_ptrs, + target_component_written: false, + target_components_buffer, + components, + component_info: components.get_info_unchecked(component_id), + entity_cloner, + type_registry, + } + } + + /// Returns true if [`write_target_component`](`Self::write_target_component`) was called before. + pub fn target_component_written(&self) -> bool { + self.target_component_written + } + + /// Returns the current source entity. + pub fn source(&self) -> Entity { + self.entity_cloner.source + } + + /// Returns the current target entity. + pub fn target(&self) -> Entity { + self.entity_cloner.target + } + + /// Returns the [`ComponentId`] of the component being cloned. + pub fn component_id(&self) -> ComponentId { + self.component_id + } + + /// Returns the [`ComponentInfo`] of the component being cloned. + pub fn component_info(&self) -> &ComponentInfo { + self.component_info + } + + /// Returns a reference to the component on the source entity. + /// + /// Will return `None` if `ComponentId` of requested component does not match `ComponentId` of source component + pub fn read_source_component(&self) -> Option<&T> { + if self + .component_info + .type_id() + .is_some_and(|id| id == TypeId::of::()) + { + // SAFETY: + // - Components and ComponentId are from the same world + // - source_component_ptr holds valid data of the type referenced by ComponentId + unsafe { Some(self.source_component_ptr.deref::()) } + } else { + None + } + } + + /// Returns a reference to the component on the source entity as [`&dyn Reflect`](bevy_reflect::Reflect). + /// + /// Will return `None` if: + /// - World does not have [`AppTypeRegistry`](`crate::reflect::AppTypeRegistry`). + /// - Component does not implement [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr). + /// - Component is not registered. + /// - Component does not have [`TypeId`] + /// - Registered [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr)'s [`TypeId`] does not match component's [`TypeId`] + #[cfg(feature = "bevy_reflect")] + pub fn read_source_component_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> { + let registry = self.type_registry?.read(); + let type_id = self.component_info.type_id()?; + let reflect_from_ptr = registry.get_type_data::(type_id)?; + if reflect_from_ptr.type_id() != type_id { + return None; + } + // SAFETY: `source_component_ptr` stores data represented by `component_id`, which we used to get `ReflectFromPtr`. + unsafe { Some(reflect_from_ptr.as_reflect(self.source_component_ptr)) } + } + + /// Writes component data to target entity. + /// + /// # Panics + /// This will panic if: + /// - Component has already been written once. + /// - Component being written is not registered in the world. + /// - `ComponentId` of component being written does not match expected `ComponentId`. + pub fn write_target_component(&mut self, component: T) { + let short_name = disqualified::ShortName::of::(); + if self.target_component_written { + panic!("Trying to write component '{short_name}' multiple times") + } + if !self + .component_info + .type_id() + .is_some_and(|id| id == TypeId::of::()) + { + panic!("TypeId of component '{short_name}' does not match source component TypeId") + }; + let component_ref = self.target_components_buffer.alloc(component); + self.target_components_ptrs + .push(PtrMut::from(component_ref)); + self.target_component_written = true; + } + + /// Writes component data to target entity by providing a pointer to source component data and a pointer to uninitialized target component data. + /// + /// This method allows caller to provide a function (`clone_fn`) to clone component using untyped pointers. + /// First argument to `clone_fn` points to source component data ([`Ptr`]), second argument points to uninitialized buffer ([`NonNull`]) allocated with layout + /// described by [`ComponentInfo`] stored in this [`ComponentCloneCtx`]. If cloning is successful and uninitialized buffer contains a valid clone of + /// source component, `clone_fn` should return `true`, otherwise it should return `false`. + /// + /// # Safety + /// Caller must ensure that if `clone_fn` is called and returns `true`, the second argument ([`NonNull`] pointer) points to a valid component data + /// described by [`ComponentInfo`] stored in this [`ComponentCloneCtx`]. + /// # Panics + /// This will panic if component has already been written once. + pub unsafe fn write_target_component_ptr( + &mut self, + clone_fn: impl FnOnce(Ptr, NonNull) -> bool, + ) { + if self.target_component_written { + panic!("Trying to write component multiple times") + } + let layout = self.component_info.layout(); + let target_component_data_ptr = self.target_components_buffer.alloc_layout(layout); + + if clone_fn(self.source_component_ptr, target_component_data_ptr) { + self.target_components_ptrs + .push(PtrMut::new(target_component_data_ptr)); + self.target_component_written = true; + } + } + + /// Writes component data to target entity. + /// + /// # Panics + /// This will panic if: + /// - World does not have [`AppTypeRegistry`](`crate::reflect::AppTypeRegistry`). + /// - Component does not implement [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr). + /// - Source component does not have [`TypeId`]. + /// - Passed component's [`TypeId`] does not match source component [`TypeId`]. + /// - Component has already been written once. + #[cfg(feature = "bevy_reflect")] + pub fn write_target_component_reflect(&mut self, component: Box) { + if self.target_component_written { + panic!("Trying to write component multiple times") + } + let source_type_id = self + .component_info + .type_id() + .expect("Source component must have TypeId"); + let component_type_id = component.type_id(); + if source_type_id != component_type_id { + panic!("Passed component TypeId does not match source component TypeId") + } + let component_layout = self.component_info.layout(); + + let component_data_ptr = Box::into_raw(component).cast::(); + let target_component_data_ptr = + self.target_components_buffer.alloc_layout(component_layout); + // SAFETY: + // - target_component_data_ptr and component_data have the same data type. + // - component_data_ptr has layout of component_layout + unsafe { + core::ptr::copy_nonoverlapping( + component_data_ptr, + target_component_data_ptr.as_ptr(), + component_layout.size(), + ); + self.target_components_ptrs + .push(PtrMut::new(target_component_data_ptr)); + alloc::alloc::dealloc(component_data_ptr, component_layout); + } + + self.target_component_written = true; + } + + /// Return a reference to this context's `EntityCloner` instance. + /// + /// This can be used to issue clone commands using the same cloning configuration: + /// ``` + /// # use bevy_ecs::world::{DeferredWorld, World}; + /// # use bevy_ecs::entity::ComponentCloneCtx; + /// fn clone_handler(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + /// let another_target = world.commands().spawn_empty().id(); + /// let mut entity_cloner = ctx + /// .entity_cloner() + /// .with_source_and_target(ctx.source(), another_target); + /// world.commands().queue(move |world: &mut World| { + /// entity_cloner.clone_entity(world); + /// }); + /// } + /// ``` + pub fn entity_cloner(&self) -> &EntityCloner { + self.entity_cloner + } + + /// Returns instance of [`Components`]. + pub fn components(&self) -> &Components { + self.components + } + + /// Returns [`AppTypeRegistry`](`crate::reflect::AppTypeRegistry`) if it exists in the world. + /// + /// NOTE: Prefer this method instead of manually reading the resource from the world. + #[cfg(feature = "bevy_reflect")] + pub fn type_registry(&self) -> Option<&crate::reflect::AppTypeRegistry> { + self.type_registry + } +} + +/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`]. pub struct EntityCloner { source: Entity, target: Entity, - component_id: Option, filter_allows_components: bool, filter: Arc>, clone_handlers_overrides: Arc>, @@ -30,32 +280,96 @@ pub struct EntityCloner { impl EntityCloner { /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. pub fn clone_entity(&mut self, world: &mut World) { - let source_entity = world - .get_entity(self.source) - .expect("Source entity must exist"); + // SAFETY: + // - `source_entity` is read-only. + // - `type_registry` is read-only. + // - `components` is read-only. + // - `deferred_world` disallows structural ecs changes, which means all read-only resources above a not affected. + let (type_registry, source_entity, components, mut deferred_world) = unsafe { + let world = world.as_unsafe_world_cell(); + let source_entity = world + .get_entity(self.source) + .expect("Source entity must exist"); + + #[cfg(feature = "bevy_reflect")] + let app_registry = world.get_resource::(); + #[cfg(not(feature = "bevy_reflect"))] + let app_registry = Option::<()>::None; + + ( + app_registry, + source_entity, + world.components(), + world.into_deferred(), + ) + }; 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)), - ); + let component_data = Bump::new(); + let mut component_ids: Vec = Vec::with_capacity(archetype.component_count()); + let mut component_data_ptrs: Vec = Vec::with_capacity(archetype.component_count()); + + for component in archetype.components() { + if !self.is_cloning_allowed(&component) { + continue; + } + + let global_handlers = components.get_component_clone_handlers(); + let handler = match self.clone_handlers_overrides.get(&component) { + Some(handler) => handler + .get_handler() + .unwrap_or_else(|| global_handlers.get_default_handler()), + None => global_handlers.get_handler(component), + }; - for component in &components { - let global_handlers = world.components().get_component_clone_handlers(); - let handler = match self.clone_handlers_overrides.get(component) { - None => global_handlers.get_handler(*component), - Some(ComponentCloneHandler::Default) => global_handlers.get_default_handler(), - Some(ComponentCloneHandler::Ignore) => component_clone_ignore, - Some(ComponentCloneHandler::Custom(handler)) => *handler, + // SAFETY: + // - There are no other mutable references to source entity. + // - `component` is from `source_entity`'s archetype + let source_component_ptr = + unsafe { source_entity.get_by_id(component).debug_checked_unwrap() }; + + // SAFETY: + // - `components` and `component` are from the same world + // - `source_component_ptr` is valid and points to the same type as represented by `component` + let mut ctx = unsafe { + ComponentCloneCtx::new( + component, + source_component_ptr, + &mut component_data_ptrs, + &component_data, + components, + self, + type_registry, + ) }; - self.component_id = Some(*component); - (handler)(&mut world.into(), self); + + (handler)(&mut deferred_world, &mut ctx); + + if ctx.target_component_written { + component_ids.push(component); + } + } + + world.flush(); + + if !world.entities.contains(self.target) { + panic!("Target entity does not exist"); + } + + debug_assert_eq!(component_data_ptrs.len(), component_ids.len()); + + // SAFETY: + // - All `component_ids` are from the same world as `target` entity + // - All `component_data_ptrs` are valid types represented by `component_ids` + unsafe { + world.entity_mut(self.target).insert_by_ids( + &component_ids, + component_data_ptrs.into_iter().map(|ptr| ptr.promote()), + ); } if self.move_components { - world.entity_mut(self.source).remove_by_ids(&components); + world.entity_mut(self.source).remove_by_ids(&component_ids); } } @@ -64,22 +378,6 @@ impl EntityCloner { || (!self.filter_allows_components && !self.filter.contains(component)) } - /// Returns the current source entity. - pub fn source(&self) -> Entity { - self.source - } - - /// Returns the current target entity. - pub fn target(&self) -> Entity { - 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 { EntityCloner { @@ -123,7 +421,7 @@ impl EntityCloner { /// 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 +/// To use `Clone`-based handler ([`ComponentCloneHandler::clone_handler`]) 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): @@ -137,7 +435,7 @@ impl EntityCloner { /// const STORAGE_TYPE: StorageType = StorageType::Table; /// type Mutability = Mutable; /// fn get_component_clone_handler() -> ComponentCloneHandler { -/// ComponentCloneHandler::Custom(component_clone_via_clone::) +/// ComponentCloneHandler::clone_handler::() /// } /// } /// ``` @@ -187,15 +485,12 @@ impl<'w> EntityCloneBuilder<'w> { EntityCloner { source, target, - component_id: None, filter_allows_components, filter: Arc::new(filter), clone_handlers_overrides: Arc::new(clone_handlers_overrides), move_components, } .clone_entity(world); - - world.flush_commands(); } /// By default, any components allowed/denied through the filter will automatically @@ -369,77 +664,229 @@ impl<'w> EntityCloneBuilder<'w> { #[cfg(test)] mod tests { - use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World}; + use super::ComponentCloneCtx; + use crate::{ + self as bevy_ecs, + component::{Component, ComponentCloneHandler, ComponentDescriptor, StorageType}, + entity::EntityCloneBuilder, + world::{DeferredWorld, World}, + }; use bevy_ecs_macros::require; + use bevy_ptr::OwningPtr; + use core::alloc::Layout; #[cfg(feature = "bevy_reflect")] - #[test] - fn clone_entity_using_reflect() { - use crate::reflect::{AppTypeRegistry, ReflectComponent}; - use bevy_reflect::Reflect; + mod reflect { + use super::*; + use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}; + use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr}; + + #[test] + fn clone_entity_using_reflect() { + #[derive(Component, Reflect, Clone, PartialEq, Eq)] + #[reflect(Component)] + struct A { + field: usize, + } - #[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::(); + + world.register_component::(); + let id = world.component_id::().unwrap(); + world + .get_component_clone_handlers_mut() + .set_component_handler(id, ComponentCloneHandler::reflect_handler()); + + let component = A { field: 5 }; + + let e = world.spawn(component.clone()).id(); + let e_clone = world.spawn_empty().id(); + + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } - let mut world = World::default(); - world.init_resource::(); - let registry = world.get_resource::().unwrap(); - registry.write().register::(); + // TODO: remove this when https://github.com/bevyengine/bevy/pull/13432 lands + #[test] + fn clone_entity_using_reflect_all_paths() { + // `ReflectDefault`-based fast path + #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + #[reflect(Default)] + #[reflect(from_reflect = false)] + struct A { + field: usize, + field2: Vec, + } - let component = A { field: 5 }; + // `ReflectFromReflect`-based fast path + #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + struct B { + field: usize, + field2: Vec, + } - let e = world.spawn(component.clone()).id(); - let e_clone = world.spawn_empty().id(); + // `ReflectFromWorld`-based fast path + #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + #[reflect(FromWorld)] + #[reflect(from_reflect = false)] + struct C { + field: usize, + field2: Vec, + } - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + let mut world = World::default(); + world.init_resource::(); + let registry = world.get_resource::().unwrap(); + registry.write().register::<(A, B, C)>(); + + let a_id = world.register_component::(); + let b_id = world.register_component::(); + let c_id = world.register_component::(); + let handlers = world.get_component_clone_handlers_mut(); + handlers.set_component_handler(a_id, ComponentCloneHandler::reflect_handler()); + handlers.set_component_handler(b_id, ComponentCloneHandler::reflect_handler()); + handlers.set_component_handler(c_id, ComponentCloneHandler::reflect_handler()); + + let component_a = A { + field: 5, + field2: vec![1, 2, 3, 4, 5], + }; + let component_b = B { + field: 6, + field2: vec![1, 2, 3, 4, 5], + }; + let component_c = C { + field: 7, + field2: vec![1, 2, 3, 4, 5], + }; - assert!(world.get::(e_clone).is_some_and(|c| *c == component)); - } + let e = world.spawn((component_a, component_b, component_c)).id(); + let e_clone = world.spawn_empty().id(); - #[test] - fn clone_entity_using_clone() { - #[derive(Component, Clone, PartialEq, Eq)] - struct A { - field: usize, + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + + assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); + assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); + assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); } - let mut world = World::default(); + #[test] + fn read_source_component_reflect_should_return_none_on_invalid_reflect_from_ptr() { + #[derive(Component, Reflect)] + struct A; - let component = A { field: 5 }; + #[derive(Component, Reflect)] + struct B; - let e = world.spawn(component.clone()).id(); - let e_clone = world.spawn_empty().id(); + fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + assert!(ctx.read_source_component_reflect().is_none()); + } - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + let mut world = World::default(); + world.init_resource::(); + let registry = world.get_resource::().unwrap(); + { + let mut registry = registry.write(); + registry.register::(); + registry + .get_mut(core::any::TypeId::of::()) + .unwrap() + .insert(>::from_type()); + } - assert!(world.get::(e_clone).is_some_and(|c| *c == component)); - } + let a_id = world.register_component::(); + let handlers = world.get_component_clone_handlers_mut(); + handlers + .set_component_handler(a_id, ComponentCloneHandler::custom_handler(test_handler)); - #[cfg(feature = "bevy_reflect")] - #[test] - fn clone_entity_specialization() { - use crate::reflect::{AppTypeRegistry, ReflectComponent}; - use bevy_reflect::Reflect; + let e = world.spawn(A).id(); + let e_clone = world.spawn_empty().id(); - #[derive(Component, Reflect, PartialEq, Eq)] - #[reflect(Component)] - struct A { - field: usize, + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); } - impl Clone for A { - fn clone(&self) -> Self { - Self { field: 10 } + #[test] + fn clone_entity_specialization() { + #[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::new(&mut world).clone_entity(e, e_clone); + + assert!(world + .get::(e_clone) + .is_some_and(|comp| *comp == A { field: 10 })); + } + + #[test] + fn clone_entity_using_reflect_should_skip_without_panic() { + // Not reflected + #[derive(Component, PartialEq, Eq, Default, Debug)] + struct A; + + // No valid type data + #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + #[reflect(Component)] + #[reflect(from_reflect = false)] + struct B; + + let mut world = World::default(); + let a_id = world.register_component::(); + let b_id = world.register_component::(); + let handlers = world.get_component_clone_handlers_mut(); + handlers.set_component_handler(a_id, ComponentCloneHandler::reflect_handler()); + handlers.set_component_handler(b_id, ComponentCloneHandler::reflect_handler()); + + // No AppTypeRegistry + let e = world.spawn((A, B)).id(); + let e_clone = world.spawn_empty().id(); + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + assert_eq!(world.get::(e_clone), None); + assert_eq!(world.get::(e_clone), None); + + // With AppTypeRegistry + world.init_resource::(); + let registry = world.get_resource::().unwrap(); + registry.write().register::(); + + let e = world.spawn((A, B)).id(); + let e_clone = world.spawn_empty().id(); + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + assert_eq!(world.get::(e_clone), None); + assert_eq!(world.get::(e_clone), None); + } + } + + #[test] + fn clone_entity_using_clone() { + #[derive(Component, Clone, PartialEq, Eq)] + 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 }; @@ -448,9 +895,7 @@ mod tests { EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); - assert!(world - .get::(e_clone) - .is_some_and(|comp| *comp == A { field: 10 })); + assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } #[test] @@ -601,4 +1046,70 @@ mod tests { assert_eq!(world.entity(e_clone).get::(), Some(&B)); assert_eq!(world.entity(e_clone).get::(), Some(&C(5))); } + + #[test] + fn clone_entity_with_dynamic_components() { + const COMPONENT_SIZE: usize = 10; + fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + // SAFETY: this handler is only going to be used with a component represented by [u8; COMPONENT_SIZE] + unsafe { + ctx.write_target_component_ptr(move |source_ptr, target_ptr| { + core::ptr::copy_nonoverlapping( + source_ptr.as_ptr(), + target_ptr.as_ptr(), + COMPONENT_SIZE, + ); + true + }); + } + } + + let mut world = World::default(); + + let layout = Layout::array::(COMPONENT_SIZE).unwrap(); + // SAFETY: + // - No drop command is required + // - The component will store [u8; COMPONENT_SIZE], which is Send + Sync + let descriptor = unsafe { + ComponentDescriptor::new_with_layout( + "DynamicComp", + StorageType::Table, + layout, + None, + true, + ) + }; + let component_id = world.register_component_with_descriptor(descriptor); + + let handlers = world.get_component_clone_handlers_mut(); + handlers.set_component_handler( + component_id, + ComponentCloneHandler::custom_handler(test_handler), + ); + + let mut entity = world.spawn_empty(); + let data = [5u8; COMPONENT_SIZE]; + + // SAFETY: + // - ptr points to data represented by component_id ([u8; COMPONENT_SIZE]) + // - component_id is from the same world as entity + OwningPtr::make(data, |ptr| unsafe { + entity.insert_by_id(component_id, ptr); + }); + let entity = entity.id(); + + let entity_clone = world.spawn_empty().id(); + let builder = EntityCloneBuilder::new(&mut world); + builder.clone_entity(entity, entity_clone); + + let ptr = world.get_by_id(entity, component_id).unwrap(); + let clone_ptr = world.get_by_id(entity_clone, component_id).unwrap(); + // SAFETY: ptr and clone_ptr store component represented by [u8; COMPONENT_SIZE] + unsafe { + assert_eq!( + core::slice::from_raw_parts(ptr.as_ptr(), COMPONENT_SIZE), + core::slice::from_raw_parts(clone_ptr.as_ptr(), COMPONENT_SIZE), + ); + } + } } diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 68b5bdc0e0ed6..ee94cfa62a73e 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -1,6 +1,6 @@ use crate::{ component::{Component, ComponentCloneHandler, ComponentHooks, Mutable, StorageType}, - entity::{Entity, EntityCloneBuilder, EntityCloner}, + entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, observer::ObserverState, world::{DeferredWorld, World}, }; @@ -44,7 +44,7 @@ impl Component for ObservedBy { } fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::Ignore + ComponentCloneHandler::ignore() } } @@ -57,18 +57,18 @@ pub trait CloneEntityWithObserversExt { 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, - )) + self.override_component_clone_handler::( + ComponentCloneHandler::custom_handler(component_clone_observed_by), + ) } else { self.remove_component_clone_handler_override::() } } } -fn component_clone_observed_by(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { - let target = entity_cloner.target(); - let source = entity_cloner.source(); +fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + let target = ctx.target(); + let source = ctx.source(); world.commands().queue(move |world: &mut World| { let observed_by = world diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index c6337fbc99f3c..997d9fcee7bd0 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3155,12 +3155,12 @@ impl World { /// ``` /// # use bevy_ecs::prelude::*; /// use bevy_ecs::component::{ComponentId, ComponentCloneHandler}; - /// use bevy_ecs::entity::EntityCloner; + /// use bevy_ecs::entity::ComponentCloneCtx; /// use bevy_ecs::world::DeferredWorld; /// /// fn custom_clone_handler( /// _world: &mut DeferredWorld, - /// _entity_cloner: &EntityCloner, + /// _ctx: &mut ComponentCloneCtx, /// ) { /// // Custom cloning logic for component /// } @@ -3173,7 +3173,7 @@ impl World { /// let component_id = world.register_component::(); /// /// world.get_component_clone_handlers_mut() - /// .set_component_handler(component_id, ComponentCloneHandler::Custom(custom_clone_handler)) + /// .set_component_handler(component_id, ComponentCloneHandler::custom_handler(custom_clone_handler)) /// ``` pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { self.components.get_component_clone_handlers_mut() diff --git a/crates/bevy_hierarchy/src/components/children.rs b/crates/bevy_hierarchy/src/components/children.rs index a561d3cd9d5f3..4780d31eb2e67 100644 --- a/crates/bevy_hierarchy/src/components/children.rs +++ b/crates/bevy_hierarchy/src/components/children.rs @@ -45,7 +45,7 @@ impl Component for Children { type Mutability = Mutable; fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::Ignore + ComponentCloneHandler::ignore() } } diff --git a/crates/bevy_hierarchy/src/components/parent.rs b/crates/bevy_hierarchy/src/components/parent.rs index b808a6dcff183..4fc97aa914a24 100644 --- a/crates/bevy_hierarchy/src/components/parent.rs +++ b/crates/bevy_hierarchy/src/components/parent.rs @@ -45,7 +45,7 @@ impl Component for Parent { type Mutability = Mutable; fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::Ignore + ComponentCloneHandler::ignore() } } diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index e5a8ba6b1967d..37dcb83061f93 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -4,7 +4,7 @@ use crate::{ }; use bevy_ecs::{ component::ComponentCloneHandler, - entity::{Entity, EntityCloneBuilder, EntityCloner}, + entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, system::EntityCommands, world::{Command, DeferredWorld, EntityWorldMut, World}, }; @@ -214,16 +214,16 @@ pub trait CloneEntityHierarchyExt { impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> { fn recursive(&mut self, recursive: bool) -> &mut Self { if recursive { - self.override_component_clone_handler::(ComponentCloneHandler::Custom( - component_clone_children, - )) + self.override_component_clone_handler::( + ComponentCloneHandler::custom_handler(component_clone_children), + ) } else { self.remove_component_clone_handler_override::() } } fn as_child(&mut self, as_child: bool) -> &mut Self { if as_child { - self.override_component_clone_handler::(ComponentCloneHandler::Custom( + self.override_component_clone_handler::(ComponentCloneHandler::custom_handler( component_clone_parent, )) } else { @@ -233,34 +233,31 @@ impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> { } /// Clone handler for the [`Children`] component. Allows to clone the entity recursively. -fn component_clone_children(world: &mut DeferredWorld, entity_cloner: &EntityCloner) { - let children = world - .get::(entity_cloner.source()) +fn component_clone_children(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + let children = ctx + .read_source_component::() .expect("Source entity must have Children component") - .iter() - .cloned() - .collect::>(); - let parent = entity_cloner.target(); + .iter(); + let parent = ctx.target(); for child in children { let child_clone = world.commands().spawn_empty().id(); - let mut entity_cloner = entity_cloner.with_source_and_target(child, child_clone); + let mut clone_entity = ctx + .entity_cloner() + .with_source_and_target(*child, child_clone); world.commands().queue(move |world: &mut World| { - entity_cloner.clone_entity(world); + clone_entity.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 DeferredWorld, entity_cloner: &EntityCloner) { - let parent = world - .get::(entity_cloner.source()) +fn component_clone_parent(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + let parent = ctx + .read_source_component::() .map(|p| p.0) .expect("Source entity must have Parent component"); - world - .commands() - .entity(entity_cloner.target()) - .set_parent(parent); + world.commands().entity(ctx.target()).set_parent(parent); } #[cfg(test)]