bevy/crates/bevy_ecs/src/entity/clone_entities.rs
Zachary Harrold a35811d088
Add Immutable Component Support (#16372)
# Objective

- Fixes #16208

## Solution

- Added an associated type to `Component`, `Mutability`, which flags
whether a component is mutable, or immutable. If `Mutability= Mutable`,
the component is mutable. If `Mutability= Immutable`, the component is
immutable.
- Updated `derive_component` to default to mutable unless an
`#[component(immutable)]` attribute is added.
- Updated `ReflectComponent` to check if a component is mutable and, if
not, panic when attempting to mutate.

## Testing

- CI
- `immutable_components` example.

---

## Showcase

Users can now mark a component as `#[component(immutable)]` to prevent
safe mutation of a component while it is attached to an entity:

```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
    // ...
}
```

This prevents creating an exclusive reference to the component while it
is attached to an entity. This is particularly powerful when combined
with component hooks, as you can now fully track a component's value,
ensuring whatever invariants you desire are upheld. Before this would be
done my making a component private, and manually creating a `QueryData`
implementation which only permitted read access.

<details>
  <summary>Using immutable components as an index</summary>
  
```rust
/// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)]
#[component(
    immutable,
    on_insert = on_insert_name,
    on_replace = on_replace_name,
)]
pub struct Name(pub &'static str);

/// This index allows for O(1) lookups of an [`Entity`] by its [`Name`].
#[derive(Resource, Default)]
struct NameIndex {
    name_to_entity: HashMap<Name, Entity>,
}

impl NameIndex {
    fn get_entity(&self, name: &'static str) -> Option<Entity> {
        self.name_to_entity.get(&Name(name)).copied()
    }
}

fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
    let Some(&name) = world.entity(entity).get::<Name>() else {
        unreachable!()
    };
    let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
        return;
    };

    index.name_to_entity.insert(name, entity);
}

fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
    let Some(&name) = world.entity(entity).get::<Name>() else {
        unreachable!()
    };
    let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
        return;
    };

    index.name_to_entity.remove(&name);
}

// Setup our name index
world.init_resource::<NameIndex>();

// Spawn some entities!
let alyssa = world.spawn(Name("Alyssa")).id();
let javier = world.spawn(Name("Javier")).id();

// Check our index
let index = world.resource::<NameIndex>();

assert_eq!(index.get_entity("Alyssa"), Some(alyssa));
assert_eq!(index.get_entity("Javier"), Some(javier));

// Changing the name of an entity is also fully capture by our index
world.entity_mut(javier).insert(Name("Steven"));

// Javier changed their name to Steven
let steven = javier;

// Check our index
let index = world.resource::<NameIndex>();

assert_eq!(index.get_entity("Javier"), None);
assert_eq!(index.get_entity("Steven"), Some(steven));
```
  
</details>

Additionally, users can use `Component<Mutability = ...>` in trait
bounds to enforce that a component _is_ mutable or _is_ immutable. When
using `Component` as a trait bound without specifying `Mutability`, any
component is applicable. However, methods which only work on mutable or
immutable components are unavailable, since the compiler must be
pessimistic about the type.

## Migration Guide

- When implementing `Component` manually, you must now provide a type
for `Mutability`. The type `Mutable` provides equivalent behaviour to
earlier versions of `Component`:
```rust
impl Component for Foo {
    type Mutability = Mutable;
    // ...
}
```
- When working with generic components, you may need to specify that
your generic parameter implements `Component<Mutability = Mutable>`
rather than `Component` if you require mutable access to said component.
- The entity entry API has had to have some changes made to minimise
friction when working with immutable components. Methods which
previously returned a `Mut<T>` will now typically return an
`OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get
the `Mut<T>` item again.

## Draft Release Notes

Components can now be made immutable while stored within the ECS.

Components are the fundamental unit of data within an ECS, and Bevy
provides a number of ways to work with them that align with Rust's rules
around ownership and borrowing. One part of this is hooks, which allow
for defining custom behavior at key points in a component's lifecycle,
such as addition and removal. However, there is currently no way to
respond to _mutation_ of a component using hooks. The reasons for this
are quite technical, but to summarize, their addition poses a
significant challenge to Bevy's core promises around performance.
Without mutation hooks, it's relatively trivial to modify a component in
such a way that breaks invariants it intends to uphold. For example, you
can use `core::mem::swap` to swap the components of two entities,
bypassing the insertion and removal hooks.

This means the only way to react to this modification is via change
detection in a system, which then begs the question of what happens
_between_ that alteration and the next run of that system?
Alternatively, you could make your component private to prevent
mutation, but now you need to provide commands and a custom `QueryData`
implementation to allow users to interact with your component at all.

Immutable components solve this problem by preventing the creation of an
exclusive reference to the component entirely. Without an exclusive
reference, the only way to modify an immutable component is via removal
or replacement, which is fully captured by component hooks. To make a
component immutable, simply add `#[component(immutable)]`:

```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
    // ...
}
```

When implementing `Component` manually, there is an associated type
`Mutability` which controls this behavior:

```rust
impl Component for Foo {
    type Mutability = Mutable;
    // ...
}
```

Note that this means when working with generic components, you may need
to specify that a component is mutable to gain access to certain
methods:

```rust
// Before
fn bar<C: Component>() {
    // ...
}

// After
fn bar<C: Component<Mutability = Mutable>>() {
    // ...
}
```

With this new tool, creating index components, or caching data on an
entity should be more user friendly, allowing libraries to provide APIs
relying on components and hooks to uphold their invariants.

## Notes

- ~~I've done my best to implement this feature, but I'm not happy with
how reflection has turned out. If any reflection SMEs know a way to
improve this situation I'd greatly appreciate it.~~ There is an
outstanding issue around the fallibility of mutable methods on
`ReflectComponent`, but the DX is largely unchanged from `main` now.
- I've attempted to prevent all safe mutable access to a component that
does not implement `Component<Mutability = Mutable>`, but there may
still be some methods I have missed. Please indicate so and I will
address them, as they are bugs.
- Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever
you do with unsafe is between you and your compiler.
- I am marking this PR as ready, but I suspect it will undergo fairly
major revisions based on SME feedback.
- I've marked this PR as _Uncontroversial_ based on the feature, not the
implementation.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: Nuutti Kotivuori <naked@iki.fi>
2024-12-05 14:27:48 +00:00

523 lines
18 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>>,
}
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, 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>,
}
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());
}
}