diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 464e4efabf072..24e8ed1fd93a4 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -92,12 +92,17 @@ pub fn derive_component(input: TokenStream) -> TokenStream { Err(err) => err.into_compile_error().into(), }; - let visit_entities = visit_entities( + let map_entities = map_entities( &ast.data, - &bevy_ecs_path, + Ident::new("this", Span::call_site()), relationship.is_some(), relationship_target.is_some(), - ); + ).map(|map_entities_impl| quote! { + fn map_entities(this: &mut Self, mapper: &mut M) { + use #bevy_ecs_path::entity::MapEntities; + #map_entities_impl + } + }); let storage = storage_path(&bevy_ecs_path, attrs.storage); @@ -295,7 +300,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { #clone_behavior } - #visit_entities + #map_entities } #relationship @@ -306,19 +311,18 @@ pub fn derive_component(input: TokenStream) -> TokenStream { const ENTITIES: &str = "entities"; -fn visit_entities( +pub(crate) fn map_entities( data: &Data, - bevy_ecs_path: &Path, + self_ident: Ident, is_relationship: bool, is_relationship_target: bool, -) -> TokenStream2 { +) -> Option { match data { Data::Struct(DataStruct { fields, .. }) => { - let mut visit = Vec::with_capacity(fields.len()); - let mut visit_mut = Vec::with_capacity(fields.len()); + let mut map = Vec::with_capacity(fields.len()); let relationship = if is_relationship || is_relationship_target { - relationship_field(fields, "VisitEntities", fields.span()).ok() + relationship_field(fields, "MapEntities", fields.span()).ok() } else { None }; @@ -335,27 +339,17 @@ fn visit_entities( .clone() .map_or(Member::from(index), Member::Named); - visit.push(quote!(this.#field_member.visit_entities(&mut func);)); - visit_mut.push(quote!(this.#field_member.visit_entities_mut(&mut func);)); + map.push(quote!(#self_ident.#field_member.map_entities(mapper);)); }); - if visit.is_empty() { - return quote!(); + if map.is_empty() { + return None; }; - quote!( - fn visit_entities(this: &Self, mut func: impl FnMut(#bevy_ecs_path::entity::Entity)) { - use #bevy_ecs_path::entity::VisitEntities; - #(#visit)* - } - - fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut #bevy_ecs_path::entity::Entity)) { - use #bevy_ecs_path::entity::VisitEntitiesMut; - #(#visit_mut)* - } - ) + Some(quote!( + #(#map)* + )) } Data::Enum(DataEnum { variants, .. }) => { - let mut visit = Vec::with_capacity(variants.len()); - let mut visit_mut = Vec::with_capacity(variants.len()); + let mut map = Vec::with_capacity(variants.len()); for variant in variants.iter() { let field_members = variant @@ -377,40 +371,25 @@ fn visit_entities( .map(|member| format_ident!("__self_{}", member)) .collect::>(); - visit.push( + map.push( quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => { - #(#field_idents.visit_entities(&mut func);)* - }), - ); - visit_mut.push( - quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => { - #(#field_idents.visit_entities_mut(&mut func);)* + #(#field_idents.map_entities(mapper);)* }), ); } - if visit.is_empty() { - return quote!(); + if map.is_empty() { + return None; }; - quote!( - fn visit_entities(this: &Self, mut func: impl FnMut(#bevy_ecs_path::entity::Entity)) { - use #bevy_ecs_path::entity::VisitEntities; - match this { - #(#visit,)* - _ => {} - } - } - fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut #bevy_ecs_path::entity::Entity)) { - use #bevy_ecs_path::entity::VisitEntitiesMut; - match this { - #(#visit_mut,)* - _ => {} - } + Some(quote!( + match #self_ident { + #(#map,)* + _ => {} } - ) + )) } - Data::Union(_) => quote!(), + Data::Union(_) => None, } } diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index afe56c23e0781..74a68e9de602d 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -9,10 +9,13 @@ mod query_filter; mod states; mod world_query; -use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl}; +use crate::{ + component::map_entities, query_data::derive_query_data_impl, + query_filter::derive_query_filter_impl, +}; use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest}; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::{Ident, Span}; use quote::{format_ident, quote}; use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, @@ -185,105 +188,22 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { }) } -fn derive_visit_entities_base( - input: TokenStream, - trait_name: TokenStream2, - gen_methods: impl FnOnce(Vec) -> TokenStream2, -) -> TokenStream { +#[proc_macro_derive(MapEntities, attributes(entities))] +pub fn derive_map_entities(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let ecs_path = bevy_ecs_path(); - - let named_fields = match get_struct_fields(&ast.data) { - Ok(fields) => fields, - Err(e) => return e.into_compile_error().into(), - }; - - let field = named_fields - .iter() - .filter_map(|field| { - if let Some(attr) = field - .attrs - .iter() - .find(|a| a.path().is_ident("visit_entities")) - { - let ignore = attr.parse_nested_meta(|meta| { - if meta.path.is_ident("ignore") { - Ok(()) - } else { - Err(meta.error("Invalid visit_entities attribute. Use `ignore`")) - } - }); - return match ignore { - Ok(()) => None, - Err(e) => Some(Err(e)), - }; - } - Some(Ok(field)) - }) - .map(|res| res.map(|field| field.ident.as_ref())) - .collect::, _>>(); - - let field = match field { - Ok(field) => field, - Err(e) => return e.into_compile_error().into(), - }; - - if field.is_empty() { - return syn::Error::new( - ast.span(), - format!("Invalid `{}` type: at least one field", trait_name), - ) - .into_compile_error() - .into(); - } - - let field_access = field - .iter() - .enumerate() - .map(|(n, f)| { - if let Some(ident) = f { - quote! { - self.#ident - } - } else { - let idx = Index::from(n); - quote! { - self.#idx - } - } - }) - .collect::>(); - - let methods = gen_methods(field_access); - - let generics = ast.generics; - let (impl_generics, ty_generics, _) = generics.split_for_impl(); + let map_entities_impl = map_entities( + &ast.data, + Ident::new("self", Span::call_site()), + false, + false, + ); let struct_name = &ast.ident; - + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); TokenStream::from(quote! { - impl #impl_generics #ecs_path::entity:: #trait_name for #struct_name #ty_generics { - #methods - } - }) -} - -#[proc_macro_derive(VisitEntitiesMut, attributes(visit_entities))] -pub fn derive_visit_entities_mut(input: TokenStream) -> TokenStream { - derive_visit_entities_base(input, quote! { VisitEntitiesMut }, |field| { - quote! { - fn visit_entities_mut(&mut self, mut f: F) { - #(#field.visit_entities_mut(&mut f);)* - } - } - }) -} - -#[proc_macro_derive(VisitEntities, attributes(visit_entities))] -pub fn derive_visit_entities(input: TokenStream) -> TokenStream { - derive_visit_entities_base(input, quote! { VisitEntities }, |field| { - quote! { - fn visit_entities(&self, mut f: F) { - #(#field.visit_entities(&mut f);)* + impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause { + fn map_entities(&mut self, mapper: &mut M) { + #map_entities_impl } } }) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 9a2bdff16ebf2..247bfb2c54673 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -4,12 +4,12 @@ use crate::{ archetype::ArchetypeFlags, bundle::BundleInfo, change_detection::{MaybeLocation, MAX_CHANGE_AGE}, - entity::{ComponentCloneCtx, Entity, SourceComponent}, + entity::{ComponentCloneCtx, Entity, EntityMapper, SourceComponent}, query::DebugCheckedUnwrap, relationship::RelationshipHookMode, resource::Resource, storage::{SparseSetIndex, SparseSets, Table, TableRow}, - system::{Commands, Local, SystemParam}, + system::{Local, SystemParam}, world::{DeferredWorld, FromWorld, World}, }; use alloc::boxed::Box; @@ -485,14 +485,21 @@ pub trait Component: Send + Sync + 'static { 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. + /// Maps the entities on this component using the given [`EntityMapper`]. This is used to remap entities in contexts like scenes and entity cloning. + /// When deriving [`Component`], this is populated by annotating fields containing entities with `#[entities]` + /// + /// ``` + /// # use bevy_ecs::{component::Component, entity::Entity}; + /// #[derive(Component)] + /// struct Inventory { + /// #[entities] + /// items: Vec + /// } + /// ``` + /// + /// Fields with `#[entities]` must implement [`MapEntities`](crate::entity::MapEntities). #[inline] - fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {} + fn map_entities(_this: &mut Self, _mapper: &mut E) {} } mod private { @@ -1126,7 +1133,7 @@ impl ComponentDescriptor { } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut Commands, &SourceComponent, &mut ComponentCloneCtx); +pub type ComponentCloneFn = fn(&SourceComponent, &mut ComponentCloneCtx); /// The clone behavior to use when cloning a [`Component`]. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -2893,7 +2900,6 @@ pub fn enforce_no_required_components_recursion( /// It will panic if set as handler for any other component. /// pub fn component_clone_via_clone( - _commands: &mut Commands, source: &SourceComponent, ctx: &mut ComponentCloneCtx, ) { @@ -2920,11 +2926,7 @@ pub fn component_clone_via_clone( /// /// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone #[cfg(feature = "bevy_reflect")] -pub fn component_clone_via_reflect( - commands: &mut Commands, - source: &SourceComponent, - ctx: &mut ComponentCloneCtx, -) { +pub fn component_clone_via_reflect(source: &SourceComponent, ctx: &mut ComponentCloneCtx) { let Some(app_registry) = ctx.type_registry().cloned() else { return; }; @@ -2941,9 +2943,7 @@ pub fn component_clone_via_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); - }); + reflect_component.map_entities(&mut *component, ctx.entity_mapper()); } drop(registry); @@ -2961,9 +2961,7 @@ pub fn component_clone_via_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); - }); + reflect_component.map_entities(&mut *component, ctx.entity_mapper()); } drop(registry); @@ -2986,23 +2984,12 @@ pub fn component_clone_via_reflect( 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.to_dynamic(); let component_layout = component_info.layout(); let target = ctx.target(); let component_id = ctx.component_id(); - for entity in mapped_entities.iter_mut() { - *entity = ctx.entity_mapper().get_mapped(*entity); - } drop(registry); - commands.queue(move |world: &mut World| { + ctx.queue_deferred(move |world: &mut World, mapper: &mut dyn EntityMapper| { let mut component = reflect_from_world.from_world(world); assert_eq!(type_id, (*component).type_id()); component.apply(source_component_cloned.as_partial_reflect()); @@ -3010,11 +2997,7 @@ pub fn component_clone_via_reflect( .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; - }); + reflect_component.map_entities(&mut *component, mapper); } // SAFETY: // - component_id is from the same world as target entity @@ -3038,12 +3021,7 @@ pub fn component_clone_via_reflect( /// Noop implementation of component clone handler function. /// /// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details. -pub fn component_clone_ignore( - _commands: &mut Commands, - _source: &SourceComponent, - _ctx: &mut ComponentCloneCtx, -) { -} +pub fn component_clone_ignore(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {} /// Wrapper for components clone specialization using autoderef. #[doc(hidden)] diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 25e1534532b56..6efdbe0b3a511 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,22 +1,15 @@ -use alloc::{borrow::ToOwned, collections::VecDeque, vec::Vec}; +use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec}; use bevy_platform_support::collections::{HashMap, HashSet}; use bevy_ptr::{Ptr, PtrMut}; use bumpalo::Bump; use core::any::TypeId; -#[cfg(feature = "bevy_reflect")] -use alloc::boxed::Box; - -use crate::component::{ComponentCloneBehavior, ComponentCloneFn}; -use crate::entity::hash_map::EntityHashMap; -use crate::entity::{Entities, EntityMapper}; -use crate::relationship::RelationshipHookMode; -use crate::system::Commands; use crate::{ bundle::Bundle, - component::{Component, ComponentId, ComponentInfo}, - entity::Entity, + component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo}, + entity::{hash_map::EntityHashMap, Entities, Entity, EntityMapper}, query::DebugCheckedUnwrap, + relationship::RelationshipHookMode, world::World, }; @@ -176,9 +169,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - 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, mut component: C) { - C::visit_entities_mut(&mut component, |entity| { - *entity = self.mapper.get_mapped(*entity); - }); + C::map_entities(&mut component, &mut self.mapper); let short_name = disqualified::ShortName::of::(); if self.target_component_written { panic!("Trying to write component '{short_name}' multiple times") @@ -280,6 +271,17 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.mapper.set_mapped(entity, target); self.entity_cloner.clone_queue.push_back(entity); } + + /// Queues a deferred clone operation, which will run with exclusive [`World`] access immediately after calling the clone handler for each component on an entity. + /// This exists, despite its similarity to [`Commands`](crate::system::Commands), to provide access to the entity mapper in the current context. + pub fn queue_deferred( + &mut self, + deferred: impl FnOnce(&mut World, &mut dyn EntityMapper) + 'static, + ) { + self.entity_cloner + .deferred_commands + .push_back(Box::new(deferred)); + } } /// A configuration determining how to clone entities. This can be built using [`EntityCloner::build`], which @@ -341,7 +343,6 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// 2. component-defined handler using [`Component::clone_behavior`] /// 3. default handler override using [`EntityClonerBuilder::with_default_clone_fn`]. /// 4. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not. -#[derive(Debug)] pub struct EntityCloner { filter_allows_components: bool, filter: HashSet, @@ -350,18 +351,20 @@ pub struct EntityCloner { linked_cloning: bool, default_clone_fn: ComponentCloneFn, clone_queue: VecDeque, + deferred_commands: VecDeque>, } impl Default for EntityCloner { fn default() -> Self { Self { filter_allows_components: false, - filter: Default::default(), - clone_behavior_overrides: Default::default(), move_components: false, linked_cloning: false, default_clone_fn: ComponentCloneBehavior::global_default_fn(), + filter: Default::default(), + clone_behavior_overrides: Default::default(), clone_queue: Default::default(), + deferred_commands: Default::default(), } } } @@ -476,10 +479,6 @@ impl EntityCloner { 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) { @@ -527,12 +526,16 @@ impl EntityCloner { ) }; - (handler)(&mut commands, &source_component, &mut ctx); + (handler)(&source_component, &mut ctx); } } world.flush(); + for deferred in self.deferred_commands.drain(..) { + (deferred)(world, mapper); + } + if !world.entities.contains(target) { panic!("Target entity does not exist"); } @@ -609,7 +612,6 @@ impl EntityCloner { } /// A builder for configuring [`EntityCloner`]. See [`EntityCloner`] for more information. -#[derive(Debug)] pub struct EntityClonerBuilder<'w> { world: &'w mut World, entity_cloner: EntityCloner, @@ -845,7 +847,6 @@ mod tests { entity::{hash_map::EntityHashMap, Entity, EntityCloner, SourceComponent}, prelude::{ChildOf, Children, Resource}, reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}, - system::Commands, world::{FromWorld, World}, }; use alloc::vec::Vec; @@ -861,7 +862,6 @@ mod tests { component::{Component, ComponentCloneBehavior}, entity::{EntityCloner, SourceComponent}, reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld}, - system::Commands, }; use alloc::vec; use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr}; @@ -991,11 +991,7 @@ mod tests { #[derive(Component, Reflect)] struct B; - fn test_handler( - _commands: &mut Commands, - source: &SourceComponent, - ctx: &mut ComponentCloneCtx, - ) { + fn test_handler(source: &SourceComponent, ctx: &mut ComponentCloneCtx) { let registry = ctx.type_registry().unwrap(); assert!(source.read_reflect(®istry.read()).is_none()); } @@ -1287,11 +1283,7 @@ mod tests { #[test] fn clone_entity_with_dynamic_components() { const COMPONENT_SIZE: usize = 10; - fn test_handler( - _commands: &mut Commands, - source: &SourceComponent, - ctx: &mut ComponentCloneCtx, - ) { + fn test_handler(source: &SourceComponent, ctx: &mut ComponentCloneCtx) { // SAFETY: the passed in ptr corresponds to copy-able data that matches the type of the source / target component unsafe { ctx.write_target_component_ptr(source.ptr()); diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index caa02eaeac2bd..3dc938f678b47 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -1,10 +1,15 @@ +pub use bevy_ecs_macros::MapEntities; + use crate::{ - entity::Entity, + entity::{hash_map::EntityHashMap, Entity}, identifier::masks::{IdentifierMask, HIGH_MASK}, world::World, }; -use super::{hash_map::EntityHashMap, VisitEntitiesMut}; +use alloc::{collections::VecDeque, vec::Vec}; +use bevy_platform_support::collections::HashSet; +use core::hash::BuildHasher; +use smallvec::SmallVec; /// Operation to map all contained [`Entity`] fields in a type to new values. /// @@ -15,13 +20,9 @@ use super::{hash_map::EntityHashMap, VisitEntitiesMut}; /// (usually by using an [`EntityHashMap`] between source entities and entities in the /// current world). /// -/// This trait is similar to [`VisitEntitiesMut`]. They differ in that [`VisitEntitiesMut`] operates -/// on `&mut Entity` and allows for in-place modification, while this trait makes no assumption that -/// such in-place modification is occurring, which is impossible for types such as [`HashSet`] -/// and [`EntityHashMap`] which must be rebuilt when their contained [`Entity`]s are remapped. -/// -/// Implementing this trait correctly is required for properly loading components -/// with entity references from scenes. +/// Components use [`Component::map_entities`](crate::component::Component::map_entities) to map +/// entities in the context of scenes and entity cloning, which generally uses [`MapEntities`] internally +/// to map each field (see those docs for usage). /// /// [`HashSet`]: bevy_platform_support::collections::HashSet /// @@ -49,17 +50,51 @@ pub trait MapEntities { /// /// Implementors should look up any and all [`Entity`] values stored within `self` and /// update them to the mapped values via `entity_mapper`. - fn map_entities(&mut self, entity_mapper: &mut M); + fn map_entities(&mut self, entity_mapper: &mut E); +} + +impl MapEntities for Entity { + fn map_entities(&mut self, entity_mapper: &mut E) { + *self = entity_mapper.get_mapped(*self); + } } -impl MapEntities for T { - fn map_entities(&mut self, entity_mapper: &mut M) { - self.visit_entities_mut(|entity| { +impl MapEntities for Option { + fn map_entities(&mut self, entity_mapper: &mut E) { + if let Some(entity) = self { *entity = entity_mapper.get_mapped(*entity); - }); + } } } +impl MapEntities for HashSet { + fn map_entities(&mut self, entity_mapper: &mut E) { + *self = self.drain().map(|e| entity_mapper.get_mapped(e)).collect(); + } +} +impl MapEntities for Vec { + fn map_entities(&mut self, entity_mapper: &mut E) { + for entity in self.iter_mut() { + *entity = entity_mapper.get_mapped(*entity); + } + } +} + +impl MapEntities for VecDeque { + fn map_entities(&mut self, entity_mapper: &mut E) { + for entity in self.iter_mut() { + *entity = entity_mapper.get_mapped(*entity); + } + } +} + +impl> MapEntities for SmallVec { + fn map_entities(&mut self, entity_mapper: &mut E) { + for entity in self.iter_mut() { + *entity = entity_mapper.get_mapped(*entity); + } + } +} /// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`]. /// /// Usually this is done by using an [`EntityHashMap`] to map source entities @@ -67,8 +102,7 @@ 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. +/// This is used by [`MapEntities`] implementors. /// /// ## Example /// diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 79c5f8c9e705a..2756648e94218 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -39,7 +39,6 @@ mod clone_entities; mod entity_set; mod map_entities; -mod visit_entities; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; #[cfg(all(feature = "bevy_reflect", feature = "serialize"))] @@ -48,7 +47,6 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; pub use clone_entities::*; pub use entity_set::*; pub use map_entities::*; -pub use visit_entities::*; mod hash; pub use hash::*; diff --git a/crates/bevy_ecs/src/entity/visit_entities.rs b/crates/bevy_ecs/src/entity/visit_entities.rs deleted file mode 100644 index 734c96e1132b9..0000000000000 --- a/crates/bevy_ecs/src/entity/visit_entities.rs +++ /dev/null @@ -1,149 +0,0 @@ -pub use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut}; - -use crate::entity::Entity; - -/// Apply an operation to all entities in a container. -/// -/// This is implemented by default for types that implement [`IntoIterator`]. -/// -/// It may be useful to implement directly for types that can't produce an -/// iterator for lifetime reasons, such as those involving internal mutexes. -pub trait VisitEntities { - /// Apply an operation to all contained entities. - fn visit_entities(&self, f: F); -} - -impl VisitEntities for T -where - for<'a> &'a T: IntoIterator, -{ - fn visit_entities(&self, f: F) { - self.into_iter().copied().for_each(f); - } -} - -impl VisitEntities for Entity { - fn visit_entities(&self, mut f: F) { - f(*self); - } -} - -/// Apply an operation to mutable references to all entities in a container. -/// -/// This is implemented by default for types that implement [`IntoIterator`]. -/// -/// It may be useful to implement directly for types that can't produce an -/// iterator for lifetime reasons, such as those involving internal mutexes. -pub trait VisitEntitiesMut: VisitEntities { - /// Apply an operation to mutable references to all contained entities. - fn visit_entities_mut(&mut self, f: F); -} - -impl VisitEntitiesMut for T -where - for<'a> &'a mut T: IntoIterator, -{ - fn visit_entities_mut(&mut self, f: F) { - self.into_iter().for_each(f); - } -} - -impl VisitEntitiesMut for Entity { - fn visit_entities_mut(&mut self, mut f: F) { - f(self); - } -} - -#[cfg(test)] -mod tests { - use crate::{ - entity::{hash_map::EntityHashMap, MapEntities, SceneEntityMapper}, - world::World, - }; - use alloc::{string::String, vec, vec::Vec}; - use bevy_platform_support::collections::HashSet; - - use super::*; - - #[derive(VisitEntities, Debug, PartialEq)] - struct Foo { - ordered: Vec, - unordered: HashSet, - single: Entity, - #[visit_entities(ignore)] - not_an_entity: String, - } - - // Need a manual impl since VisitEntitiesMut isn't implemented for `HashSet`. - // We don't expect users to actually do this - it's only for test purposes - // to prove out the automatic `MapEntities` impl we get with `VisitEntitiesMut`. - impl VisitEntitiesMut for Foo { - fn visit_entities_mut(&mut self, mut f: F) { - self.ordered.visit_entities_mut(&mut f); - self.unordered = self - .unordered - .drain() - .map(|mut entity| { - f(&mut entity); - entity - }) - .collect(); - f(&mut self.single); - } - } - - #[test] - fn visit_entities() { - let mut world = World::new(); - let entities = world.entities(); - let mut foo = Foo { - ordered: vec![entities.reserve_entity(), entities.reserve_entity()], - unordered: [ - entities.reserve_entity(), - entities.reserve_entity(), - entities.reserve_entity(), - ] - .into_iter() - .collect(), - single: entities.reserve_entity(), - not_an_entity: "Bar".into(), - }; - - let mut entity_map = EntityHashMap::::default(); - let mut remapped = Foo { - ordered: vec![], - unordered: HashSet::default(), - single: Entity::PLACEHOLDER, - not_an_entity: foo.not_an_entity.clone(), - }; - - // Note: this assumes that the VisitEntities derive is field-ordered, - // which isn't explicitly stated/guaranteed. - // If that changes, this test will fail, but that might be OK if - // we're intentionally breaking that assumption. - let mut i = 0; - foo.visit_entities(|entity| { - let new_entity = entities.reserve_entity(); - if i < foo.ordered.len() { - assert_eq!(entity, foo.ordered[i]); - remapped.ordered.push(new_entity); - } else if i < foo.ordered.len() + foo.unordered.len() { - assert!(foo.unordered.contains(&entity)); - remapped.unordered.insert(new_entity); - } else { - assert_eq!(entity, foo.single); - remapped.single = new_entity; - } - - entity_map.insert(entity, new_entity); - - i += 1; - }); - - SceneEntityMapper::world_scope(&mut entity_map, &mut world, |_, mapper| { - foo.map_entities(mapper); - }); - - assert_eq!(foo, remapped); - } -} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 7ccfd4caa9774..0a97a6df98d10 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -133,7 +133,7 @@ mod tests { bundle::Bundle, change_detection::Ref, component::{Component, ComponentId, RequiredComponents, RequiredComponentsError}, - entity::Entity, + entity::{Entity, EntityMapper}, entity_disabling::DefaultQueryFilters, prelude::Or, query::{Added, Changed, FilteredAccess, QueryFilter, With, Without}, @@ -146,7 +146,6 @@ mod tests { vec, vec::Vec, }; - use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut}; use bevy_platform_support::collections::HashSet; use bevy_tasks::{ComputeTaskPool, TaskPool}; use core::{ @@ -2705,8 +2704,19 @@ mod tests { World::new().register_component::(); } + #[derive(Default)] + struct CaptureMapper(Vec); + impl EntityMapper for CaptureMapper { + fn get_mapped(&mut self, source: Entity) -> Entity { + self.0.push(source); + source + } + + fn set_mapped(&mut self, _source: Entity, _target: Entity) {} + } + #[test] - fn visit_struct_entities() { + fn map_struct_entities() { #[derive(Component)] #[expect( unused, @@ -2733,30 +2743,22 @@ mod tests { 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 mapper = CaptureMapper::default(); + Component::map_entities(&mut foo, &mut mapper); + assert_eq!(&mapper.0, &[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]); + let mut mapper = CaptureMapper::default(); + Component::map_entities(&mut bar, &mut mapper); + assert_eq!(&mapper.0, &[e1, e2, e3]); } #[test] - fn visit_enum_entities() { + fn map_enum_entities() { #[derive(Component)] #[expect( unused, @@ -2779,26 +2781,18 @@ mod tests { 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 mapper = CaptureMapper::default(); + Component::map_entities(&mut foo, &mut mapper); + assert_eq!(&mapper.0, &[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]); + let mut mapper = CaptureMapper::default(); + Component::map_entities(&mut foo, &mut mapper); + assert_eq!(&mapper.0, &[e1, e2, e3]); } #[expect( @@ -2827,16 +2821,18 @@ mod tests { field1: ComponentB, } - #[derive(Component, VisitEntities, VisitEntitiesMut)] + #[derive(Component)] struct MyEntities { + #[entities] entities: Vec, + #[entities] another_one: Entity, + #[entities] maybe_entity: Option, #[expect( dead_code, reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used." )] - #[visit_entities(ignore)] something_else: String, } @@ -2844,6 +2840,6 @@ mod tests { dead_code, reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed." )] - #[derive(Component, VisitEntities, VisitEntitiesMut)] - struct MyEntitiesTuple(Vec, Entity, #[visit_entities(ignore)] usize); + #[derive(Component)] + struct MyEntitiesTuple(#[entities] Vec, #[entities] Entity, usize); } diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 32afa41cc8f8e..d69f7764fe489 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -2,9 +2,8 @@ use crate::{ component::{ Component, ComponentCloneBehavior, ComponentHook, HookContext, Mutable, StorageType, }, - entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, SourceComponent}, + entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, EntityMapper, SourceComponent}, observer::ObserverState, - system::Commands, world::World, }; use alloc::vec::Vec; @@ -64,15 +63,11 @@ impl EntityClonerBuilder<'_> { } } -fn component_clone_observed_by( - commands: &mut Commands, - _source: &SourceComponent, - ctx: &mut ComponentCloneCtx, -) { +fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentCloneCtx) { let target = ctx.target(); let source = ctx.source(); - commands.queue(move |world: &mut World| { + ctx.queue_deferred(move |world: &mut World, _mapper: &mut dyn EntityMapper| { let observed_by = world .get::(source) .map(|observed_by| observed_by.0.clone()) diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 77b0587279297..893e9b13fa8e3 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -121,10 +121,8 @@ pub struct ReflectComponentFns { pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>, /// Function pointer implementing [`ReflectComponent::reflect_mut()`]. pub reflect_mut: fn(FilteredEntityMut) -> Option>, - /// Function pointer implementing [`ReflectComponent::visit_entities()`]. - pub visit_entities: fn(&dyn Reflect, &mut dyn FnMut(Entity)), - /// Function pointer implementing [`ReflectComponent::visit_entities_mut()`]. - pub visit_entities_mut: fn(&mut dyn Reflect, &mut dyn FnMut(&mut Entity)), + /// Function pointer implementing [`ReflectComponent::map_entities()`]. + pub map_entities: fn(&mut dyn Reflect, &mut dyn EntityMapper), /// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`]. /// /// # Safety @@ -291,18 +289,9 @@ impl ReflectComponent { &self.0 } - /// Calls a dynamic version of [`Component::visit_entities`]. - pub fn visit_entities(&self, component: &dyn Reflect, func: &mut dyn FnMut(Entity)) { - (self.0.visit_entities)(component, func); - } - - /// Calls a dynamic version of [`Component::visit_entities_mut`]. - pub fn visit_entities_mut( - &self, - component: &mut dyn Reflect, - func: &mut dyn FnMut(&mut Entity), - ) { - (self.0.visit_entities_mut)(component, func); + /// Calls a dynamic version of [`Component::map_entities`]. + pub fn map_entities(&self, component: &mut dyn Reflect, func: &mut dyn EntityMapper) { + (self.0.map_entities)(component, func); } } @@ -330,19 +319,18 @@ impl FromType for ReflectComponent { apply_or_insert_mapped: |entity, reflected_component, registry, - mapper, + mut mapper, relationship_hook_mode| { - let map_fn = map_function(mapper); 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); + C::map_entities(&mut component, &mut mapper); } else { let mut component = entity.world_scope(|world| { from_reflect_with_fallback::(reflected_component, world, registry) }); - C::visit_entities_mut(&mut component, map_fn); + C::map_entities(&mut component, &mut mapper); entity .insert_with_relationship_hook_mode(component, relationship_hook_mode); } @@ -350,7 +338,7 @@ impl FromType for ReflectComponent { let mut component = entity.world_scope(|world| { from_reflect_with_fallback::(reflected_component, world, registry) }); - C::visit_entities_mut(&mut component, map_fn); + C::map_entities(&mut component, &mut mapper); entity.insert_with_relationship_hook_mode(component, relationship_hook_mode); } }, @@ -395,20 +383,10 @@ impl FromType for ReflectComponent { register_component: |world: &mut World| -> ComponentId { world.register_component::() }, - visit_entities: |reflect: &dyn Reflect, func: &mut dyn FnMut(Entity)| { - let component = reflect.downcast_ref::().unwrap(); - Component::visit_entities(component, func); - }, - visit_entities_mut: |reflect: &mut dyn Reflect, func: &mut dyn FnMut(&mut Entity)| { + map_entities: |reflect: &mut dyn Reflect, mut mapper: &mut dyn EntityMapper| { let component = reflect.downcast_mut::().unwrap(); - Component::visit_entities_mut(component, func); + Component::map_entities(component, &mut mapper); }, }) } } - -fn map_function(mapper: &mut dyn EntityMapper) -> impl FnMut(&mut Entity) + '_ { - move |entity: &mut Entity| { - *entity = mapper.get_mapped(*entity); - } -} diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 4d94945afc1e9..b630f587197d7 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -17,7 +17,6 @@ mod entity_commands; mod from_world; mod map_entities; mod resource; -mod visit_entities; pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use component::{ReflectComponent, ReflectComponentFns}; @@ -25,7 +24,6 @@ pub use entity_commands::ReflectCommandExt; pub use from_world::{ReflectFromWorld, ReflectFromWorldFns}; pub use map_entities::ReflectMapEntities; pub use resource::{ReflectResource, ReflectResourceFns}; -pub use visit_entities::{ReflectVisitEntities, ReflectVisitEntitiesMut}; /// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. diff --git a/crates/bevy_ecs/src/reflect/visit_entities.rs b/crates/bevy_ecs/src/reflect/visit_entities.rs deleted file mode 100644 index 11f02612ba1f9..0000000000000 --- a/crates/bevy_ecs/src/reflect/visit_entities.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::entity::{Entity, VisitEntities, VisitEntitiesMut}; -use bevy_reflect::{FromReflect, FromType, PartialReflect}; - -/// For a reflected value, apply an operation to all contained entities. -/// -/// See [`VisitEntities`] for more details. -#[derive(Clone)] -pub struct ReflectVisitEntities { - visit_entities: fn(&dyn PartialReflect, &mut dyn FnMut(Entity)), -} - -impl ReflectVisitEntities { - /// A general method for applying an operation to all entities in a - /// reflected component. - pub fn visit_entities(&self, component: &dyn PartialReflect, f: &mut dyn FnMut(Entity)) { - (self.visit_entities)(component, f); - } -} - -impl FromType for ReflectVisitEntities { - fn from_type() -> Self { - ReflectVisitEntities { - visit_entities: |component, f| { - let concrete = C::from_reflect(component).unwrap(); - concrete.visit_entities(f); - }, - } - } -} - -/// For a reflected value, apply an operation to mutable references to all -/// contained entities. -/// -/// See [`VisitEntitiesMut`] for more details. -#[derive(Clone)] -pub struct ReflectVisitEntitiesMut { - visit_entities_mut: fn(&mut dyn PartialReflect, &mut dyn FnMut(&mut Entity)), -} - -impl ReflectVisitEntitiesMut { - /// A general method for applying an operation to all entities in a - /// reflected component. - pub fn visit_entities( - &self, - component: &mut dyn PartialReflect, - f: &mut dyn FnMut(&mut Entity), - ) { - (self.visit_entities_mut)(component, f); - } -} - -impl FromType for ReflectVisitEntitiesMut { - fn from_type() -> Self { - ReflectVisitEntitiesMut { - visit_entities_mut: |component, f| { - let mut concrete = C::from_reflect(component).unwrap(); - concrete.visit_entities_mut(f); - component.apply(&concrete); - }, - } - } -} diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index 9230d77b3f6ef..a7e4c5766711f 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -14,10 +14,7 @@ use crate::{ component::{Component, HookContext, Mutable}, entity::{ComponentCloneCtx, Entity, SourceComponent}, error::{ignore, CommandWithEntity, HandleError}, - system::{ - entity_command::{self}, - Commands, - }, + system::entity_command::{self}, world::{DeferredWorld, EntityWorldMut}, }; use log::warn; @@ -304,7 +301,6 @@ pub trait RelationshipTarget: Component + Sized { /// 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, source: &SourceComponent, context: &mut ComponentCloneCtx, ) { diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 28b392ad175cd..19d5045fab142 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -214,24 +214,24 @@ where mod tests { use bevy_ecs::{ component::Component, - entity::{ - hash_map::EntityHashMap, Entity, EntityMapper, MapEntities, VisitEntities, - VisitEntitiesMut, - }, + entity::{hash_map::EntityHashMap, Entity, EntityMapper, MapEntities}, hierarchy::ChildOf, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, resource::Resource, world::World, }; + use bevy_reflect::Reflect; use crate::dynamic_scene::DynamicScene; use crate::dynamic_scene_builder::DynamicSceneBuilder; - #[derive(Resource, Reflect, Debug, VisitEntities, VisitEntitiesMut)] + #[derive(Resource, Reflect, MapEntities, Debug)] #[reflect(Resource, MapEntities)] struct TestResource { + #[entities] entity_a: Entity, + #[entities] entity_b: Entity, } @@ -360,7 +360,7 @@ mod tests { struct B(pub Entity); impl MapEntities for B { - fn map_entities(&mut self, entity_mapper: &mut M) { + fn map_entities(&mut self, entity_mapper: &mut E) { self.0 = entity_mapper.get_mapped(self.0); } } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 0414eb5098237..4e9ffd3d82415 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -2,7 +2,7 @@ use alloc::{borrow::ToOwned, string::String}; use core::num::NonZero; use bevy_ecs::{ - entity::{Entity, EntityBorrow, VisitEntities, VisitEntitiesMut}, + entity::{Entity, EntityBorrow}, prelude::Component, }; use bevy_math::{CompassOctant, DVec2, IVec2, UVec2, Vec2}; @@ -74,24 +74,6 @@ impl WindowRef { } } -impl VisitEntities for WindowRef { - fn visit_entities(&self, mut f: F) { - match self { - Self::Entity(entity) => f(*entity), - Self::Primary => {} - } - } -} - -impl VisitEntitiesMut for WindowRef { - fn visit_entities_mut(&mut self, mut f: F) { - match self { - Self::Entity(entity) => f(entity), - Self::Primary => {} - } - } -} - /// A flattened representation of a window reference for equality/hashing purposes. /// /// For most purposes you probably want to use the unnormalized version [`WindowRef`].