From 2f6a747405e840f00117c969c7f2ac0cc9b0424b Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 29 Jan 2025 20:21:40 -0800 Subject: [PATCH 01/12] Improved Entity Mapping and Cloning --- benches/benches/bevy_ecs/entity_cloning.rs | 53 +- crates/bevy_animation/src/lib.rs | 13 +- crates/bevy_ecs/macros/src/component.rs | 182 ++++- crates/bevy_ecs/macros/src/lib.rs | 5 +- crates/bevy_ecs/src/component.rs | 222 ++--- crates/bevy_ecs/src/entity/clone_entities.rs | 760 ++++++++++-------- crates/bevy_ecs/src/entity/map_entities.rs | 81 +- crates/bevy_ecs/src/hierarchy.rs | 29 +- crates/bevy_ecs/src/lib.rs | 98 ++- .../bevy_ecs/src/observer/entity_observer.rs | 47 +- crates/bevy_ecs/src/observer/mod.rs | 2 +- crates/bevy_ecs/src/reflect/bundle.rs | 39 +- crates/bevy_ecs/src/reflect/component.rs | 40 +- crates/bevy_ecs/src/relationship/mod.rs | 32 +- .../relationship_source_collection.rs | 6 +- .../src/system/commands/entity_command.rs | 6 +- crates/bevy_ecs/src/system/commands/mod.rs | 25 +- crates/bevy_ecs/src/world/entity_ref.rs | 54 +- crates/bevy_ecs/src/world/mod.rs | 37 +- crates/bevy_mesh/src/skinning.rs | 20 +- crates/bevy_scene/src/dynamic_scene.rs | 43 +- crates/bevy_scene/src/lib.rs | 1 - crates/bevy_scene/src/scene.rs | 29 +- crates/bevy_scene/src/serde.rs | 10 +- examples/ecs/dynamic.rs | 5 +- examples/ecs/immutable_components.rs | 5 +- examples/stress_tests/many_components.rs | 3 +- 27 files changed, 1100 insertions(+), 747 deletions(-) diff --git a/benches/benches/bevy_ecs/entity_cloning.rs b/benches/benches/bevy_ecs/entity_cloning.rs index 92687cf67892a..7c474cc4f8aaf 100644 --- a/benches/benches/bevy_ecs/entity_cloning.rs +++ b/benches/benches/bevy_ecs/entity_cloning.rs @@ -2,7 +2,8 @@ use core::hint::black_box; use benches::bench; use bevy_ecs::bundle::Bundle; -use bevy_ecs::component::ComponentCloneHandler; +use bevy_ecs::component::ComponentCloneBehavior; +use bevy_ecs::entity::EntityCloner; use bevy_ecs::hierarchy::ChildOf; use bevy_ecs::reflect::AppTypeRegistry; use bevy_ecs::{component::Component, world::World}; @@ -52,7 +53,10 @@ type ComplexBundle = (C1, C2, C3, C4, C5, C6, C7, C8, C9, C10); /// Sets the [`ComponentCloneHandler`] for all explicit and required components in a bundle `B` to /// use the [`Reflect`] trait instead of [`Clone`]. -fn set_reflect_clone_handler(world: &mut World) { +fn reflection_cloner( + world: &mut World, + recursive: bool, +) -> EntityCloner { // Get mutable access to the type registry, creating it if it does not exist yet. let registry = world.get_resource_or_init::(); @@ -67,12 +71,15 @@ fn set_reflect_clone_handler(world: &mut World) // this bundle are saved. let component_ids: Vec<_> = world.register_bundle::().contributed_components().into(); - let clone_handlers = world.get_component_clone_handlers_mut(); + let mut builder = EntityCloner::build(world); // Overwrite the clone handler for all components in the bundle to use `Reflect`, not `Clone`. for component in component_ids { - clone_handlers.set_component_handler(component, ComponentCloneHandler::reflect_handler()); + builder.override_clone_behavior_with_id(component, ComponentCloneBehavior::reflect()); } + builder.recursive(recursive); + + builder.finish() } /// A helper function that benchmarks running the [`EntityCommands::clone_and_spawn()`] command on a @@ -91,18 +98,18 @@ fn bench_clone( ) { let mut world = World::default(); - if clone_via_reflect { - set_reflect_clone_handler::(&mut world); - } + let mut cloner = if clone_via_reflect { + reflection_cloner::(&mut world, false) + } else { + EntityCloner::default() + }; // Spawn the first entity, which will be cloned in the benchmark routine. let id = world.spawn(B::default()).id(); b.iter(|| { - // Queue the command to clone the entity. - world.commands().entity(black_box(id)).clone_and_spawn(); - - // Run the command. + // clones the given entity + cloner.spawn_clone(&mut world, black_box(id)); world.flush(); }); } @@ -125,9 +132,15 @@ fn bench_clone_hierarchy( ) { let mut world = World::default(); - if clone_via_reflect { - set_reflect_clone_handler::(&mut world); - } + let mut cloner = if clone_via_reflect { + reflection_cloner::(&mut world, true) + } else { + let mut builder = EntityCloner::build(&mut world); + builder.recursive(true); + builder.finish() + }; + + // Make the clone command recursive, so children are cloned as well. // Spawn the first entity, which will be cloned in the benchmark routine. let id = world.spawn(B::default()).id(); @@ -148,18 +161,8 @@ fn bench_clone_hierarchy( } } - // Flush all `set_parent()` commands. - world.flush(); - b.iter(|| { - world - .commands() - .entity(black_box(id)) - .clone_and_spawn_with(|builder| { - // Make the clone command recursive, so children are cloned as well. - builder.recursive(true); - }); - + cloner.spawn_clone(&mut world, black_box(id)); world.flush(); }); } diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 2bfd54d847d84..0bbfe4f3de484 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -33,12 +33,7 @@ use crate::{ use bevy_app::{Animation, App, Plugin, PostUpdate}; use bevy_asset::{Asset, AssetApp, Assets}; -use bevy_ecs::{ - entity::{VisitEntities, VisitEntitiesMut}, - prelude::*, - reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut}, - world::EntityMutExcept, -}; +use bevy_ecs::{prelude::*, world::EntityMutExcept}; use bevy_math::FloatOrd; use bevy_platform_support::{collections::HashMap, hash::NoOpHash}; use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath}; @@ -207,16 +202,16 @@ impl Hash for AnimationTargetId { /// Note that each entity can only be animated by one animation player at a /// time. However, you can change [`AnimationTarget`]'s `player` property at /// runtime to change which player is responsible for animating the entity. -#[derive(Clone, Copy, Component, Reflect, VisitEntities, VisitEntitiesMut)] -#[reflect(Component, MapEntities, VisitEntities, VisitEntitiesMut)] +#[derive(Clone, Copy, Component, Reflect)] +#[reflect(Component)] pub struct AnimationTarget { /// The ID of this animation target. /// /// Typically, this is derived from the path. - #[visit_entities(ignore)] pub id: AnimationTargetId, /// The entity containing the [`AnimationPlayer`]. + #[entities] pub player: Entity, } diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index ba695bef49550..784f9b1f9aee0 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -1,6 +1,6 @@ use proc_macro::{TokenStream, TokenTree}; use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; +use quote::{format_ident, quote, ToTokens}; use std::collections::HashSet; use syn::{ parenthesized, @@ -9,8 +9,8 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Comma, Paren}, - Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Fields, Ident, LitStr, Path, Result, - Token, Visibility, + Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Fields, Ident, Index, LitStr, Member, + Path, Result, Token, Visibility, }; pub fn derive_event(input: TokenStream) -> TokenStream { @@ -51,6 +51,8 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { }) } +const ENTITIES_ATTR: &str = "entities"; + pub fn derive_component(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); let bevy_ecs_path: Path = crate::bevy_ecs_path(); @@ -69,6 +71,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { Err(err) => err.into_compile_error().into(), }; + let visit_entities = visit_entities(&ast.data, &bevy_ecs_path, relationship.is_some()); + let storage = storage_path(&bevy_ecs_path, attrs.storage); let on_add = hook_register_function_call(quote! {on_add}, attrs.on_add); @@ -120,11 +124,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream { quote!(hooks.on_replace(::on_replace);), ); - if relationship_target.despawn_descendants { + if relationship_target.linked_spawn { if on_despawn.is_some() { return syn::Error::new( ast.span(), - "Custom on_despawn hooks are not supported as this RelationshipTarget already defines an on_despawn hook, via the despawn_descendants attribute", + "Custom on_despawn hooks are not supported as this RelationshipTarget already defines an on_despawn hook, via the 'linked_spawn' attribute", ) .into_compile_error() .into(); @@ -201,12 +205,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream { .then_some(quote! { #bevy_ecs_path::component::Immutable }) .unwrap_or(quote! { #bevy_ecs_path::component::Mutable }); - let clone_handler = if relationship_target.is_some() { - quote!(#bevy_ecs_path::component::ComponentCloneHandler::ignore()) + let clone_behavior = if relationship_target.is_some() { + quote!(#bevy_ecs_path::component::ComponentCloneBehavior::RelationshipTarget(#bevy_ecs_path::relationship::clone_relationship_target::)) } else { quote!( - use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase}; - (&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::::default()).get_component_clone_handler() + use #bevy_ecs_path::component::{DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone}; + (&&&#bevy_ecs_path::component::DefaultCloneBehaviorSpecialization::::default()).default_clone_behavior() ) }; @@ -241,9 +245,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream { #on_despawn } - fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler { - #clone_handler + fn clone_behavior() -> #bevy_ecs_path::component::ComponentCloneBehavior { + #clone_behavior } + + #visit_entities } #relationship @@ -252,6 +258,144 @@ pub fn derive_component(input: TokenStream) -> TokenStream { }) } +fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> TokenStream2 { + match data { + Data::Struct(DataStruct { ref fields, .. }) => { + let mut visited_fields = Vec::new(); + let mut visited_indices = Vec::new(); + match fields { + Fields::Named(fields) => { + for field in &fields.named { + if field + .attrs + .iter() + .any(|a| a.meta.path().is_ident(ENTITIES_ATTR)) + { + if let Some(ident) = field.ident.clone() { + visited_fields.push(ident); + } + } + } + } + Fields::Unnamed(fields) => { + for (index, field) in fields.unnamed.iter().enumerate() { + if index == 0 && is_relationship { + visited_indices.push(Index::from(0)); + } else if field + .attrs + .iter() + .any(|a| a.meta.path().is_ident(ENTITIES_ATTR)) + { + visited_indices.push(Index::from(index)); + } + } + } + Fields::Unit => {} + } + + if visited_fields.is_empty() && visited_indices.is_empty() { + TokenStream2::new() + } else { + let visit = visited_fields + .iter() + .map(|field| quote!(this.#field.visit_entities(&mut func);)) + .chain( + visited_indices + .iter() + .map(|index| quote!(this.#index.visit_entities(&mut func);)), + ); + let visit_mut = visited_fields + .iter() + .map(|field| quote!(this.#field.visit_entities_mut(&mut func);)) + .chain( + visited_indices + .iter() + .map(|index| quote!(this.#index.visit_entities_mut(&mut func);)), + ); + quote!( + fn visit_entities(this: &Self, mut func: impl FnMut(Entity)) { + use #bevy_ecs_path::entity::VisitEntities; + #(#visit)* + } + + fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut Entity)) { + use #bevy_ecs_path::entity::VisitEntitiesMut; + #(#visit_mut)* + } + ) + } + } + Data::Enum(data_enum) => { + let mut has_visited_fields = false; + let mut visit_variants = Vec::with_capacity(data_enum.variants.len()); + let mut visit_variants_mut = Vec::with_capacity(data_enum.variants.len()); + for variant in &data_enum.variants { + let mut variant_fields = Vec::new(); + let mut variant_fields_mut = Vec::new(); + + let mut visit_variant_fields = Vec::new(); + let mut visit_variant_fields_mut = Vec::new(); + + for (index, field) in variant.fields.iter().enumerate() { + if field + .attrs + .iter() + .any(|a| a.meta.path().is_ident(ENTITIES_ATTR)) + { + has_visited_fields = true; + let field_member = ident_or_index(field.ident.as_ref(), index); + let field_ident = format_ident!("field_{}", field_member); + + variant_fields.push(quote!(#field_member: ref #field_ident)); + variant_fields_mut.push(quote!(#field_member: ref mut #field_ident)); + + visit_variant_fields.push(quote!(#field_ident.visit_entities(&mut func);)); + visit_variant_fields_mut + .push(quote!(#field_ident.visit_entities_mut(&mut func);)); + } + } + + let ident = &variant.ident; + visit_variants.push(quote!(Self::#ident {#(#variant_fields,)* ..} => { + #(#visit_variant_fields)* + })); + visit_variants_mut.push(quote!(Self::#ident {#(#variant_fields_mut,)* ..} => { + #(#visit_variant_fields_mut)* + })); + } + if has_visited_fields { + quote!( + fn visit_entities(this: &Self, mut func: impl FnMut(Entity)) { + use #bevy_ecs_path::entity::VisitEntities; + match this { + #(#visit_variants,)* + _ => {} + } + } + + fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut Entity)) { + use #bevy_ecs_path::entity::VisitEntitiesMut; + match this { + #(#visit_variants_mut,)* + _ => {} + } + } + ) + } else { + TokenStream2::new() + } + } + Data::Union(_) => TokenStream2::new(), + } +} + +pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { + ident.map_or_else( + || Member::Unnamed(index.into()), + |ident| Member::Named(ident.clone()), + ) +} + pub fn document_required_components(attr: TokenStream, item: TokenStream) -> TokenStream { let paths = parse_macro_input!(attr with Punctuated::::parse_terminated) .iter() @@ -329,7 +473,7 @@ struct Relationship { struct RelationshipTarget { relationship: Ident, - despawn_descendants: bool, + linked_spawn: bool, } // values for `storage` attribute @@ -464,18 +608,18 @@ impl Parse for Relationship { impl Parse for RelationshipTarget { fn parse(input: syn::parse::ParseStream) -> Result { let mut relationship_ident = None; - let mut despawn_descendants_exists = false; + let mut linked_spawn_exists = false; syn::custom_keyword!(relationship); - syn::custom_keyword!(despawn_descendants); + syn::custom_keyword!(linked_spawn); let mut done = false; loop { if input.peek(relationship) { input.parse::()?; input.parse::()?; relationship_ident = Some(input.parse::()?); - } else if input.peek(despawn_descendants) { - input.parse::()?; - despawn_descendants_exists = true; + } else if input.peek(linked_spawn) { + input.parse::()?; + linked_spawn_exists = true; } else { done = true; } @@ -490,7 +634,7 @@ impl Parse for RelationshipTarget { let relationship = relationship_ident.ok_or_else(|| syn::Error::new(input.span(), "RelationshipTarget derive must specify a relationship via #[relationship_target(relationship = X)"))?; Ok(RelationshipTarget { relationship, - despawn_descendants: despawn_descendants_exists, + linked_spawn: linked_spawn_exists, }) } } @@ -582,8 +726,10 @@ fn derive_relationship_target( let relationship = &relationship_target.relationship; let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let linked_spawn = relationship_target.linked_spawn; Ok(Some(quote! { impl #impl_generics #bevy_ecs_path::relationship::RelationshipTarget for #struct_name #type_generics #where_clause { + const LINKED_SPAWN: bool = #linked_spawn; type Relationship = #relationship; type Collection = #collection; diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index e4cca41a4d7aa..e6d2fe2f4cc13 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -589,7 +589,10 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { component::derive_resource(input) } -#[proc_macro_derive(Component, attributes(component, relationship, relationship_target))] +#[proc_macro_derive( + Component, + attributes(component, relationship, relationship_target, entities) +)] pub fn derive_component(input: TokenStream) -> TokenStream { component::derive_component(input) } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 190910d7d0b96..a31a735e54a95 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -9,7 +9,7 @@ use crate::{ query::DebugCheckedUnwrap, resource::Resource, storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, - system::{Local, SystemParam}, + system::{Commands, Local, SystemParam}, world::{DeferredWorld, FromWorld, World}, }; #[cfg(feature = "bevy_reflect")] @@ -179,10 +179,6 @@ pub use bevy_ecs_macros::require; /// } /// /// # let mut world = World::default(); -/// // This will implicitly also insert C with the init_c() constructor -/// let id = world.spawn(A).id(); -/// assert_eq!(&C(10), world.entity(id).get::().unwrap()); -/// /// // This will implicitly also insert C with the `|| C(20)` constructor closure /// let id = world.spawn(B).id(); /// assert_eq!(&C(20), world.entity(id).get::().unwrap()); @@ -416,10 +412,20 @@ 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_handler() + /// See [Handlers section of `EntityClonerBuilder`](crate::entity::EntityClonerBuilder#handlers) to understand how this affects handler priority. + #[inline] + fn clone_behavior() -> ComponentCloneBehavior { + ComponentCloneBehavior::Default } + + /// Visits entities stored on the component. + #[inline] + fn visit_entities(_this: &Self, _f: impl FnMut(Entity)) {} + + /// Returns pointers to every entity stored on the component. This will be used to remap entity references when this entity + /// is cloned. + #[inline] + fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {} } mod private { @@ -743,6 +749,12 @@ impl ComponentInfo { self.descriptor.mutable } + /// Returns `true` if the current component is mutable. + #[inline] + pub fn clone_behavior(&self) -> &ComponentCloneBehavior { + &self.descriptor.clone_behavior + } + /// Returns the [`TypeId`] of the underlying component type. /// Returns `None` if the component does not correspond to a Rust type. #[inline] @@ -899,6 +911,7 @@ pub struct ComponentDescriptor { // None if the underlying type doesn't need to be dropped drop: Option unsafe fn(OwningPtr<'a>)>, mutable: bool, + clone_behavior: ComponentCloneBehavior, } // We need to ignore the `drop` field in our `Debug` impl @@ -911,6 +924,7 @@ impl Debug for ComponentDescriptor { .field("type_id", &self.type_id) .field("layout", &self.layout) .field("mutable", &self.mutable) + .field("clone_behavior", &self.clone_behavior) .finish() } } @@ -936,6 +950,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: T::Mutability::MUTABLE, + clone_behavior: T::clone_behavior(), } } @@ -950,6 +965,7 @@ impl ComponentDescriptor { layout: Layout, drop: Option unsafe fn(OwningPtr<'a>)>, mutable: bool, + clone_behavior: ComponentCloneBehavior, ) -> Self { Self { name: name.into(), @@ -959,6 +975,7 @@ impl ComponentDescriptor { layout, drop, mutable, + clone_behavior, } } @@ -976,6 +993,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + clone_behavior: ComponentCloneBehavior::Default, } } @@ -988,6 +1006,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + clone_behavior: ComponentCloneBehavior::Default, } } @@ -1018,107 +1037,55 @@ impl ComponentDescriptor { } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut DeferredWorld, &mut ComponentCloneCtx); - -/// A struct instructing which clone handler to use when cloning a component. -#[derive(Debug)] -pub struct ComponentCloneHandler(Option); +pub type ComponentCloneFn = fn(&mut Commands, &mut ComponentCloneCtx); -impl ComponentCloneHandler { - /// Use the global default function to clone the component with this handler. - 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. - pub fn ignore() -> Self { - Self(Some(component_clone_ignore)) - } +/// The clone behavior to use when cloning a [`Component`]. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum ComponentCloneBehavior { + /// Uses the default behavior (which is passed to [`ComponentCloneBehavior::resolve`]) + #[default] + Default, + /// Do not clone this component. + Ignore, + /// Uses a custom [`ComponentCloneFn`]. + Custom(ComponentCloneFn), + /// Uses a [`ComponentCloneFn`] that produces an empty version of the given relationship target. + // TODO: this exists so that the current scene spawning code can know when to skip these components. + // When we move to actually cloning entities in scene spawning code, this should be removed in favor of Custom, as the + // distinction will no longer be necessary. + RelationshipTarget(ComponentCloneFn), +} +impl ComponentCloneBehavior { /// 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::)) + pub fn clone() -> Self { + Self::Custom(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. - 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. -#[derive(Debug)] -pub struct ComponentCloneHandlers { - handlers: Vec>, - default_handler: ComponentCloneFn, -} - -impl ComponentCloneHandlers { - /// 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. - pub fn set_default_handler(&mut self, handler: ComponentCloneFn) { - 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. - /// - /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. - pub fn set_component_handler(&mut self, id: ComponentId, handler: ComponentCloneHandler) { - if id.0 >= self.handlers.len() { - self.handlers.resize(id.0 + 1, None); - } - self.handlers[id.0] = handler.0; - } - - /// Checks if the specified component is registered. If not, the component will use the default global handler. - /// - /// 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). - /// - /// 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, - } - } -} - -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, + pub fn reflect() -> Self { + Self::Custom(component_clone_via_reflect) + } + + /// Returns the "global default" + pub fn global_default_fn() -> ComponentCloneFn { + #[cfg(feature = "bevy_reflect")] + return component_clone_via_reflect; + #[cfg(not(feature = "bevy_reflect"))] + return component_clone_ignore; + } + + /// Resolves the [`ComponentCloneBehavior`] to a [`ComponentCloneFn`]. If [`ComponentCloneBehavior::Default`] is + /// specified, the given `default` function will be used. + pub fn resolve(&self, default: ComponentCloneFn) -> ComponentCloneFn { + match self { + ComponentCloneBehavior::Default => default, + ComponentCloneBehavior::Ignore => component_clone_ignore, + ComponentCloneBehavior::Custom(custom) + | ComponentCloneBehavior::RelationshipTarget(custom) => *custom, } } } @@ -1129,7 +1096,6 @@ pub struct Components { components: Vec, indices: TypeIdMap, resource_indices: TypeIdMap, - component_clone_handlers: ComponentCloneHandlers, } impl Components { @@ -1183,9 +1149,6 @@ 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 } @@ -1548,16 +1511,6 @@ 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 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 - } - /// Type-erased equivalent of [`Components::component_id()`]. #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { @@ -2201,12 +2154,11 @@ pub fn enforce_no_required_components_recursion( } /// 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. +/// Can be [set](Component::clone_behavior) 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 DeferredWorld, + _commands: &mut Commands, ctx: &mut ComponentCloneCtx, ) { if let Some(component) = ctx.read_source_component::() { @@ -2215,7 +2167,7 @@ pub fn component_clone_via_clone( } /// Component [clone handler function](ComponentCloneFn) implemented using reflect. -/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component, +/// Can be [set](Component::clone_behavior) as clone handler for any registered component, /// but only reflected components will be cloned. /// /// To clone a component using this handler, the following must be true: @@ -2228,9 +2180,9 @@ pub fn component_clone_via_clone( /// /// If any of the conditions is not satisfied, the component will be skipped. /// -/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. +/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details. #[cfg(feature = "bevy_reflect")] -pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { +pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentCloneCtx) { let Some(registry) = ctx.type_registry() else { return; }; @@ -2273,7 +2225,7 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen let component_layout = component_info.layout(); let target = ctx.target(); let component_id = ctx.component_id(); - world.commands().queue(move |world: &mut 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()); @@ -2294,14 +2246,14 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen /// Noop implementation of component clone handler function. /// -/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. -pub fn component_clone_ignore(_world: &mut DeferredWorld, _ctx: &mut ComponentCloneCtx) {} +/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details. +pub fn component_clone_ignore(_commands: &mut Commands, _ctx: &mut ComponentCloneCtx) {} /// Wrapper for components clone specialization using autoderef. #[doc(hidden)] -pub struct ComponentCloneSpecializationWrapper(PhantomData); +pub struct DefaultCloneBehaviorSpecialization(PhantomData); -impl Default for ComponentCloneSpecializationWrapper { +impl Default for DefaultCloneBehaviorSpecialization { fn default() -> Self { Self(PhantomData) } @@ -2309,22 +2261,22 @@ impl Default for ComponentCloneSpecializationWrapper { /// Base trait for components clone specialization using autoderef. #[doc(hidden)] -pub trait ComponentCloneBase { - fn get_component_clone_handler(&self) -> ComponentCloneHandler; +pub trait DefaultCloneBehaviorBase { + fn default_clone_behavior(&self) -> ComponentCloneBehavior; } -impl ComponentCloneBase for ComponentCloneSpecializationWrapper { - fn get_component_clone_handler(&self) -> ComponentCloneHandler { - ComponentCloneHandler::default_handler() +impl DefaultCloneBehaviorBase for DefaultCloneBehaviorSpecialization { + fn default_clone_behavior(&self) -> ComponentCloneBehavior { + ComponentCloneBehavior::Default } } /// Specialized trait for components clone specialization using autoderef. #[doc(hidden)] -pub trait ComponentCloneViaClone { - fn get_component_clone_handler(&self) -> ComponentCloneHandler; +pub trait DefaultCloneBehaviorViaClone { + fn default_clone_behavior(&self) -> ComponentCloneBehavior; } -impl ComponentCloneViaClone for &ComponentCloneSpecializationWrapper { - fn get_component_clone_handler(&self) -> ComponentCloneHandler { - ComponentCloneHandler::clone_handler::() +impl DefaultCloneBehaviorViaClone for &DefaultCloneBehaviorSpecialization { + fn default_clone_behavior(&self) -> ComponentCloneBehavior { + ComponentCloneBehavior::clone::() } } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b337b3c079d33..2dfcc21931e51 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,6 +1,5 @@ use alloc::{borrow::ToOwned, vec::Vec}; use bevy_platform_support::collections::{HashMap, HashSet}; -use bevy_platform_support::sync::Arc; use bevy_ptr::{Ptr, PtrMut}; use bumpalo::Bump; use core::{any::TypeId, ptr::NonNull}; @@ -8,14 +7,19 @@ use core::{any::TypeId, ptr::NonNull}; #[cfg(feature = "bevy_reflect")] use alloc::boxed::Box; +use crate::component::{ComponentCloneBehavior, ComponentCloneFn}; +use crate::entity::hash_map::EntityHashMap; +use crate::entity::EntityMapper; +use crate::system::Commands; use crate::{ bundle::Bundle, - component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components}, + component::{Component, ComponentId, ComponentInfo, Components}, entity::Entity, - hierarchy::{ChildOf, Children}, query::DebugCheckedUnwrap, - world::{DeferredWorld, World}, + world::World, }; +use alloc::collections::VecDeque; +use core::cell::RefCell; /// Context for component clone handlers. /// @@ -25,11 +29,14 @@ 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, + bundle_scratch: &'a mut BundleScratch<'b>, + bump: &'b Bump, + source: Entity, + target: Entity, components: &'a Components, component_info: &'a ComponentInfo, - entity_cloner: &'a EntityCloner, + entity_cloner: &'a mut EntityCloner, + mapper: &'a mut dyn EntityMapper, #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, #[cfg(not(feature = "bevy_reflect"))] @@ -46,21 +53,27 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - `source_component_ptr` points to a valid component of type represented by `component_id`. unsafe fn new( component_id: ComponentId, + source: Entity, + target: Entity, source_component_ptr: Ptr<'a>, - target_components_ptrs: &'a mut Vec>, - target_components_buffer: &'b Bump, + bump: &'b Bump, + bundle_scratch: &'a mut BundleScratch<'b>, components: &'a Components, - entity_cloner: &'a EntityCloner, + entity_cloner: &'a mut EntityCloner, + mapper: &'a mut dyn EntityMapper, #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<()>, ) -> Self { Self { component_id, + source, + target, source_component_ptr, - target_components_ptrs, + bundle_scratch, target_component_written: false, - target_components_buffer, + bump, components, + mapper, component_info: components.get_info_unchecked(component_id), entity_cloner, type_registry, @@ -74,12 +87,12 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// Returns the current source entity. pub fn source(&self) -> Entity { - self.entity_cloner.source + self.source } /// Returns the current target entity. pub fn target(&self) -> Entity { - self.entity_cloner.target + self.target } /// Returns the [`ComponentId`] of the component being cloned. @@ -92,6 +105,12 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.component_info } + /// Returns true if the [`EntityCloner`] is configured to recursively clone entities. + #[inline] + pub fn is_recursive(&self) -> bool { + self.entity_cloner.is_recursive + } + /// 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 @@ -137,21 +156,26 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - 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::(); + pub fn write_target_component(&mut self, mut component: C) { + C::visit_entities_mut(&mut component, |entity| { + *entity = self.mapper.get_mapped(*entity); + }); + 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_none_or(|id| id != TypeId::of::()) + .is_none_or(|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)); + // SAFETY: the TypeId of self.component_id has been checked to ensure it matches `C` + unsafe { + self.bundle_scratch + .push(self.bump, self.component_id, component); + }; self.target_component_written = true; } @@ -175,11 +199,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { 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); + let target_component_data_ptr = self.bump.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.bundle_scratch + .push_ptr(self.component_id, PtrMut::new(target_component_data_ptr)); self.target_component_written = true; } } @@ -209,8 +233,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { 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); + let target_component_data_ptr = self.bump.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 @@ -220,34 +243,14 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { target_component_data_ptr.as_ptr(), component_layout.size(), ); - self.target_components_ptrs - .push(PtrMut::new(target_component_data_ptr)); + self.bundle_scratch + .push_ptr(self.component_id, 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 @@ -260,112 +263,248 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { pub fn type_registry(&self) -> Option<&crate::reflect::AppTypeRegistry> { self.type_registry } + + /// Queues the `entity` to be cloned by the current [`EntityCloner`] + pub fn queue_entity_clone(&self, entity: Entity) { + self.entity_cloner + .clone_queue + .borrow_mut() + .push_back(entity); + } } -/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`]. +/// A cloning configuration capable of cloning entities in a given world. See [`EntityClonerBuilder`] for configuration +/// options, and [`EntityCloner::clone_entity`] to clone a given entity. +#[derive(Debug)] pub struct EntityCloner { - source: Entity, - target: Entity, filter_allows_components: bool, - filter: Arc>, - clone_handlers_overrides: Arc>, + filter: HashSet, + clone_behavior_overrides: HashMap, move_components: bool, + is_recursive: bool, + default_clone_fn: ComponentCloneFn, + clone_queue: RefCell>, } -impl EntityCloner { - /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. - #[track_caller] - pub fn clone_entity(&mut self, world: &mut World) { - // 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"); +impl Default for EntityCloner { + fn default() -> Self { + Self { + filter_allows_components: false, + filter: Default::default(), + clone_behavior_overrides: Default::default(), + move_components: false, + is_recursive: false, + default_clone_fn: ComponentCloneBehavior::global_default_fn(), + clone_queue: Default::default(), + } + } +} - #[cfg(feature = "bevy_reflect")] - let app_registry = world.get_resource::(); - #[cfg(not(feature = "bevy_reflect"))] - let app_registry = Option::<()>::None; +/// An expandable scratch space for defining a dynamic bundle. +struct BundleScratch<'a> { + component_ids: Vec, + component_ptrs: Vec>, +} - ( - app_registry, - source_entity, - world.components(), - world.into_deferred(), - ) - }; - let archetype = source_entity.archetype(); +impl<'a> BundleScratch<'a> { + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { + component_ids: Vec::with_capacity(capacity), + component_ptrs: Vec::with_capacity(capacity), + } + } - 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()); + /// Pushes the `ptr` component onto this storage with the given `id` [`ComponentId`]. + /// + /// # Safety + /// The `id` [`ComponentId`] must match the component `ptr` for whatever [`World`] this scratch will + /// be written to. `ptr` must contain valid uniquely-owned data that matches the type of component referenced + /// in `id`. + pub(crate) unsafe fn push_ptr(&mut self, id: ComponentId, ptr: PtrMut<'a>) { + self.component_ids.push(id); + self.component_ptrs.push(ptr); + } - for component in archetype.components() { - if !self.is_cloning_allowed(&component) { - continue; - } + /// Pushes the `C` component onto this storage with the given `id` [`ComponentId`], using the given `bump` allocator. + /// + /// # Safety + /// The `id` [`ComponentId`] must match the component `C` for whatever [`World`] this scratch will + /// be written to. + pub(crate) unsafe fn push( + &mut self, + bump: &'a Bump, + id: ComponentId, + component: C, + ) { + let component_ref = bump.alloc(component); + self.component_ids.push(id); + self.component_ptrs.push(PtrMut::from(component_ref)); + } - 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), - }; + /// Writes the scratch components to the given entity in the given world. + /// + /// # Safety + /// All [`ComponentId`] values in this instance must come from `world`. + pub(crate) unsafe fn write(self, world: &mut World, entity: Entity) { + // 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(entity).insert_by_ids( + &self.component_ids, + self.component_ptrs.into_iter().map(|ptr| ptr.promote()), + ); + } + } +} - // 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() }; +impl EntityCloner { + /// Returns a new [`EntityClonerBuilder`] using the given `world`. + pub fn build(world: &mut World) -> EntityClonerBuilder { + EntityClonerBuilder { + world, + attach_required_components: false, + entity_cloner: EntityCloner::default(), + } + } + /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. + fn clone_entity_internal( + &mut self, + world: &mut World, + source: Entity, + mapper: &mut dyn EntityMapper, + ) { + let target = mapper.get_mapped(source); + // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. + let bump = Bump::new(); + let mut bundle_scratch: BundleScratch; + { // 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, - ) + // - `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 world = world.as_unsafe_world_cell(); + let source_entity = world.get_entity(source).expect("Source entity must exist"); + + #[cfg(feature = "bevy_reflect")] + // SAFETY: we have unique access to `world`, nothing else accesses the registry at this moment, and we clone + // the registry, which prevents future conflicts. + let app_registry = unsafe { + world + .get_resource::() + .cloned() }; + #[cfg(not(feature = "bevy_reflect"))] + let app_registry = Option::<()>::None; - (handler)(&mut deferred_world, &mut ctx); + let archetype = source_entity.archetype(); + bundle_scratch = BundleScratch::with_capacity(archetype.component_count()); + // SAFETY: no other references to command queue exist + let mut commands = unsafe { + Commands::new_raw_from_entities(world.get_raw_command_queue(), world.entities()) + }; + + for component in archetype.components() { + if !self.is_cloning_allowed(&component) { + continue; + } - if ctx.target_component_written { - component_ids.push(component); + let handler = match self.clone_behavior_overrides.get(&component) { + Some(clone_behavior) => clone_behavior.resolve(self.default_clone_fn), + None => world + .components() + .get_info(component) + .map(|info| info.clone_behavior().resolve(self.default_clone_fn)) + .unwrap_or(self.default_clone_fn), + }; + + // 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, + target, + source_component_ptr, + &bump, + &mut bundle_scratch, + world.components(), + self, + mapper, + app_registry.as_ref(), + ) + }; + + (handler)(&mut commands, &mut ctx); } } world.flush(); - if !world.entities.contains(self.target) { + if !world.entities.contains(target) { panic!("Target entity does not exist"); } - debug_assert_eq!(component_data_ptrs.len(), component_ids.len()); + if self.move_components { + world + .entity_mut(source) + .remove_by_ids(&bundle_scratch.component_ids); + } // 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()), - ); - } + unsafe { bundle_scratch.write(world, target) }; + } - if self.move_components { - world.entity_mut(self.source).remove_by_ids(&component_ids); + /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. + /// If this [`EntityCloner`] has [`EntityCloner::is_recursive`], then it will recursively spawn entities as defined + /// by [`RelationshipTarget`](crate::relationship::RelationshipTarget) components with + /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN) + #[track_caller] + pub fn clone_entity(&mut self, world: &mut World, source: Entity, target: Entity) { + let mut map = EntityHashMap::::new(); + map.set_mapped(source, target); + self.clone_entity_mapped(world, source, &mut map); + } + + /// Clones and inserts components from the `source` entity into a newly spawned entity using the stored configuration. + /// If this [`EntityCloner`] has [`EntityCloner::is_recursive`], then it will recursively spawn entities as defined + /// by [`RelationshipTarget`](crate::relationship::RelationshipTarget) components with + /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN) + #[track_caller] + pub fn spawn_clone(&mut self, world: &mut World, source: Entity) { + let target = world.spawn_empty().id(); + self.clone_entity(world, source, target); + } + + /// Clones the entity into whatever entity `mapper` chooses for it. + #[track_caller] + pub fn clone_entity_mapped( + &mut self, + world: &mut World, + source: Entity, + mapper: &mut dyn EntityMapper, + ) { + self.clone_entity_internal(world, source, mapper); + loop { + let queued = self.clone_queue.borrow_mut().pop_front(); + if let Some(queued) = queued { + let target = world.entities.reserve_entity(); + mapper.set_mapped(queued, target); + self.clone_entity_internal(world, queued, mapper); + } else { + break; + } } } @@ -373,17 +512,6 @@ impl EntityCloner { (self.filter_allows_components && self.filter.contains(component)) || (!self.filter_allows_components && !self.filter.contains(component)) } - - /// Reuse existing [`EntityCloner`] configuration with new source and target. - 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 - } - } } /// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them. @@ -391,7 +519,7 @@ impl EntityCloner { /// ///``` /// use bevy_ecs::prelude::*; -/// use bevy_ecs::entity::EntityCloneBuilder; +/// use bevy_ecs::entity::EntityCloner; /// /// #[derive(Component, Clone, PartialEq, Eq)] /// struct A { @@ -405,7 +533,7 @@ impl EntityCloner { /// let entity = world.spawn(component.clone()).id(); /// let entity_clone = world.spawn_empty().id(); /// -/// EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone); +/// EntityCloner::build(&mut world).clone_entity(entity, entity_clone); /// /// assert!(world.get::(entity_clone).is_some_and(|c| *c == component)); ///``` @@ -416,77 +544,50 @@ 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 ([`ComponentCloneHandler::clone_handler`]) in this case it should be set manually using one +/// the component will be cloned using the [default cloning strategy](crate::component::ComponentCloneBehavior::global_default_fn). +/// To use `Clone`-based handler ([`ComponentCloneBehavior::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): +/// Here's an example of how to do it using [`clone_behavior`](Component::clone_behavior): /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::component::{StorageType, component_clone_via_clone, ComponentCloneHandler, Mutable}; +/// # use bevy_ecs::component::{StorageType, ComponentCloneBehavior, Mutable}; /// #[derive(Clone)] /// struct SomeComponent; /// /// impl Component for SomeComponent { /// const STORAGE_TYPE: StorageType = StorageType::Table; /// type Mutability = Mutable; -/// fn get_component_clone_handler() -> ComponentCloneHandler { -/// ComponentCloneHandler::clone_handler::() +/// fn clone_behavior() -> ComponentCloneBehavior { +/// ComponentCloneBehavior::clone::() /// } /// } /// ``` /// /// # Handlers -/// `EntityCloneBuilder` clones entities by cloning components using [`handlers`](ComponentCloneHandler), and there are multiple layers +/// `EntityClonerBuilder` clones entities by cloning components using [`ComponentCloneBehavior`], 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. +/// 1. local overrides using [`EntityClonerBuilder::override_clone_behavior`] +/// 2. component-defined handler using [`Component::clone_behavior`] +/// 3. default handler override using [`EntityClonerBuilder::with_default_clone_fn`]. +/// 4. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not. #[derive(Debug)] -pub struct EntityCloneBuilder<'w> { +pub struct EntityClonerBuilder<'w> { world: &'w mut World, - filter_allows_components: bool, - filter: HashSet, - clone_handlers_overrides: HashMap, + entity_cloner: EntityCloner, attach_required_components: bool, - move_components: bool, } -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(), - attach_required_components: true, - move_components: false, - } +impl<'w> EntityClonerBuilder<'w> { + /// Internally consumes the builder to construct an [`EntityCloner`], calls [`EntityCloner::clone_entity`] on the + /// builder's [`World`], and returns the resulting [`EntityCloner`]. + pub fn clone_entity(&mut self, source: Entity, target: Entity) -> &mut Self { + self.entity_cloner.clone_entity(self.world, source, target); + self } - /// Finishes configuring the builder and clones `source` entity to `target`. - pub fn clone_entity(self, source: Entity, target: Entity) { - let EntityCloneBuilder { - world, - filter_allows_components, - filter, - clone_handlers_overrides, - move_components, - .. - } = self; - - EntityCloner { - source, - target, - filter_allows_components, - filter: Arc::new(filter), - clone_handlers_overrides: Arc::new(clone_handlers_overrides), - move_components, - } - .clone_entity(world); + pub fn finish(self) -> EntityCloner { + self.entity_cloner } /// By default, any components allowed/denied through the filter will automatically @@ -496,7 +597,7 @@ impl<'w> EntityCloneBuilder<'w> { /// will not involve required components. pub fn without_required_components( &mut self, - builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + builder: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> &mut Self { self.attach_required_components = false; builder(self); @@ -504,15 +605,21 @@ impl<'w> EntityCloneBuilder<'w> { self } + /// Sets the default clone function to use. + pub fn with_default_clone_fn(&mut self, clone_fn: ComponentCloneFn) -> &mut Self { + self.entity_cloner.default_clone_fn = clone_fn; + self + } + /// Sets whether the cloner should remove any components that were cloned, /// effectively moving them from the source entity to the target. /// /// This is disabled by default. /// /// The setting only applies to components that are allowed through the filter - /// at the time [`EntityCloneBuilder::clone_entity`] is called. + /// at the time [`EntityClonerBuilder::clone_entity`] is called. pub fn move_components(&mut self, enable: bool) -> &mut Self { - self.move_components = enable; + self.entity_cloner.move_components = enable; self } @@ -555,8 +662,8 @@ impl<'w> EntityCloneBuilder<'w> { /// 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.entity_cloner.filter_allows_components = false; + self.entity_cloner.filter.clear(); self } @@ -590,70 +697,71 @@ impl<'w> EntityCloneBuilder<'w> { /// Sets the filter to deny all components. pub fn deny_all(&mut self) -> &mut Self { - self.filter_allows_components = true; - self.filter.clear(); + self.entity_cloner.filter_allows_components = true; + self.entity_cloner.filter.clear(); self } - /// 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`](crate::component::ComponentCloneHandlers) + /// Overrides the [`ComponentCloneBehavior`] for a component in this builder. + /// This handler will be used to clone the component instead of the global one defined by the [`EntityCloner`]. /// - /// See [Handlers section of `EntityCloneBuilder`](EntityCloneBuilder#handlers) to understand how this affects handler priority. - pub fn override_component_clone_handler( + /// See [Handlers section of `EntityClonerBuilder`](EntityClonerBuilder#handlers) to understand how this affects handler priority. + pub fn override_clone_behavior( &mut self, - handler: ComponentCloneHandler, + clone_behavior: ComponentCloneBehavior, ) -> &mut Self { if let Some(id) = self.world.components().component_id::() { - self.clone_handlers_overrides.insert(id, handler); + self.entity_cloner + .clone_behavior_overrides + .insert(id, clone_behavior); } 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); - } + /// Overrides the [`ComponentCloneBehavior`] for a component with the given `component_id` in this builder. + /// This handler will be used to clone the component instead of the global one defined by the [`EntityCloner`]. + /// + /// See [Handlers section of `EntityClonerBuilder`](EntityClonerBuilder#handlers) to understand how this affects handler priority. + pub fn override_clone_behavior_with_id( + &mut self, + component_id: ComponentId, + clone_behavior: ComponentCloneBehavior, + ) -> &mut Self { + self.entity_cloner + .clone_behavior_overrides + .insert(component_id, clone_behavior); self } - /// Sets the option to recursively clone entities. - /// When set to true all children will be cloned with the same options as the parent. - pub fn recursive(&mut self, recursive: bool) -> &mut Self { - if recursive { - self.override_component_clone_handler::( - ComponentCloneHandler::custom_handler(component_clone_children), - ) - } else { - self.remove_component_clone_handler_override::() + /// Removes a previously set override of [`ComponentCloneBehavior`] for a component in this builder. + pub fn remove_clone_behavior_override(&mut self) -> &mut Self { + if let Some(id) = self.world.components().component_id::() { + self.entity_cloner.clone_behavior_overrides.remove(&id); } + self } - /// Sets the option to add cloned entity as a child to the parent entity. - pub fn as_child(&mut self, as_child: bool) -> &mut Self { - if as_child { - self.override_component_clone_handler::(ComponentCloneHandler::custom_handler( - component_clone_parent, - )) - } else { - self.remove_component_clone_handler_override::() - } + /// If `true`, makes the built [`EntityCloner`] recursively clone entities, as defined by + /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN). + pub fn recursive(&mut self, is_recursive: bool) -> &mut Self { + self.entity_cloner.is_recursive = is_recursive; + self } /// Helper function that allows a component through the filter. fn filter_allow(&mut self, id: ComponentId) { - if self.filter_allows_components { - self.filter.insert(id); + if self.entity_cloner.filter_allows_components { + self.entity_cloner.filter.insert(id); } else { - self.filter.remove(&id); + self.entity_cloner.filter.remove(&id); } if self.attach_required_components { if let Some(info) = self.world.components().get_info(id) { for required_id in info.required_components().iter_ids() { - if self.filter_allows_components { - self.filter.insert(required_id); + if self.entity_cloner.filter_allows_components { + self.entity_cloner.filter.insert(required_id); } else { - self.filter.remove(&required_id); + self.entity_cloner.filter.remove(&required_id); } } } @@ -662,18 +770,18 @@ impl<'w> EntityCloneBuilder<'w> { /// Helper function that disallows a component through the filter. fn filter_deny(&mut self, id: ComponentId) { - if self.filter_allows_components { - self.filter.remove(&id); + if self.entity_cloner.filter_allows_components { + self.entity_cloner.filter.remove(&id); } else { - self.filter.insert(id); + self.entity_cloner.filter.insert(id); } if self.attach_required_components { if let Some(info) = self.world.components().get_info(id) { for required_id in info.required_components().iter_ids() { - if self.filter_allows_components { - self.filter.remove(&required_id); + if self.entity_cloner.filter_allows_components { + self.entity_cloner.filter.remove(&required_id); } else { - self.filter.insert(required_id); + self.entity_cloner.filter.insert(required_id); } } } @@ -681,55 +789,30 @@ impl<'w> EntityCloneBuilder<'w> { } } -/// Clone handler for the [`Children`] component. Allows to clone the entity recursively. -fn component_clone_children(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { - let children = ctx - .read_source_component::() - .expect("Source entity must have Children component") - .iter(); - let parent = ctx.target(); - for child in children { - let child_clone = world.commands().spawn_empty().id(); - let mut clone_entity = ctx - .entity_cloner() - .with_source_and_target(*child, child_clone); - world.commands().queue(move |world: &mut World| { - clone_entity.clone_entity(world); - world.entity_mut(child_clone).insert(ChildOf(parent)); - }); - } -} - -/// Clone handler for the [`ChildOf`] component. Allows to add clone as a child to the parent entity. -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 a ChildOf component"); - world - .commands() - .entity(ctx.target()) - .insert(ChildOf(parent)); -} - #[cfg(test)] mod tests { use super::ComponentCloneCtx; use crate::{ self as bevy_ecs, - component::{Component, ComponentCloneHandler, ComponentDescriptor, StorageType}, - entity::EntityCloneBuilder, - world::{DeferredWorld, World}, + component::{Component, ComponentCloneBehavior, ComponentDescriptor, StorageType}, + entity::EntityCloner, + hierarchy::{ChildOf, Children}, + system::Commands, + world::World, }; use alloc::vec::Vec; use bevy_ecs_macros::require; use bevy_ptr::OwningPtr; - use core::alloc::Layout; + use core::{alloc::Layout, ops::Deref}; #[cfg(feature = "bevy_reflect")] mod reflect { use super::*; - use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}; + use crate::{ + entity::EntityCloner, + reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}, + system::Commands, + }; use alloc::vec; use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr}; @@ -747,17 +830,14 @@ mod tests { 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); + EntityCloner::build(&mut world) + .override_clone_behavior::(ComponentCloneBehavior::reflect()) + .clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } @@ -798,11 +878,6 @@ mod tests { 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], @@ -819,7 +894,11 @@ mod tests { let e = world.spawn((component_a, component_b, component_c)).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .override_clone_behavior_with_id(a_id, ComponentCloneBehavior::reflect()) + .override_clone_behavior_with_id(b_id, ComponentCloneBehavior::reflect()) + .override_clone_behavior_with_id(c_id, ComponentCloneBehavior::reflect()) + .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())); @@ -834,7 +913,7 @@ mod tests { #[derive(Component, Reflect)] struct B; - fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + fn test_handler(_commands: &mut Commands, ctx: &mut ComponentCloneCtx) { assert!(ctx.read_source_component_reflect().is_none()); } @@ -850,15 +929,12 @@ mod tests { .insert(>::from_type()); } - 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)); - let e = world.spawn(A).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .override_clone_behavior::(ComponentCloneBehavior::Custom(test_handler)) + .clone_entity(e, e_clone); } #[test] @@ -885,7 +961,7 @@ mod tests { let e = world.spawn(component.clone()).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + EntityCloner::build(&mut world).clone_entity(e, e_clone); assert!(world .get::(e_clone) @@ -905,16 +981,14 @@ mod tests { 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); + EntityCloner::build(&mut world) + .override_clone_behavior::(ComponentCloneBehavior::reflect()) + .override_clone_behavior::(ComponentCloneBehavior::reflect()) + .clone_entity(e, e_clone); assert_eq!(world.get::(e_clone), None); assert_eq!(world.get::(e_clone), None); @@ -925,7 +999,7 @@ mod tests { let e = world.spawn((A, B)).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + EntityCloner::build(&mut world).clone_entity(e, e_clone); assert_eq!(world.get::(e_clone), None); assert_eq!(world.get::(e_clone), None); } @@ -945,7 +1019,7 @@ mod tests { let e = world.spawn(component.clone()).id(); let e_clone = world.spawn_empty().id(); - EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + EntityCloner::build(&mut world).clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } @@ -967,10 +1041,10 @@ mod tests { let e = world.spawn((component.clone(), B)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::new(&mut world); - builder.deny_all(); - builder.allow::(); - builder.clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .deny_all() + .allow::() + .clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -996,9 +1070,9 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::new(&mut world); - builder.deny::(); - builder.clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .deny::() + .clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -1025,13 +1099,13 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::new(&mut world); - builder.deny_all(); - builder.allow::(); - builder.allow::(); - builder.allow::(); - builder.deny::(); - builder.clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .deny_all() + .allow::() + .allow::() + .allow::() + .deny::() + .clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -1058,11 +1132,11 @@ mod tests { let e = world.spawn((component.clone(), B, C)).id(); let e_clone = world.spawn_empty().id(); - 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); + EntityCloner::build(&mut world) + .deny_all() + .allow::<(A, B, C)>() + .deny::<(B, C)>() + .clone_entity(e, e_clone); assert!(world.get::(e_clone).is_some_and(|c| *c == component)); assert!(world.get::(e_clone).is_none()); @@ -1087,12 +1161,12 @@ mod tests { let e = world.spawn(A).id(); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::new(&mut world); - builder.deny_all(); - builder.without_required_components(|builder| { - builder.allow::(); - }); - builder.clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .deny_all() + .without_required_components(|builder| { + builder.allow::(); + }) + .clone_entity(e, e_clone); assert_eq!(world.entity(e_clone).get::(), None); assert_eq!(world.entity(e_clone).get::(), Some(&B)); @@ -1102,7 +1176,7 @@ mod tests { #[test] fn clone_entity_with_dynamic_components() { const COMPONENT_SIZE: usize = 10; - fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + fn test_handler(_commands: &mut Commands, 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| { @@ -1129,16 +1203,11 @@ mod tests { layout, None, true, + ComponentCloneBehavior::Custom(test_handler), ) }; 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]; @@ -1151,8 +1220,7 @@ mod tests { let entity = entity.id(); let entity_clone = world.spawn_empty().id(); - let builder = EntityCloneBuilder::new(&mut world); - builder.clone_entity(entity, entity_clone); + EntityCloner::build(&mut world).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(); @@ -1164,4 +1232,38 @@ mod tests { ); } } + + #[test] + fn recursive_clone() { + let mut world = World::new(); + let root = world.spawn_empty().id(); + let child1 = world.spawn(ChildOf(root)).id(); + let grandchild = world.spawn(ChildOf(child1)).id(); + let child2 = world.spawn(ChildOf(root)).id(); + + let clone_root = world.spawn_empty().id(); + EntityCloner::build(&mut world) + .recursive(true) + .clone_entity(root, clone_root); + + let root_children = world + .entity(clone_root) + .get::() + .unwrap() + .iter() + .cloned() + .collect::>(); + + assert!(root_children.iter().all(|e| *e != child1 && *e != child2)); + assert_eq!(root_children.len(), 2); + let child1_children = world.entity(root_children[0]).get::().unwrap(); + assert_eq!(child1_children.len(), 1); + assert_ne!(child1_children[0], grandchild); + assert!(world.entity(root_children[1]).get::().is_none()); + + assert_eq!( + world.entity(root).get::().unwrap().deref(), + &[child1, child2] + ); + } } diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 05c11781de788..6cf00981838e5 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -39,8 +39,8 @@ use super::{hash_map::EntityHashMap, VisitEntitiesMut}; /// /// impl MapEntities for Spring { /// fn map_entities(&mut self, entity_mapper: &mut M) { -/// self.a = entity_mapper.map_entity(self.a); -/// self.b = entity_mapper.map_entity(self.b); +/// self.a = entity_mapper.get_mapped(self.a); +/// self.b = entity_mapper.get_mapped(self.b); /// } /// } /// ``` @@ -55,7 +55,7 @@ pub trait MapEntities { impl MapEntities for T { fn map_entities(&mut self, entity_mapper: &mut M) { self.visit_entities_mut(|entity| { - *entity = entity_mapper.map_entity(*entity); + *entity = entity_mapper.get_mapped(*entity); }); } } @@ -80,26 +80,60 @@ impl MapEntities for T { /// // Example implementation of EntityMapper where we map an entity to another entity if it exists /// // in the underlying `EntityHashMap`, otherwise we just return the original entity. /// impl EntityMapper for SimpleEntityMapper { -/// fn map_entity(&mut self, entity: Entity) -> Entity { +/// fn get_mapped(&mut self, entity: Entity) -> Entity { /// self.map.get(&entity).copied().unwrap_or(entity) /// } +/// +/// fn set_mapped(&mut self, source: Entity, target: Entity) { +/// self.map.insert(source, target); +/// } /// } /// ``` pub trait EntityMapper { - /// Map an entity to another entity - fn map_entity(&mut self, entity: Entity) -> Entity; + /// Returns the "target" entity that maps to the given `source`. + fn get_mapped(&mut self, source: Entity) -> Entity; + /// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result + /// of [`EntityMapper::get_mapped`]. + fn set_mapped(&mut self, source: Entity, target: Entity); +} + +impl EntityMapper for () { + #[inline] + fn get_mapped(&mut self, source: Entity) -> Entity { + source + } + + #[inline] + fn set_mapped(&mut self, _source: Entity, _target: Entity) {} +} + +impl EntityMapper for (Entity, Entity) { + #[inline] + fn get_mapped(&mut self, source: Entity) -> Entity { + if source == self.0 { + self.1 + } else { + source + } + } + + fn set_mapped(&mut self, _source: Entity, _target: Entity) {} } impl EntityMapper for &mut dyn EntityMapper { - fn map_entity(&mut self, entity: Entity) -> Entity { - (*self).map_entity(entity) + fn get_mapped(&mut self, source: Entity) -> Entity { + (*self).get_mapped(source) + } + + fn set_mapped(&mut self, source: Entity, target: Entity) { + (*self).set_mapped(source, target); } } impl EntityMapper for SceneEntityMapper<'_> { /// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent. - fn map_entity(&mut self, entity: Entity) -> Entity { - if let Some(&mapped) = self.map.get(&entity) { + fn get_mapped(&mut self, source: Entity) -> Entity { + if let Some(&mapped) = self.map.get(&source) { return mapped; } @@ -112,10 +146,25 @@ impl EntityMapper for SceneEntityMapper<'_> { // Prevent generations counter from being a greater value than HIGH_MASK. self.generations = (self.generations + 1) & HIGH_MASK; - self.map.insert(entity, new); + self.map.insert(source, new); new } + + fn set_mapped(&mut self, source: Entity, target: Entity) { + self.map.insert(source, target); + } +} + +impl EntityMapper for EntityHashMap { + /// Returns the corresponding mapped entity or returns `entity` if there is no mapped entity + fn get_mapped(&mut self, source: Entity) -> Entity { + self.get(&source).cloned().unwrap_or(source) + } + + fn set_mapped(&mut self, source: Entity, target: Entity) { + self.insert(source, target); + } } /// A wrapper for [`EntityHashMap`], augmenting it with the ability to allocate new [`Entity`] references in a destination @@ -208,15 +257,15 @@ mod tests { let mut mapper = SceneEntityMapper::new(&mut map, &mut world); let mapped_ent = Entity::from_raw(FIRST_IDX); - let dead_ref = mapper.map_entity(mapped_ent); + let dead_ref = mapper.get_mapped(mapped_ent); assert_eq!( dead_ref, - mapper.map_entity(mapped_ent), + mapper.get_mapped(mapped_ent), "should persist the allocated mapping from the previous line" ); assert_eq!( - mapper.map_entity(Entity::from_raw(SECOND_IDX)).index(), + mapper.get_mapped(Entity::from_raw(SECOND_IDX)).index(), dead_ref.index(), "should re-use the same index for further dead refs" ); @@ -234,7 +283,7 @@ mod tests { let mut world = World::new(); let dead_ref = SceneEntityMapper::world_scope(&mut map, &mut world, |_, mapper| { - mapper.map_entity(Entity::from_raw(0)) + mapper.get_mapped(Entity::from_raw(0)) }); // Next allocated entity should be a further generation on the same index @@ -253,7 +302,7 @@ mod tests { // Create and exercise a SceneEntityMapper - should not panic because it flushes // `Entities` first. SceneEntityMapper::world_scope(&mut Default::default(), &mut world, |_, m| { - m.map_entity(Entity::PLACEHOLDER); + m.get_mapped(Entity::PLACEHOLDER); }); // The SceneEntityMapper should leave `Entities` in a flushed state. diff --git a/crates/bevy_ecs/src/hierarchy.rs b/crates/bevy_ecs/src/hierarchy.rs index d4eb2475b95c9..b4ed612a1d70e 100644 --- a/crates/bevy_ecs/src/hierarchy.rs +++ b/crates/bevy_ecs/src/hierarchy.rs @@ -7,21 +7,17 @@ //! [`RelationshipTarget`]: crate::relationship::RelationshipTarget #[cfg(feature = "bevy_reflect")] -use crate::reflect::{ - ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities, - ReflectVisitEntitiesMut, -}; +use crate::reflect::{ReflectComponent, ReflectFromWorld}; use crate::{ self as bevy_ecs, bundle::Bundle, component::{Component, HookContext}, - entity::{Entity, VisitEntities}, + entity::Entity, relationship::{RelatedSpawner, RelatedSpawnerCommands}, system::EntityCommands, world::{DeferredWorld, EntityWorldMut, FromWorld, World}, }; use alloc::{format, string::String, vec::Vec}; -use bevy_ecs_macros::VisitEntitiesMut; use core::ops::Deref; use core::slice; use disqualified::ShortName; @@ -90,19 +86,11 @@ use log::warn; /// assert_eq!(&**world.entity(root).get::().unwrap(), &[child1, child2]); /// assert_eq!(&**world.entity(child1).get::().unwrap(), &[grandchild]); /// ``` -#[derive(Component, Clone, VisitEntities, VisitEntitiesMut, PartialEq, Eq, Debug)] +#[derive(Component, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] #[cfg_attr( feature = "bevy_reflect", - reflect( - Component, - MapEntities, - VisitEntities, - VisitEntitiesMut, - PartialEq, - Debug, - FromWorld - ) + reflect(Component, PartialEq, Debug, FromWorld) )] #[relationship(relationship_target = Children)] pub struct ChildOf(pub Entity); @@ -139,13 +127,10 @@ impl FromWorld for ChildOf { /// /// Together, these components form the "canonical parent-child hierarchy". See the [`ChildOf`] component for all full /// description of this relationship and instructions on how to use it. -#[derive(Component, Default, VisitEntitiesMut, Debug, PartialEq, Eq)] -#[relationship_target(relationship = ChildOf, despawn_descendants)] +#[derive(Component, Default, Debug, PartialEq, Eq)] +#[relationship_target(relationship = ChildOf, linked_spawn)] #[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] -#[cfg_attr( - feature = "bevy_reflect", - reflect(Component, MapEntities, VisitEntities, VisitEntitiesMut, FromWorld) -)] +#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld))] pub struct Children(Vec); impl<'a> IntoIterator for &'a Children { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ae325aa0857e3..8f86a6fc4eb2d 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -79,7 +79,7 @@ pub mod prelude { event::{Event, EventMutator, EventReader, EventWriter, Events}, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, name::{Name, NameOrEntity}, - observer::{CloneEntityWithObserversExt, Observer, Trigger}, + observer::{Observer, Trigger}, query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without}, removal_detection::RemovedComponents, resource::Resource, @@ -2675,6 +2675,102 @@ mod tests { World::new().register_component::(); } + #[test] + fn visit_struct_entities() { + #[derive(Component)] + #[expect( + unused, + reason = "extra fields are used to ensure the derive works properly" + )] + struct Foo(usize, #[entities] Entity); + + #[derive(Component)] + #[expect( + unused, + reason = "extra fields are used to ensure the derive works properly" + )] + struct Bar { + #[entities] + a: Entity, + b: usize, + #[entities] + c: Vec, + } + + let mut world = World::new(); + let e1 = world.spawn_empty().id(); + let e2 = world.spawn_empty().id(); + let e3 = world.spawn_empty().id(); + + let mut foo = Foo(1, e1); + let mut entities = Vec::new(); + Component::visit_entities(&foo, |e| entities.push(e)); + assert_eq!(&entities, &[e1]); + + let mut entities = Vec::new(); + Component::visit_entities_mut(&mut foo, |e| entities.push(*e)); + assert_eq!(&entities, &[e1]); + + let mut bar = Bar { + a: e1, + b: 1, + c: vec![e2, e3], + }; + let mut entities = Vec::new(); + Component::visit_entities(&bar, |e| entities.push(e)); + assert_eq!(&entities, &[e1, e2, e3]); + + let mut entities = Vec::new(); + Component::visit_entities_mut(&mut bar, |e| entities.push(*e)); + assert_eq!(&entities, &[e1, e2, e3]); + } + + #[test] + fn visit_enum_entities() { + #[derive(Component)] + #[expect( + unused, + reason = "extra fields are used to ensure the derive works properly" + )] + enum Foo { + Bar(usize, #[entities] Entity), + Baz { + #[entities] + a: Entity, + b: usize, + #[entities] + c: Vec, + }, + } + + let mut world = World::new(); + let e1 = world.spawn_empty().id(); + let e2 = world.spawn_empty().id(); + let e3 = world.spawn_empty().id(); + + let mut foo = Foo::Bar(1, e1); + let mut entities = Vec::new(); + Component::visit_entities(&foo, |e| entities.push(e)); + assert_eq!(&entities, &[e1]); + + let mut entities = Vec::new(); + Component::visit_entities_mut(&mut foo, |e| entities.push(*e)); + assert_eq!(&entities, &[e1]); + + let mut foo = Foo::Baz { + a: e1, + b: 1, + c: vec![e2, e3], + }; + let mut entities = Vec::new(); + Component::visit_entities(&foo, |e| entities.push(e)); + assert_eq!(&entities, &[e1, e2, e3]); + + let mut entities = Vec::new(); + Component::visit_entities_mut(&mut foo, |e| entities.push(*e)); + assert_eq!(&entities, &[e1, e2, e3]); + } + #[expect( dead_code, reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed." diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 1ad30890e494a..6af3f48e611b1 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -1,10 +1,11 @@ use crate::{ component::{ - Component, ComponentCloneHandler, ComponentHooks, HookContext, Mutable, StorageType, + Component, ComponentCloneBehavior, ComponentHooks, HookContext, Mutable, StorageType, }, - entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, + entity::{ComponentCloneCtx, Entity, EntityClonerBuilder}, observer::ObserverState, - world::{DeferredWorld, World}, + system::Commands, + world::World, }; use alloc::vec::Vec; @@ -45,34 +46,29 @@ impl Component for ObservedBy { }); } - fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::ignore() + fn clone_behavior() -> ComponentCloneBehavior { + ComponentCloneBehavior::Ignore } } -/// Trait that holds functions for configuring interaction with observers during entity cloning. -pub trait CloneEntityWithObserversExt { +impl EntityClonerBuilder<'_> { /// Sets the option to automatically add cloned entities to the observers targeting source entity. - fn add_observers(&mut self, add_observers: bool) -> &mut Self; -} - -impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { - fn add_observers(&mut self, add_observers: bool) -> &mut Self { + pub fn add_observers(&mut self, add_observers: bool) -> &mut Self { if add_observers { - self.override_component_clone_handler::( - ComponentCloneHandler::custom_handler(component_clone_observed_by), - ) + self.override_clone_behavior::(ComponentCloneBehavior::Custom( + component_clone_observed_by, + )) } else { - self.remove_component_clone_handler_override::() + self.remove_clone_behavior_override::() } } } -fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { +fn component_clone_observed_by(commands: &mut Commands, ctx: &mut ComponentCloneCtx) { let target = ctx.target(); let source = ctx.source(); - world.commands().queue(move |world: &mut World| { + commands.queue(move |world: &mut World| { let observed_by = world .get::(source) .map(|observed_by| observed_by.0.clone()) @@ -114,13 +110,8 @@ fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentClo #[cfg(test)] mod tests { use crate::{ - self as bevy_ecs, - entity::EntityCloneBuilder, - event::Event, - observer::{CloneEntityWithObserversExt, Trigger}, - resource::Resource, - system::ResMut, - world::World, + self as bevy_ecs, entity::EntityCloner, event::Event, observer::Trigger, + resource::Resource, system::ResMut, world::World, }; #[derive(Resource, Default)] @@ -143,9 +134,9 @@ mod tests { world.trigger_targets(E, e); let e_clone = world.spawn_empty().id(); - let mut builder = EntityCloneBuilder::new(&mut world); - builder.add_observers(true); - builder.clone_entity(e, e_clone); + EntityCloner::build(&mut world) + .add_observers(true) + .clone_entity(e, e_clone); world.trigger_targets(E, [e, e_clone]); diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 1fd58610fb00a..82eb260cb4936 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -3,7 +3,7 @@ mod entity_observer; mod runner; -pub use entity_observer::{CloneEntityWithObserversExt, ObservedBy}; +pub use entity_observer::ObservedBy; pub use runner::*; use crate::{ diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index 248ca1a7048c1..baa6bc7d08bfd 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -8,6 +8,7 @@ use alloc::boxed::Box; use core::any::{Any, TypeId}; use crate::{ + entity::EntityMapper, prelude::Bundle, world::{EntityMut, EntityWorldMut}, }; @@ -33,8 +34,9 @@ pub struct ReflectBundleFns { pub insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry), /// Function pointer implementing [`ReflectBundle::apply`]. pub apply: fn(EntityMut, &dyn PartialReflect, &TypeRegistry), - /// Function pointer implementing [`ReflectBundle::apply_or_insert`]. - pub apply_or_insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry), + /// Function pointer implementing [`ReflectBundle::apply_or_insert_mapped`]. + pub apply_or_insert_mapped: + fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry, &mut dyn EntityMapper), /// Function pointer implementing [`ReflectBundle::remove`]. pub remove: fn(&mut EntityWorldMut), /// Function pointer implementing [`ReflectBundle::take`]. @@ -78,13 +80,14 @@ impl ReflectBundle { } /// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value or insert a new one if it does not exist. - pub fn apply_or_insert( + pub fn apply_or_insert_mapped( &self, entity: &mut EntityWorldMut, bundle: &dyn PartialReflect, registry: &TypeRegistry, + mapper: &mut dyn EntityMapper, ) { - (self.0.apply_or_insert)(entity, bundle, registry); + (self.0.apply_or_insert_mapped)(entity, bundle, registry, mapper); } /// Removes this [`Bundle`] type from the entity. Does nothing if it doesn't exist. @@ -166,19 +169,24 @@ impl FromType for ReflectBundle { } } }, - apply_or_insert: |entity, reflected_bundle, registry| { + apply_or_insert_mapped: |entity, reflected_bundle, registry, mapper| { if let Some(reflect_component) = registry.get_type_data::(TypeId::of::()) { - reflect_component.apply_or_insert(entity, reflected_bundle, registry); + reflect_component.apply_or_insert_mapped( + entity, + reflected_bundle, + registry, + mapper, + ); } else { match reflected_bundle.reflect_ref() { - ReflectRef::Struct(bundle) => bundle - .iter_fields() - .for_each(|field| apply_or_insert_field(entity, field, registry)), - ReflectRef::Tuple(bundle) => bundle - .iter_fields() - .for_each(|field| apply_or_insert_field(entity, field, registry)), + ReflectRef::Struct(bundle) => bundle.iter_fields().for_each(|field| { + apply_or_insert_field_mapped(entity, field, registry, mapper); + }), + ReflectRef::Tuple(bundle) => bundle.iter_fields().for_each(|field| { + apply_or_insert_field_mapped(entity, field, registry, mapper); + }), _ => panic!( "expected bundle `{}` to be a named struct or tuple", // FIXME: once we have unique reflect, use `TypePath`. @@ -218,10 +226,11 @@ fn apply_field(entity: &mut EntityMut, field: &dyn PartialReflect, registry: &Ty } } -fn apply_or_insert_field( +fn apply_or_insert_field_mapped( entity: &mut EntityWorldMut, field: &dyn PartialReflect, registry: &TypeRegistry, + mapper: &mut dyn EntityMapper, ) { let Some(type_id) = field.try_as_reflect().map(Any::type_id) else { panic!( @@ -231,9 +240,9 @@ fn apply_or_insert_field( }; if let Some(reflect_component) = registry.get_type_data::(type_id) { - reflect_component.apply_or_insert(entity, field, registry); + reflect_component.apply_or_insert_mapped(entity, field, registry, mapper); } else if let Some(reflect_bundle) = registry.get_type_data::(type_id) { - reflect_bundle.apply_or_insert(entity, field, registry); + reflect_bundle.apply_or_insert_mapped(entity, field, registry, mapper); } else { let is_component = entity.world().components().get_id(type_id).is_some(); diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 6af8e34a31304..14a0dc0e9cdff 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -61,7 +61,7 @@ use super::from_reflect_with_fallback; use crate::{ change_detection::Mut, component::{ComponentId, ComponentMutability}, - entity::Entity, + entity::{Entity, EntityMapper}, prelude::Component, world::{ unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityWorldMut, FilteredEntityMut, @@ -104,8 +104,9 @@ pub struct ReflectComponentFns { pub insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry), /// Function pointer implementing [`ReflectComponent::apply()`]. pub apply: fn(EntityMut, &dyn PartialReflect), - /// Function pointer implementing [`ReflectComponent::apply_or_insert()`]. - pub apply_or_insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry), + /// Function pointer implementing [`ReflectComponent::apply_or_insert_mapped()`]. + pub apply_or_insert_mapped: + fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry, &mut dyn EntityMapper), /// Function pointer implementing [`ReflectComponent::remove()`]. pub remove: fn(&mut EntityWorldMut), /// Function pointer implementing [`ReflectComponent::contains()`]. @@ -114,6 +115,8 @@ pub struct ReflectComponentFns { pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>, /// Function pointer implementing [`ReflectComponent::reflect_mut()`]. pub reflect_mut: fn(FilteredEntityMut) -> Option>, + /// Function pointer implementing [`ReflectComponent::visit_entities_mut()`]. + pub visit_entities_mut: fn(&mut dyn Reflect, &mut dyn FnMut(&mut Entity)), /// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`]. /// /// # Safety @@ -163,13 +166,14 @@ impl ReflectComponent { /// # Panics /// /// Panics if [`Component`] is immutable. - pub fn apply_or_insert( + pub fn apply_or_insert_mapped( &self, entity: &mut EntityWorldMut, component: &dyn PartialReflect, registry: &TypeRegistry, + map: &mut dyn EntityMapper, ) { - (self.0.apply_or_insert)(entity, component, registry); + (self.0.apply_or_insert_mapped)(entity, component, registry, map); } /// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist. @@ -277,6 +281,15 @@ impl ReflectComponent { pub fn fn_pointers(&self) -> &ReflectComponentFns { &self.0 } + + /// Calls a dynamic version of [`Component::visit_entities_mut`]. + pub fn visit_entities_mut( + &self, + component: &mut dyn Reflect, + func: &mut dyn FnMut(&mut Entity), + ) { + (self.0.visit_entities_mut)(component, func); + } } impl FromType for ReflectComponent { @@ -300,21 +313,28 @@ impl FromType for ReflectComponent { let mut component = unsafe { entity.get_mut_assume_mutable::() }.unwrap(); component.apply(reflected_component); }, - apply_or_insert: |entity, reflected_component, registry| { + apply_or_insert_mapped: |entity, reflected_component, registry, mapper| { + // TODO: if we can externalize this impl to cut down on monomorphization that would be great + let map_fn = move |entity: &mut Entity| { + *entity = mapper.get_mapped(*entity); + }; if C::Mutability::MUTABLE { // SAFETY: guard ensures `C` is a mutable component if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::() } { component.apply(reflected_component.as_partial_reflect()); + C::visit_entities_mut(&mut component, map_fn); } else { - let component = entity.world_scope(|world| { + let mut component = entity.world_scope(|world| { from_reflect_with_fallback::(reflected_component, world, registry) }); + C::visit_entities_mut(&mut component, map_fn); entity.insert(component); } } else { - let component = entity.world_scope(|world| { + let mut component = entity.world_scope(|world| { from_reflect_with_fallback::(reflected_component, world, registry) }); + C::visit_entities_mut(&mut component, map_fn); entity.insert(component); } }, @@ -359,6 +379,10 @@ impl FromType for ReflectComponent { register_component: |world: &mut World| -> ComponentId { world.register_component::() }, + visit_entities_mut: |reflect: &mut dyn Reflect, func: &mut dyn FnMut(&mut Entity)| { + let component = reflect.downcast_mut::().unwrap(); + Component::visit_entities_mut(component, func); + }, }) } } diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index b04b4f2b58887..b131354c74520 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -12,11 +12,11 @@ pub use relationship_source_collection::*; use crate::{ component::{Component, HookContext, Mutable}, - entity::Entity, + entity::{ComponentCloneCtx, Entity}, system::{ command::HandleError, entity_command::{self, CommandWithEntity}, - error_handler, + error_handler, Commands, }, world::{DeferredWorld, EntityWorldMut}, }; @@ -47,7 +47,7 @@ use log::warn; /// pub struct Children(Vec); /// ``` /// -/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(despawn_descendants)]` attribute to +/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(linked_spawn)]` attribute to /// automatically despawn entities stored in an entity's [`RelationshipTarget`] when that entity is despawned: /// /// ``` @@ -58,7 +58,7 @@ use log::warn; /// pub struct ChildOf(pub Entity); /// /// #[derive(Component)] -/// #[relationship_target(relationship = ChildOf, despawn_descendants)] +/// #[relationship_target(relationship = ChildOf, linked_spawn)] /// pub struct Children(Vec); /// ``` pub trait Relationship: Component + Sized { @@ -142,6 +142,8 @@ pub type SourceIter<'w, R> = /// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type. /// See the [`Relationship`] documentation for more information. pub trait RelationshipTarget: Component + Sized { + /// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. + const LINKED_SPAWN: bool; /// The [`Relationship`] that populates this [`RelationshipTarget`] collection. type Relationship: Relationship; /// The collection type that stores the "source" entities for this [`RelationshipTarget`] component. @@ -253,6 +255,28 @@ pub trait RelationshipTarget: Component + Sized { } } +/// The "clone behavior" for [`RelationshipTarget`]. This actually creates an empty +/// [`RelationshipTarget`] instance with space reserved for the number of targets in the +/// original instance. The [`RelationshipTarget`] will then be populated with the proper components +/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities +/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that! +/// +/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured +/// to spawn recursively. +pub fn clone_relationship_target( + _commands: &mut Commands, + context: &mut ComponentCloneCtx, +) { + if let Some(component) = context.read_source_component::() { + if context.is_recursive() && T::LINKED_SPAWN { + for entity in component.iter() { + context.queue_entity_clone(entity); + } + } + context.write_target_component(T::with_capacity(component.len())); + } +} + #[cfg(test)] mod tests { use crate as bevy_ecs; diff --git a/crates/bevy_ecs/src/relationship/relationship_source_collection.rs b/crates/bevy_ecs/src/relationship/relationship_source_collection.rs index d9b26a720d44b..7894452e9f3e7 100644 --- a/crates/bevy_ecs/src/relationship/relationship_source_collection.rs +++ b/crates/bevy_ecs/src/relationship/relationship_source_collection.rs @@ -130,7 +130,7 @@ mod tests { struct Rel(Entity); #[derive(Component)] - #[relationship_target(relationship = Rel, despawn_descendants)] + #[relationship_target(relationship = Rel, linked_spawn)] struct RelTarget(Vec); let mut world = World::new(); @@ -151,7 +151,7 @@ mod tests { struct Rel(Entity); #[derive(Component)] - #[relationship_target(relationship = Rel, despawn_descendants)] + #[relationship_target(relationship = Rel, linked_spawn)] struct RelTarget(EntityHashSet); let mut world = World::new(); @@ -172,7 +172,7 @@ mod tests { struct Rel(Entity); #[derive(Component)] - #[relationship_target(relationship = Rel, despawn_descendants)] + #[relationship_target(relationship = Rel, linked_spawn)] struct RelTarget(SmallVec<[Entity; 4]>); let mut world = World::new(); diff --git a/crates/bevy_ecs/src/system/commands/entity_command.rs b/crates/bevy_ecs/src/system/commands/entity_command.rs index e5a1097abcf82..e0b0bffc6928b 100644 --- a/crates/bevy_ecs/src/system/commands/entity_command.rs +++ b/crates/bevy_ecs/src/system/commands/entity_command.rs @@ -13,7 +13,7 @@ use core::panic::Location; use crate::{ bundle::{Bundle, InsertMode}, component::{Component, ComponentId, ComponentInfo}, - entity::{Entity, EntityCloneBuilder}, + entity::{Entity, EntityClonerBuilder}, event::Event, result::Result, system::{command::HandleError, Command, IntoObserverSystem}, @@ -324,10 +324,10 @@ pub fn observe( } /// An [`EntityCommand`] that clones parts of an entity onto another entity, -/// configured through [`EntityCloneBuilder`]. +/// configured through [`EntityClonerBuilder`]. pub fn clone_with( target: Entity, - config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> impl EntityCommand { move |mut entity: EntityWorldMut| { entity.clone_with(target, config); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 5694df4a49451..5f4fb1976d445 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -21,7 +21,7 @@ use crate::{ bundle::{Bundle, InsertMode}, change_detection::Mut, component::{Component, ComponentId, Mutable}, - entity::{Entities, Entity, EntityCloneBuilder}, + entity::{Entities, Entity, EntityClonerBuilder}, event::Event, observer::{Observer, TriggerTargets}, resource::Resource, @@ -1913,7 +1913,7 @@ impl<'a> EntityCommands<'a> { } /// Clones parts of an entity (components, observers, etc.) onto another entity, - /// configured through [`EntityCloneBuilder`]. + /// configured through [`EntityClonerBuilder`]. /// /// By default, the other entity will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). @@ -1924,7 +1924,7 @@ impl<'a> EntityCommands<'a> { /// /// # Example /// - /// Configure through [`EntityCloneBuilder`] as follows: + /// Configure through [`EntityClonerBuilder`] as follows: /// ``` /// # use bevy_ecs::prelude::*; /// @@ -1948,14 +1948,11 @@ impl<'a> EntityCommands<'a> { /// # bevy_ecs::system::assert_is_system(example_system); /// ``` /// - /// See the following for more options: - /// - [`EntityCloneBuilder`] - /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) - /// - `CloneEntityHierarchyExt` + /// See [`EntityClonerBuilder`] for more options. pub fn clone_with( &mut self, target: Entity, - config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> &mut Self { self.queue(entity_command::clone_with(target, config)) } @@ -1996,16 +1993,16 @@ impl<'a> EntityCommands<'a> { } /// Spawns a clone of this entity and allows configuring cloning behavior - /// using [`EntityCloneBuilder`], returning the [`EntityCommands`] of the clone. + /// using [`EntityClonerBuilder`], returning the [`EntityCommands`] of the clone. /// /// By default, the clone will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// - /// To exclude specific components, use [`EntityCloneBuilder::deny`]. - /// To only include specific components, use [`EntityCloneBuilder::deny_all`] - /// followed by [`EntityCloneBuilder::allow`]. + /// To exclude specific components, use [`EntityClonerBuilder::deny`]. + /// To only include specific components, use [`EntityClonerBuilder::deny_all`] + /// followed by [`EntityClonerBuilder::allow`]. /// - /// See the methods on [`EntityCloneBuilder`] for more options. + /// See the methods on [`EntityClonerBuilder`] for more options. /// /// # Note /// @@ -2034,7 +2031,7 @@ impl<'a> EntityCommands<'a> { /// # bevy_ecs::system::assert_is_system(example_system); pub fn clone_and_spawn_with( &mut self, - config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> EntityCommands<'_> { let entity_clone = self.commands().spawn_empty().id(); self.clone_with(entity_clone, config); diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 912f4a968606f..3e459d299df18 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4,7 +4,8 @@ use crate::{ change_detection::MutUntyped, component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType}, entity::{ - Entities, Entity, EntityBorrow, EntityCloneBuilder, EntityLocation, TrustedEntityBorrow, + Entities, Entity, EntityBorrow, EntityCloner, EntityClonerBuilder, EntityLocation, + TrustedEntityBorrow, }, event::Event, observer::Observer, @@ -2580,12 +2581,12 @@ impl<'w> EntityWorldMut<'w> { } /// Clones parts of an entity (components, observers, etc.) onto another entity, - /// configured through [`EntityCloneBuilder`]. + /// configured through [`EntityClonerBuilder`]. /// /// By default, the other entity will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// - /// Configure through [`EntityCloneBuilder`] as follows: + /// Configure through [`EntityClonerBuilder`] as follows: /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Component, Clone, PartialEq, Debug)] @@ -2602,10 +2603,7 @@ impl<'w> EntityWorldMut<'w> { /// # assert_eq!(world.get::(target), None); /// ``` /// - /// See the following for more options: - /// - [`EntityCloneBuilder`] - /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) - /// - `CloneEntityHierarchyExt` + /// See [`EntityClonerBuilder`] for more options. /// /// # Panics /// @@ -2614,11 +2612,11 @@ impl<'w> EntityWorldMut<'w> { pub fn clone_with( &mut self, target: Entity, - config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> &mut Self { self.assert_not_despawned(); - let mut builder = EntityCloneBuilder::new(self.world); + let mut builder = EntityCloner::build(self.world); config(&mut builder); builder.clone_entity(self.entity, target); @@ -2643,12 +2641,12 @@ impl<'w> EntityWorldMut<'w> { } /// Spawns a clone of this entity and allows configuring cloning behavior - /// using [`EntityCloneBuilder`], returning the [`Entity`] of the clone. + /// using [`EntityClonerBuilder`], returning the [`Entity`] of the clone. /// /// By default, the clone will receive all the components of the original that implement /// [`Clone`] or [`Reflect`](bevy_reflect::Reflect). /// - /// Configure through [`EntityCloneBuilder`] as follows: + /// Configure through [`EntityClonerBuilder`] as follows: /// ``` /// # use bevy_ecs::prelude::*; /// # #[derive(Component, Clone, PartialEq, Debug)] @@ -2664,24 +2662,21 @@ impl<'w> EntityWorldMut<'w> { /// # assert_eq!(world.get::(entity_clone), None); /// ``` /// - /// See the following for more options: - /// - [`EntityCloneBuilder`] - /// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt) - /// - `CloneEntityHierarchyExt` + /// See [`EntityClonerBuilder`] for more options. /// /// # Panics /// /// If this entity has been despawned while this `EntityWorldMut` is still alive. pub fn clone_and_spawn_with( &mut self, - config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static, + config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static, ) -> Entity { self.assert_not_despawned(); let entity_clone = self.world.entities.reserve_entity(); self.world.flush(); - let mut builder = EntityCloneBuilder::new(self.world); + let mut builder = EntityCloner::build(self.world); config(&mut builder); builder.clone_entity(self.entity, entity_clone); @@ -2702,9 +2697,10 @@ impl<'w> EntityWorldMut<'w> { pub fn clone_components(&mut self, target: Entity) -> &mut Self { self.assert_not_despawned(); - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow::(); - builder.clone_entity(self.entity, target); + EntityCloner::build(self.world) + .deny_all() + .allow::() + .clone_entity(self.entity, target); self.world.flush(); self.update_location(); @@ -2724,10 +2720,11 @@ impl<'w> EntityWorldMut<'w> { pub fn move_components(&mut self, target: Entity) -> &mut Self { self.assert_not_despawned(); - let mut builder = EntityCloneBuilder::new(self.world); - builder.deny_all().allow::(); - builder.move_components(true); - builder.clone_entity(self.entity, target); + EntityCloner::build(self.world) + .deny_all() + .allow::() + .move_components(true) + .clone_entity(self.entity, target); self.world.flush(); self.update_location(); @@ -5665,10 +5662,11 @@ mod tests { let entity_b = world.spawn_empty().id(); world.entity_mut(entity_a).clone_with(entity_b, |builder| { - builder.move_components(true); - builder.without_required_components(|builder| { - builder.deny::(); - }); + builder + .move_components(true) + .without_required_components(|builder| { + builder.deny::(); + }); }); assert_eq!(world.entity(entity_a).get::(), Some(&A)); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8ea17168a38c4..969eebc5baa18 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -35,9 +35,8 @@ use crate::{ bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode}, change_detection::{MutUntyped, TicksMut}, component::{ - Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId, - ComponentInfo, ComponentTicks, Components, Mutable, RequiredComponents, - RequiredComponentsError, Tick, + Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, + Components, Mutable, RequiredComponents, RequiredComponentsError, Tick, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, entity_disabling::{DefaultQueryFilters, Disabled}, @@ -3190,35 +3189,6 @@ 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 reference 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::ComponentCloneCtx; - /// use bevy_ecs::world::DeferredWorld; - /// - /// fn custom_clone_handler( - /// _world: &mut DeferredWorld, - /// _ctx: &mut ComponentCloneCtx, - /// ) { - /// // 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_handler(custom_clone_handler)) - /// ``` - pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { - self.components.get_component_clone_handlers_mut() - } } impl World { @@ -3770,7 +3740,7 @@ mod tests { use super::{FromWorld, World}; use crate::{ change_detection::DetectChangesMut, - component::{ComponentDescriptor, ComponentInfo, StorageType}, + component::{ComponentCloneBehavior, ComponentDescriptor, ComponentInfo, StorageType}, entity::hash_set::EntityHashSet, entity_disabling::{DefaultQueryFilters, Disabled}, ptr::OwningPtr, @@ -4074,6 +4044,7 @@ mod tests { DROP_COUNT.fetch_add(1, Ordering::SeqCst); }), true, + ComponentCloneBehavior::Default, ) }; diff --git a/crates/bevy_mesh/src/skinning.rs b/crates/bevy_mesh/src/skinning.rs index faf4e8be3ea97..f55f51af10ee6 100644 --- a/crates/bevy_mesh/src/skinning.rs +++ b/crates/bevy_mesh/src/skinning.rs @@ -1,26 +1,14 @@ use bevy_asset::{Asset, Handle}; -use bevy_ecs::{ - component::Component, - entity::{Entity, VisitEntities, VisitEntitiesMut}, - prelude::ReflectComponent, - reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut}, -}; +use bevy_ecs::{component::Component, entity::Entity, prelude::ReflectComponent}; use bevy_math::Mat4; use bevy_reflect::prelude::*; use core::ops::Deref; -#[derive(Component, Debug, Default, Clone, Reflect, VisitEntities, VisitEntitiesMut)] -#[reflect( - Component, - MapEntities, - VisitEntities, - VisitEntitiesMut, - Default, - Debug -)] +#[derive(Component, Debug, Default, Clone, Reflect)] +#[reflect(Component, Default, Debug)] pub struct SkinnedMesh { - #[visit_entities(ignore)] pub inverse_bindposes: Handle, + #[entities] pub joints: Vec, } diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index a40053ed5f28c..be45b54a811a9 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -1,15 +1,16 @@ use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError}; use bevy_asset::Asset; -use bevy_ecs::reflect::ReflectResource; +use bevy_ecs::reflect::{ReflectMapEntities, ReflectResource}; use bevy_ecs::{ entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper}, - reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, + reflect::{AppTypeRegistry, ReflectComponent}, world::World, }; use bevy_reflect::{PartialReflect, TypePath, TypeRegistry}; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; +use bevy_ecs::component::ComponentCloneBehavior; #[cfg(feature = "serialize")] use serde::Serialize; @@ -85,7 +86,7 @@ impl DynamicScene { // Apply/ add each component to the given entity. for component in &scene_entity.components { - let mut component = component.clone_value(); + let component = component.clone_value(); let type_info = component.get_represented_type_info().ok_or_else(|| { SceneSpawnError::NoRepresentedType { type_path: component.reflect_type_path().to_string(), @@ -103,19 +104,27 @@ impl DynamicScene { } })?; - // If this component references entities in the scene, update - // them to the entities in the world. - if let Some(map_entities) = registration.data::() { - SceneEntityMapper::world_scope(entity_map, world, |_, mapper| { - map_entities.map_entities(component.as_partial_reflect_mut(), mapper); - }); + { + let component_id = reflect_component.register_component(world); + // SAFETY: we registered the component above. the info exists + #[expect(unsafe_code, reason = "this is faster")] + let component_info = + unsafe { world.components().get_info_unchecked(component_id) }; + match component_info.clone_behavior() { + ComponentCloneBehavior::Ignore + | ComponentCloneBehavior::RelationshipTarget(_) => continue, + _ => {} + } } - reflect_component.apply_or_insert( - &mut world.entity_mut(entity), - component.as_partial_reflect(), - &type_registry, - ); + SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { + reflect_component.apply_or_insert_mapped( + &mut world.entity_mut(entity), + component.as_partial_reflect(), + &type_registry, + mapper, + ); + }); } } @@ -340,13 +349,13 @@ mod tests { #[reflect(Component)] struct A; - #[derive(Component, Reflect, VisitEntities)] - #[reflect(Component, MapEntities)] + #[derive(Component, Reflect)] + #[reflect(Component)] struct B(pub Entity); impl MapEntities for B { fn map_entities(&mut self, entity_mapper: &mut M) { - self.0 = entity_mapper.map_entity(self.0); + self.0 = entity_mapper.get_mapped(self.0); } } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index 507f18f63a069..fe15866c1408a 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -1,5 +1,4 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![forbid(unsafe_code)] #![doc( html_logo_url = "https://bevyengine.org/assets/icon.png", html_favicon_url = "https://bevyengine.org/assets/icon.png" diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 7d74ce8cb25e3..9040b15e85f7c 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -3,9 +3,10 @@ use core::any::TypeId; use crate::{DynamicScene, SceneSpawnError}; use bevy_asset::Asset; use bevy_ecs::{ + component::ComponentCloneBehavior, entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper}, entity_disabling::DefaultQueryFilters, - reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, + reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, world::World, }; use bevy_reflect::{PartialReflect, TypePath}; @@ -123,6 +124,12 @@ impl Scene { .get_info(component_id) .expect("component_ids in archetypes should have ComponentInfo"); + match component_info.clone_behavior() { + ComponentCloneBehavior::Ignore + | ComponentCloneBehavior::RelationshipTarget(_) => continue, + _ => {} + } + let registration = type_registry .get(component_info.type_id().unwrap()) .ok_or_else(|| SceneSpawnError::UnregisteredType { @@ -135,7 +142,7 @@ impl Scene { } })?; - let Some(mut component) = reflect_component + let Some(component) = reflect_component .reflect(self.world.entity(scene_entity.id())) .map(PartialReflect::clone_value) else { @@ -144,16 +151,14 @@ impl Scene { // If this component references entities in the scene, // update them to the entities in the world. - if let Some(map_entities) = registration.data::() { - SceneEntityMapper::world_scope(entity_map, world, |_, mapper| { - map_entities.map_entities(component.as_partial_reflect_mut(), mapper); - }); - } - reflect_component.apply_or_insert( - &mut world.entity_mut(entity), - component.as_partial_reflect(), - &type_registry, - ); + SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { + reflect_component.apply_or_insert_mapped( + &mut world.entity_mut(entity), + component.as_partial_reflect(), + &type_registry, + mapper, + ); + }); } } } diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index ede9076ea2325..8e26863e792ee 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -515,10 +515,10 @@ mod tests { DynamicScene, DynamicSceneBuilder, }; use bevy_ecs::{ - entity::{hash_map::EntityHashMap, Entity, VisitEntities, VisitEntitiesMut}, + entity::{hash_map::EntityHashMap, Entity}, prelude::{Component, ReflectComponent, ReflectResource, Resource, World}, query::{With, Without}, - reflect::{AppTypeRegistry, ReflectMapEntities}, + reflect::AppTypeRegistry, world::FromWorld, }; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; @@ -584,9 +584,9 @@ mod tests { foo: i32, } - #[derive(Clone, Component, Reflect, PartialEq, VisitEntities, VisitEntitiesMut)] - #[reflect(Component, MapEntities, PartialEq)] - struct MyEntityRef(Entity); + #[derive(Clone, Component, Reflect, PartialEq)] + #[reflect(Component, PartialEq)] + struct MyEntityRef(#[entities] Entity); impl FromWorld for MyEntityRef { fn from_world(_world: &mut World) -> Self { diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index d5ca961f77d64..09db13ae2c167 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -10,7 +10,9 @@ use std::{alloc::Layout, collections::HashMap, io::Write, ptr::NonNull}; use bevy::{ ecs::{ - component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType}, + component::{ + ComponentCloneBehavior, ComponentDescriptor, ComponentId, ComponentInfo, StorageType, + }, query::QueryData, world::FilteredEntityMut, }, @@ -94,6 +96,7 @@ fn main() { Layout::array::(size).unwrap(), None, true, + ComponentCloneBehavior::Default, ) }); let Some(info) = world.components().get_info(id) else { diff --git a/examples/ecs/immutable_components.rs b/examples/ecs/immutable_components.rs index 1fe1dde23ad78..6fd434f446f13 100644 --- a/examples/ecs/immutable_components.rs +++ b/examples/ecs/immutable_components.rs @@ -2,7 +2,9 @@ use bevy::{ ecs::{ - component::{ComponentDescriptor, ComponentId, HookContext, StorageType}, + component::{ + ComponentCloneBehavior, ComponentDescriptor, ComponentId, HookContext, StorageType, + }, world::DeferredWorld, }, platform_support::collections::HashMap, @@ -152,6 +154,7 @@ fn demo_3(world: &mut World) { Layout::array::(size).unwrap(), None, false, + ComponentCloneBehavior::Default, ) }; diff --git a/examples/stress_tests/many_components.rs b/examples/stress_tests/many_components.rs index 9729b8573c581..6234ca90e83e3 100644 --- a/examples/stress_tests/many_components.rs +++ b/examples/stress_tests/many_components.rs @@ -17,7 +17,7 @@ use bevy::{ DiagnosticPath, DiagnosticsPlugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin, }, ecs::{ - component::{ComponentDescriptor, ComponentId, StorageType}, + component::{ComponentCloneBehavior, ComponentDescriptor, ComponentId, StorageType}, system::QueryParamBuilder, world::FilteredEntityMut, }, @@ -99,6 +99,7 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) { Layout::new::(), None, true, // is mutable + ComponentCloneBehavior::Default, ) }, ) From 2854558b02bf901190940f23c9b6b3c1ae01429b Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 14:05:18 -0800 Subject: [PATCH 02/12] Move docs --- crates/bevy_ecs/src/entity/clone_entities.rs | 124 +++++++++---------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 2dfcc21931e51..9c0d3259249b8 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -273,8 +273,65 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { } } -/// A cloning configuration capable of cloning entities in a given world. See [`EntityClonerBuilder`] for configuration -/// options, and [`EntityCloner::clone_entity`] to clone a given entity. +/// A configuration determining how to clone enities. This can be built using [`EntityCloner::build`], which +/// returns an [`EntityClonerBuilder`]. +/// +/// After configuration is complete an entity can be cloned using [`Self::clone_entity`]. +/// +///``` +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::entity::EntityCloner; +/// +/// #[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(); +/// +/// EntityCloner::build(&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::ComponentCloneBehavior::global_default_fn). +/// To use `Clone`-based handler ([`ComponentCloneBehavior::clone`]) in this case it should be set manually using one +/// of the methods mentioned in the [Clone Behaviors](#Clone-Behaviors) section +/// +/// Here's an example of how to do it using [`clone_behavior`](Component::clone_behavior): +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::component::{StorageType, ComponentCloneBehavior, Mutable}; +/// #[derive(Clone)] +/// struct SomeComponent; +/// +/// impl Component for SomeComponent { +/// const STORAGE_TYPE: StorageType = StorageType::Table; +/// type Mutability = Mutable; +/// fn clone_behavior() -> ComponentCloneBehavior { +/// ComponentCloneBehavior::clone::() +/// } +/// } +/// ``` +/// +/// # Clone Behaviors +/// [`EntityCloner`] clones entities by cloning components using [`ComponentCloneBehavior`], 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 [`EntityClonerBuilder::override_clone_behavior`] +/// 2. component-defined handler using [`Component::clone_behavior`] +/// 3. default handler override using [`EntityClonerBuilder::with_default_clone_fn`]. +/// 4. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not. #[derive(Debug)] pub struct EntityCloner { filter_allows_components: bool, @@ -380,11 +437,6 @@ impl EntityCloner { let bump = Bump::new(); let mut bundle_scratch: BundleScratch; { - // 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 world = world.as_unsafe_world_cell(); let source_entity = world.get_entity(source).expect("Source entity must exist"); @@ -514,63 +566,7 @@ impl EntityCloner { } } -/// 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::EntityCloner; -/// -/// #[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(); -/// -/// EntityCloner::build(&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::ComponentCloneBehavior::global_default_fn). -/// To use `Clone`-based handler ([`ComponentCloneBehavior::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 [`clone_behavior`](Component::clone_behavior): -/// ``` -/// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::component::{StorageType, ComponentCloneBehavior, Mutable}; -/// #[derive(Clone)] -/// struct SomeComponent; -/// -/// impl Component for SomeComponent { -/// const STORAGE_TYPE: StorageType = StorageType::Table; -/// type Mutability = Mutable; -/// fn clone_behavior() -> ComponentCloneBehavior { -/// ComponentCloneBehavior::clone::() -/// } -/// } -/// ``` -/// -/// # Handlers -/// `EntityClonerBuilder` clones entities by cloning components using [`ComponentCloneBehavior`], 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 [`EntityClonerBuilder::override_clone_behavior`] -/// 2. component-defined handler using [`Component::clone_behavior`] -/// 3. default handler override using [`EntityClonerBuilder::with_default_clone_fn`]. -/// 4. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not. +/// A builder for configuring [`EntityCloner`]. See [`EntityCloner`] for more information. #[derive(Debug)] pub struct EntityClonerBuilder<'w> { world: &'w mut World, From fd7500a957c4d241951b1fcd4dd28d03a8638d3e Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 13:53:02 -0800 Subject: [PATCH 03/12] Apply suggestions from code review Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com> --- crates/bevy_ecs/src/component.rs | 2 +- crates/bevy_ecs/src/entity/clone_entities.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 9fea346261a63..81301766c30bb 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -799,7 +799,7 @@ impl ComponentInfo { self.descriptor.mutable } - /// Returns `true` if the current component is mutable. + /// Returns [`ComponentCloneBehavior`] of the current component. #[inline] pub fn clone_behavior(&self) -> &ComponentCloneBehavior { &self.descriptor.clone_behavior diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 9c0d3259249b8..a0d373b4ec7e7 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -581,7 +581,7 @@ impl<'w> EntityClonerBuilder<'w> { self.entity_cloner.clone_entity(self.world, source, target); self } - /// Finishes configuring the builder and clones `source` entity to `target`. + /// Finishes configuring [`EntityCloner`] returns it. pub fn finish(self) -> EntityCloner { self.entity_cloner } From e3ad84b3501ab7d5d3f471579f44830076985258 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 14:14:56 -0800 Subject: [PATCH 04/12] Address comments --- crates/bevy_ecs/src/entity/clone_entities.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index a0d373b4ec7e7..5f35d67701d9c 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -420,7 +420,7 @@ impl EntityCloner { pub fn build(world: &mut World) -> EntityClonerBuilder { EntityClonerBuilder { world, - attach_required_components: false, + attach_required_components: true, entity_cloner: EntityCloner::default(), } } @@ -575,8 +575,7 @@ pub struct EntityClonerBuilder<'w> { } impl<'w> EntityClonerBuilder<'w> { - /// Internally consumes the builder to construct an [`EntityCloner`], calls [`EntityCloner::clone_entity`] on the - /// builder's [`World`], and returns the resulting [`EntityCloner`]. + /// Internally calls [`EntityCloner::clone_entity`] on the builder's [`World`]. pub fn clone_entity(&mut self, source: Entity, target: Entity) -> &mut Self { self.entity_cloner.clone_entity(self.world, source, target); self @@ -737,6 +736,17 @@ impl<'w> EntityClonerBuilder<'w> { self } + /// Removes a previously set override of [`ComponentCloneBehavior`] for a given `component_id` in this builder. + pub fn remove_clone_behavior_override_with_id( + &mut self, + component_id: ComponentId, + ) -> &mut Self { + self.entity_cloner + .clone_behavior_overrides + .remove(&component_id); + self + } + /// If `true`, makes the built [`EntityCloner`] recursively clone entities, as defined by /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN). pub fn recursive(&mut self, is_recursive: bool) -> &mut Self { From ebd543356013f9db5160d8199452bfbe65edc90b Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 14:22:58 -0800 Subject: [PATCH 05/12] CI fixes --- crates/bevy_ecs/src/entity/clone_entities.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 5f35d67701d9c..c33897f163d48 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -41,7 +41,7 @@ pub struct ComponentCloneCtx<'a, 'b> { type_registry: Option<&'a crate::reflect::AppTypeRegistry>, #[cfg(not(feature = "bevy_reflect"))] #[expect(dead_code)] - type_registry: Option<()>, + type_registry: Option<&'a ()>, } impl<'a, 'b> ComponentCloneCtx<'a, 'b> { @@ -62,7 +62,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { entity_cloner: &'a mut EntityCloner, mapper: &'a mut dyn EntityMapper, #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, - #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<()>, + #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<&'a ()>, ) -> Self { Self { component_id, @@ -273,7 +273,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { } } -/// A configuration determining how to clone enities. This can be built using [`EntityCloner::build`], which +/// A configuration determining how to clone entities. This can be built using [`EntityCloner::build`], which /// returns an [`EntityClonerBuilder`]. /// /// After configuration is complete an entity can be cloned using [`Self::clone_entity`]. From ee0fe2748f579bf854936d067db3b509fbb7692d Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 14:41:41 -0800 Subject: [PATCH 06/12] Add is_recursive function --- crates/bevy_ecs/src/entity/clone_entities.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index c33897f163d48..4197f25f4c889 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -425,6 +425,12 @@ impl EntityCloner { } } + /// Returns `true` if this cloner is configured to clone entities recursively. + #[inline] + pub fn is_recursive(&self) -> bool { + self.is_recursive + } + /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. fn clone_entity_internal( &mut self, From d8a5965560eacec98b8c14558892ee397be97914 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 14:48:16 -0800 Subject: [PATCH 07/12] Update crates/bevy_ecs/src/entity/clone_entities.rs Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com> --- crates/bevy_ecs/src/entity/clone_entities.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 4197f25f4c889..4963b02896d6c 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -431,7 +431,7 @@ impl EntityCloner { self.is_recursive } - /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. + /// Clones and inserts components from the `source` entity into the entity mapped by `mapper` from `source` using the stored configuration. fn clone_entity_internal( &mut self, world: &mut World, From 399d680bef3f4dd31c08652b900887fc12251dcb Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 18:49:40 -0800 Subject: [PATCH 08/12] Map entities in clone_via_reflect --- crates/bevy_ecs/src/component.rs | 36 +++++++++++-- crates/bevy_ecs/src/entity/clone_entities.rs | 56 +++++++++++++++++--- crates/bevy_ecs/src/reflect/component.rs | 11 ++++ 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 81301766c30bb..3deb201e43567 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -2230,7 +2230,7 @@ pub fn component_clone_via_clone( /// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details. #[cfg(feature = "bevy_reflect")] pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentCloneCtx) { - let Some(registry) = ctx.type_registry() else { + let Some(app_registry) = ctx.type_registry().cloned() else { return; }; let Some(source_component_reflect) = ctx.read_source_component_reflect() else { @@ -2239,16 +2239,24 @@ pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentC let component_info = ctx.component_info(); // checked in read_source_component_reflect let type_id = component_info.type_id().unwrap(); - let registry = registry.read(); + let registry = app_registry.read(); // Try to clone using ReflectFromReflect if let Some(reflect_from_reflect) = registry.get_type_data::(type_id) { - if let Some(component) = + if let Some(mut component) = reflect_from_reflect.from_reflect(source_component_reflect.as_partial_reflect()) { + if let Some(reflect_component) = + registry.get_type_data::(type_id) + { + reflect_component.visit_entities_mut(&mut *component, &mut |entity| { + *entity = ctx.entity_mapper().get_mapped(*entity); + }); + } drop(registry); + ctx.write_target_component_reflect(component); return; } @@ -2268,14 +2276,36 @@ pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentC registry.get_type_data::(type_id) { let reflect_from_world = reflect_from_world.clone(); + let mut mapped_entities = Vec::new(); + if let Some(reflect_component) = + registry.get_type_data::(type_id) + { + reflect_component.visit_entities(&*source_component_reflect, &mut |entity| { + mapped_entities.push(entity); + }); + } 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(); + for entity in mapped_entities.iter_mut() { + *entity = ctx.entity_mapper().get_mapped(*entity); + } + drop(registry); 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()); + if let Some(reflect_component) = app_registry + .read() + .get_type_data::(type_id) + { + let mut i = 0; + reflect_component.visit_entities_mut(&mut *component, &mut |entity| { + *entity = mapped_entities[i]; + i += 1; + }); + } // SAFETY: // - component_id is from the same world as target entity // - component is a valid value represented by component_id diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 4963b02896d6c..b1c1e6a9753fa 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -111,6 +111,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.entity_cloner.is_recursive } + /// Returns this context's [`EntityMapper`]. + pub fn entity_mapper(&mut self) -> &mut dyn EntityMapper { + self.mapper + } + /// 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 @@ -437,7 +442,7 @@ impl EntityCloner { world: &mut World, source: Entity, mapper: &mut dyn EntityMapper, - ) { + ) -> Entity { let target = mapper.get_mapped(source); // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. let bump = Bump::new(); @@ -522,6 +527,7 @@ impl EntityCloner { // - All `component_ids` are from the same world as `target` entity // - All `component_data_ptrs` are valid types represented by `component_ids` unsafe { bundle_scratch.write(world, target) }; + target } /// Clones and inserts components from the `source` entity into `target` entity using the stored configuration. @@ -540,9 +546,10 @@ impl EntityCloner { /// by [`RelationshipTarget`](crate::relationship::RelationshipTarget) components with /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN) #[track_caller] - pub fn spawn_clone(&mut self, world: &mut World, source: Entity) { + pub fn spawn_clone(&mut self, world: &mut World, source: Entity) -> Entity { let target = world.spawn_empty().id(); self.clone_entity(world, source, target); + target } /// Clones the entity into whatever entity `mapper` chooses for it. @@ -552,8 +559,8 @@ impl EntityCloner { world: &mut World, source: Entity, mapper: &mut dyn EntityMapper, - ) { - self.clone_entity_internal(world, source, mapper); + ) -> Entity { + let target = self.clone_entity_internal(world, source, mapper); loop { let queued = self.clone_queue.borrow_mut().pop_front(); if let Some(queued) = queued { @@ -564,6 +571,7 @@ impl EntityCloner { break; } } + target } fn is_cloning_allowed(&self, component: &ComponentId) -> bool { @@ -807,14 +815,17 @@ mod tests { use crate::{ self as bevy_ecs, component::{Component, ComponentCloneBehavior, ComponentDescriptor, StorageType}, - entity::EntityCloner, + entity::{hash_map::EntityHashMap, Entity, EntityCloner}, hierarchy::{ChildOf, Children}, + reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}, + resource::Resource, system::Commands, - world::World, + world::{FromWorld, World}, }; use alloc::vec::Vec; use bevy_ecs_macros::require; use bevy_ptr::OwningPtr; + use bevy_reflect::Reflect; use core::{alloc::Layout, ops::Deref}; #[cfg(feature = "bevy_reflect")] @@ -1278,4 +1289,37 @@ mod tests { &[child1, child2] ); } + + #[test] + fn clone_with_reflect_from_world() { + #[derive(Component, Reflect, PartialEq, Eq, Debug)] + #[reflect(Component, FromWorld, from_reflect = false)] + struct SomeRef(#[entities] Entity); + + #[derive(Resource)] + struct FromWorldCalled(bool); + + impl FromWorld for SomeRef { + fn from_world(world: &mut World) -> Self { + world.insert_resource(FromWorldCalled(true)); + SomeRef(Entity::PLACEHOLDER) + } + } + let mut world = World::new(); + let registry = AppTypeRegistry::default(); + registry.write().register::(); + world.insert_resource(registry); + + let a = world.spawn_empty().id(); + let b = world.spawn_empty().id(); + let c = world.spawn(SomeRef(a)).id(); + let d = world.spawn_empty().id(); + let mut map = EntityHashMap::::new(); + map.insert(a, b); + map.insert(c, d); + + let cloned = EntityCloner::default().clone_entity_mapped(&mut world, c, &mut map); + assert_eq!(*world.entity(cloned).get::().unwrap(), SomeRef(b)); + assert!(world.resource::().0); + } } diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 14a0dc0e9cdff..bffd2e9c290b7 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -115,6 +115,8 @@ pub struct ReflectComponentFns { pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>, /// Function pointer implementing [`ReflectComponent::reflect_mut()`]. pub reflect_mut: fn(FilteredEntityMut) -> Option>, + /// Function pointer implementing [`ReflectComponent::visit_entities()`]. + pub visit_entities: fn(&dyn Reflect, &mut dyn FnMut(Entity)), /// Function pointer implementing [`ReflectComponent::visit_entities_mut()`]. pub visit_entities_mut: fn(&mut dyn Reflect, &mut dyn FnMut(&mut Entity)), /// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`]. @@ -282,6 +284,11 @@ impl ReflectComponent { &self.0 } + /// Calls a dynamic version of [`Component::visit_entities`]. + pub fn visit_entities(&self, component: &dyn Reflect, func: &mut dyn FnMut(Entity)) { + (self.0.visit_entities)(component, func); + } + /// Calls a dynamic version of [`Component::visit_entities_mut`]. pub fn visit_entities_mut( &self, @@ -379,6 +386,10 @@ impl FromType for ReflectComponent { register_component: |world: &mut World| -> ComponentId { world.register_component::() }, + visit_entities: |reflect: &dyn Reflect, func: &mut dyn FnMut(Entity)| { + let component = reflect.downcast_ref::().unwrap(); + Component::visit_entities(component, func); + }, visit_entities_mut: |reflect: &mut dyn Reflect, func: &mut dyn FnMut(&mut Entity)| { let component = reflect.downcast_mut::().unwrap(); Component::visit_entities_mut(component, func); From 233b6a95c75a5f5cfb78c8c86e5c568d42ec957d Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 5 Feb 2025 18:53:09 -0800 Subject: [PATCH 09/12] clippy --- crates/bevy_ecs/src/component.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 3deb201e43567..89e7788e01c0d 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -2280,7 +2280,7 @@ pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentC if let Some(reflect_component) = registry.get_type_data::(type_id) { - reflect_component.visit_entities(&*source_component_reflect, &mut |entity| { + reflect_component.visit_entities(source_component_reflect, &mut |entity| { mapped_entities.push(entity); }); } From af4785c4e7fb8de41c34a69b83f07d77fca5b6a8 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 6 Feb 2025 13:42:20 -0800 Subject: [PATCH 10/12] Resolve comments --- crates/bevy_ecs/src/entity/clone_entities.rs | 25 +++++++++++--------- crates/bevy_ecs/src/entity/map_entities.rs | 4 ++++ crates/bevy_ecs/src/relationship/mod.rs | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b1c1e6a9753fa..5ee0295bec424 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -30,7 +30,7 @@ pub struct ComponentCloneCtx<'a, 'b> { source_component_ptr: Ptr<'a>, target_component_written: bool, bundle_scratch: &'a mut BundleScratch<'b>, - bump: &'b Bump, + bundle_scratch_allocator: &'b Bump, source: Entity, target: Entity, components: &'a Components, @@ -56,7 +56,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { source: Entity, target: Entity, source_component_ptr: Ptr<'a>, - bump: &'b Bump, + bundle_scratch_allocator: &'b Bump, bundle_scratch: &'a mut BundleScratch<'b>, components: &'a Components, entity_cloner: &'a mut EntityCloner, @@ -71,7 +71,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { source_component_ptr, bundle_scratch, target_component_written: false, - bump, + bundle_scratch_allocator, components, mapper, component_info: components.get_info_unchecked(component_id), @@ -105,7 +105,9 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.component_info } - /// Returns true if the [`EntityCloner`] is configured to recursively clone entities. + /// Returns true if the [`EntityCloner`] is configured to recursively clone entities. When this is enabled, + /// entities stored in a cloned entity's [`RelationshipTarget`](crate::relationship::RelationshipTarget) component with + /// [`RelationshipTarget::LINKED_SPAWN`](crate::relationship::RelationshipTarget::LINKED_SPAWN) will also be cloned. #[inline] pub fn is_recursive(&self) -> bool { self.entity_cloner.is_recursive @@ -179,7 +181,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { // SAFETY: the TypeId of self.component_id has been checked to ensure it matches `C` unsafe { self.bundle_scratch - .push(self.bump, self.component_id, component); + .push(self.bundle_scratch_allocator, self.component_id, component); }; self.target_component_written = true; } @@ -204,7 +206,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { panic!("Trying to write component multiple times") } let layout = self.component_info.layout(); - let target_component_data_ptr = self.bump.alloc_layout(layout); + let target_component_data_ptr = self.bundle_scratch_allocator.alloc_layout(layout); if clone_fn(self.source_component_ptr, target_component_data_ptr) { self.bundle_scratch @@ -238,7 +240,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { let component_layout = self.component_info.layout(); let component_data_ptr = Box::into_raw(component).cast::(); - let target_component_data_ptr = self.bump.alloc_layout(component_layout); + let target_component_data_ptr = + self.bundle_scratch_allocator.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 @@ -394,11 +397,11 @@ impl<'a> BundleScratch<'a> { /// be written to. pub(crate) unsafe fn push( &mut self, - bump: &'a Bump, + allocator: &'a Bump, id: ComponentId, component: C, ) { - let component_ref = bump.alloc(component); + let component_ref = allocator.alloc(component); self.component_ids.push(id); self.component_ptrs.push(PtrMut::from(component_ref)); } @@ -445,7 +448,7 @@ impl EntityCloner { ) -> Entity { let target = mapper.get_mapped(source); // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. - let bump = Bump::new(); + let bundle_scratch_allocator = Bump::new(); let mut bundle_scratch: BundleScratch; { let world = world.as_unsafe_world_cell(); @@ -498,7 +501,7 @@ impl EntityCloner { source, target, source_component_ptr, - &bump, + &bundle_scratch_allocator, &mut bundle_scratch, world.components(), self, diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 6cf00981838e5..caa02eaeac2bd 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -67,6 +67,9 @@ impl MapEntities for T { /// /// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World). /// +/// This can be used in tandem with [`Component::visit_entities`](crate::component::Component::visit_entities) +/// and [`Component::visit_entities_mut`](crate::component::Component::visit_entities_mut) to map a component's entities. +/// /// ## Example /// /// ``` @@ -92,6 +95,7 @@ impl MapEntities for T { pub trait EntityMapper { /// Returns the "target" entity that maps to the given `source`. fn get_mapped(&mut self, source: Entity) -> Entity; + /// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result /// of [`EntityMapper::get_mapped`]. fn set_mapped(&mut self, source: Entity, target: Entity); diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index adfe1cb57334e..5d260555020b4 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -144,6 +144,7 @@ pub type SourceIter<'w, R> = /// See the [`Relationship`] documentation for more information. pub trait RelationshipTarget: Component + Sized { /// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. + /// This defaults to false when derived. const LINKED_SPAWN: bool; /// The [`Relationship`] that populates this [`RelationshipTarget`] collection. type Relationship: Relationship; From f38a8a1844b2f64f638bf00931248091d9033bd6 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 6 Feb 2025 13:43:03 -0800 Subject: [PATCH 11/12] Update crates/bevy_ecs/src/relationship/mod.rs Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/relationship/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index 5d260555020b4..9ff16b028c835 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -144,6 +144,11 @@ pub type SourceIter<'w, R> = /// See the [`Relationship`] documentation for more information. pub trait RelationshipTarget: Component + Sized { /// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. + /// + /// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children). + /// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning). + /// + /// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone. /// This defaults to false when derived. const LINKED_SPAWN: bool; /// The [`Relationship`] that populates this [`RelationshipTarget`] collection. From c97bf746c7acb2d91605540ae975498d45e92f46 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 6 Feb 2025 13:50:49 -0800 Subject: [PATCH 12/12] fmt --- crates/bevy_ecs/src/relationship/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index 9ff16b028c835..4a9b5e61f6333 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -148,7 +148,7 @@ pub trait RelationshipTarget: Component + Sized { /// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children). /// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning). /// - /// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone. + /// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone. /// This defaults to false when derived. const LINKED_SPAWN: bool; /// The [`Relationship`] that populates this [`RelationshipTarget`] collection.