Skip to content

Commit

Permalink
Entity cloning (#16132)
Browse files Browse the repository at this point in the history
## 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.
  • Loading branch information
eugineerd authored Dec 3, 2024
1 parent d221665 commit 2e267bb
Show file tree
Hide file tree
Showing 12 changed files with 1,111 additions and 14 deletions.
6 changes: 6 additions & 0 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
})
}
Expand Down
201 changes: 200 additions & 1 deletion crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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>)
}
}
Loading

0 comments on commit 2e267bb

Please sign in to comment.