Entity cloning (#16132)

## Objective

Fixes #1515 

This PR implements a flexible entity cloning system. The primary use
case for it is to clone dynamically-generated entities.

Example:
```rs
#[derive(Component, Clone)]
pub struct Projectile;

#[derive(Component, Clone)]
pub struct Damage {
    value: f32,
}

fn player_input(
    mut commands: Commands,
    projectiles: Query<Entity, With<Projectile>>,
    input: Res<ButtonInput<KeyCode>>,
) {
    // Fire a projectile
    if input.just_pressed(KeyCode::KeyF) {
        commands.spawn((Projectile, Damage { value: 10.0 }));
    }

    // Triplicate all active projectiles
    if input.just_pressed(KeyCode::KeyT) {
        for projectile in projectiles.iter() {
            // To triplicate a projectile we need to create 2 more clones
            for _ in 0..2{
                commands.clone_entity(projectile)
            }
        }
    }
}
```

## Solution

### Commands
Add a `clone_entity` command to create a clone of an entity with all
components that can be cloned. Components that can't be cloned will be
ignored.
```rs
commands.clone_entity(entity)
```
If there is a need to configure the cloning process (like set to clone
recursively), there is a second command:
```rs
commands.clone_entity_with(entity, |builder| {
    builder.recursive(true)
});
```
Both of these commands return `EntityCommands` of the cloned entity, so
the copy can be modified afterwards.

### Builder
All these commands use `EntityCloneBuilder` internally. If there is a
need to clone an entity using `World` instead, it is also possible:
```rs
let entity = world.spawn(Component).id();
let entity_clone = world.spawn_empty().id();
EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone);
```

Builder has methods to `allow` or `deny` certain components during
cloning if required and can be extended by implementing traits on it.
This PR includes two `EntityCloneBuilder` extensions:
`CloneEntityWithObserversExt` to configure adding cloned entity to
observers of the original entity, and `CloneEntityRecursiveExt` to
configure cloning an entity recursively.

### Clone implementations
By default, all components that implement either `Clone` or `Reflect`
will be cloned (with `Clone`-based implementation preferred in case
component implements both).

This can be overriden on a per-component basis:
```rs
impl Component for SomeComponent {
    const STORAGE_TYPE: StorageType = StorageType::Table;

    fn get_component_clone_handler() -> ComponentCloneHandler {
        // Don't clone this component
        ComponentCloneHandler::Ignore
    }
}
```

### `ComponentCloneHandlers`
Clone implementation specified in `get_component_clone_handler` will get
registered in `ComponentCloneHandlers` (stored in
`bevy_ecs::component::Components`) at component registration time.

The clone handler implementation provided by a component can be
overriden after registration like so:
```rs
let component_id = world.components().component_id::<Component>().unwrap()
world.get_component_clone_handlers_mut()
     .set_component_handler(component_id, ComponentCloneHandler::Custom(component_clone_custom))
```
The default clone handler for all components that do not explicitly
define one (or don't derive `Component`) is
`component_clone_via_reflect` if `bevy_reflect` feature is enabled, and
`component_clone_ignore` (noop) otherwise.
Default handler can be overriden using
`ComponentCloneHandlers::set_default_handler`

### Handlers
Component clone handlers can be used to modify component cloning
behavior. The general signature for a handler that can be used in
`ComponentCloneHandler::Custom` is as follows:
```rs
pub fn component_clone_custom(
    world: &mut DeferredWorld,
    entity_cloner: &EntityCloner,
) {
    // implementation
}
```
The `EntityCloner` implementation (used internally by
`EntityCloneBuilder`) assumes that after calling this custom handler,
the `target` entity has the desired version of the component from the
`source` entity.

### Builder handler overrides
Besides component-defined and world-overriden handlers,
`EntityCloneBuilder` also has a way to override handlers locally. It is
mainly used to allow configuration methods like `recursive` and
`add_observers`.
```rs
// From observer clone handler implementation
impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> {
    fn add_observers(&mut self, add_observers: bool) -> &mut Self {
        if add_observers {
            self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom(
                component_clone_observed_by,
            ))
        } else {
            self.remove_component_clone_handler_override::<ObservedBy>()
        }
    }
}
```

## Testing
Includes some basic functionality tests and doctests.

Performance-wise this feature is the same as calling `clone` followed by
`insert` for every entity component. There is also some inherent
overhead due to every component clone handler having to access component
data through `World`, but this can be reduced without breaking current
public API in a later PR.
This commit is contained in:
eugineerd 2024-12-03 20:38:10 +03:00 committed by GitHub
parent d221665386
commit 2e267bba5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1111 additions and 14 deletions

View file

@ -163,6 +163,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
#on_replace
#on_remove
}
fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler {
use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase};
(&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::<Self>::default())
.get_component_clone_handler()
}
}
})
}

