Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have a helper method that calls set_component_handler(id, component_clone_via_clone::<C>) with the correct id? You couldn't put it on ComponentCloneHandlers since you can't get the component_id from that, but you could put it on World or Components.

Copy link
Contributor Author

@eugineerd eugineerd Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how useful that will be. I though that most users would not need to add component_clone_via_clone handlers at runtime, instead they would manually impl Clone for a component or set a handler in get_component_clone_handler. Is it for components with conditional Clone implementation with generics?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking about the conditional Clone implementations. One of the reasons it seems perfectly fine to use default cloning in that case is that anyone who really needs component_clone_via_clone can just register it for the concrete component types! But I agree that's likely to be rare, so it's probably not worth adding extra methods for it unless we see folks doing that in the wild.

/// 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
Loading