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 aeaa0d0ab429c..8663ea3f3f970 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, AssetEvents, 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 652f0a9d35331..76732c0b895ac 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_path = attrs.on_add.map(|path| path.to_token_stream()); @@ -117,12 +121,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let on_despawn_path = if attrs .relationship_target - .is_some_and(|target| target.despawn_descendants) + .is_some_and(|target| target.linked_spawn) { if attrs.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(); @@ -202,12 +206,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() ) }; @@ -238,9 +242,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream { #on_remove #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 @@ -249,6 +255,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() @@ -326,7 +470,7 @@ struct Relationship { struct RelationshipTarget { relationship: Ident, - despawn_descendants: bool, + linked_spawn: bool, } // values for `storage` attribute @@ -468,18 +612,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; } @@ -494,7 +638,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, }) } } @@ -586,8 +730,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 2c6d79a03e3b8..2c06ad1fe6bd5 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -587,7 +587,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 c8fedf474362b..89e7788e01c0d 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, 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()); @@ -446,10 +442,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 { @@ -793,6 +799,12 @@ impl ComponentInfo { self.descriptor.mutable } + /// Returns [`ComponentCloneBehavior`] of the current component. + #[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] @@ -949,6 +961,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 @@ -961,6 +974,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() } } @@ -986,6 +1000,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: T::Mutability::MUTABLE, + clone_behavior: T::clone_behavior(), } } @@ -1000,6 +1015,7 @@ impl ComponentDescriptor { layout: Layout, drop: Option unsafe fn(OwningPtr<'a>)>, mutable: bool, + clone_behavior: ComponentCloneBehavior, ) -> Self { Self { name: name.into(), @@ -1009,6 +1025,7 @@ impl ComponentDescriptor { layout, drop, mutable, + clone_behavior, } } @@ -1026,6 +1043,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + clone_behavior: ComponentCloneBehavior::Default, } } @@ -1038,6 +1056,7 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + clone_behavior: ComponentCloneBehavior::Default, } } @@ -1068,107 +1087,55 @@ impl ComponentDescriptor { } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut DeferredWorld, &mut ComponentCloneCtx); +pub type ComponentCloneFn = fn(&mut Commands, &mut ComponentCloneCtx); -/// A struct instructing which clone handler to use when cloning a component. -#[derive(Debug)] -pub struct ComponentCloneHandler(Option); - -impl ComponentCloneHandler { - /// Use the global default function to clone the component with this handler. - 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, } } } @@ -1179,7 +1146,6 @@ pub struct Components { components: Vec, indices: TypeIdMap, resource_indices: TypeIdMap, - component_clone_handlers: ComponentCloneHandlers, } impl Components { @@ -1237,9 +1203,6 @@ impl Components { 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 } @@ -1596,16 +1559,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 { @@ -2248,12 +2201,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::() { @@ -2262,7 +2214,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: @@ -2275,10 +2227,10 @@ 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) { - let Some(registry) = ctx.type_registry() else { +pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentCloneCtx) { + let Some(app_registry) = ctx.type_registry().cloned() else { return; }; let Some(source_component_reflect) = ctx.read_source_component_reflect() else { @@ -2287,16 +2239,24 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen 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; } @@ -2316,14 +2276,36 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen 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(); - world.commands().queue(move |world: &mut World| { + 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 @@ -2341,14 +2323,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) } @@ -2356,22 +2338,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..5ee0295bec424 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,16 +29,19 @@ 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>, + bundle_scratch_allocator: &'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"))] #[expect(dead_code)] - type_registry: Option<()>, + type_registry: Option<&'a ()>, } impl<'a, 'b> ComponentCloneCtx<'a, 'b> { @@ -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, + bundle_scratch_allocator: &'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<()>, + #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<&'a ()>, ) -> Self { Self { component_id, + source, + target, source_component_ptr, - target_components_ptrs, + bundle_scratch, target_component_written: false, - target_components_buffer, + bundle_scratch_allocator, 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,19 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.component_info } + /// 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 + } + + /// 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 @@ -137,21 +163,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.bundle_scratch_allocator, self.component_id, component); + }; self.target_component_written = true; } @@ -175,11 +206,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.bundle_scratch_allocator.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; } } @@ -210,7 +241,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { let component_data_ptr = Box::into_raw(component).cast::(); let target_component_data_ptr = - self.target_components_buffer.alloc_layout(component_layout); + 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 @@ -220,34 +251,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,138 +271,24 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { pub fn type_registry(&self) -> Option<&crate::reflect::AppTypeRegistry> { self.type_registry } -} - -/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`]. -pub struct EntityCloner { - source: Entity, - target: Entity, - filter_allows_components: bool, - filter: Arc>, - clone_handlers_overrides: Arc>, - move_components: bool, -} - -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"); - - #[cfg(feature = "bevy_reflect")] - let app_registry = world.get_resource::(); - #[cfg(not(feature = "bevy_reflect"))] - let app_registry = Option::<()>::None; - - ( - app_registry, - source_entity, - world.components(), - world.into_deferred(), - ) - }; - let archetype = source_entity.archetype(); - - let component_data = Bump::new(); - let mut component_ids: Vec = Vec::with_capacity(archetype.component_count()); - let mut component_data_ptrs: Vec = Vec::with_capacity(archetype.component_count()); - - for component in archetype.components() { - if !self.is_cloning_allowed(&component) { - continue; - } - - let global_handlers = components.get_component_clone_handlers(); - let handler = match self.clone_handlers_overrides.get(&component) { - Some(handler) => handler - .get_handler() - .unwrap_or_else(|| global_handlers.get_default_handler()), - None => global_handlers.get_handler(component), - }; - - // SAFETY: - // - There are no other mutable references to source entity. - // - `component` is from `source_entity`'s archetype - let source_component_ptr = - unsafe { source_entity.get_by_id(component).debug_checked_unwrap() }; - - // SAFETY: - // - `components` and `component` are from the same world - // - `source_component_ptr` is valid and points to the same type as represented by `component` - let mut ctx = unsafe { - ComponentCloneCtx::new( - component, - source_component_ptr, - &mut component_data_ptrs, - &component_data, - components, - self, - type_registry, - ) - }; - - (handler)(&mut deferred_world, &mut ctx); - - if ctx.target_component_written { - component_ids.push(component); - } - } - - world.flush(); - - if !world.entities.contains(self.target) { - panic!("Target entity does not exist"); - } - - debug_assert_eq!(component_data_ptrs.len(), component_ids.len()); - - // SAFETY: - // - All `component_ids` are from the same world as `target` entity - // - All `component_data_ptrs` are valid types represented by `component_ids` - unsafe { - world.entity_mut(self.target).insert_by_ids( - &component_ids, - component_data_ptrs.into_iter().map(|ptr| ptr.promote()), - ); - } - - if self.move_components { - world.entity_mut(self.source).remove_by_ids(&component_ids); - } - } - - fn is_cloning_allowed(&self, component: &ComponentId) -> bool { - (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 - } + /// 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); } } -/// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them. +/// 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`]. /// ///``` /// use bevy_ecs::prelude::*; -/// use bevy_ecs::entity::EntityCloneBuilder; +/// use bevy_ecs::entity::EntityCloner; /// /// #[derive(Component, Clone, PartialEq, Eq)] /// struct A { @@ -405,7 +302,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 +313,293 @@ 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 -/// of the methods mentioned in the [Handlers](#handlers) section +/// 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 [`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 +/// # 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 [`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> { - world: &'w mut World, +pub struct EntityCloner { filter_allows_components: bool, filter: HashSet, - clone_handlers_overrides: HashMap, - attach_required_components: bool, + clone_behavior_overrides: HashMap, move_components: bool, + is_recursive: bool, + default_clone_fn: ComponentCloneFn, + clone_queue: RefCell>, } -impl<'w> EntityCloneBuilder<'w> { - /// Creates a new [`EntityCloneBuilder`] for world. - pub fn new(world: &'w mut World) -> Self { +impl Default for EntityCloner { + fn default() -> Self { Self { - world, filter_allows_components: false, filter: Default::default(), - clone_handlers_overrides: Default::default(), - attach_required_components: true, + clone_behavior_overrides: Default::default(), move_components: false, + is_recursive: false, + default_clone_fn: ComponentCloneBehavior::global_default_fn(), + clone_queue: Default::default(), + } + } +} + +/// An expandable scratch space for defining a dynamic bundle. +struct BundleScratch<'a> { + component_ids: Vec, + component_ptrs: Vec>, +} + +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), + } + } + + /// 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); + } + + /// 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, + allocator: &'a Bump, + id: ComponentId, + component: C, + ) { + let component_ref = allocator.alloc(component); + self.component_ids.push(id); + self.component_ptrs.push(PtrMut::from(component_ref)); + } + + /// 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()), + ); } } +} - /// Finishes configuring the builder and clones `source` entity to `target`. - pub fn clone_entity(self, source: Entity, target: Entity) { - let EntityCloneBuilder { +impl EntityCloner { + /// Returns a new [`EntityClonerBuilder`] using the given `world`. + pub fn build(world: &mut World) -> EntityClonerBuilder { + EntityClonerBuilder { 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, + attach_required_components: true, + entity_cloner: EntityCloner::default(), + } + } + + /// 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 the entity mapped by `mapper` from `source` using the stored configuration. + fn clone_entity_internal( + &mut self, + 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 bundle_scratch_allocator = Bump::new(); + let mut bundle_scratch: BundleScratch; + { + 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; + + 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; + } + + 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, + &bundle_scratch_allocator, + &mut bundle_scratch, + world.components(), + self, + mapper, + app_registry.as_ref(), + ) + }; + + (handler)(&mut commands, &mut ctx); + } + } + + world.flush(); + + if !world.entities.contains(target) { + panic!("Target entity does not exist"); + } + + 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 { bundle_scratch.write(world, target) }; + target + } + + /// 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) -> Entity { + let target = world.spawn_empty().id(); + self.clone_entity(world, source, target); + 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, + ) -> 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 { + let target = world.entities.reserve_entity(); + mapper.set_mapped(queued, target); + self.clone_entity_internal(world, queued, mapper); + } else { + break; + } } - .clone_entity(world); + target + } + + fn is_cloning_allowed(&self, component: &ComponentId) -> bool { + (self.filter_allows_components && self.filter.contains(component)) + || (!self.filter_allows_components && !self.filter.contains(component)) + } +} + +/// A builder for configuring [`EntityCloner`]. See [`EntityCloner`] for more information. +#[derive(Debug)] +pub struct EntityClonerBuilder<'w> { + world: &'w mut World, + entity_cloner: EntityCloner, + attach_required_components: bool, +} + +impl<'w> EntityClonerBuilder<'w> { + /// 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 + } + /// Finishes configuring [`EntityCloner`] returns it. + pub fn finish(self) -> EntityCloner { + self.entity_cloner } /// By default, any components allowed/denied through the filter will automatically @@ -496,7 +609,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 +617,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 +674,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 +709,82 @@ 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 { + /// 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 + } + + /// 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.clone_handlers_overrides.remove(&id); + self.entity_cloner.clone_behavior_overrides.remove(&id); } 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 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 } - /// 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 +793,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 +812,33 @@ 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::{hash_map::EntityHashMap, Entity, EntityCloner}, + hierarchy::{ChildOf, Children}, + reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}, + resource::Resource, + system::Commands, + world::{FromWorld, World}, }; use alloc::vec::Vec; use bevy_ecs_macros::require; use bevy_ptr::OwningPtr; - use core::alloc::Layout; + use bevy_reflect::Reflect; + 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 +856,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 +904,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 +920,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 +939,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 +955,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 +987,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 +1007,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 +1025,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 +1045,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 +1067,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 +1096,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 +1125,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 +1158,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 +1187,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 +1202,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 +1229,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 +1246,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 +1258,71 @@ 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] + ); + } + + #[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/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 05c11781de788..caa02eaeac2bd 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); }); } } @@ -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 /// /// ``` @@ -80,26 +83,61 @@ 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 +150,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 +261,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 +287,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 +306,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 8d0bdcc38f3ad..57d173b975ddd 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, @@ -2663,6 +2663,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 fe7a9fb3c9af5..893bd3d4917d0 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, ComponentHook, HookContext, Mutable, StorageType, + Component, ComponentCloneBehavior, ComponentHook, 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..bffd2e9c290b7 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,10 @@ 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()`]. /// /// # Safety @@ -163,13 +168,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 +283,20 @@ impl ReflectComponent { pub fn fn_pointers(&self) -> &ReflectComponentFns { &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, + component: &mut dyn Reflect, + func: &mut dyn FnMut(&mut Entity), + ) { + (self.0.visit_entities_mut)(component, func); + } } impl FromType for ReflectComponent { @@ -300,21 +320,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 +386,14 @@ 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); + }, }) } } diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index da03bbb5068fe..4a9b5e61f6333 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 { @@ -143,6 +143,14 @@ 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. + /// + /// 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. type Relationship: Relationship; /// The collection type that stores the "source" entities for this [`RelationshipTarget`] component. @@ -254,6 +262,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 1240fba66eaa0..a6cab26fd6774 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, @@ -2591,12 +2592,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)] @@ -2613,10 +2614,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 /// @@ -2625,11 +2623,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); @@ -2654,12 +2652,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)] @@ -2675,24 +2673,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); @@ -2713,9 +2708,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(); @@ -2735,10 +2731,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(); @@ -5679,10 +5676,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 732e02e189feb..b7ecf5b6f3312 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, ) }, )