View file

@ -5,7 +5,7 @@ use crate::{
archetype::ArchetypeFlags,
bundle::BundleInfo,
change_detection::MAX_CHANGE_AGE,
entity::Entity,
entity::{Entity, EntityCloner},
query::DebugCheckedUnwrap,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
system::{Local, Resource, SystemParam},
@ -390,6 +390,13 @@ pub trait Component: Send + Sync + 'static {
_inheritance_depth: u16,
) {
}
/// 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()
}
}
/// The storage used for a specific component type.
@ -875,12 +882,96 @@ impl ComponentDescriptor {
}
}
/// Function type that can be used to clone an entity.
pub type ComponentCloneFn = fn(&mut DeferredWorld, &EntityCloner);
/// An enum instructing how to clone a component.
#[derive(Debug, Default)]
pub enum ComponentCloneHandler {
#[default]
/// Use the global default function to clone the component with this handler.
Default,
/// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped.
Ignore,
/// Set a custom handler for the component.
Custom(ComponentCloneFn),
}
/// 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, 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);
}
match handler {
ComponentCloneHandler::Default => self.handlers[id.0] = None,
ComponentCloneHandler::Ignore => self.handlers[id.0] = Some(component_clone_ignore),
ComponentCloneHandler::Custom(handler) => self.handlers[id.0] = Some(handler),
};
}
/// Checks if the specified component is registered. If not, the component will use the default global handler.
///
/// This will return an incorrect result if `id` did not come from the same world as `self`.
pub fn is_handler_registered(&self, id: ComponentId) -> bool {
self.handlers.get(id.0).is_some_and(Option::is_some)
}
/// Gets a handler to clone a component. This can be one of the following:
/// - Custom clone function for this specific component.
/// - Default global handler.
/// - A [`component_clone_ignore`] (no cloning).
///
/// This will return an incorrect result if `id` did not come from the same world as `self`.
pub fn get_handler(&self, id: ComponentId) -> ComponentCloneFn {
match self.handlers.get(id.0) {
Some(Some(handler)) => *handler,
Some(None) | None => self.default_handler,
}
}
}
impl Default for ComponentCloneHandlers {
fn default() -> Self {
Self {
handlers: Default::default(),
#[cfg(feature = "bevy_reflect")]
default_handler: component_clone_via_reflect,
#[cfg(not(feature = "bevy_reflect"))]
default_handler: component_clone_ignore,
}
}
}
/// Stores metadata associated with each kind of [`Component`] in a given [`World`].
#[derive(Debug, Default)]
pub struct Components {
components: Vec<ComponentInfo>,
indices: TypeIdMap<ComponentId>,
resource_indices: TypeIdMap<ComponentId>,
component_clone_handlers: ComponentCloneHandlers,
}
impl Components {
@ -918,6 +1009,9 @@ impl Components {
let info = &mut self.components[id.index()];
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
}
@ -1276,6 +1370,16 @@ 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> {
@ -1853,3 +1957,98 @@ impl RequiredComponents {
}
}
}
/// 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.
/// 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,
entity_cloner: &EntityCloner,
) {
let component = world
.entity(entity_cloner.source())
.get::<C>()
.expect("Component must exists on source entity")
.clone();
world
.commands()
.entity(entity_cloner.target())
.insert(component);
}
/// Component [clone handler function](ComponentCloneFn) implemented using reflect.
/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component,
/// but only reflected components will be cloned.
///
/// See [`ComponentCloneHandlers`] for more details.
#[cfg(feature = "bevy_reflect")]
pub fn component_clone_via_reflect(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
let component_id = entity_cloner.component_id();
let source = entity_cloner.source();
let target = entity_cloner.target();
world.commands().queue(move |world: &mut World| {
world.resource_scope::<crate::reflect::AppTypeRegistry, ()>(|world, registry| {
let registry = registry.read();
let component_info = world
.components()
.get_info(component_id)
.expect("Component must be registered");
let Some(type_id) = component_info.type_id() else {
return;
};
let Some(reflect_component) =
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
else {
return;
};
let source_component = reflect_component
.reflect(world.get_entity(source).expect("Source entity must exist"))
.expect("Source entity must have reflected component")
.clone_value();
let mut target = world
.get_entity_mut(target)
.expect("Target entity must exist");
reflect_component.apply_or_insert(&mut target, &*source_component, &registry);
});
});
}
/// Noop implementation of component clone handler function.
///
/// See [`ComponentCloneHandlers`] for more details.
pub fn component_clone_ignore(_world: &mut DeferredWorld, _entity_cloner: &EntityCloner) {}
/// Wrapper for components clone specialization using autoderef.
#[doc(hidden)]
pub struct ComponentCloneSpecializationWrapper<T>(PhantomData<T>);
impl<T> Default for ComponentCloneSpecializationWrapper<T> {
fn default() -> Self {
Self(PhantomData)
}
}
/// Base trait for components clone specialization using autoderef.
#[doc(hidden)]
pub trait ComponentCloneBase {
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
}
impl<C: Component> ComponentCloneBase for ComponentCloneSpecializationWrapper<C> {
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
ComponentCloneHandler::default()
}
}
/// Specialized trait for components clone specialization using autoderef.
#[doc(hidden)]
pub trait ComponentCloneViaClone {
fn get_component_clone_handler(&self) -> ComponentCloneHandler;
}
impl<C: Clone + Component> ComponentCloneViaClone for &ComponentCloneSpecializationWrapper<C> {
fn get_component_clone_handler(&self) -> ComponentCloneHandler {
ComponentCloneHandler::Custom(component_clone_via_clone::<C>)
}
}

