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(),
|
Err(err) => err.into_compile_error().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let visit_entities = visit_entities(
|
let map_entities = map_entities(
|
||||||
&ast.data,
|
&ast.data,
|
||||||
&bevy_ecs_path,
|
Ident::new("this", Span::call_site()),
|
||||||
relationship.is_some(),
|
relationship.is_some(),
|
||||||
relationship_target.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);
|
let storage = storage_path(&bevy_ecs_path, attrs.storage);
|
||||||
|
|
||||||
@ -295,7 +300,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
|||||||
#clone_behavior
|
#clone_behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
#visit_entities
|
#map_entities
|
||||||
}
|
}
|
||||||
|
|
||||||
#relationship
|
#relationship
|
||||||
@ -306,19 +311,18 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
const ENTITIES: &str = "entities";
|
const ENTITIES: &str = "entities";
|
||||||
|
|
||||||
fn visit_entities(
|
pub(crate) fn map_entities(
|
||||||
data: &Data,
|
data: &Data,
|
||||||
bevy_ecs_path: &Path,
|
self_ident: Ident,
|
||||||
is_relationship: bool,
|
is_relationship: bool,
|
||||||
is_relationship_target: bool,
|
is_relationship_target: bool,
|
||||||
) -> TokenStream2 {
|
) -> Option<TokenStream2> {
|
||||||
match data {
|
match data {
|
||||||
Data::Struct(DataStruct { fields, .. }) => {
|
Data::Struct(DataStruct { fields, .. }) => {
|
||||||
let mut visit = Vec::with_capacity(fields.len());
|
let mut map = Vec::with_capacity(fields.len());
|
||||||
let mut visit_mut = Vec::with_capacity(fields.len());
|
|
||||||
|
|
||||||
let relationship = if is_relationship || is_relationship_target {
|
let relationship = if is_relationship || is_relationship_target {
|
||||||
relationship_field(fields, "VisitEntities", fields.span()).ok()
|
relationship_field(fields, "MapEntities", fields.span()).ok()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -335,27 +339,17 @@ fn visit_entities(
|
|||||||
.clone()
|
.clone()
|
||||||
.map_or(Member::from(index), Member::Named);
|
.map_or(Member::from(index), Member::Named);
|
||||||
|
|
||||||
visit.push(quote!(this.#field_member.visit_entities(&mut func);));
|
map.push(quote!(#self_ident.#field_member.map_entities(mapper);));
|
||||||
visit_mut.push(quote!(this.#field_member.visit_entities_mut(&mut func);));
|
|
||||||
});
|
});
|
||||||
if visit.is_empty() {
|
if map.is_empty() {
|
||||||
return quote!();
|
return None;
|
||||||
};
|
};
|
||||||
quote!(
|
Some(quote!(
|
||||||
fn visit_entities(this: &Self, mut func: impl FnMut(#bevy_ecs_path::entity::Entity)) {
|
#(#map)*
|
||||||
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)*
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Data::Enum(DataEnum { variants, .. }) => {
|
Data::Enum(DataEnum { variants, .. }) => {
|
||||||
let mut visit = Vec::with_capacity(variants.len());
|
let mut map = Vec::with_capacity(variants.len());
|
||||||
let mut visit_mut = Vec::with_capacity(variants.len());
|
|
||||||
|
|
||||||
for variant in variants.iter() {
|
for variant in variants.iter() {
|
||||||
let field_members = variant
|
let field_members = variant
|
||||||
@ -377,40 +371,25 @@ fn visit_entities(
|
|||||||
.map(|member| format_ident!("__self_{}", member))
|
.map(|member| format_ident!("__self_{}", member))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
visit.push(
|
map.push(
|
||||||
quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => {
|
quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => {
|
||||||
#(#field_idents.visit_entities(&mut func);)*
|
#(#field_idents.map_entities(mapper);)*
|
||||||
}),
|
|
||||||
);
|
|
||||||
visit_mut.push(
|
|
||||||
quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => {
|
|
||||||
#(#field_idents.visit_entities_mut(&mut func);)*
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if visit.is_empty() {
|
if map.is_empty() {
|
||||||
return quote!();
|
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)) {
|
Some(quote!(
|
||||||
use #bevy_ecs_path::entity::VisitEntitiesMut;
|
match #self_ident {
|
||||||
match this {
|
#(#map,)*
|
||||||
#(#visit_mut,)*
|
_ => {}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
Data::Union(_) => quote!(),
|
Data::Union(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,13 @@ mod query_filter;
|
|||||||
mod states;
|
mod states;
|
||||||
mod world_query;
|
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 bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Ident, Span};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
|
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(
|
#[proc_macro_derive(MapEntities, attributes(entities))]
|
||||||
input: TokenStream,
|
pub fn derive_map_entities(input: TokenStream) -> TokenStream {
|
||||||
trait_name: TokenStream2,
|
|
||||||
gen_methods: impl FnOnce(Vec<TokenStream2>) -> TokenStream2,
|
|
||||||
) -> TokenStream {
|
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
let ecs_path = bevy_ecs_path();
|
let ecs_path = bevy_ecs_path();
|
||||||
|
let map_entities_impl = map_entities(
|
||||||
let named_fields = match get_struct_fields(&ast.data) {
|
&ast.data,
|
||||||
Ok(fields) => fields,
|
Ident::new("self", Span::call_site()),
|
||||||
Err(e) => return e.into_compile_error().into(),
|
false,
|
||||||
};
|
false,
|
||||||
|
);
|
||||||
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 struct_name = &ast.ident;
|
let struct_name = &ast.ident;
|
||||||
|
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics #ecs_path::entity:: #trait_name for #struct_name #ty_generics {
|
impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
|
||||||
#methods
|
fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
|
||||||
}
|
#map_entities_impl
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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);)*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -4,12 +4,12 @@ use crate::{
|
|||||||
archetype::ArchetypeFlags,
|
archetype::ArchetypeFlags,
|
||||||
bundle::BundleInfo,
|
bundle::BundleInfo,
|
||||||
change_detection::{MaybeLocation, MAX_CHANGE_AGE},
|
change_detection::{MaybeLocation, MAX_CHANGE_AGE},
|
||||||
entity::{ComponentCloneCtx, Entity, SourceComponent},
|
entity::{ComponentCloneCtx, Entity, EntityMapper, SourceComponent},
|
||||||
query::DebugCheckedUnwrap,
|
query::DebugCheckedUnwrap,
|
||||||
relationship::RelationshipHookMode,
|
relationship::RelationshipHookMode,
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
storage::{SparseSetIndex, SparseSets, Table, TableRow},
|
storage::{SparseSetIndex, SparseSets, Table, TableRow},
|
||||||
system::{Commands, Local, SystemParam},
|
system::{Local, SystemParam},
|
||||||
world::{DeferredWorld, FromWorld, World},
|
world::{DeferredWorld, FromWorld, World},
|
||||||
};
|
};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
@ -517,14 +517,21 @@ pub trait Component: Send + Sync + 'static {
|
|||||||
ComponentCloneBehavior::Default
|
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]
|
#[inline]
|
||||||
fn visit_entities(_this: &Self, _f: impl FnMut(Entity)) {}
|
fn map_entities<E: EntityMapper>(_this: &mut Self, _mapper: &mut E) {}
|
||||||
|
|
||||||
/// Returns pointers to every entity stored on the component. This will be used to remap entity references when this entity
|
|
||||||
/// is cloned.
|
|
||||||
#[inline]
|
|
||||||
fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod private {
|
mod private {
|
||||||
@ -1158,7 +1165,7 @@ impl ComponentDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Function type that can be used to clone an entity.
|
/// 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`].
|
/// The clone behavior to use when cloning a [`Component`].
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[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.
|
/// It will panic if set as handler for any other component.
|
||||||
///
|
///
|
||||||
pub fn component_clone_via_clone<C: Clone + Component>(
|
pub fn component_clone_via_clone<C: Clone + Component>(
|
||||||
_commands: &mut Commands,
|
|
||||||
source: &SourceComponent,
|
source: &SourceComponent,
|
||||||
ctx: &mut ComponentCloneCtx,
|
ctx: &mut ComponentCloneCtx,
|
||||||
) {
|
) {
|
||||||
@ -2952,11 +2958,7 @@ pub fn component_clone_via_clone<C: Clone + Component>(
|
|||||||
///
|
///
|
||||||
/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone
|
/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
pub fn component_clone_via_reflect(
|
pub fn component_clone_via_reflect(source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
|
||||||
commands: &mut Commands,
|
|
||||||
source: &SourceComponent,
|
|
||||||
ctx: &mut ComponentCloneCtx,
|
|
||||||
) {
|
|
||||||
let Some(app_registry) = ctx.type_registry().cloned() else {
|
let Some(app_registry) = ctx.type_registry().cloned() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -2973,9 +2975,7 @@ pub fn component_clone_via_reflect(
|
|||||||
if let Some(reflect_component) =
|
if let Some(reflect_component) =
|
||||||
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||||
{
|
{
|
||||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
reflect_component.map_entities(&mut *component, ctx.entity_mapper());
|
||||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
drop(registry);
|
drop(registry);
|
||||||
|
|
||||||
@ -2993,9 +2993,7 @@ pub fn component_clone_via_reflect(
|
|||||||
if let Some(reflect_component) =
|
if let Some(reflect_component) =
|
||||||
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||||
{
|
{
|
||||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
reflect_component.map_entities(&mut *component, ctx.entity_mapper());
|
||||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
drop(registry);
|
drop(registry);
|
||||||
|
|
||||||
@ -3018,23 +3016,12 @@ pub fn component_clone_via_reflect(
|
|||||||
registry.get_type_data::<crate::reflect::ReflectFromWorld>(type_id)
|
registry.get_type_data::<crate::reflect::ReflectFromWorld>(type_id)
|
||||||
{
|
{
|
||||||
let reflect_from_world = reflect_from_world.clone();
|
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 source_component_cloned = source_component_reflect.to_dynamic();
|
||||||
let component_layout = component_info.layout();
|
let component_layout = component_info.layout();
|
||||||
let target = ctx.target();
|
let target = ctx.target();
|
||||||
let component_id = ctx.component_id();
|
let component_id = ctx.component_id();
|
||||||
for entity in mapped_entities.iter_mut() {
|
|
||||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
|
||||||
}
|
|
||||||
drop(registry);
|
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);
|
let mut component = reflect_from_world.from_world(world);
|
||||||
assert_eq!(type_id, (*component).type_id());
|
assert_eq!(type_id, (*component).type_id());
|
||||||
component.apply(source_component_cloned.as_partial_reflect());
|
component.apply(source_component_cloned.as_partial_reflect());
|
||||||
@ -3042,11 +3029,7 @@ pub fn component_clone_via_reflect(
|
|||||||
.read()
|
.read()
|
||||||
.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||||
{
|
{
|
||||||
let mut i = 0;
|
reflect_component.map_entities(&mut *component, mapper);
|
||||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
|
||||||
*entity = mapped_entities[i];
|
|
||||||
i += 1;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - component_id is from the same world as target entity
|
// - 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.
|
/// Noop implementation of component clone handler function.
|
||||||
///
|
///
|
||||||
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
|
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
|
||||||
pub fn component_clone_ignore(
|
pub fn component_clone_ignore(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {}
|
||||||
_commands: &mut Commands,
|
|
||||||
_source: &SourceComponent,
|
|
||||||
_ctx: &mut ComponentCloneCtx,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper for components clone specialization using autoderef.
|
/// Wrapper for components clone specialization using autoderef.
|
||||||
#[doc(hidden)]
|
#[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_platform_support::collections::{HashMap, HashSet};
|
||||||
use bevy_ptr::{Ptr, PtrMut};
|
use bevy_ptr::{Ptr, PtrMut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use core::any::TypeId;
|
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::{
|
use crate::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::{Component, ComponentId, ComponentInfo},
|
component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo},
|
||||||
entity::Entity,
|
entity::{hash_map::EntityHashMap, Entities, Entity, EntityMapper},
|
||||||
query::DebugCheckedUnwrap,
|
query::DebugCheckedUnwrap,
|
||||||
|
relationship::RelationshipHookMode,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,9 +169,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
|
|||||||
/// - Component being written is not registered in the world.
|
/// - Component being written is not registered in the world.
|
||||||
/// - `ComponentId` of component being written does not match expected `ComponentId`.
|
/// - `ComponentId` of component being written does not match expected `ComponentId`.
|
||||||
pub fn write_target_component<C: Component>(&mut self, mut component: C) {
|
pub fn write_target_component<C: Component>(&mut self, mut component: C) {
|
||||||
C::visit_entities_mut(&mut component, |entity| {
|
C::map_entities(&mut component, &mut self.mapper);
|
||||||
*entity = self.mapper.get_mapped(*entity);
|
|
||||||
});
|
|
||||||
let short_name = disqualified::ShortName::of::<C>();
|
let short_name = disqualified::ShortName::of::<C>();
|
||||||
if self.target_component_written {
|
if self.target_component_written {
|
||||||
panic!("Trying to write component '{short_name}' multiple times")
|
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.mapper.set_mapped(entity, target);
|
||||||
self.entity_cloner.clone_queue.push_back(entity);
|
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
|
/// 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`]
|
/// 2. component-defined handler using [`Component::clone_behavior`]
|
||||||
/// 3. default handler override using [`EntityClonerBuilder::with_default_clone_fn`].
|
/// 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.
|
/// 4. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EntityCloner {
|
pub struct EntityCloner {
|
||||||
filter_allows_components: bool,
|
filter_allows_components: bool,
|
||||||
filter: HashSet<ComponentId>,
|
filter: HashSet<ComponentId>,
|
||||||
@ -350,18 +351,20 @@ pub struct EntityCloner {
|
|||||||
linked_cloning: bool,
|
linked_cloning: bool,
|
||||||
default_clone_fn: ComponentCloneFn,
|
default_clone_fn: ComponentCloneFn,
|
||||||
clone_queue: VecDeque<Entity>,
|
clone_queue: VecDeque<Entity>,
|
||||||
|
deferred_commands: VecDeque<Box<dyn FnOnce(&mut World, &mut dyn EntityMapper)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EntityCloner {
|
impl Default for EntityCloner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
filter_allows_components: false,
|
filter_allows_components: false,
|
||||||
filter: Default::default(),
|
|
||||||
clone_behavior_overrides: Default::default(),
|
|
||||||
move_components: false,
|
move_components: false,
|
||||||
linked_cloning: false,
|
linked_cloning: false,
|
||||||
default_clone_fn: ComponentCloneBehavior::global_default_fn(),
|
default_clone_fn: ComponentCloneBehavior::global_default_fn(),
|
||||||
|
filter: Default::default(),
|
||||||
|
clone_behavior_overrides: Default::default(),
|
||||||
clone_queue: Default::default(),
|
clone_queue: Default::default(),
|
||||||
|
deferred_commands: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,10 +479,6 @@ impl EntityCloner {
|
|||||||
|
|
||||||
let archetype = source_entity.archetype();
|
let archetype = source_entity.archetype();
|
||||||
bundle_scratch = BundleScratch::with_capacity(archetype.component_count());
|
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() {
|
for component in archetype.components() {
|
||||||
if !self.is_cloning_allowed(&component) {
|
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();
|
world.flush();
|
||||||
|
|
||||||
|
for deferred in self.deferred_commands.drain(..) {
|
||||||
|
(deferred)(world, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
if !world.entities.contains(target) {
|
if !world.entities.contains(target) {
|
||||||
panic!("Target entity does not exist");
|
panic!("Target entity does not exist");
|
||||||
}
|
}
|
||||||
@ -609,7 +612,6 @@ impl EntityCloner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A builder for configuring [`EntityCloner`]. See [`EntityCloner`] for more information.
|
/// A builder for configuring [`EntityCloner`]. See [`EntityCloner`] for more information.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EntityClonerBuilder<'w> {
|
pub struct EntityClonerBuilder<'w> {
|
||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
entity_cloner: EntityCloner,
|
entity_cloner: EntityCloner,
|
||||||
@ -845,7 +847,6 @@ mod tests {
|
|||||||
entity::{hash_map::EntityHashMap, Entity, EntityCloner, SourceComponent},
|
entity::{hash_map::EntityHashMap, Entity, EntityCloner, SourceComponent},
|
||||||
prelude::{ChildOf, Children, Resource},
|
prelude::{ChildOf, Children, Resource},
|
||||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld},
|
reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld},
|
||||||
system::Commands,
|
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@ -861,7 +862,6 @@ mod tests {
|
|||||||
component::{Component, ComponentCloneBehavior},
|
component::{Component, ComponentCloneBehavior},
|
||||||
entity::{EntityCloner, SourceComponent},
|
entity::{EntityCloner, SourceComponent},
|
||||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld},
|
reflect::{AppTypeRegistry, ReflectComponent, ReflectFromWorld},
|
||||||
system::Commands,
|
|
||||||
};
|
};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr};
|
use bevy_reflect::{std_traits::ReflectDefault, FromType, Reflect, ReflectFromPtr};
|
||||||
@ -991,11 +991,7 @@ mod tests {
|
|||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
struct B;
|
struct B;
|
||||||
|
|
||||||
fn test_handler(
|
fn test_handler(source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
|
||||||
_commands: &mut Commands,
|
|
||||||
source: &SourceComponent,
|
|
||||||
ctx: &mut ComponentCloneCtx,
|
|
||||||
) {
|
|
||||||
let registry = ctx.type_registry().unwrap();
|
let registry = ctx.type_registry().unwrap();
|
||||||
assert!(source.read_reflect(®istry.read()).is_none());
|
assert!(source.read_reflect(®istry.read()).is_none());
|
||||||
}
|
}
|
||||||
@ -1287,11 +1283,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn clone_entity_with_dynamic_components() {
|
fn clone_entity_with_dynamic_components() {
|
||||||
const COMPONENT_SIZE: usize = 10;
|
const COMPONENT_SIZE: usize = 10;
|
||||||
fn test_handler(
|
fn test_handler(source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
|
||||||
_commands: &mut Commands,
|
|
||||||
source: &SourceComponent,
|
|
||||||
ctx: &mut ComponentCloneCtx,
|
|
||||||
) {
|
|
||||||
// SAFETY: the passed in ptr corresponds to copy-able data that matches the type of the source / target component
|
// SAFETY: the passed in ptr corresponds to copy-able data that matches the type of the source / target component
|
||||||
unsafe {
|
unsafe {
|
||||||
ctx.write_target_component_ptr(source.ptr());
|
ctx.write_target_component_ptr(source.ptr());
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
|
pub use bevy_ecs_macros::MapEntities;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
entity::Entity,
|
entity::{hash_map::EntityHashMap, Entity},
|
||||||
identifier::masks::{IdentifierMask, HIGH_MASK},
|
identifier::masks::{IdentifierMask, HIGH_MASK},
|
||||||
world::World,
|
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.
|
/// 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
|
/// (usually by using an [`EntityHashMap<Entity>`] between source entities and entities in the
|
||||||
/// current world).
|
/// current world).
|
||||||
///
|
///
|
||||||
/// This trait is similar to [`VisitEntitiesMut`]. They differ in that [`VisitEntitiesMut`] operates
|
/// Components use [`Component::map_entities`](crate::component::Component::map_entities) to map
|
||||||
/// on `&mut Entity` and allows for in-place modification, while this trait makes no assumption that
|
/// entities in the context of scenes and entity cloning, which generally uses [`MapEntities`] internally
|
||||||
/// such in-place modification is occurring, which is impossible for types such as [`HashSet<Entity>`]
|
/// to map each field (see those docs for usage).
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
/// [`HashSet<Entity>`]: bevy_platform_support::collections::HashSet
|
/// [`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
|
/// Implementors should look up any and all [`Entity`] values stored within `self` and
|
||||||
/// update them to the mapped values via `entity_mapper`.
|
/// 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 {
|
impl MapEntities for Entity {
|
||||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
|
||||||
self.visit_entities_mut(|entity| {
|
*self = entity_mapper.get_mapped(*self);
|
||||||
*entity = entity_mapper.get_mapped(*entity);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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`].
|
/// 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
|
/// 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).
|
/// 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)
|
/// This is used by [`MapEntities`] implementors.
|
||||||
/// and [`Component::visit_entities_mut`](crate::component::Component::visit_entities_mut) to map a component's entities.
|
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
mod clone_entities;
|
mod clone_entities;
|
||||||
mod entity_set;
|
mod entity_set;
|
||||||
mod map_entities;
|
mod map_entities;
|
||||||
mod visit_entities;
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
|
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
|
||||||
@ -48,7 +47,6 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
|||||||
pub use clone_entities::*;
|
pub use clone_entities::*;
|
||||||
pub use entity_set::*;
|
pub use entity_set::*;
|
||||||
pub use map_entities::*;
|
pub use map_entities::*;
|
||||||
pub use visit_entities::*;
|
|
||||||
|
|
||||||
mod hash;
|
mod hash;
|
||||||
pub use 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,
|
bundle::Bundle,
|
||||||
change_detection::Ref,
|
change_detection::Ref,
|
||||||
component::{Component, ComponentId, RequiredComponents, RequiredComponentsError},
|
component::{Component, ComponentId, RequiredComponents, RequiredComponentsError},
|
||||||
entity::Entity,
|
entity::{Entity, EntityMapper},
|
||||||
entity_disabling::DefaultQueryFilters,
|
entity_disabling::DefaultQueryFilters,
|
||||||
prelude::Or,
|
prelude::Or,
|
||||||
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
|
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
|
||||||
@ -146,7 +146,6 @@ mod tests {
|
|||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut};
|
|
||||||
use bevy_platform_support::collections::HashSet;
|
use bevy_platform_support::collections::HashSet;
|
||||||
use bevy_tasks::{ComputeTaskPool, TaskPool};
|
use bevy_tasks::{ComputeTaskPool, TaskPool};
|
||||||
use core::{
|
use core::{
|
||||||
@ -2705,8 +2704,19 @@ mod tests {
|
|||||||
World::new().register_component::<A>();
|
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]
|
#[test]
|
||||||
fn visit_struct_entities() {
|
fn map_struct_entities() {
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
#[expect(
|
#[expect(
|
||||||
unused,
|
unused,
|
||||||
@ -2733,30 +2743,22 @@ mod tests {
|
|||||||
let e3 = world.spawn_empty().id();
|
let e3 = world.spawn_empty().id();
|
||||||
|
|
||||||
let mut foo = Foo(1, e1);
|
let mut foo = Foo(1, e1);
|
||||||
let mut entities = Vec::new();
|
let mut mapper = CaptureMapper::default();
|
||||||
Component::visit_entities(&foo, |e| entities.push(e));
|
Component::map_entities(&mut foo, &mut mapper);
|
||||||
assert_eq!(&entities, &[e1]);
|
assert_eq!(&mapper.0, &[e1]);
|
||||||
|
|
||||||
let mut entities = Vec::new();
|
|
||||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
|
||||||
assert_eq!(&entities, &[e1]);
|
|
||||||
|
|
||||||
let mut bar = Bar {
|
let mut bar = Bar {
|
||||||
a: e1,
|
a: e1,
|
||||||
b: 1,
|
b: 1,
|
||||||
c: vec![e2, e3],
|
c: vec![e2, e3],
|
||||||
};
|
};
|
||||||
let mut entities = Vec::new();
|
let mut mapper = CaptureMapper::default();
|
||||||
Component::visit_entities(&bar, |e| entities.push(e));
|
Component::map_entities(&mut bar, &mut mapper);
|
||||||
assert_eq!(&entities, &[e1, e2, e3]);
|
assert_eq!(&mapper.0, &[e1, e2, e3]);
|
||||||
|
|
||||||
let mut entities = Vec::new();
|
|
||||||
Component::visit_entities_mut(&mut bar, |e| entities.push(*e));
|
|
||||||
assert_eq!(&entities, &[e1, e2, e3]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn visit_enum_entities() {
|
fn map_enum_entities() {
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
#[expect(
|
#[expect(
|
||||||
unused,
|
unused,
|
||||||
@ -2779,26 +2781,18 @@ mod tests {
|
|||||||
let e3 = world.spawn_empty().id();
|
let e3 = world.spawn_empty().id();
|
||||||
|
|
||||||
let mut foo = Foo::Bar(1, e1);
|
let mut foo = Foo::Bar(1, e1);
|
||||||
let mut entities = Vec::new();
|
let mut mapper = CaptureMapper::default();
|
||||||
Component::visit_entities(&foo, |e| entities.push(e));
|
Component::map_entities(&mut foo, &mut mapper);
|
||||||
assert_eq!(&entities, &[e1]);
|
assert_eq!(&mapper.0, &[e1]);
|
||||||
|
|
||||||
let mut entities = Vec::new();
|
|
||||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
|
||||||
assert_eq!(&entities, &[e1]);
|
|
||||||
|
|
||||||
let mut foo = Foo::Baz {
|
let mut foo = Foo::Baz {
|
||||||
a: e1,
|
a: e1,
|
||||||
b: 1,
|
b: 1,
|
||||||
c: vec![e2, e3],
|
c: vec![e2, e3],
|
||||||
};
|
};
|
||||||
let mut entities = Vec::new();
|
let mut mapper = CaptureMapper::default();
|
||||||
Component::visit_entities(&foo, |e| entities.push(e));
|
Component::map_entities(&mut foo, &mut mapper);
|
||||||
assert_eq!(&entities, &[e1, e2, e3]);
|
assert_eq!(&mapper.0, &[e1, e2, e3]);
|
||||||
|
|
||||||
let mut entities = Vec::new();
|
|
||||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
|
||||||
assert_eq!(&entities, &[e1, e2, e3]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(
|
#[expect(
|
||||||
@ -2827,16 +2821,18 @@ mod tests {
|
|||||||
field1: ComponentB,
|
field1: ComponentB,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, VisitEntities, VisitEntitiesMut)]
|
#[derive(Component)]
|
||||||
struct MyEntities {
|
struct MyEntities {
|
||||||
|
#[entities]
|
||||||
entities: Vec<Entity>,
|
entities: Vec<Entity>,
|
||||||
|
#[entities]
|
||||||
another_one: Entity,
|
another_one: Entity,
|
||||||
|
#[entities]
|
||||||
maybe_entity: Option<Entity>,
|
maybe_entity: Option<Entity>,
|
||||||
#[expect(
|
#[expect(
|
||||||
dead_code,
|
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."
|
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,
|
something_else: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2844,6 +2840,6 @@ mod tests {
|
|||||||
dead_code,
|
dead_code,
|
||||||
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
|
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)]
|
#[derive(Component)]
|
||||||
struct MyEntitiesTuple(Vec<Entity>, Entity, #[visit_entities(ignore)] usize);
|
struct MyEntitiesTuple(#[entities] Vec<Entity>, #[entities] Entity, usize);
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,8 @@ use crate::{
|
|||||||
component::{
|
component::{
|
||||||
Component, ComponentCloneBehavior, ComponentHook, HookContext, Mutable, StorageType,
|
Component, ComponentCloneBehavior, ComponentHook, HookContext, Mutable, StorageType,
|
||||||
},
|
},
|
||||||
entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, SourceComponent},
|
entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, EntityMapper, SourceComponent},
|
||||||
observer::ObserverState,
|
observer::ObserverState,
|
||||||
system::Commands,
|
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@ -64,15 +63,11 @@ impl EntityClonerBuilder<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn component_clone_observed_by(
|
fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
|
||||||
commands: &mut Commands,
|
|
||||||
_source: &SourceComponent,
|
|
||||||
ctx: &mut ComponentCloneCtx,
|
|
||||||
) {
|
|
||||||
let target = ctx.target();
|
let target = ctx.target();
|
||||||
let source = ctx.source();
|
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
|
let observed_by = world
|
||||||
.get::<ObservedBy>(source)
|
.get::<ObservedBy>(source)
|
||||||
.map(|observed_by| observed_by.0.clone())
|
.map(|observed_by| observed_by.0.clone())
|
||||||
|
@ -121,10 +121,8 @@ pub struct ReflectComponentFns {
|
|||||||
pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>,
|
pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>,
|
||||||
/// Function pointer implementing [`ReflectComponent::reflect_mut()`].
|
/// Function pointer implementing [`ReflectComponent::reflect_mut()`].
|
||||||
pub reflect_mut: fn(FilteredEntityMut) -> Option<Mut<dyn Reflect>>,
|
pub reflect_mut: fn(FilteredEntityMut) -> Option<Mut<dyn Reflect>>,
|
||||||
/// Function pointer implementing [`ReflectComponent::visit_entities()`].
|
/// Function pointer implementing [`ReflectComponent::map_entities()`].
|
||||||
pub visit_entities: fn(&dyn Reflect, &mut dyn FnMut(Entity)),
|
pub map_entities: fn(&mut dyn Reflect, &mut dyn EntityMapper),
|
||||||
/// Function pointer implementing [`ReflectComponent::visit_entities_mut()`].
|
|
||||||
pub visit_entities_mut: fn(&mut dyn Reflect, &mut dyn FnMut(&mut Entity)),
|
|
||||||
/// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`].
|
/// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`].
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -291,18 +289,9 @@ impl ReflectComponent {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a dynamic version of [`Component::visit_entities`].
|
/// Calls a dynamic version of [`Component::map_entities`].
|
||||||
pub fn visit_entities(&self, component: &dyn Reflect, func: &mut dyn FnMut(Entity)) {
|
pub fn map_entities(&self, component: &mut dyn Reflect, func: &mut dyn EntityMapper) {
|
||||||
(self.0.visit_entities)(component, func);
|
(self.0.map_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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,19 +319,18 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
|
|||||||
apply_or_insert_mapped: |entity,
|
apply_or_insert_mapped: |entity,
|
||||||
reflected_component,
|
reflected_component,
|
||||||
registry,
|
registry,
|
||||||
mapper,
|
mut mapper,
|
||||||
relationship_hook_mode| {
|
relationship_hook_mode| {
|
||||||
let map_fn = map_function(mapper);
|
|
||||||
if C::Mutability::MUTABLE {
|
if C::Mutability::MUTABLE {
|
||||||
// SAFETY: guard ensures `C` is a mutable component
|
// SAFETY: guard ensures `C` is a mutable component
|
||||||
if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::<C>() } {
|
if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::<C>() } {
|
||||||
component.apply(reflected_component.as_partial_reflect());
|
component.apply(reflected_component.as_partial_reflect());
|
||||||
C::visit_entities_mut(&mut component, map_fn);
|
C::map_entities(&mut component, &mut mapper);
|
||||||
} else {
|
} else {
|
||||||
let mut component = entity.world_scope(|world| {
|
let mut component = entity.world_scope(|world| {
|
||||||
from_reflect_with_fallback::<C>(reflected_component, world, registry)
|
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
|
entity
|
||||||
.insert_with_relationship_hook_mode(component, relationship_hook_mode);
|
.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| {
|
let mut component = entity.world_scope(|world| {
|
||||||
from_reflect_with_fallback::<C>(reflected_component, world, registry)
|
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);
|
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 {
|
register_component: |world: &mut World| -> ComponentId {
|
||||||
world.register_component::<C>()
|
world.register_component::<C>()
|
||||||
},
|
},
|
||||||
visit_entities: |reflect: &dyn Reflect, func: &mut dyn FnMut(Entity)| {
|
map_entities: |reflect: &mut dyn Reflect, mut mapper: &mut dyn EntityMapper| {
|
||||||
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)| {
|
|
||||||
let component = reflect.downcast_mut::<C>().unwrap();
|
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 from_world;
|
||||||
mod map_entities;
|
mod map_entities;
|
||||||
mod resource;
|
mod resource;
|
||||||
mod visit_entities;
|
|
||||||
|
|
||||||
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
||||||
pub use component::{ReflectComponent, ReflectComponentFns};
|
pub use component::{ReflectComponent, ReflectComponentFns};
|
||||||
@ -25,7 +24,6 @@ pub use entity_commands::ReflectCommandExt;
|
|||||||
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
|
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
|
||||||
pub use map_entities::ReflectMapEntities;
|
pub use map_entities::ReflectMapEntities;
|
||||||
pub use resource::{ReflectResource, ReflectResourceFns};
|
pub use resource::{ReflectResource, ReflectResourceFns};
|
||||||
pub use visit_entities::{ReflectVisitEntities, ReflectVisitEntitiesMut};
|
|
||||||
|
|
||||||
/// A [`Resource`] storing [`TypeRegistry`] for
|
/// A [`Resource`] storing [`TypeRegistry`] for
|
||||||
/// type registrations relevant to a whole app.
|
/// 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},
|
component::{Component, HookContext, Mutable},
|
||||||
entity::{ComponentCloneCtx, Entity, SourceComponent},
|
entity::{ComponentCloneCtx, Entity, SourceComponent},
|
||||||
error::{ignore, CommandWithEntity, HandleError},
|
error::{ignore, CommandWithEntity, HandleError},
|
||||||
system::{
|
system::entity_command::{self},
|
||||||
entity_command::{self},
|
|
||||||
Commands,
|
|
||||||
},
|
|
||||||
world::{DeferredWorld, EntityWorldMut},
|
world::{DeferredWorld, EntityWorldMut},
|
||||||
};
|
};
|
||||||
use log::warn;
|
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
|
/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured
|
||||||
/// to spawn recursively.
|
/// to spawn recursively.
|
||||||
pub fn clone_relationship_target<T: RelationshipTarget>(
|
pub fn clone_relationship_target<T: RelationshipTarget>(
|
||||||
_commands: &mut Commands,
|
|
||||||
source: &SourceComponent,
|
source: &SourceComponent,
|
||||||
context: &mut ComponentCloneCtx,
|
context: &mut ComponentCloneCtx,
|
||||||
) {
|
) {
|
||||||
|
@ -214,24 +214,24 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::{
|
entity::{hash_map::EntityHashMap, Entity, EntityMapper, MapEntities},
|
||||||
hash_map::EntityHashMap, Entity, EntityMapper, MapEntities, VisitEntities,
|
|
||||||
VisitEntitiesMut,
|
|
||||||
},
|
|
||||||
hierarchy::ChildOf,
|
hierarchy::ChildOf,
|
||||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
|
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
|
|
||||||
use crate::dynamic_scene::DynamicScene;
|
use crate::dynamic_scene::DynamicScene;
|
||||||
use crate::dynamic_scene_builder::DynamicSceneBuilder;
|
use crate::dynamic_scene_builder::DynamicSceneBuilder;
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Debug, VisitEntities, VisitEntitiesMut)]
|
#[derive(Resource, Reflect, MapEntities, Debug)]
|
||||||
#[reflect(Resource, MapEntities)]
|
#[reflect(Resource, MapEntities)]
|
||||||
struct TestResource {
|
struct TestResource {
|
||||||
|
#[entities]
|
||||||
entity_a: Entity,
|
entity_a: Entity,
|
||||||
|
#[entities]
|
||||||
entity_b: Entity,
|
entity_b: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +360,7 @@ mod tests {
|
|||||||
struct B(pub Entity);
|
struct B(pub Entity);
|
||||||
|
|
||||||
impl MapEntities for B {
|
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);
|
self.0 = entity_mapper.get_mapped(self.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use alloc::{borrow::ToOwned, string::String};
|
|||||||
use core::num::NonZero;
|
use core::num::NonZero;
|
||||||
|
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
entity::{Entity, EntityBorrow, VisitEntities, VisitEntitiesMut},
|
entity::{Entity, EntityBorrow},
|
||||||
prelude::Component,
|
prelude::Component,
|
||||||
};
|
};
|
||||||
use bevy_math::{CompassOctant, DVec2, IVec2, UVec2, Vec2};
|
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.
|
/// A flattened representation of a window reference for equality/hashing purposes.
|
||||||
///
|
///
|
||||||
/// For most purposes you probably want to use the unnormalized version [`WindowRef`].
|
/// For most purposes you probably want to use the unnormalized version [`WindowRef`].
|
||||||
|
Loading…
Reference in New Issue
Block a user