bevy/crates/bevy_ecs/src/entity/clone_entities.rs
JaySpruce 5a94beb239
Extend cloning functionality and add convenience methods to EntityWorldMut and EntityCommands (#16826)
## Objective

Thanks to @eugineerd's work on entity cloning (#16132), we now have a
robust way to copy components between entities. We can extend this to
implement some useful functionality that would have been more
complicated before.

Closes #15350.

## Solution

`EntityCloneBuilder` now automatically includes required components
alongside any component added/removed from the component filter.

Added the following methods to `EntityCloneBuilder`:
- `move_components`
- `without_required_components`

Added the following methods to `EntityWorldMut` and `EntityCommands`:
- `clone_with`
- `clone_components`
- `move_components`

Also added `clone_and_spawn` and `clone_and_spawn_with` to
`EntityWorldMut` (`EntityCommands` already had them).

## Showcase

```
assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));
assert_eq!(world.entity(entity_b).get::<B>(), None);
world.entity_mut(entity_a).clone_components::<B>(entity_b);
assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));
assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));

assert_eq!(world.entity(entity_a).get::<C>(), Some(&C(5)));
assert_eq!(world.entity(entity_b).get::<C>(), None);
world.entity_mut(entity_a).move_components::<C>(entity_b);
assert_eq!(world.entity(entity_a).get::<C>(), None);
assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5)));
```
2024-12-16 19:37:32 +00:00

599 lines
20 KiB
Rust

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>>,
move_components: bool,
}
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);
}
if self.move_components {
world.entity_mut(self.source).remove_by_ids(&components);
}
}
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, Mutable};
/// #[derive(Clone)]
/// struct SomeComponent;
///
/// impl Component for SomeComponent {
/// const STORAGE_TYPE: StorageType = StorageType::Table;
/// type Mutability = Mutable;
/// 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>,
attach_required_components: bool,
move_components: bool,
}
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(),
attach_required_components: true,
move_components: false,
}
}
/// 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,
move_components,
..
} = self;
EntityCloner {
source,
target,
component_id: None,
filter_allows_components,
filter: Arc::new(filter),
clone_handlers_overrides: Arc::new(clone_handlers_overrides),
move_components,
}
.clone_entity(world);
world.flush_commands();
}
/// By default, any components allowed/denied through the filter will automatically
/// allow/deny all of their required components.
///
/// This method allows for a scoped mode where any changes to the filter
/// will not involve required components.
pub fn without_required_components(
&mut self,
builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
) -> &mut Self {
self.attach_required_components = false;
builder(self);
self.attach_required_components = true;
self
}
/// Sets whether the cloner should remove any components that were cloned,
/// effectively moving them from the source entity to the target.
///
/// This is disabled by default.
///
/// The setting only applies to components that are allowed through the filter
/// at the time [`EntityCloneBuilder::clone_entity`] is called.
pub fn move_components(&mut self, enable: bool) -> &mut Self {
self.move_components = enable;
self
}
/// 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 {
let bundle = self.world.register_bundle::<T>();
let ids = bundle.explicit_components().to_owned();
for id in ids {
self.filter_allow(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 {
for id in ids {
self.filter_allow(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 {
for type_id in ids {
if let Some(id) = self.world.components().get_id(type_id) {
self.filter_allow(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 {
let bundle = self.world.register_bundle::<T>();
let ids = bundle.explicit_components().to_owned();
for id in ids {
self.filter_deny(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 {
for id in ids {
self.filter_deny(id);
}
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 {
for type_id in ids {
if let Some(id) = self.world.components().get_id(type_id) {
self.filter_deny(id);
}
}
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
}
/// Helper function that allows a component through the filter.
fn filter_allow(&mut self, id: ComponentId) {
if self.filter_allows_components {
self.filter.insert(id);
} else {
self.filter.remove(&id);
}
if self.attach_required_components {
if let Some(info) = self.world.components().get_info(id) {
for required_id in info.required_components().iter_ids() {
if self.filter_allows_components {
self.filter.insert(required_id);
} else {
self.filter.remove(&required_id);
}
}
}
}
}
/// Helper function that disallows a component through the filter.
fn filter_deny(&mut self, id: ComponentId) {
if self.filter_allows_components {
self.filter.remove(&id);
} else {
self.filter.insert(id);
}
if self.attach_required_components {
if let Some(info) = self.world.components().get_info(id) {
for required_id in info.required_components().iter_ids() {
if self.filter_allows_components {
self.filter.remove(&required_id);
} else {
self.filter.insert(required_id);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World};
use bevy_ecs_macros::require;
#[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());
}
#[test]
fn clone_entity_with_required_components() {
#[derive(Component, Clone, PartialEq, Debug)]
#[require(B)]
struct A;
#[derive(Component, Clone, PartialEq, Debug, Default)]
#[require(C(|| C(5)))]
struct B;
#[derive(Component, Clone, PartialEq, Debug)]
struct C(u32);
let mut world = World::default();
let e = world.spawn(A).id();
let e_clone = world.spawn_empty().id();
let mut builder = EntityCloneBuilder::new(&mut world);
builder.deny_all();
builder.without_required_components(|builder| {
builder.allow::<B>();
});
builder.clone_entity(e, e_clone);
assert_eq!(world.entity(e_clone).get::<A>(), None);
assert_eq!(world.entity(e_clone).get::<B>(), Some(&B));
assert_eq!(world.entity(e_clone).get::<C>(), Some(&C(5)));
}
}