View file

@ -0,0 +1,522 @@
use alloc::sync::Arc;
use core::any::TypeId;
use bevy_utils::{HashMap, HashSet};
use crate::{
bundle::Bundle,
component::{component_clone_ignore, Component, ComponentCloneHandler, ComponentId},
entity::Entity,
world::World,
};
/// A helper struct to clone an entity. Used internally by [`EntityCloneBuilder::clone_entity`] and custom clone handlers.
pub struct EntityCloner {
source: Entity,
target: Entity,
component_id: Option<ComponentId>,
filter_allows_components: bool,
filter: Arc<HashSet<ComponentId>>,
clone_handlers_overrides: Arc<HashMap<ComponentId, ComponentCloneHandler>>,
}
impl EntityCloner {
/// Clones and inserts components from the `source` entity into `target` entity using the stored configuration.
pub fn clone_entity(&mut self, world: &mut World) {
let source_entity = world
.get_entity(self.source)
.expect("Source entity must exist");
let archetype = source_entity.archetype();
let mut components = Vec::with_capacity(archetype.component_count());
components.extend(
archetype
.components()
.filter(|id| self.is_cloning_allowed(id)),
);
for component in components {
let global_handlers = world.components().get_component_clone_handlers();
let handler = match self.clone_handlers_overrides.get(&component) {
None => global_handlers.get_handler(component),
Some(ComponentCloneHandler::Default) => global_handlers.get_default_handler(),
Some(ComponentCloneHandler::Ignore) => component_clone_ignore,
Some(ComponentCloneHandler::Custom(handler)) => *handler,
};
self.component_id = Some(component);
(handler)(&mut world.into(), self);
}
}
fn is_cloning_allowed(&self, component: &ComponentId) -> bool {
(self.filter_allows_components && self.filter.contains(component))
|| (!self.filter_allows_components && !self.filter.contains(component))
}
/// Returns the current source entity.
pub fn source(&self) -> Entity {
self.source
}
/// Returns the current target entity.
pub fn target(&self) -> Entity {
self.target
}
/// Returns the [`ComponentId`] of currently cloned component.
pub fn component_id(&self) -> ComponentId {
self.component_id
.expect("ComponentId must be set in clone_entity")
}
/// Reuse existing [`EntityCloner`] configuration with new source and target.
pub fn with_source_and_target(&self, source: Entity, target: Entity) -> EntityCloner {
EntityCloner {
source,
target,
filter: self.filter.clone(),
clone_handlers_overrides: self.clone_handlers_overrides.clone(),
..*self
}
}
}
/// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them.
/// After configuration is complete an entity can be cloned using [`Self::clone_entity`].
///
///```
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::entity::EntityCloneBuilder;
///
/// #[derive(Component, Clone, PartialEq, Eq)]
/// struct A {
/// field: usize,
/// }
///
/// let mut world = World::default();
///
/// let component = A { field: 5 };
///
/// let entity = world.spawn(component.clone()).id();
/// let entity_clone = world.spawn_empty().id();
///
/// EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone);
///
/// assert!(world.get::<A>(entity_clone).is_some_and(|c| *c == component));
///```
///
/// # Default cloning strategy
/// By default, all types that derive [`Component`] and implement either [`Clone`] or `Reflect` (with `ReflectComponent`) will be cloned
/// (with `Clone`-based implementation preferred in case component implements both).
///
/// It should be noted that if `Component` is implemented manually or if `Clone` implementation is conditional
/// (like when deriving `Clone` for a type with a generic parameter without `Clone` bound),
/// the component will be cloned using the [default cloning strategy](crate::component::ComponentCloneHandlers::get_default_handler).
/// To use `Clone`-based handler ([`component_clone_via_clone`](crate::component::component_clone_via_clone)) in this case it should be set manually using one
/// of the methods mentioned in the [Handlers](#handlers) section
///
/// Here's an example of how to do it using [`get_component_clone_handler`](Component::get_component_clone_handler):
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::component::{StorageType, component_clone_via_clone, ComponentCloneHandler};
/// #[derive(Clone)]
/// struct SomeComponent;
///
/// impl Component for SomeComponent {
/// const STORAGE_TYPE: StorageType = StorageType::Table;
/// fn get_component_clone_handler() -> ComponentCloneHandler {
/// ComponentCloneHandler::Custom(component_clone_via_clone::<Self>)
/// }
/// }
/// ```
///
/// # Handlers
/// `EntityCloneBuilder` clones entities by cloning components using [`handlers`](ComponentCloneHandler), and there are multiple layers
/// to decide which handler to use for which component. The overall hierarchy looks like this (priority from most to least):
/// 1. local overrides using [`override_component_clone_handler`](Self::override_component_clone_handler)
/// 2. global overrides using [`set_component_handler`](crate::component::ComponentCloneHandlers::set_component_handler)
/// 3. component-defined handler using [`get_component_clone_handler`](Component::get_component_clone_handler)
/// 4. default handler override using [`set_default_handler`](crate::component::ComponentCloneHandlers::set_default_handler)
/// 5. reflect-based or noop default clone handler depending on if `bevy_reflect` feature is enabled or not.
#[derive(Debug)]
pub struct EntityCloneBuilder<'w> {
world: &'w mut World,
filter_allows_components: bool,
filter: HashSet<ComponentId>,
clone_handlers_overrides: HashMap<ComponentId, ComponentCloneHandler>,
}
impl<'w> EntityCloneBuilder<'w> {
/// Creates a new [`EntityCloneBuilder`] for world.
pub fn new(world: &'w mut World) -> Self {
Self {
world,
filter_allows_components: false,
filter: Default::default(),
clone_handlers_overrides: Default::default(),
}
}
/// Finishes configuring the builder and clones `source` entity to `target`.
pub fn clone_entity(self, source: Entity, target: Entity) {
let EntityCloneBuilder {
world,
filter_allows_components,
filter,
clone_handlers_overrides,
..
} = self;
EntityCloner {
source,
target,
component_id: None,
filter_allows_components,
filter: Arc::new(filter),
clone_handlers_overrides: Arc::new(clone_handlers_overrides),
}
.clone_entity(world);
world.flush_commands();
}
/// Adds all components of the bundle to the list of components to clone.
///
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
pub fn allow<T: Bundle>(&mut self) -> &mut Self {
if self.filter_allows_components {
T::get_component_ids(self.world.components(), &mut |id| {
if let Some(id) = id {
self.filter.insert(id);
}
});
} else {
T::get_component_ids(self.world.components(), &mut |id| {
if let Some(id) = id {
self.filter.remove(&id);
}
});
}
self
}
/// Extends the list of components to clone.
///
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
pub fn allow_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
if self.filter_allows_components {
self.filter.extend(ids);
} else {
ids.into_iter().for_each(|id| {
self.filter.remove(&id);
});
}
self
}
/// Extends the list of components to clone using [`TypeId`]s.
///
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
pub fn allow_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
let ids = ids
.into_iter()
.filter_map(|id| self.world.components().get_id(id));
if self.filter_allows_components {
self.filter.extend(ids);
} else {
ids.into_iter().for_each(|id| {
self.filter.remove(&id);
});
}
self
}
/// Resets the filter to allow all components to be cloned.
pub fn allow_all(&mut self) -> &mut Self {
self.filter_allows_components = false;
self.filter.clear();
self
}
/// Disallows all components of the bundle from being cloned.
pub fn deny<T: Bundle>(&mut self) -> &mut Self {
if self.filter_allows_components {
T::get_component_ids(self.world.components(), &mut |id| {
if let Some(id) = id {
self.filter.remove(&id);
}
});
} else {
T::get_component_ids(self.world.components(), &mut |id| {
if let Some(id) = id {
self.filter.insert(id);
}
});
}
self
}
/// Extends the list of components that shouldn't be cloned.
pub fn deny_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
if self.filter_allows_components {
ids.into_iter().for_each(|id| {
self.filter.remove(&id);
});
} else {
self.filter.extend(ids);
}
self
}
/// Extends the list of components that shouldn't be cloned by type ids.
pub fn deny_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
let ids = ids
.into_iter()
.filter_map(|id| self.world.components().get_id(id));
if self.filter_allows_components {
ids.into_iter().for_each(|id| {
self.filter.remove(&id);
});
} else {
self.filter.extend(ids);
}
self
}
/// Sets the filter to deny all components.
pub fn deny_all(&mut self) -> &mut Self {
self.filter_allows_components = true;
self.filter.clear();
self
}
/// Overrides the [`ComponentCloneHandler`] for a component in this builder.
/// This handler will be used to clone the component instead of the global one defined by [`ComponentCloneHandlers`](crate::component::ComponentCloneHandlers)
///
/// See [Handlers section of `EntityCloneBuilder`](EntityCloneBuilder#handlers) to understand how this affects handler priority.
pub fn override_component_clone_handler<T: Component>(
&mut self,
handler: ComponentCloneHandler,
) -> &mut Self {
if let Some(id) = self.world.components().component_id::<T>() {
self.clone_handlers_overrides.insert(id, handler);
}
self
}
/// Removes a previously set override of [`ComponentCloneHandler`] for a component in this builder.
pub fn remove_component_clone_handler_override<T: Component>(&mut self) -> &mut Self {
if let Some(id) = self.world.components().component_id::<T>() {
self.clone_handlers_overrides.remove(&id);
}
self
}
}
#[cfg(test)]
mod tests {
use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World};
#[cfg(feature = "bevy_reflect")]
#[test]
fn clone_entity_using_reflect() {
use crate::reflect::{AppTypeRegistry, ReflectComponent};
use bevy_reflect::Reflect;
#[derive(Component, Reflect, Clone, PartialEq, Eq)]
#[reflect(Component)]
struct A {
field: usize,
}
let mut world = World::default();
world.init_resource::<AppTypeRegistry>();
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
registry.write().register::<A>();
let component = A { field: 5 };
let e = world.spawn(component.clone()).id();
let e_clone = world.spawn_empty().id();
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
}
#[test]
fn clone_entity_using_clone() {
#[derive(Component, Clone, PartialEq, Eq)]
struct A {
field: usize,
}
let mut world = World::default();
let component = A { field: 5 };
let e = world.spawn(component.clone()).id();
let e_clone = world.spawn_empty().id();
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
}
#[cfg(feature = "bevy_reflect")]
#[test]
fn clone_entity_specialization() {
use crate::reflect::{AppTypeRegistry, ReflectComponent};
use bevy_reflect::Reflect;
#[derive(Component, Reflect, PartialEq, Eq)]
#[reflect(Component)]
struct A {
field: usize,
}
impl Clone for A {
fn clone(&self) -> Self {
Self { field: 10 }
}
}
let mut world = World::default();
world.init_resource::<AppTypeRegistry>();
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
registry.write().register::<A>();
let component = A { field: 5 };
let e = world.spawn(component.clone()).id();
let e_clone = world.spawn_empty().id();
EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone);
assert!(world
.get::<A>(e_clone)
.is_some_and(|comp| *comp == A { field: 10 }));
}
#[test]
fn clone_entity_with_allow_filter() {
#[derive(Component, Clone, PartialEq, Eq)]
struct A {
field: usize,
}
#[derive(Component, Clone)]
struct B;
let mut world = World::default();
let component = A { field: 5 };
let e = world.spawn((component.clone(), B)).id();
let e_clone = world.spawn_empty().id();
let mut builder = EntityCloneBuilder::new(&mut world);
builder.deny_all();
builder.allow::<A>();
builder.clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
assert!(world.get::<B>(e_clone).is_none());
}
#[test]
fn clone_entity_with_deny_filter() {
#[derive(Component, Clone, PartialEq, Eq)]
struct A {
field: usize,
}
#[derive(Component, Clone)]
struct B;
#[derive(Component, Clone)]
struct C;
let mut world = World::default();
let component = A { field: 5 };
let e = world.spawn((component.clone(), B, C)).id();
let e_clone = world.spawn_empty().id();
let mut builder = EntityCloneBuilder::new(&mut world);
builder.deny::<B>();
builder.clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
assert!(world.get::<B>(e_clone).is_none());
assert!(world.get::<C>(e_clone).is_some());
}
#[test]
fn clone_entity_with_override_allow_filter() {
#[derive(Component, Clone, PartialEq, Eq)]
struct A {
field: usize,
}
#[derive(Component, Clone)]
struct B;
#[derive(Component, Clone)]
struct C;
let mut world = World::default();
let component = A { field: 5 };
let e = world.spawn((component.clone(), B, C)).id();
let e_clone = world.spawn_empty().id();
let mut builder = EntityCloneBuilder::new(&mut world);
builder.deny_all();
builder.allow::<A>();
builder.allow::<B>();
builder.allow::<C>();
builder.deny::<B>();
builder.clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
assert!(world.get::<B>(e_clone).is_none());
assert!(world.get::<C>(e_clone).is_some());
}
#[test]
fn clone_entity_with_override_bundle() {
#[derive(Component, Clone, PartialEq, Eq)]
struct A {
field: usize,
}
#[derive(Component, Clone)]
struct B;
#[derive(Component, Clone)]
struct C;
let mut world = World::default();
let component = A { field: 5 };
let e = world.spawn((component.clone(), B, C)).id();
let e_clone = world.spawn_empty().id();
let mut builder = EntityCloneBuilder::new(&mut world);
builder.deny_all();
builder.allow::<(A, B, C)>();
builder.deny::<(B, C)>();
builder.clone_entity(e, e_clone);
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
assert!(world.get::<B>(e_clone).is_none());
assert!(world.get::<C>(e_clone).is_none());
}
}

