Replace VisitEntities with MapEntities (#18432)
# Objective There are currently too many disparate ways to handle entity mapping, especially after #17687. We now have MapEntities, VisitEntities, VisitEntitiesMut, Component::visit_entities, Component::visit_entities_mut. Our only known use case at the moment for these is entity mapping. This means we have significant consolidation potential. Additionally, VisitEntitiesMut cannot be implemented for map-style collections like HashSets, as you cant "just" mutate a `&mut Entity`. Our current approach to Component mapping requires VisitEntitiesMut, meaning this category of entity collection isn't mappable. `MapEntities` is more generally applicable. Additionally, the _existence_ of the blanket From impl on VisitEntitiesMut blocks us from implementing MapEntities for HashSets (or any types we don't own), because the owner could always add a conflicting impl in the future. ## Solution Use `MapEntities` everywhere and remove all "visit entities" usages. * Add `Component::map_entities` * Remove `Component::visit_entities`, `Component::visit_entities_mut`, `VisitEntities`, and `VisitEntitiesMut` * Support deriving `Component::map_entities` in `#[derive(Coomponent)]` * Add `#[derive(MapEntities)]`, and share logic with the `Component::map_entities` derive. * Add `ComponentCloneCtx::queue_deferred`, which is command-like logic that runs immediately after normal clones. Reframe `FromWorld` fallback logic in the "reflect clone" impl to use it. This cuts out a lot of unnecessary work and I think justifies the existence of a pseudo-command interface (given how niche, yet performance sensitive this is). Note that we no longer auto-impl entity mapping for ` IntoIterator<Item = &'a Entity>` types, as this would block our ability to implement cases like `HashMap`. This means the onus is on us (or type authors) to add explicit support for types that should be mappable. Also note that the Component-related changes do not require a migration guide as there hasn't been a release with them yet. ## Migration Guide If you were previously implementing `VisitEntities` or `VisitEntitiesMut` (likely via a derive), instead use `MapEntities`. Those were almost certainly used in the context of Bevy Scenes or reflection via `ReflectMapEntities`. If you have a case that uses `VisitEntities` or `VisitEntitiesMut` directly, where `MapEntities` is not a viable replacement, please let us know! ```rust // before #[derive(VisitEntities, VisitEntitiesMut)] struct Inventory { items: Vec<Entity>, #[visit_entities(ignore)] label: String, } // after #[derive(MapEntities)] struct Inventory { #[entities] items: Vec<Entity>, label: String, } ```
This commit is contained in:
parent
55fd10502c
commit
a033f1b206
@ -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<M: #bevy_ecs_path::entity::EntityMapper>(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<TokenStream2> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>) -> 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::<Result<Vec<_>, _>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<F: FnMut(&mut Entity)>(&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<F: FnMut(Entity)>(&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<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
|
||||
#map_entities_impl
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -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;
|
||||
@ -517,14 +517,21 @@ pub trait Component: Send + Sync + 'static {
|
||||
ComponentCloneBehavior::Default
|
||||
}
|
||||
|
||||
/// Visits entities stored on the component.
|
||||
/// 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<Entity>
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Fields with `#[entities]` must implement [`MapEntities`](crate::entity::MapEntities).
|
||||
#[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)) {}
|
||||
fn map_entities<E: EntityMapper>(_this: &mut Self, _mapper: &mut E) {}
|
||||
}
|
||||
|
||||
mod private {
|
||||
@ -1158,7 +1165,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)]
|
||||
@ -2925,7 +2932,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<C: Clone + Component>(
|
||||
_commands: &mut Commands,
|
||||
source: &SourceComponent,
|
||||
ctx: &mut ComponentCloneCtx,
|
||||
) {
|
||||
@ -2952,11 +2958,7 @@ pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
///
|
||||
/// [`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;
|
||||
};
|
||||
@ -2973,9 +2975,7 @@ pub fn component_clone_via_reflect(
|
||||
if let Some(reflect_component) =
|
||||
registry.get_type_data::<crate::reflect::ReflectComponent>(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);
|
||||
|
||||
@ -2993,9 +2993,7 @@ pub fn component_clone_via_reflect(
|
||||
if let Some(reflect_component) =
|
||||
registry.get_type_data::<crate::reflect::ReflectComponent>(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);
|
||||
|
||||
@ -3018,23 +3016,12 @@ pub fn component_clone_via_reflect(
|
||||
registry.get_type_data::<crate::reflect::ReflectFromWorld>(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::<crate::reflect::ReflectComponent>(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());
|
||||
@ -3042,11 +3029,7 @@ pub fn component_clone_via_reflect(
|
||||
.read()
|
||||
.get_type_data::<crate::reflect::ReflectComponent>(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
|
||||
@ -3070,12 +3053,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)]
|
||||
|
@ -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<C: 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::<C>();
|
||||
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<ComponentId>,
|
||||
@ -350,18 +351,20 @@ pub struct EntityCloner {
|
||||
linked_cloning: bool,
|
||||
default_clone_fn: ComponentCloneFn,
|
||||
clone_queue: VecDeque<Entity>,
|
||||
deferred_commands: VecDeque<Box<dyn FnOnce(&mut World, &mut dyn EntityMapper)>>,
|
||||
}
|
||||
|
||||
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());
|
||||
|
@ -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<Entity>`] 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<Entity>`]
|
||||
/// 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<Entity>`]: 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<M: EntityMapper>(&mut self, entity_mapper: &mut M);
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E);
|
||||
}
|
||||
|
||||
impl<T: VisitEntitiesMut> MapEntities for T {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.visit_entities_mut(|entity| {
|
||||
*entity = entity_mapper.get_mapped(*entity);
|
||||
});
|
||||
impl MapEntities for Entity {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
*self = entity_mapper.get_mapped(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for Option<Entity> {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
if let Some(entity) = self {
|
||||
*entity = entity_mapper.get_mapped(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BuildHasher + Default> MapEntities for HashSet<Entity, S> {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
*self = self.drain().map(|e| entity_mapper.get_mapped(e)).collect();
|
||||
}
|
||||
}
|
||||
impl MapEntities for Vec<Entity> {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
for entity in self.iter_mut() {
|
||||
*entity = entity_mapper.get_mapped(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEntities for VecDeque<Entity> {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
for entity in self.iter_mut() {
|
||||
*entity = entity_mapper.get_mapped(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: smallvec::Array<Item = Entity>> MapEntities for SmallVec<A> {
|
||||
fn map_entities<E: EntityMapper>(&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<Entity>`] to map source entities
|
||||
@ -67,8 +102,7 @@ impl<T: VisitEntitiesMut> 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
|
||||
///
|
||||
|
@ -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::*;
|
||||
|
@ -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<F: FnMut(Entity)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl<T> VisitEntities for T
|
||||
where
|
||||
for<'a> &'a T: IntoIterator<Item = &'a Entity>,
|
||||
{
|
||||
fn visit_entities<F: FnMut(Entity)>(&self, f: F) {
|
||||
self.into_iter().copied().for_each(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntities for Entity {
|
||||
fn visit_entities<F: FnMut(Entity)>(&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<F: FnMut(&mut Entity)>(&mut self, f: F);
|
||||
}
|
||||
|
||||
impl<T: VisitEntities> VisitEntitiesMut for T
|
||||
where
|
||||
for<'a> &'a mut T: IntoIterator<Item = &'a mut Entity>,
|
||||
{
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, f: F) {
|
||||
self.into_iter().for_each(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntitiesMut for Entity {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&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<Entity>,
|
||||
unordered: HashSet<Entity>,
|
||||
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<F: FnMut(&mut Entity)>(&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::<Entity>::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);
|
||||
}
|
||||
}
|
@ -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::<A>();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CaptureMapper(Vec<Entity>);
|
||||
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<Entity>,
|
||||
#[entities]
|
||||
another_one: Entity,
|
||||
#[entities]
|
||||
maybe_entity: Option<Entity>,
|
||||
#[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>, Entity, #[visit_entities(ignore)] usize);
|
||||
#[derive(Component)]
|
||||
struct MyEntitiesTuple(#[entities] Vec<Entity>, #[entities] Entity, usize);
|
||||
}
|
||||
|
@ -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::<ObservedBy>(source)
|
||||
.map(|observed_by| observed_by.0.clone())
|
||||
|
@ -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<Mut<dyn Reflect>>,
|
||||
/// 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<C: Component + Reflect + TypePath> FromType<C> 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::<C>() } {
|
||||
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::<C>(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<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
|
||||
let mut component = entity.world_scope(|world| {
|
||||
from_reflect_with_fallback::<C>(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<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
|
||||
register_component: |world: &mut World| -> ComponentId {
|
||||
world.register_component::<C>()
|
||||
},
|
||||
visit_entities: |reflect: &dyn Reflect, func: &mut dyn FnMut(Entity)| {
|
||||
let component = reflect.downcast_ref::<C>().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::<C>().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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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<C: FromReflect + VisitEntities> FromType<C> 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<C: FromReflect + VisitEntitiesMut> FromType<C> 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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Mutability = Mutable> + 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<T: RelationshipTarget>(
|
||||
_commands: &mut Commands,
|
||||
source: &SourceComponent,
|
||||
context: &mut ComponentCloneCtx,
|
||||
) {
|
||||
|
@ -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<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||
self.0 = entity_mapper.get_mapped(self.0);
|
||||
}
|
||||
}
|
||||
|
@ -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<F: FnMut(Entity)>(&self, mut f: F) {
|
||||
match self {
|
||||
Self::Entity(entity) => f(*entity),
|
||||
Self::Primary => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitEntitiesMut for WindowRef {
|
||||
fn visit_entities_mut<F: FnMut(&mut Entity)>(&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`].
|
||||
|
Loading…
Reference in New Issue
Block a user