Improved Entity Mapping and Cloning (#17687)
Fixes #17535 Bevy's approach to handling "entity mapping" during spawning and cloning needs some work. The addition of [Relations](https://github.com/bevyengine/bevy/pull/17398) both [introduced a new "duplicate entities" bug when spawning scenes in the scene system](#17535) and made the weaknesses of the current mapping system exceedingly clear: 1. Entity mapping requires _a ton_ of boilerplate (implement or derive VisitEntities and VisitEntitesMut, then register / reflect MapEntities). Knowing the incantation is challenging and if you forget to do it in part or in whole, spawning subtly breaks. 2. Entity mapping a spawned component in scenes incurs unnecessary overhead: look up ReflectMapEntities, create a _brand new temporary instance_ of the component using FromReflect, map the entities in that instance, and then apply that on top of the actual component using reflection. We can do much better. Additionally, while our new [Entity cloning system](https://github.com/bevyengine/bevy/pull/16132) is already pretty great, it has some areas we can make better: * It doesn't expose semantic info about the clone (ex: ignore or "clone empty"), meaning we can't key off of that in places where it would be useful, such as scene spawning. Rather than duplicating this info across contexts, I think it makes more sense to add that info to the clone system, especially given that we'd like to use cloning code in some of our spawning scenarios. * EntityCloner is currently built in a way that prioritizes a single entity clone * EntityCloner's recursive cloning is built to be done "inside out" in a parallel context (queue commands that each have a clone of EntityCloner). By making EntityCloner the orchestrator of the clone we can remove internal arcs, improve the clarity of the code, make EntityCloner mutable again, and simplify the builder code. * EntityCloner does not currently take into account entity mapping. This is necessary to do true "bullet proof" cloning, would allow us to unify the per-component scene spawning and cloning UX, and ultimately would allow us to use EntityCloner in place of raw reflection for scenes like `Scene(World)` (which would give us a nice performance boost: fewer archetype moves, less reflection overhead). ## Solution ### Improved Entity Mapping First, components now have first-class "entity visiting and mapping" behavior: ```rust #[derive(Component, Reflect)] #[reflect(Component)] struct Inventory { size: usize, #[entities] items: Vec<Entity>, } ``` Any field with the `#[entities]` annotation will be viewable and mappable when cloning and spawning scenes. Compare that to what was required before! ```rust #[derive(Component, Reflect, VisitEntities, VisitEntitiesMut)] #[reflect(Component, MapEntities)] struct Inventory { #[visit_entities(ignore)] size: usize, items: Vec<Entity>, } ``` Additionally, for relationships `#[entities]` is implied, meaning this "just works" in scenes and cloning: ```rust #[derive(Component, Reflect)] #[relationship(relationship_target = Children)] #[reflect(Component)] struct ChildOf(pub Entity); ``` Note that Component _does not_ implement `VisitEntities` directly. Instead, it has `Component::visit_entities` and `Component::visit_entities_mut` methods. This is for a few reasons: 1. We cannot implement `VisitEntities for C: Component` because that would conflict with our impl of VisitEntities for anything that implements `IntoIterator<Item=Entity>`. Preserving that impl is more important from a UX perspective. 2. We should not implement `Component: VisitEntities` VisitEntities in the Component derive, as that would increase the burden of manual Component trait implementors. 3. Making VisitEntitiesMut directly callable for components would make it easy to invalidate invariants defined by a component author. By putting it in the `Component` impl, we can make it harder to call naturally / unavailable to autocomplete using `fn visit_entities_mut(this: &mut Self, ...)`. `ReflectComponent::apply_or_insert` is now `ReflectComponent::apply_or_insert_mapped`. By moving mapping inside this impl, we remove the need to go through the reflection system to do entity mapping, meaning we no longer need to create a clone of the target component, map the entities in that component, and patch those values on top. This will make spawning mapped entities _much_ faster (The default `Component::visit_entities_mut` impl is an inlined empty function, so it will incur no overhead for unmapped entities). ### The Bug Fix To solve #17535, spawning code now skips entities with the new `ComponentCloneBehavior::Ignore` and `ComponentCloneBehavior::RelationshipTarget` variants (note RelationshipTarget is a temporary "workaround" variant that allows scenes to skip these components. This is a temporary workaround that can be removed as these cases should _really_ be using EntityCloner logic, which should be done in a followup PR. When that is done, `ComponentCloneBehavior::RelationshipTarget` can be merged into the normal `ComponentCloneBehavior::Custom`). ### Improved Cloning * `Option<ComponentCloneHandler>` has been replaced by `ComponentCloneBehavior`, which encodes additional intent and context (ex: `Default`, `Ignore`, `Custom`, `RelationshipTarget` (this last one is temporary)). * Global per-world entity cloning configuration has been removed. This felt overly complicated, increased our API surface, and felt too generic. Each clone context can have different requirements (ex: what a user wants in a specific system, what a scene spawner wants, etc). I'd prefer to see how far context-specific EntityCloners get us first. * EntityCloner's internals have been reworked to remove Arcs and make it mutable. * EntityCloner is now directly stored on EntityClonerBuilder, simplifying the code somewhat * EntityCloner's "bundle scratch" pattern has been moved into the new BundleScratch type, improving its usability and making it usable in other contexts (such as future cross-world cloning code). Currently this is still private, but with some higher level safe APIs it could be used externally for making dynamic bundles * EntityCloner's recursive cloning behavior has been "externalized". It is now responsible for orchestrating recursive clones, meaning it no longer needs to be sharable/clone-able across threads / read-only. * EntityCloner now does entity mapping during clones, like scenes do. This gives behavior parity and also makes it more generically useful. * `RelatonshipTarget::RECURSIVE_SPAWN` is now `RelationshipTarget::LINKED_SPAWN`, and this field is used when cloning relationship targets to determine if cloning should happen recursively. The new `LINKED_SPAWN` term was picked to make it more generically applicable across spawning and cloning scenarios. ## Next Steps * I think we should adapt EntityCloner to support cross world cloning. I think this PR helps set the stage for that by making the internals slightly more generalized. We could have a CrossWorldEntityCloner that reuses a lot of this infrastructure. * Once we support cross world cloning, we should use EntityCloner to spawn `Scene(World)` scenes. This would yield significant performance benefits (no archetype moves, less reflection overhead). --------- Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
f2a65c2dd3
commit
3c8fae2390
@ -2,7 +2,8 @@ use core::hint::black_box;
|
||||
|
||||
use benches::bench;
|
||||
use bevy_ecs::bundle::Bundle;
|
||||
use bevy_ecs::component::ComponentCloneHandler;
|
||||
use bevy_ecs::component::ComponentCloneBehavior;
|
||||
use bevy_ecs::entity::EntityCloner;
|
||||
use bevy_ecs::hierarchy::ChildOf;
|
||||
use bevy_ecs::reflect::AppTypeRegistry;
|
||||
use bevy_ecs::{component::Component, world::World};
|
||||
@ -52,7 +53,10 @@ type ComplexBundle = (C1, C2, C3, C4, C5, C6, C7, C8, C9, C10);
|
||||
|
||||
/// Sets the [`ComponentCloneHandler`] for all explicit and required components in a bundle `B` to
|
||||
/// use the [`Reflect`] trait instead of [`Clone`].
|
||||
fn set_reflect_clone_handler<B: Bundle + GetTypeRegistration>(world: &mut World) {
|
||||
fn reflection_cloner<B: Bundle + GetTypeRegistration>(
|
||||
world: &mut World,
|
||||
recursive: bool,
|
||||
) -> EntityCloner {
|
||||
// Get mutable access to the type registry, creating it if it does not exist yet.
|
||||
let registry = world.get_resource_or_init::<AppTypeRegistry>();
|
||||
|
||||
@ -67,12 +71,15 @@ fn set_reflect_clone_handler<B: Bundle + GetTypeRegistration>(world: &mut World)
|
||||
// this bundle are saved.
|
||||
let component_ids: Vec<_> = world.register_bundle::<B>().contributed_components().into();
|
||||
|
||||
let clone_handlers = world.get_component_clone_handlers_mut();
|
||||
let mut builder = EntityCloner::build(world);
|
||||
|
||||
// Overwrite the clone handler for all components in the bundle to use `Reflect`, not `Clone`.
|
||||
for component in component_ids {
|
||||
clone_handlers.set_component_handler(component, ComponentCloneHandler::reflect_handler());
|
||||
builder.override_clone_behavior_with_id(component, ComponentCloneBehavior::reflect());
|
||||
}
|
||||
builder.recursive(recursive);
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
/// A helper function that benchmarks running the [`EntityCommands::clone_and_spawn()`] command on a
|
||||
@ -91,18 +98,18 @@ fn bench_clone<B: Bundle + Default + GetTypeRegistration>(
|
||||
) {
|
||||
let mut world = World::default();
|
||||
|
||||
if clone_via_reflect {
|
||||
set_reflect_clone_handler::<B>(&mut world);
|
||||
}
|
||||
let mut cloner = if clone_via_reflect {
|
||||
reflection_cloner::<B>(&mut world, false)
|
||||
} else {
|
||||
EntityCloner::default()
|
||||
};
|
||||
|
||||
// Spawn the first entity, which will be cloned in the benchmark routine.
|
||||
let id = world.spawn(B::default()).id();
|
||||
|
||||
b.iter(|| {
|
||||
// Queue the command to clone the entity.
|
||||
world.commands().entity(black_box(id)).clone_and_spawn();
|
||||
|
||||
// Run the command.
|
||||
// clones the given entity
|
||||
cloner.spawn_clone(&mut world, black_box(id));
|
||||
world.flush();
|
||||
});
|
||||
}
|
||||
@ -125,9 +132,15 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
|
||||
) {
|
||||
let mut world = World::default();
|
||||
|
||||
if clone_via_reflect {
|
||||
set_reflect_clone_handler::<B>(&mut world);
|
||||
}
|
||||
let mut cloner = if clone_via_reflect {
|
||||
reflection_cloner::<B>(&mut world, true)
|
||||
} else {
|
||||
let mut builder = EntityCloner::build(&mut world);
|
||||
builder.recursive(true);
|
||||
builder.finish()
|
||||
};
|
||||
|
||||
// Make the clone command recursive, so children are cloned as well.
|
||||
|
||||
// Spawn the first entity, which will be cloned in the benchmark routine.
|
||||
let id = world.spawn(B::default()).id();
|
||||
@ -148,18 +161,8 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all `set_parent()` commands.
|
||||
world.flush();
|
||||
|
||||
b.iter(|| {
|
||||
world
|
||||
.commands()
|
||||
.entity(black_box(id))
|
||||
.clone_and_spawn_with(|builder| {
|
||||
// Make the clone command recursive, so children are cloned as well.
|
||||
builder.recursive(true);
|
||||
});
|
||||
|
||||
cloner.spawn_clone(&mut world, black_box(id));
|
||||
world.flush();
|
||||
});
|
||||
}
|
||||
|
||||
@ -33,12 +33,7 @@ use crate::{
|
||||
|
||||
use bevy_app::{Animation, App, Plugin, PostUpdate};
|
||||
use bevy_asset::{Asset, AssetApp, AssetEvents, Assets};
|
||||
use bevy_ecs::{
|
||||
entity::{VisitEntities, VisitEntitiesMut},
|
||||
prelude::*,
|
||||
reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut},
|
||||
world::EntityMutExcept,
|
||||
};
|
||||
use bevy_ecs::{prelude::*, world::EntityMutExcept};
|
||||
use bevy_math::FloatOrd;
|
||||
use bevy_platform_support::{collections::HashMap, hash::NoOpHash};
|
||||
use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath};
|
||||
@ -207,16 +202,16 @@ impl Hash for AnimationTargetId {
|
||||
/// Note that each entity can only be animated by one animation player at a
|
||||
/// time. However, you can change [`AnimationTarget`]'s `player` property at
|
||||
/// runtime to change which player is responsible for animating the entity.
|
||||
#[derive(Clone, Copy, Component, Reflect, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(Component, MapEntities, VisitEntities, VisitEntitiesMut)]
|
||||
#[derive(Clone, Copy, Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct AnimationTarget {
|
||||
/// The ID of this animation target.
|
||||
///
|
||||
/// Typically, this is derived from the path.
|
||||
#[visit_entities(ignore)]
|
||||
pub id: AnimationTargetId,
|
||||
|
||||
/// The entity containing the [`AnimationPlayer`].
|
||||
#[entities]
|
||||
pub player: Entity,
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, ToTokens};
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use std::collections::HashSet;
|
||||
use syn::{
|
||||
parenthesized,
|
||||
@ -9,8 +9,8 @@ use syn::{
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
token::{Comma, Paren},
|
||||
Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Fields, Ident, LitStr, Path, Result,
|
||||
Token, Visibility,
|
||||
Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Fields, Ident, Index, LitStr, Member,
|
||||
Path, Result, Token, Visibility,
|
||||
};
|
||||
|
||||
pub fn derive_event(input: TokenStream) -> TokenStream {
|
||||
@ -51,6 +51,8 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
|
||||
})
|
||||
}
|
||||
|
||||
const ENTITIES_ATTR: &str = "entities";
|
||||
|
||||
pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
let mut ast = parse_macro_input!(input as DeriveInput);
|
||||
let bevy_ecs_path: Path = crate::bevy_ecs_path();
|
||||
@ -69,6 +71,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
};
|
||||
|
||||
let visit_entities = visit_entities(&ast.data, &bevy_ecs_path, relationship.is_some());
|
||||
|
||||
let storage = storage_path(&bevy_ecs_path, attrs.storage);
|
||||
|
||||
let on_add_path = attrs.on_add.map(|path| path.to_token_stream());
|
||||
@ -117,12 +121,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
|
||||
let on_despawn_path = if attrs
|
||||
.relationship_target
|
||||
.is_some_and(|target| target.despawn_descendants)
|
||||
.is_some_and(|target| target.linked_spawn)
|
||||
{
|
||||
if attrs.on_despawn.is_some() {
|
||||
return syn::Error::new(
|
||||
ast.span(),
|
||||
"Custom on_despawn hooks are not supported as this RelationshipTarget already defines an on_despawn hook, via the despawn_descendants attribute",
|
||||
"Custom on_despawn hooks are not supported as this RelationshipTarget already defines an on_despawn hook, via the 'linked_spawn' attribute",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
@ -202,12 +206,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
.then_some(quote! { #bevy_ecs_path::component::Immutable })
|
||||
.unwrap_or(quote! { #bevy_ecs_path::component::Mutable });
|
||||
|
||||
let clone_handler = if relationship_target.is_some() {
|
||||
quote!(#bevy_ecs_path::component::ComponentCloneHandler::ignore())
|
||||
let clone_behavior = if relationship_target.is_some() {
|
||||
quote!(#bevy_ecs_path::component::ComponentCloneBehavior::RelationshipTarget(#bevy_ecs_path::relationship::clone_relationship_target::<Self>))
|
||||
} else {
|
||||
quote!(
|
||||
use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase};
|
||||
(&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::<Self>::default()).get_component_clone_handler()
|
||||
use #bevy_ecs_path::component::{DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone};
|
||||
(&&&#bevy_ecs_path::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
|
||||
)
|
||||
};
|
||||
|
||||
@ -238,9 +242,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
#on_remove
|
||||
#on_despawn
|
||||
|
||||
fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler {
|
||||
#clone_handler
|
||||
fn clone_behavior() -> #bevy_ecs_path::component::ComponentCloneBehavior {
|
||||
#clone_behavior
|
||||
}
|
||||
|
||||
#visit_entities
|
||||
}
|
||||
|
||||
#relationship
|
||||
@ -249,6 +255,144 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> TokenStream2 {
|
||||
match data {
|
||||
Data::Struct(DataStruct { ref fields, .. }) => {
|
||||
let mut visited_fields = Vec::new();
|
||||
let mut visited_indices = Vec::new();
|
||||
match fields {
|
||||
Fields::Named(fields) => {
|
||||
for field in &fields.named {
|
||||
if field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.meta.path().is_ident(ENTITIES_ATTR))
|
||||
{
|
||||
if let Some(ident) = field.ident.clone() {
|
||||
visited_fields.push(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
for (index, field) in fields.unnamed.iter().enumerate() {
|
||||
if index == 0 && is_relationship {
|
||||
visited_indices.push(Index::from(0));
|
||||
} else if field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.meta.path().is_ident(ENTITIES_ATTR))
|
||||
{
|
||||
visited_indices.push(Index::from(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unit => {}
|
||||
}
|
||||
|
||||
if visited_fields.is_empty() && visited_indices.is_empty() {
|
||||
TokenStream2::new()
|
||||
} else {
|
||||
let visit = visited_fields
|
||||
.iter()
|
||||
.map(|field| quote!(this.#field.visit_entities(&mut func);))
|
||||
.chain(
|
||||
visited_indices
|
||||
.iter()
|
||||
.map(|index| quote!(this.#index.visit_entities(&mut func);)),
|
||||
);
|
||||
let visit_mut = visited_fields
|
||||
.iter()
|
||||
.map(|field| quote!(this.#field.visit_entities_mut(&mut func);))
|
||||
.chain(
|
||||
visited_indices
|
||||
.iter()
|
||||
.map(|index| quote!(this.#index.visit_entities_mut(&mut func);)),
|
||||
);
|
||||
quote!(
|
||||
fn visit_entities(this: &Self, mut func: impl FnMut(Entity)) {
|
||||
use #bevy_ecs_path::entity::VisitEntities;
|
||||
#(#visit)*
|
||||
}
|
||||
|
||||
fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut Entity)) {
|
||||
use #bevy_ecs_path::entity::VisitEntitiesMut;
|
||||
#(#visit_mut)*
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Data::Enum(data_enum) => {
|
||||
let mut has_visited_fields = false;
|
||||
let mut visit_variants = Vec::with_capacity(data_enum.variants.len());
|
||||
let mut visit_variants_mut = Vec::with_capacity(data_enum.variants.len());
|
||||
for variant in &data_enum.variants {
|
||||
let mut variant_fields = Vec::new();
|
||||
let mut variant_fields_mut = Vec::new();
|
||||
|
||||
let mut visit_variant_fields = Vec::new();
|
||||
let mut visit_variant_fields_mut = Vec::new();
|
||||
|
||||
for (index, field) in variant.fields.iter().enumerate() {
|
||||
if field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.meta.path().is_ident(ENTITIES_ATTR))
|
||||
{
|
||||
has_visited_fields = true;
|
||||
let field_member = ident_or_index(field.ident.as_ref(), index);
|
||||
let field_ident = format_ident!("field_{}", field_member);
|
||||
|
||||
variant_fields.push(quote!(#field_member: ref #field_ident));
|
||||
variant_fields_mut.push(quote!(#field_member: ref mut #field_ident));
|
||||
|
||||
visit_variant_fields.push(quote!(#field_ident.visit_entities(&mut func);));
|
||||
visit_variant_fields_mut
|
||||
.push(quote!(#field_ident.visit_entities_mut(&mut func);));
|
||||
}
|
||||
}
|
||||
|
||||
let ident = &variant.ident;
|
||||
visit_variants.push(quote!(Self::#ident {#(#variant_fields,)* ..} => {
|
||||
#(#visit_variant_fields)*
|
||||
}));
|
||||
visit_variants_mut.push(quote!(Self::#ident {#(#variant_fields_mut,)* ..} => {
|
||||
#(#visit_variant_fields_mut)*
|
||||
}));
|
||||
}
|
||||
if has_visited_fields {
|
||||
quote!(
|
||||
fn visit_entities(this: &Self, mut func: impl FnMut(Entity)) {
|
||||
use #bevy_ecs_path::entity::VisitEntities;
|
||||
match this {
|
||||
#(#visit_variants,)*
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_entities_mut(this: &mut Self, mut func: impl FnMut(&mut Entity)) {
|
||||
use #bevy_ecs_path::entity::VisitEntitiesMut;
|
||||
match this {
|
||||
#(#visit_variants_mut,)*
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
TokenStream2::new()
|
||||
}
|
||||
}
|
||||
Data::Union(_) => TokenStream2::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
|
||||
ident.map_or_else(
|
||||
|| Member::Unnamed(index.into()),
|
||||
|ident| Member::Named(ident.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn document_required_components(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let paths = parse_macro_input!(attr with Punctuated::<Require, Comma>::parse_terminated)
|
||||
.iter()
|
||||
@ -326,7 +470,7 @@ struct Relationship {
|
||||
|
||||
struct RelationshipTarget {
|
||||
relationship: Ident,
|
||||
despawn_descendants: bool,
|
||||
linked_spawn: bool,
|
||||
}
|
||||
|
||||
// values for `storage` attribute
|
||||
@ -468,18 +612,18 @@ impl Parse for Relationship {
|
||||
impl Parse for RelationshipTarget {
|
||||
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
|
||||
let mut relationship_ident = None;
|
||||
let mut despawn_descendants_exists = false;
|
||||
let mut linked_spawn_exists = false;
|
||||
syn::custom_keyword!(relationship);
|
||||
syn::custom_keyword!(despawn_descendants);
|
||||
syn::custom_keyword!(linked_spawn);
|
||||
let mut done = false;
|
||||
loop {
|
||||
if input.peek(relationship) {
|
||||
input.parse::<relationship>()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
relationship_ident = Some(input.parse::<Ident>()?);
|
||||
} else if input.peek(despawn_descendants) {
|
||||
input.parse::<despawn_descendants>()?;
|
||||
despawn_descendants_exists = true;
|
||||
} else if input.peek(linked_spawn) {
|
||||
input.parse::<linked_spawn>()?;
|
||||
linked_spawn_exists = true;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
@ -494,7 +638,7 @@ impl Parse for RelationshipTarget {
|
||||
let relationship = relationship_ident.ok_or_else(|| syn::Error::new(input.span(), "RelationshipTarget derive must specify a relationship via #[relationship_target(relationship = X)"))?;
|
||||
Ok(RelationshipTarget {
|
||||
relationship,
|
||||
despawn_descendants: despawn_descendants_exists,
|
||||
linked_spawn: linked_spawn_exists,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -586,8 +730,10 @@ fn derive_relationship_target(
|
||||
let relationship = &relationship_target.relationship;
|
||||
let struct_name = &ast.ident;
|
||||
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
||||
let linked_spawn = relationship_target.linked_spawn;
|
||||
Ok(Some(quote! {
|
||||
impl #impl_generics #bevy_ecs_path::relationship::RelationshipTarget for #struct_name #type_generics #where_clause {
|
||||
const LINKED_SPAWN: bool = #linked_spawn;
|
||||
type Relationship = #relationship;
|
||||
type Collection = #collection;
|
||||
|
||||
|
||||
@ -587,7 +587,10 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
|
||||
component::derive_resource(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Component, attributes(component, relationship, relationship_target))]
|
||||
#[proc_macro_derive(
|
||||
Component,
|
||||
attributes(component, relationship, relationship_target, entities)
|
||||
)]
|
||||
pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
component::derive_component(input)
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
query::DebugCheckedUnwrap,
|
||||
resource::Resource,
|
||||
storage::{SparseSetIndex, SparseSets, Table, TableRow},
|
||||
system::{Local, SystemParam},
|
||||
system::{Commands, Local, SystemParam},
|
||||
world::{DeferredWorld, FromWorld, World},
|
||||
};
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
@ -179,10 +179,6 @@ pub use bevy_ecs_macros::require;
|
||||
/// }
|
||||
///
|
||||
/// # let mut world = World::default();
|
||||
/// // This will implicitly also insert C with the init_c() constructor
|
||||
/// let id = world.spawn(A).id();
|
||||
/// assert_eq!(&C(10), world.entity(id).get::<C>().unwrap());
|
||||
///
|
||||
/// // This will implicitly also insert C with the `|| C(20)` constructor closure
|
||||
/// let id = world.spawn(B).id();
|
||||
/// assert_eq!(&C(20), world.entity(id).get::<C>().unwrap());
|
||||
@ -446,10 +442,20 @@ pub trait Component: Send + Sync + 'static {
|
||||
|
||||
/// Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::default_handler()
|
||||
/// See [Handlers section of `EntityClonerBuilder`](crate::entity::EntityClonerBuilder#handlers) to understand how this affects handler priority.
|
||||
#[inline]
|
||||
fn clone_behavior() -> ComponentCloneBehavior {
|
||||
ComponentCloneBehavior::Default
|
||||
}
|
||||
|
||||
/// Visits entities stored on the component.
|
||||
#[inline]
|
||||
fn visit_entities(_this: &Self, _f: impl FnMut(Entity)) {}
|
||||
|
||||
/// Returns pointers to every entity stored on the component. This will be used to remap entity references when this entity
|
||||
/// is cloned.
|
||||
#[inline]
|
||||
fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {}
|
||||
}
|
||||
|
||||
mod private {
|
||||
@ -793,6 +799,12 @@ impl ComponentInfo {
|
||||
self.descriptor.mutable
|
||||
}
|
||||
|
||||
/// Returns [`ComponentCloneBehavior`] of the current component.
|
||||
#[inline]
|
||||
pub fn clone_behavior(&self) -> &ComponentCloneBehavior {
|
||||
&self.descriptor.clone_behavior
|
||||
}
|
||||
|
||||
/// Returns the [`TypeId`] of the underlying component type.
|
||||
/// Returns `None` if the component does not correspond to a Rust type.
|
||||
#[inline]
|
||||
@ -949,6 +961,7 @@ pub struct ComponentDescriptor {
|
||||
// None if the underlying type doesn't need to be dropped
|
||||
drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
|
||||
mutable: bool,
|
||||
clone_behavior: ComponentCloneBehavior,
|
||||
}
|
||||
|
||||
// We need to ignore the `drop` field in our `Debug` impl
|
||||
@ -961,6 +974,7 @@ impl Debug for ComponentDescriptor {
|
||||
.field("type_id", &self.type_id)
|
||||
.field("layout", &self.layout)
|
||||
.field("mutable", &self.mutable)
|
||||
.field("clone_behavior", &self.clone_behavior)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -986,6 +1000,7 @@ impl ComponentDescriptor {
|
||||
layout: Layout::new::<T>(),
|
||||
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
|
||||
mutable: T::Mutability::MUTABLE,
|
||||
clone_behavior: T::clone_behavior(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1000,6 +1015,7 @@ impl ComponentDescriptor {
|
||||
layout: Layout,
|
||||
drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
|
||||
mutable: bool,
|
||||
clone_behavior: ComponentCloneBehavior,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
@ -1009,6 +1025,7 @@ impl ComponentDescriptor {
|
||||
layout,
|
||||
drop,
|
||||
mutable,
|
||||
clone_behavior,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1026,6 +1043,7 @@ impl ComponentDescriptor {
|
||||
layout: Layout::new::<T>(),
|
||||
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
|
||||
mutable: true,
|
||||
clone_behavior: ComponentCloneBehavior::Default,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1038,6 +1056,7 @@ impl ComponentDescriptor {
|
||||
layout: Layout::new::<T>(),
|
||||
drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
|
||||
mutable: true,
|
||||
clone_behavior: ComponentCloneBehavior::Default,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1068,107 +1087,55 @@ impl ComponentDescriptor {
|
||||
}
|
||||
|
||||
/// Function type that can be used to clone an entity.
|
||||
pub type ComponentCloneFn = fn(&mut DeferredWorld, &mut ComponentCloneCtx);
|
||||
pub type ComponentCloneFn = fn(&mut Commands, &mut ComponentCloneCtx);
|
||||
|
||||
/// A struct instructing which clone handler to use when cloning a component.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentCloneHandler(Option<ComponentCloneFn>);
|
||||
|
||||
impl ComponentCloneHandler {
|
||||
/// Use the global default function to clone the component with this handler.
|
||||
pub fn default_handler() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
|
||||
/// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped.
|
||||
pub fn ignore() -> Self {
|
||||
Self(Some(component_clone_ignore))
|
||||
}
|
||||
/// The clone behavior to use when cloning a [`Component`].
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ComponentCloneBehavior {
|
||||
/// Uses the default behavior (which is passed to [`ComponentCloneBehavior::resolve`])
|
||||
#[default]
|
||||
Default,
|
||||
/// Do not clone this component.
|
||||
Ignore,
|
||||
/// Uses a custom [`ComponentCloneFn`].
|
||||
Custom(ComponentCloneFn),
|
||||
/// Uses a [`ComponentCloneFn`] that produces an empty version of the given relationship target.
|
||||
// TODO: this exists so that the current scene spawning code can know when to skip these components.
|
||||
// When we move to actually cloning entities in scene spawning code, this should be removed in favor of Custom, as the
|
||||
// distinction will no longer be necessary.
|
||||
RelationshipTarget(ComponentCloneFn),
|
||||
}
|
||||
|
||||
impl ComponentCloneBehavior {
|
||||
/// Set clone handler based on `Clone` trait.
|
||||
///
|
||||
/// If set as a handler for a component that is not the same as the one used to create this handler, it will panic.
|
||||
pub fn clone_handler<C: Component + Clone>() -> Self {
|
||||
Self(Some(component_clone_via_clone::<C>))
|
||||
pub fn clone<C: Component + Clone>() -> Self {
|
||||
Self::Custom(component_clone_via_clone::<C>)
|
||||
}
|
||||
|
||||
/// Set clone handler based on `Reflect` trait.
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub fn reflect_handler() -> Self {
|
||||
Self(Some(component_clone_via_reflect))
|
||||
pub fn reflect() -> Self {
|
||||
Self::Custom(component_clone_via_reflect)
|
||||
}
|
||||
|
||||
/// Set a custom handler for the component.
|
||||
pub fn custom_handler(handler: ComponentCloneFn) -> Self {
|
||||
Self(Some(handler))
|
||||
}
|
||||
|
||||
/// Get [`ComponentCloneFn`] representing this handler or `None` if set to default handler.
|
||||
pub fn get_handler(&self) -> Option<ComponentCloneFn> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A registry of component clone handlers. Allows to set global default and per-component clone function for all components in the world.
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentCloneHandlers {
|
||||
handlers: Vec<Option<ComponentCloneFn>>,
|
||||
default_handler: ComponentCloneFn,
|
||||
}
|
||||
|
||||
impl ComponentCloneHandlers {
|
||||
/// Sets the default handler for this registry. All components with [`default`](ComponentCloneHandler::default_handler) handler, as well as any component that does not have an
|
||||
/// explicitly registered clone function will use this handler.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
pub fn set_default_handler(&mut self, handler: ComponentCloneFn) {
|
||||
self.default_handler = handler;
|
||||
}
|
||||
|
||||
/// Returns the currently registered default handler.
|
||||
pub fn get_default_handler(&self) -> ComponentCloneFn {
|
||||
self.default_handler
|
||||
}
|
||||
|
||||
/// Sets a handler for a specific component.
|
||||
///
|
||||
/// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority.
|
||||
pub fn set_component_handler(&mut self, id: ComponentId, handler: ComponentCloneHandler) {
|
||||
if id.0 >= self.handlers.len() {
|
||||
self.handlers.resize(id.0 + 1, None);
|
||||
}
|
||||
self.handlers[id.0] = handler.0;
|
||||
}
|
||||
|
||||
/// Checks if the specified component is registered. If not, the component will use the default global handler.
|
||||
///
|
||||
/// This will return an incorrect result if `id` did not come from the same world as `self`.
|
||||
pub fn is_handler_registered(&self, id: ComponentId) -> bool {
|
||||
self.handlers.get(id.0).is_some_and(Option::is_some)
|
||||
}
|
||||
|
||||
/// Gets a handler to clone a component. This can be one of the following:
|
||||
/// - Custom clone function for this specific component.
|
||||
/// - Default global handler.
|
||||
/// - A [`component_clone_ignore`] (no cloning).
|
||||
///
|
||||
/// This will return an incorrect result if `id` did not come from the same world as `self`.
|
||||
pub fn get_handler(&self, id: ComponentId) -> ComponentCloneFn {
|
||||
match self.handlers.get(id.0) {
|
||||
Some(Some(handler)) => *handler,
|
||||
Some(None) | None => self.default_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ComponentCloneHandlers {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handlers: Default::default(),
|
||||
/// Returns the "global default"
|
||||
pub fn global_default_fn() -> ComponentCloneFn {
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
default_handler: component_clone_via_reflect,
|
||||
return component_clone_via_reflect;
|
||||
#[cfg(not(feature = "bevy_reflect"))]
|
||||
default_handler: component_clone_ignore,
|
||||
return component_clone_ignore;
|
||||
}
|
||||
|
||||
/// Resolves the [`ComponentCloneBehavior`] to a [`ComponentCloneFn`]. If [`ComponentCloneBehavior::Default`] is
|
||||
/// specified, the given `default` function will be used.
|
||||
pub fn resolve(&self, default: ComponentCloneFn) -> ComponentCloneFn {
|
||||
match self {
|
||||
ComponentCloneBehavior::Default => default,
|
||||
ComponentCloneBehavior::Ignore => component_clone_ignore,
|
||||
ComponentCloneBehavior::Custom(custom)
|
||||
| ComponentCloneBehavior::RelationshipTarget(custom) => *custom,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1179,7 +1146,6 @@ pub struct Components {
|
||||
components: Vec<ComponentInfo>,
|
||||
indices: TypeIdMap<ComponentId>,
|
||||
resource_indices: TypeIdMap<ComponentId>,
|
||||
component_clone_handlers: ComponentCloneHandlers,
|
||||
}
|
||||
|
||||
impl Components {
|
||||
@ -1237,9 +1203,6 @@ impl Components {
|
||||
T::register_component_hooks(&mut info.hooks);
|
||||
|
||||
info.required_components = required_components;
|
||||
let clone_handler = T::get_component_clone_handler();
|
||||
self.component_clone_handlers
|
||||
.set_component_handler(id, clone_handler);
|
||||
}
|
||||
id
|
||||
}
|
||||
@ -1596,16 +1559,6 @@ impl Components {
|
||||
.map(|info| &mut info.required_by)
|
||||
}
|
||||
|
||||
/// Retrieves the [`ComponentCloneHandlers`]. Can be used to get clone functions for components.
|
||||
pub fn get_component_clone_handlers(&self) -> &ComponentCloneHandlers {
|
||||
&self.component_clone_handlers
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components.
|
||||
pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers {
|
||||
&mut self.component_clone_handlers
|
||||
}
|
||||
|
||||
/// Type-erased equivalent of [`Components::component_id()`].
|
||||
#[inline]
|
||||
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
|
||||
@ -2248,12 +2201,11 @@ pub fn enforce_no_required_components_recursion(
|
||||
}
|
||||
|
||||
/// Component [clone handler function](ComponentCloneFn) implemented using the [`Clone`] trait.
|
||||
/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for the specific component it is implemented for.
|
||||
/// Can be [set](Component::clone_behavior) as clone handler for the specific component it is implemented for.
|
||||
/// It will panic if set as handler for any other component.
|
||||
///
|
||||
/// See [`ComponentCloneHandlers`] for more details.
|
||||
pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
_world: &mut DeferredWorld,
|
||||
_commands: &mut Commands,
|
||||
ctx: &mut ComponentCloneCtx,
|
||||
) {
|
||||
if let Some(component) = ctx.read_source_component::<C>() {
|
||||
@ -2262,7 +2214,7 @@ pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
}
|
||||
|
||||
/// Component [clone handler function](ComponentCloneFn) implemented using reflect.
|
||||
/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component,
|
||||
/// Can be [set](Component::clone_behavior) as clone handler for any registered component,
|
||||
/// but only reflected components will be cloned.
|
||||
///
|
||||
/// To clone a component using this handler, the following must be true:
|
||||
@ -2275,10 +2227,10 @@ pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
///
|
||||
/// If any of the conditions is not satisfied, the component will be skipped.
|
||||
///
|
||||
/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details.
|
||||
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) {
|
||||
let Some(registry) = ctx.type_registry() else {
|
||||
pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentCloneCtx) {
|
||||
let Some(app_registry) = ctx.type_registry().cloned() else {
|
||||
return;
|
||||
};
|
||||
let Some(source_component_reflect) = ctx.read_source_component_reflect() else {
|
||||
@ -2287,16 +2239,24 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen
|
||||
let component_info = ctx.component_info();
|
||||
// checked in read_source_component_reflect
|
||||
let type_id = component_info.type_id().unwrap();
|
||||
let registry = registry.read();
|
||||
let registry = app_registry.read();
|
||||
|
||||
// Try to clone using ReflectFromReflect
|
||||
if let Some(reflect_from_reflect) =
|
||||
registry.get_type_data::<bevy_reflect::ReflectFromReflect>(type_id)
|
||||
{
|
||||
if let Some(component) =
|
||||
if let Some(mut component) =
|
||||
reflect_from_reflect.from_reflect(source_component_reflect.as_partial_reflect())
|
||||
{
|
||||
if let Some(reflect_component) =
|
||||
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||
{
|
||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
||||
});
|
||||
}
|
||||
drop(registry);
|
||||
|
||||
ctx.write_target_component_reflect(component);
|
||||
return;
|
||||
}
|
||||
@ -2316,14 +2276,36 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen
|
||||
registry.get_type_data::<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.clone_value();
|
||||
let component_layout = component_info.layout();
|
||||
let target = ctx.target();
|
||||
let component_id = ctx.component_id();
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
for entity in mapped_entities.iter_mut() {
|
||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
||||
}
|
||||
drop(registry);
|
||||
commands.queue(move |world: &mut World| {
|
||||
let mut component = reflect_from_world.from_world(world);
|
||||
assert_eq!(type_id, (*component).type_id());
|
||||
component.apply(source_component_cloned.as_partial_reflect());
|
||||
if let Some(reflect_component) = app_registry
|
||||
.read()
|
||||
.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||
{
|
||||
let mut i = 0;
|
||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
||||
*entity = mapped_entities[i];
|
||||
i += 1;
|
||||
});
|
||||
}
|
||||
// SAFETY:
|
||||
// - component_id is from the same world as target entity
|
||||
// - component is a valid value represented by component_id
|
||||
@ -2341,14 +2323,14 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen
|
||||
|
||||
/// Noop implementation of component clone handler function.
|
||||
///
|
||||
/// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details.
|
||||
pub fn component_clone_ignore(_world: &mut DeferredWorld, _ctx: &mut ComponentCloneCtx) {}
|
||||
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
|
||||
pub fn component_clone_ignore(_commands: &mut Commands, _ctx: &mut ComponentCloneCtx) {}
|
||||
|
||||
/// Wrapper for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub struct ComponentCloneSpecializationWrapper<T>(PhantomData<T>);
|
||||
pub struct DefaultCloneBehaviorSpecialization<T>(PhantomData<T>);
|
||||
|
||||
impl<T> Default for ComponentCloneSpecializationWrapper<T> {
|
||||
impl<T> Default for DefaultCloneBehaviorSpecialization<T> {
|
||||
fn default() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
@ -2356,22 +2338,22 @@ impl<T> Default for ComponentCloneSpecializationWrapper<T> {
|
||||
|
||||
/// Base trait for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentCloneBase {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
|
||||
pub trait DefaultCloneBehaviorBase {
|
||||
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
|
||||
}
|
||||
impl<C: Component> ComponentCloneBase for ComponentCloneSpecializationWrapper<C> {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::default_handler()
|
||||
impl<C> DefaultCloneBehaviorBase for DefaultCloneBehaviorSpecialization<C> {
|
||||
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
|
||||
ComponentCloneBehavior::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialized trait for components clone specialization using autoderef.
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentCloneViaClone {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
|
||||
pub trait DefaultCloneBehaviorViaClone {
|
||||
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
|
||||
}
|
||||
impl<C: Clone + Component> ComponentCloneViaClone for &ComponentCloneSpecializationWrapper<C> {
|
||||
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::clone_handler::<C>()
|
||||
impl<C: Clone + Component> DefaultCloneBehaviorViaClone for &DefaultCloneBehaviorSpecialization<C> {
|
||||
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
|
||||
ComponentCloneBehavior::clone::<C>()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -39,8 +39,8 @@ use super::{hash_map::EntityHashMap, VisitEntitiesMut};
|
||||
///
|
||||
/// impl MapEntities for Spring {
|
||||
/// fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
/// self.a = entity_mapper.map_entity(self.a);
|
||||
/// self.b = entity_mapper.map_entity(self.b);
|
||||
/// self.a = entity_mapper.get_mapped(self.a);
|
||||
/// self.b = entity_mapper.get_mapped(self.b);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
@ -55,7 +55,7 @@ pub trait MapEntities {
|
||||
impl<T: VisitEntitiesMut> MapEntities for T {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.visit_entities_mut(|entity| {
|
||||
*entity = entity_mapper.map_entity(*entity);
|
||||
*entity = entity_mapper.get_mapped(*entity);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -67,6 +67,9 @@ impl<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.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
@ -80,26 +83,61 @@ impl<T: VisitEntitiesMut> MapEntities for T {
|
||||
/// // Example implementation of EntityMapper where we map an entity to another entity if it exists
|
||||
/// // in the underlying `EntityHashMap`, otherwise we just return the original entity.
|
||||
/// impl EntityMapper for SimpleEntityMapper {
|
||||
/// fn map_entity(&mut self, entity: Entity) -> Entity {
|
||||
/// fn get_mapped(&mut self, entity: Entity) -> Entity {
|
||||
/// self.map.get(&entity).copied().unwrap_or(entity)
|
||||
/// }
|
||||
///
|
||||
/// fn set_mapped(&mut self, source: Entity, target: Entity) {
|
||||
/// self.map.insert(source, target);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait EntityMapper {
|
||||
/// Map an entity to another entity
|
||||
fn map_entity(&mut self, entity: Entity) -> Entity;
|
||||
/// Returns the "target" entity that maps to the given `source`.
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity;
|
||||
|
||||
/// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result
|
||||
/// of [`EntityMapper::get_mapped`].
|
||||
fn set_mapped(&mut self, source: Entity, target: Entity);
|
||||
}
|
||||
|
||||
impl EntityMapper for () {
|
||||
#[inline]
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity {
|
||||
source
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_mapped(&mut self, _source: Entity, _target: Entity) {}
|
||||
}
|
||||
|
||||
impl EntityMapper for (Entity, Entity) {
|
||||
#[inline]
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity {
|
||||
if source == self.0 {
|
||||
self.1
|
||||
} else {
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mapped(&mut self, _source: Entity, _target: Entity) {}
|
||||
}
|
||||
|
||||
impl EntityMapper for &mut dyn EntityMapper {
|
||||
fn map_entity(&mut self, entity: Entity) -> Entity {
|
||||
(*self).map_entity(entity)
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity {
|
||||
(*self).get_mapped(source)
|
||||
}
|
||||
|
||||
fn set_mapped(&mut self, source: Entity, target: Entity) {
|
||||
(*self).set_mapped(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityMapper for SceneEntityMapper<'_> {
|
||||
/// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.
|
||||
fn map_entity(&mut self, entity: Entity) -> Entity {
|
||||
if let Some(&mapped) = self.map.get(&entity) {
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity {
|
||||
if let Some(&mapped) = self.map.get(&source) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
@ -112,10 +150,25 @@ impl EntityMapper for SceneEntityMapper<'_> {
|
||||
// Prevent generations counter from being a greater value than HIGH_MASK.
|
||||
self.generations = (self.generations + 1) & HIGH_MASK;
|
||||
|
||||
self.map.insert(entity, new);
|
||||
self.map.insert(source, new);
|
||||
|
||||
new
|
||||
}
|
||||
|
||||
fn set_mapped(&mut self, source: Entity, target: Entity) {
|
||||
self.map.insert(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityMapper for EntityHashMap<Entity> {
|
||||
/// Returns the corresponding mapped entity or returns `entity` if there is no mapped entity
|
||||
fn get_mapped(&mut self, source: Entity) -> Entity {
|
||||
self.get(&source).cloned().unwrap_or(source)
|
||||
}
|
||||
|
||||
fn set_mapped(&mut self, source: Entity, target: Entity) {
|
||||
self.insert(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for [`EntityHashMap<Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
|
||||
@ -208,15 +261,15 @@ mod tests {
|
||||
let mut mapper = SceneEntityMapper::new(&mut map, &mut world);
|
||||
|
||||
let mapped_ent = Entity::from_raw(FIRST_IDX);
|
||||
let dead_ref = mapper.map_entity(mapped_ent);
|
||||
let dead_ref = mapper.get_mapped(mapped_ent);
|
||||
|
||||
assert_eq!(
|
||||
dead_ref,
|
||||
mapper.map_entity(mapped_ent),
|
||||
mapper.get_mapped(mapped_ent),
|
||||
"should persist the allocated mapping from the previous line"
|
||||
);
|
||||
assert_eq!(
|
||||
mapper.map_entity(Entity::from_raw(SECOND_IDX)).index(),
|
||||
mapper.get_mapped(Entity::from_raw(SECOND_IDX)).index(),
|
||||
dead_ref.index(),
|
||||
"should re-use the same index for further dead refs"
|
||||
);
|
||||
@ -234,7 +287,7 @@ mod tests {
|
||||
let mut world = World::new();
|
||||
|
||||
let dead_ref = SceneEntityMapper::world_scope(&mut map, &mut world, |_, mapper| {
|
||||
mapper.map_entity(Entity::from_raw(0))
|
||||
mapper.get_mapped(Entity::from_raw(0))
|
||||
});
|
||||
|
||||
// Next allocated entity should be a further generation on the same index
|
||||
@ -253,7 +306,7 @@ mod tests {
|
||||
// Create and exercise a SceneEntityMapper - should not panic because it flushes
|
||||
// `Entities` first.
|
||||
SceneEntityMapper::world_scope(&mut Default::default(), &mut world, |_, m| {
|
||||
m.map_entity(Entity::PLACEHOLDER);
|
||||
m.get_mapped(Entity::PLACEHOLDER);
|
||||
});
|
||||
|
||||
// The SceneEntityMapper should leave `Entities` in a flushed state.
|
||||
|
||||
@ -7,21 +7,17 @@
|
||||
//! [`RelationshipTarget`]: crate::relationship::RelationshipTarget
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use crate::reflect::{
|
||||
ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities,
|
||||
ReflectVisitEntitiesMut,
|
||||
};
|
||||
use crate::reflect::{ReflectComponent, ReflectFromWorld};
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
bundle::Bundle,
|
||||
component::{Component, HookContext},
|
||||
entity::{Entity, VisitEntities},
|
||||
entity::Entity,
|
||||
relationship::{RelatedSpawner, RelatedSpawnerCommands},
|
||||
system::EntityCommands,
|
||||
world::{DeferredWorld, EntityWorldMut, FromWorld, World},
|
||||
};
|
||||
use alloc::{format, string::String, vec::Vec};
|
||||
use bevy_ecs_macros::VisitEntitiesMut;
|
||||
use core::ops::Deref;
|
||||
use core::slice;
|
||||
use disqualified::ShortName;
|
||||
@ -90,19 +86,11 @@ use log::warn;
|
||||
/// assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1, child2]);
|
||||
/// assert_eq!(&**world.entity(child1).get::<Children>().unwrap(), &[grandchild]);
|
||||
/// ```
|
||||
#[derive(Component, Clone, VisitEntities, VisitEntitiesMut, PartialEq, Eq, Debug)]
|
||||
#[derive(Component, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(
|
||||
feature = "bevy_reflect",
|
||||
reflect(
|
||||
Component,
|
||||
MapEntities,
|
||||
VisitEntities,
|
||||
VisitEntitiesMut,
|
||||
PartialEq,
|
||||
Debug,
|
||||
FromWorld
|
||||
)
|
||||
reflect(Component, PartialEq, Debug, FromWorld)
|
||||
)]
|
||||
#[relationship(relationship_target = Children)]
|
||||
pub struct ChildOf(pub Entity);
|
||||
@ -139,13 +127,10 @@ impl FromWorld for ChildOf {
|
||||
///
|
||||
/// Together, these components form the "canonical parent-child hierarchy". See the [`ChildOf`] component for all full
|
||||
/// description of this relationship and instructions on how to use it.
|
||||
#[derive(Component, Default, VisitEntitiesMut, Debug, PartialEq, Eq)]
|
||||
#[relationship_target(relationship = ChildOf, despawn_descendants)]
|
||||
#[derive(Component, Default, Debug, PartialEq, Eq)]
|
||||
#[relationship_target(relationship = ChildOf, linked_spawn)]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
|
||||
#[cfg_attr(
|
||||
feature = "bevy_reflect",
|
||||
reflect(Component, MapEntities, VisitEntities, VisitEntitiesMut, FromWorld)
|
||||
)]
|
||||
#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld))]
|
||||
pub struct Children(Vec<Entity>);
|
||||
|
||||
impl<'a> IntoIterator for &'a Children {
|
||||
|
||||
@ -79,7 +79,7 @@ pub mod prelude {
|
||||
event::{Event, EventMutator, EventReader, EventWriter, Events},
|
||||
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
|
||||
name::{Name, NameOrEntity},
|
||||
observer::{CloneEntityWithObserversExt, Observer, Trigger},
|
||||
observer::{Observer, Trigger},
|
||||
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
|
||||
removal_detection::RemovedComponents,
|
||||
resource::Resource,
|
||||
@ -2663,6 +2663,102 @@ mod tests {
|
||||
World::new().register_component::<A>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visit_struct_entities() {
|
||||
#[derive(Component)]
|
||||
#[expect(
|
||||
unused,
|
||||
reason = "extra fields are used to ensure the derive works properly"
|
||||
)]
|
||||
struct Foo(usize, #[entities] Entity);
|
||||
|
||||
#[derive(Component)]
|
||||
#[expect(
|
||||
unused,
|
||||
reason = "extra fields are used to ensure the derive works properly"
|
||||
)]
|
||||
struct Bar {
|
||||
#[entities]
|
||||
a: Entity,
|
||||
b: usize,
|
||||
#[entities]
|
||||
c: Vec<Entity>,
|
||||
}
|
||||
|
||||
let mut world = World::new();
|
||||
let e1 = world.spawn_empty().id();
|
||||
let e2 = world.spawn_empty().id();
|
||||
let e3 = world.spawn_empty().id();
|
||||
|
||||
let mut foo = Foo(1, e1);
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities(&foo, |e| entities.push(e));
|
||||
assert_eq!(&entities, &[e1]);
|
||||
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
||||
assert_eq!(&entities, &[e1]);
|
||||
|
||||
let mut bar = Bar {
|
||||
a: e1,
|
||||
b: 1,
|
||||
c: vec![e2, e3],
|
||||
};
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities(&bar, |e| entities.push(e));
|
||||
assert_eq!(&entities, &[e1, e2, e3]);
|
||||
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities_mut(&mut bar, |e| entities.push(*e));
|
||||
assert_eq!(&entities, &[e1, e2, e3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visit_enum_entities() {
|
||||
#[derive(Component)]
|
||||
#[expect(
|
||||
unused,
|
||||
reason = "extra fields are used to ensure the derive works properly"
|
||||
)]
|
||||
enum Foo {
|
||||
Bar(usize, #[entities] Entity),
|
||||
Baz {
|
||||
#[entities]
|
||||
a: Entity,
|
||||
b: usize,
|
||||
#[entities]
|
||||
c: Vec<Entity>,
|
||||
},
|
||||
}
|
||||
|
||||
let mut world = World::new();
|
||||
let e1 = world.spawn_empty().id();
|
||||
let e2 = world.spawn_empty().id();
|
||||
let e3 = world.spawn_empty().id();
|
||||
|
||||
let mut foo = Foo::Bar(1, e1);
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities(&foo, |e| entities.push(e));
|
||||
assert_eq!(&entities, &[e1]);
|
||||
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
||||
assert_eq!(&entities, &[e1]);
|
||||
|
||||
let mut foo = Foo::Baz {
|
||||
a: e1,
|
||||
b: 1,
|
||||
c: vec![e2, e3],
|
||||
};
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities(&foo, |e| entities.push(e));
|
||||
assert_eq!(&entities, &[e1, e2, e3]);
|
||||
|
||||
let mut entities = Vec::new();
|
||||
Component::visit_entities_mut(&mut foo, |e| entities.push(*e));
|
||||
assert_eq!(&entities, &[e1, e2, e3]);
|
||||
}
|
||||
|
||||
#[expect(
|
||||
dead_code,
|
||||
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use crate::{
|
||||
component::{
|
||||
Component, ComponentCloneHandler, ComponentHook, HookContext, Mutable, StorageType,
|
||||
Component, ComponentCloneBehavior, ComponentHook, HookContext, Mutable, StorageType,
|
||||
},
|
||||
entity::{ComponentCloneCtx, Entity, EntityCloneBuilder},
|
||||
entity::{ComponentCloneCtx, Entity, EntityClonerBuilder},
|
||||
observer::ObserverState,
|
||||
world::{DeferredWorld, World},
|
||||
system::Commands,
|
||||
world::World,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
@ -45,34 +46,29 @@ impl Component for ObservedBy {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_component_clone_handler() -> ComponentCloneHandler {
|
||||
ComponentCloneHandler::ignore()
|
||||
fn clone_behavior() -> ComponentCloneBehavior {
|
||||
ComponentCloneBehavior::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that holds functions for configuring interaction with observers during entity cloning.
|
||||
pub trait CloneEntityWithObserversExt {
|
||||
impl EntityClonerBuilder<'_> {
|
||||
/// Sets the option to automatically add cloned entities to the observers targeting source entity.
|
||||
fn add_observers(&mut self, add_observers: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> {
|
||||
fn add_observers(&mut self, add_observers: bool) -> &mut Self {
|
||||
pub fn add_observers(&mut self, add_observers: bool) -> &mut Self {
|
||||
if add_observers {
|
||||
self.override_component_clone_handler::<ObservedBy>(
|
||||
ComponentCloneHandler::custom_handler(component_clone_observed_by),
|
||||
)
|
||||
self.override_clone_behavior::<ObservedBy>(ComponentCloneBehavior::Custom(
|
||||
component_clone_observed_by,
|
||||
))
|
||||
} else {
|
||||
self.remove_component_clone_handler_override::<ObservedBy>()
|
||||
self.remove_clone_behavior_override::<ObservedBy>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) {
|
||||
fn component_clone_observed_by(commands: &mut Commands, ctx: &mut ComponentCloneCtx) {
|
||||
let target = ctx.target();
|
||||
let source = ctx.source();
|
||||
|
||||
world.commands().queue(move |world: &mut World| {
|
||||
commands.queue(move |world: &mut World| {
|
||||
let observed_by = world
|
||||
.get::<ObservedBy>(source)
|
||||
.map(|observed_by| observed_by.0.clone())
|
||||
@ -114,13 +110,8 @@ fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentClo
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
entity::EntityCloneBuilder,
|
||||
event::Event,
|
||||
observer::{CloneEntityWithObserversExt, Trigger},
|
||||
resource::Resource,
|
||||
system::ResMut,
|
||||
world::World,
|
||||
self as bevy_ecs, entity::EntityCloner, event::Event, observer::Trigger,
|
||||
resource::Resource, system::ResMut, world::World,
|
||||
};
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
@ -143,9 +134,9 @@ mod tests {
|
||||
world.trigger_targets(E, e);
|
||||
|
||||
let e_clone = world.spawn_empty().id();
|
||||
let mut builder = EntityCloneBuilder::new(&mut world);
|
||||
builder.add_observers(true);
|
||||
builder.clone_entity(e, e_clone);
|
||||
EntityCloner::build(&mut world)
|
||||
.add_observers(true)
|
||||
.clone_entity(e, e_clone);
|
||||
|
||||
world.trigger_targets(E, [e, e_clone]);
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
mod entity_observer;
|
||||
mod runner;
|
||||
|
||||
pub use entity_observer::{CloneEntityWithObserversExt, ObservedBy};
|
||||
pub use entity_observer::ObservedBy;
|
||||
pub use runner::*;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -8,6 +8,7 @@ use alloc::boxed::Box;
|
||||
use core::any::{Any, TypeId};
|
||||
|
||||
use crate::{
|
||||
entity::EntityMapper,
|
||||
prelude::Bundle,
|
||||
world::{EntityMut, EntityWorldMut},
|
||||
};
|
||||
@ -33,8 +34,9 @@ pub struct ReflectBundleFns {
|
||||
pub insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry),
|
||||
/// Function pointer implementing [`ReflectBundle::apply`].
|
||||
pub apply: fn(EntityMut, &dyn PartialReflect, &TypeRegistry),
|
||||
/// Function pointer implementing [`ReflectBundle::apply_or_insert`].
|
||||
pub apply_or_insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry),
|
||||
/// Function pointer implementing [`ReflectBundle::apply_or_insert_mapped`].
|
||||
pub apply_or_insert_mapped:
|
||||
fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry, &mut dyn EntityMapper),
|
||||
/// Function pointer implementing [`ReflectBundle::remove`].
|
||||
pub remove: fn(&mut EntityWorldMut),
|
||||
/// Function pointer implementing [`ReflectBundle::take`].
|
||||
@ -78,13 +80,14 @@ impl ReflectBundle {
|
||||
}
|
||||
|
||||
/// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value or insert a new one if it does not exist.
|
||||
pub fn apply_or_insert(
|
||||
pub fn apply_or_insert_mapped(
|
||||
&self,
|
||||
entity: &mut EntityWorldMut,
|
||||
bundle: &dyn PartialReflect,
|
||||
registry: &TypeRegistry,
|
||||
mapper: &mut dyn EntityMapper,
|
||||
) {
|
||||
(self.0.apply_or_insert)(entity, bundle, registry);
|
||||
(self.0.apply_or_insert_mapped)(entity, bundle, registry, mapper);
|
||||
}
|
||||
|
||||
/// Removes this [`Bundle`] type from the entity. Does nothing if it doesn't exist.
|
||||
@ -166,19 +169,24 @@ impl<B: Bundle + Reflect + TypePath> FromType<B> for ReflectBundle {
|
||||
}
|
||||
}
|
||||
},
|
||||
apply_or_insert: |entity, reflected_bundle, registry| {
|
||||
apply_or_insert_mapped: |entity, reflected_bundle, registry, mapper| {
|
||||
if let Some(reflect_component) =
|
||||
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
|
||||
{
|
||||
reflect_component.apply_or_insert(entity, reflected_bundle, registry);
|
||||
reflect_component.apply_or_insert_mapped(
|
||||
entity,
|
||||
reflected_bundle,
|
||||
registry,
|
||||
mapper,
|
||||
);
|
||||
} else {
|
||||
match reflected_bundle.reflect_ref() {
|
||||
ReflectRef::Struct(bundle) => bundle
|
||||
.iter_fields()
|
||||
.for_each(|field| apply_or_insert_field(entity, field, registry)),
|
||||
ReflectRef::Tuple(bundle) => bundle
|
||||
.iter_fields()
|
||||
.for_each(|field| apply_or_insert_field(entity, field, registry)),
|
||||
ReflectRef::Struct(bundle) => bundle.iter_fields().for_each(|field| {
|
||||
apply_or_insert_field_mapped(entity, field, registry, mapper);
|
||||
}),
|
||||
ReflectRef::Tuple(bundle) => bundle.iter_fields().for_each(|field| {
|
||||
apply_or_insert_field_mapped(entity, field, registry, mapper);
|
||||
}),
|
||||
_ => panic!(
|
||||
"expected bundle `{}` to be a named struct or tuple",
|
||||
// FIXME: once we have unique reflect, use `TypePath`.
|
||||
@ -218,10 +226,11 @@ fn apply_field(entity: &mut EntityMut, field: &dyn PartialReflect, registry: &Ty
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_or_insert_field(
|
||||
fn apply_or_insert_field_mapped(
|
||||
entity: &mut EntityWorldMut,
|
||||
field: &dyn PartialReflect,
|
||||
registry: &TypeRegistry,
|
||||
mapper: &mut dyn EntityMapper,
|
||||
) {
|
||||
let Some(type_id) = field.try_as_reflect().map(Any::type_id) else {
|
||||
panic!(
|
||||
@ -231,9 +240,9 @@ fn apply_or_insert_field(
|
||||
};
|
||||
|
||||
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(type_id) {
|
||||
reflect_component.apply_or_insert(entity, field, registry);
|
||||
reflect_component.apply_or_insert_mapped(entity, field, registry, mapper);
|
||||
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(type_id) {
|
||||
reflect_bundle.apply_or_insert(entity, field, registry);
|
||||
reflect_bundle.apply_or_insert_mapped(entity, field, registry, mapper);
|
||||
} else {
|
||||
let is_component = entity.world().components().get_id(type_id).is_some();
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ use super::from_reflect_with_fallback;
|
||||
use crate::{
|
||||
change_detection::Mut,
|
||||
component::{ComponentId, ComponentMutability},
|
||||
entity::Entity,
|
||||
entity::{Entity, EntityMapper},
|
||||
prelude::Component,
|
||||
world::{
|
||||
unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityWorldMut, FilteredEntityMut,
|
||||
@ -104,8 +104,9 @@ pub struct ReflectComponentFns {
|
||||
pub insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry),
|
||||
/// Function pointer implementing [`ReflectComponent::apply()`].
|
||||
pub apply: fn(EntityMut, &dyn PartialReflect),
|
||||
/// Function pointer implementing [`ReflectComponent::apply_or_insert()`].
|
||||
pub apply_or_insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry),
|
||||
/// Function pointer implementing [`ReflectComponent::apply_or_insert_mapped()`].
|
||||
pub apply_or_insert_mapped:
|
||||
fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry, &mut dyn EntityMapper),
|
||||
/// Function pointer implementing [`ReflectComponent::remove()`].
|
||||
pub remove: fn(&mut EntityWorldMut),
|
||||
/// Function pointer implementing [`ReflectComponent::contains()`].
|
||||
@ -114,6 +115,10 @@ pub struct ReflectComponentFns {
|
||||
pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>,
|
||||
/// Function pointer implementing [`ReflectComponent::reflect_mut()`].
|
||||
pub reflect_mut: fn(FilteredEntityMut) -> Option<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::reflect_unchecked_mut()`].
|
||||
///
|
||||
/// # Safety
|
||||
@ -163,13 +168,14 @@ impl ReflectComponent {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if [`Component`] is immutable.
|
||||
pub fn apply_or_insert(
|
||||
pub fn apply_or_insert_mapped(
|
||||
&self,
|
||||
entity: &mut EntityWorldMut,
|
||||
component: &dyn PartialReflect,
|
||||
registry: &TypeRegistry,
|
||||
map: &mut dyn EntityMapper,
|
||||
) {
|
||||
(self.0.apply_or_insert)(entity, component, registry);
|
||||
(self.0.apply_or_insert_mapped)(entity, component, registry, map);
|
||||
}
|
||||
|
||||
/// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist.
|
||||
@ -277,6 +283,20 @@ impl ReflectComponent {
|
||||
pub fn fn_pointers(&self) -> &ReflectComponentFns {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Calls a dynamic version of [`Component::visit_entities`].
|
||||
pub fn visit_entities(&self, component: &dyn Reflect, func: &mut dyn FnMut(Entity)) {
|
||||
(self.0.visit_entities)(component, func);
|
||||
}
|
||||
|
||||
/// Calls a dynamic version of [`Component::visit_entities_mut`].
|
||||
pub fn visit_entities_mut(
|
||||
&self,
|
||||
component: &mut dyn Reflect,
|
||||
func: &mut dyn FnMut(&mut Entity),
|
||||
) {
|
||||
(self.0.visit_entities_mut)(component, func);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
|
||||
@ -300,21 +320,28 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
|
||||
let mut component = unsafe { entity.get_mut_assume_mutable::<C>() }.unwrap();
|
||||
component.apply(reflected_component);
|
||||
},
|
||||
apply_or_insert: |entity, reflected_component, registry| {
|
||||
apply_or_insert_mapped: |entity, reflected_component, registry, mapper| {
|
||||
// TODO: if we can externalize this impl to cut down on monomorphization that would be great
|
||||
let map_fn = move |entity: &mut Entity| {
|
||||
*entity = mapper.get_mapped(*entity);
|
||||
};
|
||||
if C::Mutability::MUTABLE {
|
||||
// SAFETY: guard ensures `C` is a mutable component
|
||||
if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::<C>() } {
|
||||
component.apply(reflected_component.as_partial_reflect());
|
||||
C::visit_entities_mut(&mut component, map_fn);
|
||||
} else {
|
||||
let component = entity.world_scope(|world| {
|
||||
let mut component = entity.world_scope(|world| {
|
||||
from_reflect_with_fallback::<C>(reflected_component, world, registry)
|
||||
});
|
||||
C::visit_entities_mut(&mut component, map_fn);
|
||||
entity.insert(component);
|
||||
}
|
||||
} else {
|
||||
let component = entity.world_scope(|world| {
|
||||
let mut component = entity.world_scope(|world| {
|
||||
from_reflect_with_fallback::<C>(reflected_component, world, registry)
|
||||
});
|
||||
C::visit_entities_mut(&mut component, map_fn);
|
||||
entity.insert(component);
|
||||
}
|
||||
},
|
||||
@ -359,6 +386,14 @@ 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)| {
|
||||
let component = reflect.downcast_mut::<C>().unwrap();
|
||||
Component::visit_entities_mut(component, func);
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,11 +12,11 @@ pub use relationship_source_collection::*;
|
||||
|
||||
use crate::{
|
||||
component::{Component, HookContext, Mutable},
|
||||
entity::Entity,
|
||||
entity::{ComponentCloneCtx, Entity},
|
||||
system::{
|
||||
command::HandleError,
|
||||
entity_command::{self, CommandWithEntity},
|
||||
error_handler,
|
||||
error_handler, Commands,
|
||||
},
|
||||
world::{DeferredWorld, EntityWorldMut},
|
||||
};
|
||||
@ -47,7 +47,7 @@ use log::warn;
|
||||
/// pub struct Children(Vec<Entity>);
|
||||
/// ```
|
||||
///
|
||||
/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(despawn_descendants)]` attribute to
|
||||
/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(linked_spawn)]` attribute to
|
||||
/// automatically despawn entities stored in an entity's [`RelationshipTarget`] when that entity is despawned:
|
||||
///
|
||||
/// ```
|
||||
@ -58,7 +58,7 @@ use log::warn;
|
||||
/// pub struct ChildOf(pub Entity);
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// #[relationship_target(relationship = ChildOf, despawn_descendants)]
|
||||
/// #[relationship_target(relationship = ChildOf, linked_spawn)]
|
||||
/// pub struct Children(Vec<Entity>);
|
||||
/// ```
|
||||
pub trait Relationship: Component + Sized {
|
||||
@ -143,6 +143,14 @@ pub type SourceIter<'w, R> =
|
||||
/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type.
|
||||
/// See the [`Relationship`] documentation for more information.
|
||||
pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
|
||||
/// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned.
|
||||
///
|
||||
/// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children).
|
||||
/// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning).
|
||||
///
|
||||
/// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone.
|
||||
/// This defaults to false when derived.
|
||||
const LINKED_SPAWN: bool;
|
||||
/// The [`Relationship`] that populates this [`RelationshipTarget`] collection.
|
||||
type Relationship: Relationship<RelationshipTarget = Self>;
|
||||
/// The collection type that stores the "source" entities for this [`RelationshipTarget`] component.
|
||||
@ -254,6 +262,28 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
|
||||
}
|
||||
}
|
||||
|
||||
/// The "clone behavior" for [`RelationshipTarget`]. This actually creates an empty
|
||||
/// [`RelationshipTarget`] instance with space reserved for the number of targets in the
|
||||
/// original instance. The [`RelationshipTarget`] will then be populated with the proper components
|
||||
/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities
|
||||
/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that!
|
||||
///
|
||||
/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured
|
||||
/// to spawn recursively.
|
||||
pub fn clone_relationship_target<T: RelationshipTarget>(
|
||||
_commands: &mut Commands,
|
||||
context: &mut ComponentCloneCtx,
|
||||
) {
|
||||
if let Some(component) = context.read_source_component::<T>() {
|
||||
if context.is_recursive() && T::LINKED_SPAWN {
|
||||
for entity in component.iter() {
|
||||
context.queue_entity_clone(entity);
|
||||
}
|
||||
}
|
||||
context.write_target_component(T::with_capacity(component.len()));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate as bevy_ecs;
|
||||
|
||||
@ -130,7 +130,7 @@ mod tests {
|
||||
struct Rel(Entity);
|
||||
|
||||
#[derive(Component)]
|
||||
#[relationship_target(relationship = Rel, despawn_descendants)]
|
||||
#[relationship_target(relationship = Rel, linked_spawn)]
|
||||
struct RelTarget(Vec<Entity>);
|
||||
|
||||
let mut world = World::new();
|
||||
@ -151,7 +151,7 @@ mod tests {
|
||||
struct Rel(Entity);
|
||||
|
||||
#[derive(Component)]
|
||||
#[relationship_target(relationship = Rel, despawn_descendants)]
|
||||
#[relationship_target(relationship = Rel, linked_spawn)]
|
||||
struct RelTarget(EntityHashSet);
|
||||
|
||||
let mut world = World::new();
|
||||
@ -172,7 +172,7 @@ mod tests {
|
||||
struct Rel(Entity);
|
||||
|
||||
#[derive(Component)]
|
||||
#[relationship_target(relationship = Rel, despawn_descendants)]
|
||||
#[relationship_target(relationship = Rel, linked_spawn)]
|
||||
struct RelTarget(SmallVec<[Entity; 4]>);
|
||||
|
||||
let mut world = World::new();
|
||||
|
||||
@ -13,7 +13,7 @@ use core::panic::Location;
|
||||
use crate::{
|
||||
bundle::{Bundle, InsertMode},
|
||||
component::{Component, ComponentId, ComponentInfo},
|
||||
entity::{Entity, EntityCloneBuilder},
|
||||
entity::{Entity, EntityClonerBuilder},
|
||||
event::Event,
|
||||
result::Result,
|
||||
system::{command::HandleError, Command, IntoObserverSystem},
|
||||
@ -324,10 +324,10 @@ pub fn observe<E: Event, B: Bundle, M>(
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
|
||||
/// configured through [`EntityCloneBuilder`].
|
||||
/// configured through [`EntityClonerBuilder`].
|
||||
pub fn clone_with(
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
|
||||
) -> impl EntityCommand {
|
||||
move |mut entity: EntityWorldMut| {
|
||||
entity.clone_with(target, config);
|
||||
|
||||
@ -21,7 +21,7 @@ use crate::{
|
||||
bundle::{Bundle, InsertMode},
|
||||
change_detection::Mut,
|
||||
component::{Component, ComponentId, Mutable},
|
||||
entity::{Entities, Entity, EntityCloneBuilder},
|
||||
entity::{Entities, Entity, EntityClonerBuilder},
|
||||
event::Event,
|
||||
observer::{Observer, TriggerTargets},
|
||||
resource::Resource,
|
||||
@ -1913,7 +1913,7 @@ impl<'a> EntityCommands<'a> {
|
||||
}
|
||||
|
||||
/// Clones parts of an entity (components, observers, etc.) onto another entity,
|
||||
/// configured through [`EntityCloneBuilder`].
|
||||
/// configured through [`EntityClonerBuilder`].
|
||||
///
|
||||
/// By default, the other entity will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
@ -1924,7 +1924,7 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// Configure through [`EntityClonerBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
///
|
||||
@ -1948,14 +1948,11 @@ impl<'a> EntityCommands<'a> {
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
/// See [`EntityClonerBuilder`] for more options.
|
||||
pub fn clone_with(
|
||||
&mut self,
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.queue(entity_command::clone_with(target, config))
|
||||
}
|
||||
@ -1996,16 +1993,16 @@ impl<'a> EntityCommands<'a> {
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and allows configuring cloning behavior
|
||||
/// using [`EntityCloneBuilder`], returning the [`EntityCommands`] of the clone.
|
||||
/// using [`EntityClonerBuilder`], returning the [`EntityCommands`] of the clone.
|
||||
///
|
||||
/// By default, the clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// To exclude specific components, use [`EntityCloneBuilder::deny`].
|
||||
/// To only include specific components, use [`EntityCloneBuilder::deny_all`]
|
||||
/// followed by [`EntityCloneBuilder::allow`].
|
||||
/// To exclude specific components, use [`EntityClonerBuilder::deny`].
|
||||
/// To only include specific components, use [`EntityClonerBuilder::deny_all`]
|
||||
/// followed by [`EntityClonerBuilder::allow`].
|
||||
///
|
||||
/// See the methods on [`EntityCloneBuilder`] for more options.
|
||||
/// See the methods on [`EntityClonerBuilder`] for more options.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@ -2034,7 +2031,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// # bevy_ecs::system::assert_is_system(example_system);
|
||||
pub fn clone_and_spawn_with(
|
||||
&mut self,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
|
||||
) -> EntityCommands<'_> {
|
||||
let entity_clone = self.commands().spawn_empty().id();
|
||||
self.clone_with(entity_clone, config);
|
||||
|
||||
@ -4,7 +4,8 @@ use crate::{
|
||||
change_detection::MutUntyped,
|
||||
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
||||
entity::{
|
||||
Entities, Entity, EntityBorrow, EntityCloneBuilder, EntityLocation, TrustedEntityBorrow,
|
||||
Entities, Entity, EntityBorrow, EntityCloner, EntityClonerBuilder, EntityLocation,
|
||||
TrustedEntityBorrow,
|
||||
},
|
||||
event::Event,
|
||||
observer::Observer,
|
||||
@ -2591,12 +2592,12 @@ impl<'w> EntityWorldMut<'w> {
|
||||
}
|
||||
|
||||
/// Clones parts of an entity (components, observers, etc.) onto another entity,
|
||||
/// configured through [`EntityCloneBuilder`].
|
||||
/// configured through [`EntityClonerBuilder`].
|
||||
///
|
||||
/// By default, the other entity will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// Configure through [`EntityClonerBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
@ -2613,10 +2614,7 @@ impl<'w> EntityWorldMut<'w> {
|
||||
/// # assert_eq!(world.get::<ComponentB>(target), None);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
/// See [`EntityClonerBuilder`] for more options.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@ -2625,11 +2623,11 @@ impl<'w> EntityWorldMut<'w> {
|
||||
pub fn clone_with(
|
||||
&mut self,
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
let mut builder = EntityCloner::build(self.world);
|
||||
config(&mut builder);
|
||||
builder.clone_entity(self.entity, target);
|
||||
|
||||
@ -2654,12 +2652,12 @@ impl<'w> EntityWorldMut<'w> {
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and allows configuring cloning behavior
|
||||
/// using [`EntityCloneBuilder`], returning the [`Entity`] of the clone.
|
||||
/// using [`EntityClonerBuilder`], returning the [`Entity`] of the clone.
|
||||
///
|
||||
/// By default, the clone will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// Configure through [`EntityClonerBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component, Clone, PartialEq, Debug)]
|
||||
@ -2675,24 +2673,21 @@ impl<'w> EntityWorldMut<'w> {
|
||||
/// # assert_eq!(world.get::<ComponentB>(entity_clone), None);
|
||||
/// ```
|
||||
///
|
||||
/// See the following for more options:
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
/// See [`EntityClonerBuilder`] for more options.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
pub fn clone_and_spawn_with(
|
||||
&mut self,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
|
||||
) -> Entity {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let entity_clone = self.world.entities.reserve_entity();
|
||||
self.world.flush();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
let mut builder = EntityCloner::build(self.world);
|
||||
config(&mut builder);
|
||||
builder.clone_entity(self.entity, entity_clone);
|
||||
|
||||
@ -2713,9 +2708,10 @@ impl<'w> EntityWorldMut<'w> {
|
||||
pub fn clone_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
builder.deny_all().allow::<B>();
|
||||
builder.clone_entity(self.entity, target);
|
||||
EntityCloner::build(self.world)
|
||||
.deny_all()
|
||||
.allow::<B>()
|
||||
.clone_entity(self.entity, target);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
@ -2735,10 +2731,11 @@ impl<'w> EntityWorldMut<'w> {
|
||||
pub fn move_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.assert_not_despawned();
|
||||
|
||||
let mut builder = EntityCloneBuilder::new(self.world);
|
||||
builder.deny_all().allow::<B>();
|
||||
builder.move_components(true);
|
||||
builder.clone_entity(self.entity, target);
|
||||
EntityCloner::build(self.world)
|
||||
.deny_all()
|
||||
.allow::<B>()
|
||||
.move_components(true)
|
||||
.clone_entity(self.entity, target);
|
||||
|
||||
self.world.flush();
|
||||
self.update_location();
|
||||
@ -5679,8 +5676,9 @@ mod tests {
|
||||
let entity_b = world.spawn_empty().id();
|
||||
|
||||
world.entity_mut(entity_a).clone_with(entity_b, |builder| {
|
||||
builder.move_components(true);
|
||||
builder.without_required_components(|builder| {
|
||||
builder
|
||||
.move_components(true)
|
||||
.without_required_components(|builder| {
|
||||
builder.deny::<A>();
|
||||
});
|
||||
});
|
||||
|
||||
@ -35,9 +35,8 @@ use crate::{
|
||||
bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode},
|
||||
change_detection::{MutUntyped, TicksMut},
|
||||
component::{
|
||||
Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId,
|
||||
ComponentInfo, ComponentTicks, Components, Mutable, RequiredComponents,
|
||||
RequiredComponentsError, Tick,
|
||||
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
|
||||
Components, Mutable, RequiredComponents, RequiredComponentsError, Tick,
|
||||
},
|
||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
||||
entity_disabling::{DefaultQueryFilters, Disabled},
|
||||
@ -3190,35 +3189,6 @@ impl World {
|
||||
// SAFETY: We just initialized the bundle so its id should definitely be valid.
|
||||
unsafe { self.bundles.get(id).debug_checked_unwrap() }
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the [`ComponentCloneHandlers`]. Can be used to set and update clone functions for components.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::component::{ComponentId, ComponentCloneHandler};
|
||||
/// use bevy_ecs::entity::ComponentCloneCtx;
|
||||
/// use bevy_ecs::world::DeferredWorld;
|
||||
///
|
||||
/// fn custom_clone_handler(
|
||||
/// _world: &mut DeferredWorld,
|
||||
/// _ctx: &mut ComponentCloneCtx,
|
||||
/// ) {
|
||||
/// // Custom cloning logic for component
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct ComponentA;
|
||||
///
|
||||
/// let mut world = World::new();
|
||||
///
|
||||
/// let component_id = world.register_component::<ComponentA>();
|
||||
///
|
||||
/// world.get_component_clone_handlers_mut()
|
||||
/// .set_component_handler(component_id, ComponentCloneHandler::custom_handler(custom_clone_handler))
|
||||
/// ```
|
||||
pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers {
|
||||
self.components.get_component_clone_handlers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
@ -3770,7 +3740,7 @@ mod tests {
|
||||
use super::{FromWorld, World};
|
||||
use crate::{
|
||||
change_detection::DetectChangesMut,
|
||||
component::{ComponentDescriptor, ComponentInfo, StorageType},
|
||||
component::{ComponentCloneBehavior, ComponentDescriptor, ComponentInfo, StorageType},
|
||||
entity::hash_set::EntityHashSet,
|
||||
entity_disabling::{DefaultQueryFilters, Disabled},
|
||||
ptr::OwningPtr,
|
||||
@ -4074,6 +4044,7 @@ mod tests {
|
||||
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
}),
|
||||
true,
|
||||
ComponentCloneBehavior::Default,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@ -1,26 +1,14 @@
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
prelude::ReflectComponent,
|
||||
reflect::{ReflectMapEntities, ReflectVisitEntities, ReflectVisitEntitiesMut},
|
||||
};
|
||||
use bevy_ecs::{component::Component, entity::Entity, prelude::ReflectComponent};
|
||||
use bevy_math::Mat4;
|
||||
use bevy_reflect::prelude::*;
|
||||
use core::ops::Deref;
|
||||
|
||||
#[derive(Component, Debug, Default, Clone, Reflect, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(
|
||||
Component,
|
||||
MapEntities,
|
||||
VisitEntities,
|
||||
VisitEntitiesMut,
|
||||
Default,
|
||||
Debug
|
||||
)]
|
||||
#[derive(Component, Debug, Default, Clone, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub struct SkinnedMesh {
|
||||
#[visit_entities(ignore)]
|
||||
pub inverse_bindposes: Handle<SkinnedMeshInverseBindposes>,
|
||||
#[entities]
|
||||
pub joints: Vec<Entity>,
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError};
|
||||
use bevy_asset::Asset;
|
||||
use bevy_ecs::reflect::ReflectResource;
|
||||
use bevy_ecs::reflect::{ReflectMapEntities, ReflectResource};
|
||||
use bevy_ecs::{
|
||||
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
|
||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities},
|
||||
reflect::{AppTypeRegistry, ReflectComponent},
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::{PartialReflect, TypePath, TypeRegistry};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::serde::SceneSerializer;
|
||||
use bevy_ecs::component::ComponentCloneBehavior;
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
|
||||
@ -85,7 +86,7 @@ impl DynamicScene {
|
||||
|
||||
// Apply/ add each component to the given entity.
|
||||
for component in &scene_entity.components {
|
||||
let mut component = component.clone_value();
|
||||
let component = component.clone_value();
|
||||
let type_info = component.get_represented_type_info().ok_or_else(|| {
|
||||
SceneSpawnError::NoRepresentedType {
|
||||
type_path: component.reflect_type_path().to_string(),
|
||||
@ -103,19 +104,27 @@ impl DynamicScene {
|
||||
}
|
||||
})?;
|
||||
|
||||
// If this component references entities in the scene, update
|
||||
// them to the entities in the world.
|
||||
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
|
||||
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
|
||||
map_entities.map_entities(component.as_partial_reflect_mut(), mapper);
|
||||
});
|
||||
{
|
||||
let component_id = reflect_component.register_component(world);
|
||||
// SAFETY: we registered the component above. the info exists
|
||||
#[expect(unsafe_code, reason = "this is faster")]
|
||||
let component_info =
|
||||
unsafe { world.components().get_info_unchecked(component_id) };
|
||||
match component_info.clone_behavior() {
|
||||
ComponentCloneBehavior::Ignore
|
||||
| ComponentCloneBehavior::RelationshipTarget(_) => continue,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
reflect_component.apply_or_insert(
|
||||
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
|
||||
reflect_component.apply_or_insert_mapped(
|
||||
&mut world.entity_mut(entity),
|
||||
component.as_partial_reflect(),
|
||||
&type_registry,
|
||||
mapper,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,13 +349,13 @@ mod tests {
|
||||
#[reflect(Component)]
|
||||
struct A;
|
||||
|
||||
#[derive(Component, Reflect, VisitEntities)]
|
||||
#[reflect(Component, MapEntities)]
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
struct B(pub Entity);
|
||||
|
||||
impl MapEntities for B {
|
||||
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
|
||||
self.0 = entity_mapper.map_entity(self.0);
|
||||
self.0 = entity_mapper.get_mapped(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc(
|
||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
||||
|
||||
@ -3,9 +3,10 @@ use core::any::TypeId;
|
||||
use crate::{DynamicScene, SceneSpawnError};
|
||||
use bevy_asset::Asset;
|
||||
use bevy_ecs::{
|
||||
component::ComponentCloneBehavior,
|
||||
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
|
||||
entity_disabling::DefaultQueryFilters,
|
||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
|
||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::{PartialReflect, TypePath};
|
||||
@ -123,6 +124,12 @@ impl Scene {
|
||||
.get_info(component_id)
|
||||
.expect("component_ids in archetypes should have ComponentInfo");
|
||||
|
||||
match component_info.clone_behavior() {
|
||||
ComponentCloneBehavior::Ignore
|
||||
| ComponentCloneBehavior::RelationshipTarget(_) => continue,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let registration = type_registry
|
||||
.get(component_info.type_id().unwrap())
|
||||
.ok_or_else(|| SceneSpawnError::UnregisteredType {
|
||||
@ -135,7 +142,7 @@ impl Scene {
|
||||
}
|
||||
})?;
|
||||
|
||||
let Some(mut component) = reflect_component
|
||||
let Some(component) = reflect_component
|
||||
.reflect(self.world.entity(scene_entity.id()))
|
||||
.map(PartialReflect::clone_value)
|
||||
else {
|
||||
@ -144,16 +151,14 @@ impl Scene {
|
||||
|
||||
// If this component references entities in the scene,
|
||||
// update them to the entities in the world.
|
||||
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
|
||||
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
|
||||
map_entities.map_entities(component.as_partial_reflect_mut(), mapper);
|
||||
});
|
||||
}
|
||||
reflect_component.apply_or_insert(
|
||||
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
|
||||
reflect_component.apply_or_insert_mapped(
|
||||
&mut world.entity_mut(entity),
|
||||
component.as_partial_reflect(),
|
||||
&type_registry,
|
||||
mapper,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,10 +515,10 @@ mod tests {
|
||||
DynamicScene, DynamicSceneBuilder,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
entity::{hash_map::EntityHashMap, Entity, VisitEntities, VisitEntitiesMut},
|
||||
entity::{hash_map::EntityHashMap, Entity},
|
||||
prelude::{Component, ReflectComponent, ReflectResource, Resource, World},
|
||||
query::{With, Without},
|
||||
reflect::{AppTypeRegistry, ReflectMapEntities},
|
||||
reflect::AppTypeRegistry,
|
||||
world::FromWorld,
|
||||
};
|
||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||
@ -584,9 +584,9 @@ mod tests {
|
||||
foo: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, Reflect, PartialEq, VisitEntities, VisitEntitiesMut)]
|
||||
#[reflect(Component, MapEntities, PartialEq)]
|
||||
struct MyEntityRef(Entity);
|
||||
#[derive(Clone, Component, Reflect, PartialEq)]
|
||||
#[reflect(Component, PartialEq)]
|
||||
struct MyEntityRef(#[entities] Entity);
|
||||
|
||||
impl FromWorld for MyEntityRef {
|
||||
fn from_world(_world: &mut World) -> Self {
|
||||
|
||||
@ -10,7 +10,9 @@ use std::{alloc::Layout, collections::HashMap, io::Write, ptr::NonNull};
|
||||
|
||||
use bevy::{
|
||||
ecs::{
|
||||
component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType},
|
||||
component::{
|
||||
ComponentCloneBehavior, ComponentDescriptor, ComponentId, ComponentInfo, StorageType,
|
||||
},
|
||||
query::QueryData,
|
||||
world::FilteredEntityMut,
|
||||
},
|
||||
@ -94,6 +96,7 @@ fn main() {
|
||||
Layout::array::<u64>(size).unwrap(),
|
||||
None,
|
||||
true,
|
||||
ComponentCloneBehavior::Default,
|
||||
)
|
||||
});
|
||||
let Some(info) = world.components().get_info(id) else {
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
use bevy::{
|
||||
ecs::{
|
||||
component::{ComponentDescriptor, ComponentId, HookContext, StorageType},
|
||||
component::{
|
||||
ComponentCloneBehavior, ComponentDescriptor, ComponentId, HookContext, StorageType,
|
||||
},
|
||||
world::DeferredWorld,
|
||||
},
|
||||
platform_support::collections::HashMap,
|
||||
@ -152,6 +154,7 @@ fn demo_3(world: &mut World) {
|
||||
Layout::array::<u8>(size).unwrap(),
|
||||
None,
|
||||
false,
|
||||
ComponentCloneBehavior::Default,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ use bevy::{
|
||||
DiagnosticPath, DiagnosticsPlugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin,
|
||||
},
|
||||
ecs::{
|
||||
component::{ComponentDescriptor, ComponentId, StorageType},
|
||||
component::{ComponentCloneBehavior, ComponentDescriptor, ComponentId, StorageType},
|
||||
system::QueryParamBuilder,
|
||||
world::FilteredEntityMut,
|
||||
},
|
||||
@ -99,6 +99,7 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) {
|
||||
Layout::new::<u8>(),
|
||||
None,
|
||||
true, // is mutable
|
||||
ComponentCloneBehavior::Default,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user