View file

@ -35,12 +35,14 @@
//! [`World::despawn`]: crate::world::World::despawn
//! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
//! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
mod clone_entities;
mod map_entities;
mod visit_entities;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
pub use clone_entities::*;
pub use map_entities::*;
pub use visit_entities::*;

View file

@ -49,7 +49,7 @@ pub mod prelude {
component::Component,
entity::{Entity, EntityMapper},
event::{Event, EventMutator, EventReader, EventWriter, Events},
observer::{Observer, Trigger},
observer::{CloneEntityWithObserversExt, Observer, Trigger},
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
removal_detection::RemovedComponents,
schedule::{

View file

@ -1,7 +1,8 @@
use crate::{
component::{Component, ComponentHooks, StorageType},
entity::Entity,
component::{Component, ComponentCloneHandler, ComponentHooks, StorageType},
entity::{Entity, EntityCloneBuilder, EntityCloner},
observer::ObserverState,
world::{DeferredWorld, World},
};
/// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to.
@ -39,4 +40,110 @@ impl Component for ObservedBy {
}
});
}
fn get_component_clone_handler() -> ComponentCloneHandler {
ComponentCloneHandler::Ignore
}
}
/// Trait that holds functions for configuring interaction with observers during entity cloning.
pub trait CloneEntityWithObserversExt {
/// Sets the option to automatically add cloned entities to the obsevers 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 {
if add_observers {
self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom(
component_clone_observed_by,
))
} else {
self.remove_component_clone_handler_override::<ObservedBy>()
}
}
}
fn component_clone_observed_by(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
let target = entity_cloner.target();
let source = entity_cloner.source();
world.commands().queue(move |world: &mut World| {
let observed_by = world
.get::<ObservedBy>(source)
.map(|observed_by| observed_by.0.clone())
.expect("Source entity must have ObservedBy");
world
.entity_mut(target)
.insert(ObservedBy(observed_by.clone()));
for observer in &observed_by {
let mut observer_state = world
.get_mut::<ObserverState>(*observer)
.expect("Source observer entity must have ObserverState");
observer_state.descriptor.entities.push(target);
let event_types = observer_state.descriptor.events.clone();
let components = observer_state.descriptor.components.clone();
for event_type in event_types {
let observers = world.observers.get_observers(event_type);
if components.is_empty() {
if let Some(map) = observers.entity_observers.get(&source).cloned() {
observers.entity_observers.insert(target, map);
}
} else {
for component in &components {
let Some(observers) = observers.component_observers.get_mut(component)
else {
continue;
};
if let Some(map) = observers.entity_map.get(&source).cloned() {
observers.entity_map.insert(target, map);
}
}
}
}
}
});
}
#[cfg(test)]
mod tests {
use crate::{
self as bevy_ecs,
entity::EntityCloneBuilder,
event::Event,
observer::{CloneEntityWithObserversExt, Trigger},
system::{ResMut, Resource},
world::World,
};
#[derive(Resource, Default)]
struct Num(usize);
#[derive(Event)]
struct E;
#[test]
fn clone_entity_with_observer() {
let mut world = World::default();
world.init_resource::<Num>();
let e = world
.spawn_empty()
.observe(|_: Trigger<E>, mut res: ResMut<Num>| res.0 += 1)
.id();
world.flush();
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);
world.trigger_targets(E, [e, e_clone]);
assert_eq!(world.resource::<Num>().0, 3);
}
}

View file

@ -4,6 +4,7 @@ mod entity_observer;
mod runner;
mod trigger_event;
pub use entity_observer::CloneEntityWithObserversExt;
pub use runner::*;
pub use trigger_event::*;

View file

@ -11,7 +11,7 @@ use crate::{
bundle::{Bundle, InsertMode},
change_detection::Mut,
component::{Component, ComponentId, ComponentInfo},
entity::{Entities, Entity},
entity::{Entities, Entity, EntityCloneBuilder},
event::{Event, SendEvent},
observer::{Observer, TriggerEvent, TriggerTargets},
schedule::ScheduleLabel,
@ -271,6 +271,69 @@ impl<'w, 's> Commands<'w, 's> {
}
}
/// Clones an entity and allows configuring cloning behavior using [`EntityCloneBuilder`], returning [`EntityCommands`] of the cloned entity.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
///
/// #[derive(Component, Clone)]
/// struct ComponentA(u32);
/// #[derive(Component, Clone)]
/// struct ComponentB(u32);
///
/// fn example_system(mut commands: Commands) {
/// // Create a new entity and retrieve its id.
/// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id();
///
/// // Create a clone of the first entity, but without ComponentB
/// let entity_clone = commands.clone_entity_with(entity, |builder| {
/// builder.deny::<ComponentB>();
/// }).id();
/// }
/// # bevy_ecs::system::assert_is_system(example_system);
pub fn clone_entity_with(
&mut self,
entity: Entity,
f: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
) -> EntityCommands<'_> {
let cloned_entity = self.spawn_empty().id();
self.queue(move |world: &mut World| {
let mut builder = EntityCloneBuilder::new(world);
f(&mut builder);
builder.clone_entity(entity, cloned_entity);
});
EntityCommands {
commands: self.reborrow(),
entity: cloned_entity,
}
}
/// Clones an entity and returns [`EntityCommands`] of the cloned entity.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
///
/// #[derive(Component, Clone)]
/// struct ComponentA(u32);
/// #[derive(Component, Clone)]
/// struct ComponentB(u32);
///
/// fn example_system(mut commands: Commands) {
/// // Create a new entity and retrieve its id.
/// let entity = commands.spawn((ComponentA(10), ComponentB(20))).id();
///
/// // Create a clone of the first entity
/// let entity_clone = commands.clone_entity(entity).id();
/// }
/// # bevy_ecs::system::assert_is_system(example_system);
pub fn clone_entity(&mut self, entity: Entity) -> EntityCommands<'_> {
self.clone_entity_with(entity, |_| {})
}
/// Reserves a new empty [`Entity`] to be spawned, and returns its corresponding [`EntityCommands`].
///
/// See [`World::spawn_empty`] for more details.

View file

@ -34,8 +34,9 @@ use crate::{
bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode},
change_detection::{MutUntyped, TicksMut},
component::{
Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks,
Components, RequiredComponents, RequiredComponentsError, Tick,
Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId,
ComponentInfo, ComponentTicks, Components, RequiredComponents, RequiredComponentsError,
Tick,
},
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityHashSet, EntityLocation},
event::{Event, EventId, Events, SendBatchIds},
@ -3338,6 +3339,35 @@ 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::EntityCloner;
/// use bevy_ecs::world::DeferredWorld;
///
/// fn custom_clone_handler(
/// _world: &mut DeferredWorld,
/// _entity_cloner: &EntityCloner,
/// ) {
/// // 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(custom_clone_handler))
/// ```
pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers {
self.components.get_component_clone_handlers_mut()
}
}
impl World {

View file

@ -4,7 +4,7 @@ use bevy_ecs::reflect::{
ReflectVisitEntitiesMut,
};
use bevy_ecs::{
component::Component,
component::{Component, ComponentCloneHandler, StorageType},
entity::{Entity, VisitEntitiesMut},
prelude::FromWorld,
world::World,
@ -25,7 +25,7 @@ use smallvec::SmallVec;
/// [`Query`]: bevy_ecs::system::Query
/// [`Parent`]: crate::components::parent::Parent
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
#[derive(Component, Debug, VisitEntitiesMut)]
#[derive(Debug, VisitEntitiesMut)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(
feature = "reflect",
@ -40,6 +40,14 @@ use smallvec::SmallVec;
)]
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
impl Component for Children {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn get_component_clone_handler() -> ComponentCloneHandler {
ComponentCloneHandler::Ignore
}
}
// TODO: We need to impl either FromWorld or Default so Children can be registered as Reflect.
// This is because Reflect deserialize by creating an instance and apply a patch on top.
// However Children should only ever be set with a real user-defined entities. Its worth looking

View file

@ -4,7 +4,7 @@ use bevy_ecs::reflect::{
ReflectVisitEntitiesMut,
};
use bevy_ecs::{
component::Component,
component::{Component, ComponentCloneHandler, StorageType},
entity::{Entity, VisitEntities, VisitEntitiesMut},
traversal::Traversal,
world::{FromWorld, World},
@ -24,7 +24,7 @@ use core::ops::Deref;
/// [`Query`]: bevy_ecs::system::Query
/// [`Children`]: super::children::Children
/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children
#[derive(Component, Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)]
#[derive(Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(
feature = "reflect",
@ -40,6 +40,14 @@ use core::ops::Deref;
)]
pub struct Parent(pub(crate) Entity);
impl Component for Parent {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn get_component_clone_handler() -> ComponentCloneHandler {
ComponentCloneHandler::Ignore
}
}
impl Parent {
/// Gets the [`Entity`] ID of the parent.
#[inline(always)]

View file

@ -1,8 +1,12 @@
use crate::components::{Children, Parent};
use crate::{
components::{Children, Parent},
BuildChildren,
};
use bevy_ecs::{
entity::Entity,
component::ComponentCloneHandler,
entity::{Entity, EntityCloneBuilder, EntityCloner},
system::EntityCommands,
world::{Command, EntityWorldMut, World},
world::{Command, DeferredWorld, EntityWorldMut, World},
};
use bevy_utils::tracing::debug;
@ -198,6 +202,67 @@ impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> {
}
}
/// Trait that holds functions for cloning entities recursively down the hierarchy
pub trait CloneEntityHierarchyExt {
/// Sets the option to recursively clone entities.
/// When set to true all children will be cloned with the same options as the parent.
fn recursive(&mut self, recursive: bool) -> &mut Self;
/// Sets the option to add cloned entity as a child to the parent entity.
fn as_child(&mut self, as_child: bool) -> &mut Self;
}
impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> {
fn recursive(&mut self, recursive: bool) -> &mut Self {
if recursive {
self.override_component_clone_handler::<Children>(ComponentCloneHandler::Custom(
component_clone_children,
))
} else {
self.remove_component_clone_handler_override::<Children>()
}
}
fn as_child(&mut self, as_child: bool) -> &mut Self {
if as_child {
self.override_component_clone_handler::<Parent>(ComponentCloneHandler::Custom(
component_clone_parent,
))
} else {
self.remove_component_clone_handler_override::<Parent>()
}
}
}
/// Clone handler for the [`Children`] component. Allows to clone the entity recursively.
fn component_clone_children(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
let children = world
.get::<Children>(entity_cloner.source())
.expect("Source entity must have Children component")
.iter()
.cloned()
.collect::<Vec<_>>();
let parent = entity_cloner.target();
for child in children {
let child_clone = world.commands().spawn_empty().id();
let mut entity_cloner = entity_cloner.with_source_and_target(child, child_clone);
world.commands().queue(move |world: &mut World| {
entity_cloner.clone_entity(world);
world.entity_mut(child_clone).set_parent(parent);
});
}
}
/// Clone handler for the [`Parent`] component. Allows to add clone as a child to the parent entity.
fn component_clone_parent(world: &mut DeferredWorld, entity_cloner: &EntityCloner) {
let parent = world
.get::<Parent>(entity_cloner.source())
.map(|p| p.0)
.expect("Source entity must have Parent component");
world
.commands()
.entity(entity_cloner.target())
.set_parent(parent);
}
#[cfg(test)]
mod tests {
use bevy_ecs::{
@ -210,6 +275,7 @@ mod tests {
use crate::{
child_builder::{BuildChildren, ChildBuild},
components::Children,
CloneEntityHierarchyExt,
};
#[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
@ -342,4 +408,89 @@ mod tests {
// The original child should be despawned.
assert!(world.get_entity(child).is_err());
}
#[test]
fn clone_entity_recursive() {
#[derive(Component, PartialEq, Eq, Clone)]
struct Component1 {
field: usize,
}
let parent_component = Component1 { field: 10 };
let child1_component = Component1 { field: 20 };
let child1_1_component = Component1 { field: 30 };
let child2_component = Component1 { field: 21 };
let child2_1_component = Component1 { field: 31 };
let mut world = World::default();
let mut queue = CommandQueue::default();
let e_clone = {
let mut commands = Commands::new(&mut queue, &world);
let e = commands
.spawn(parent_component.clone())
.with_children(|children| {
children
.spawn(child1_component.clone())
.with_children(|children| {
children.spawn(child1_1_component.clone());
});
children
.spawn(child2_component.clone())
.with_children(|children| {
children.spawn(child2_1_component.clone());
});
})
.id();
let e_clone = commands
.clone_entity_with(e, |builder| {
builder.recursive(true);
})
.id();
e_clone
};
queue.apply(&mut world);
assert!(world
.get::<Component1>(e_clone)
.is_some_and(|c| *c == parent_component));
let children = world.get::<Children>(e_clone).unwrap();
for (child, (component1, component2)) in children.iter().zip([
(child1_component, child1_1_component),
(child2_component, child2_1_component),
]) {
assert!(world
.get::<Component1>(*child)
.is_some_and(|c| *c == component1));
for child2 in world.get::<Children>(*child).unwrap().iter() {
assert!(world
.get::<Component1>(*child2)
.is_some_and(|c| *c == component2));
}
}
}
#[test]
fn clone_entity_as_child() {
let mut world = World::default();
let mut queue = CommandQueue::default();
let mut commands = Commands::new(&mut queue, &world);
let child = commands.spawn_empty().id();
let parent = commands.spawn_empty().add_child(child).id();
let child_clone = commands
.clone_entity_with(child, |builder| {
builder.as_child(true);
})
.id();
queue.apply(&mut world);
assert!(world
.entity(parent)
.get::<Children>()
.is_some_and(|c| c.contains(&child_clone)));
}
}