diff --git a/benches/benches/bevy_ecs/entity_cloning.rs b/benches/benches/bevy_ecs/entity_cloning.rs index 80577b9a9d0b5..d45ff7e78b036 100644 --- a/benches/benches/bevy_ecs/entity_cloning.rs +++ b/benches/benches/bevy_ecs/entity_cloning.rs @@ -67,11 +67,9 @@ fn set_reflect_clone_handler(world: &mut World) // this bundle are saved. let component_ids: Vec<_> = world.register_bundle::().contributed_components().into(); - let clone_handlers = world.get_component_clone_handlers_mut(); - // Overwrite the clone handler for all components in the bundle to use `Reflect`, not `Clone`. for component in component_ids { - clone_handlers.set_component_handler(component, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(component, ComponentCloneHandler::reflect_handler()); } } diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index cd7cd52bf6570..54031cee966da 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -51,6 +51,12 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { TokenStream::from(quote! { impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause { + fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler { + use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneViaCopy, ComponentCloneBase}; + + (&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::::default()) + .get_component_clone_handler() + } } }) } @@ -168,7 +174,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { } fn get_component_clone_handler() -> #bevy_ecs_path::component::ComponentCloneHandler { - use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneBase}; + use #bevy_ecs_path::component::{ComponentCloneViaClone, ComponentCloneViaCopy, ComponentCloneBase}; + (&&&#bevy_ecs_path::component::ComponentCloneSpecializationWrapper::::default()) .get_component_clone_handler() } diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index e0d85242f9206..81859e5358760 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -119,6 +119,7 @@ pub(crate) enum ComponentStatus { } /// Used in [`Edges`] to cache the result of inserting a bundle into the source archetype. +#[derive(Clone)] pub(crate) struct ArchetypeAfterBundleInsert { /// The target archetype after the bundle is inserted into the source archetype. pub archetype_id: ArchetypeId, @@ -194,7 +195,7 @@ impl BundleComponentStatus for SpawnBundleStatus { /// yet. /// /// [`World`]: crate::world::World -#[derive(Default)] +#[derive(Default, Clone)] pub struct Edges { insert_bundle: SparseArray, remove_bundle: SparseArray>, @@ -306,6 +307,7 @@ impl Edges { } /// Metadata about an [`Entity`] in a [`Archetype`]. +#[derive(Clone)] pub struct ArchetypeEntity { entity: Entity, table_row: TableRow, @@ -339,6 +341,7 @@ pub(crate) struct ArchetypeSwapRemoveResult { /// Internal metadata for a [`Component`] within a given [`Archetype`]. /// /// [`Component`]: crate::component::Component +#[derive(Clone)] struct ArchetypeComponentInfo { storage_type: StorageType, archetype_component_id: ArchetypeComponentId, @@ -367,6 +370,7 @@ bitflags::bitflags! { /// /// [`World`]: crate::world::World /// [module level documentation]: crate::archetype +#[derive(Clone)] pub struct Archetype { id: ArchetypeId, table_id: TableId, @@ -720,7 +724,7 @@ impl ArchetypeGeneration { } } -#[derive(Hash, PartialEq, Eq)] +#[derive(Clone, Hash, PartialEq, Eq)] struct ArchetypeComponents { table_components: Box<[ComponentId]>, sparse_set_components: Box<[ComponentId]>, @@ -776,6 +780,7 @@ pub type ComponentIndex = HashMap, archetype_component_count: usize, @@ -786,6 +791,7 @@ pub struct Archetypes { } /// Metadata about how a component is stored in an [`Archetype`]. +#[derive(Clone)] pub struct ArchetypeRecord { /// Index of the component in the archetype's [`Table`](crate::storage::Table), /// or None if the component is a sparse set component. diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 4dc96311bb25a..121d9d961388a 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -348,6 +348,7 @@ pub enum InsertMode { /// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`]. /// /// [`World`]: crate::world::World +#[derive(Clone)] pub struct BundleInfo { id: BundleId, /// The list of all components contributed by the bundle (including Required Components). This is in @@ -1433,7 +1434,7 @@ impl<'w> BundleSpawner<'w> { } /// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world. -#[derive(Default)] +#[derive(Default, Clone)] pub struct Bundles { bundle_infos: Vec, /// Cache static [`BundleId`] diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 66d5db98f8fcb..d7362ba8e7aa5 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -787,6 +787,18 @@ impl ComponentInfo { pub fn required_components(&self) -> &RequiredComponents { &self.required_components } + + /// Returns `true` if component is [`Copy`]. + /// + /// Unless this is `true`, it is not safe to clone this component's data using memory copy. + pub fn is_copy(&self) -> bool { + self.descriptor.is_copy + } + + /// Returns [`ComponentCloneHandler`] set for this component. + pub fn clone_handler(&self) -> &ComponentCloneHandler { + &self.descriptor.clone_handler + } } /// A value which uniquely identifies the type of a [`Component`] or [`Resource`] within a @@ -864,6 +876,10 @@ pub struct ComponentDescriptor { // None if the underlying type doesn't need to be dropped drop: Option unsafe fn(OwningPtr<'a>)>, mutable: bool, + // SAFETY: This must remain private. It must only be set to "true" if this component is + // actually Copy + is_copy: bool, + clone_handler: ComponentCloneHandler, } // We need to ignore the `drop` field in our `Debug` impl @@ -876,6 +892,8 @@ impl Debug for ComponentDescriptor { .field("type_id", &self.type_id) .field("layout", &self.layout) .field("mutable", &self.mutable) + .field("is_copy", &self.is_copy) + .field("clone_handler", &self.clone_handler) .finish() } } @@ -901,6 +919,9 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: T::Mutability::MUTABLE, + // Safety: is_copy() will be true only if T is actually Copy + is_copy: is_copy::(), + clone_handler: T::get_component_clone_handler(), } } @@ -909,12 +930,15 @@ impl ComponentDescriptor { /// # Safety /// - the `drop` fn must be usable on a pointer with a value of the layout `layout` /// - the component type must be safe to access from any thread (Send + Sync in rust terms) + /// - `is_copy` should be `true` only if component is safe to clone using memory copy. pub unsafe fn new_with_layout( name: impl Into>, storage_type: StorageType, layout: Layout, drop: Option unsafe fn(OwningPtr<'a>)>, mutable: bool, + is_copy: bool, + clone_handler: ComponentCloneHandler, ) -> Self { Self { name: name.into(), @@ -924,6 +948,8 @@ impl ComponentDescriptor { layout, drop, mutable, + is_copy, + clone_handler, } } @@ -941,6 +967,9 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + // Safety: is_copy() will be true only if T is actually Copy + is_copy: is_copy::(), + clone_handler: T::get_component_clone_handler(), } } @@ -953,6 +982,9 @@ impl ComponentDescriptor { layout: Layout::new::(), drop: needs_drop::().then_some(Self::drop_ptr:: as _), mutable: true, + // Safety: is_copy() will be true only if T is actually Copy + is_copy: is_copy::(), + clone_handler: ComponentCloneHandler::default_handler(), } } @@ -980,123 +1012,128 @@ impl ComponentDescriptor { pub fn mutable(&self) -> bool { self.mutable } + + /// Return currently set [`ComponentCloneHandler`] for this component. + #[inline] + pub fn clone_handler(&self) -> &ComponentCloneHandler { + &self.clone_handler + } } /// Function type that can be used to clone an entity. -pub type ComponentCloneFn = fn(&mut DeferredWorld, &mut ComponentCloneCtx); +pub type ComponentCloneFn = fn(&World, &mut ComponentCloneCtx); + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ComponentCloneHandlerKind { + Default, + Ignore, + Copy, + Custom(ComponentCloneFn), +} /// A struct instructing which clone handler to use when cloning a component. -#[derive(Debug)] -pub struct ComponentCloneHandler(Option); +#[derive(Debug, Clone, Copy)] +pub struct ComponentCloneHandler { + entity_handler: ComponentCloneHandlerKind, + world_handler: Option, +} impl ComponentCloneHandler { + fn new(entity_handler: ComponentCloneHandlerKind) -> Self { + Self { + entity_handler, + world_handler: None, + } + } + /// Use the global default function to clone the component with this handler. pub fn default_handler() -> Self { - Self(None) + Self::new(ComponentCloneHandlerKind::Default) } /// Do not clone the component. When a command to clone an entity is issued, component with this handler will be skipped. pub fn ignore() -> Self { - Self(Some(component_clone_ignore)) + Self::new(ComponentCloneHandlerKind::Ignore) } /// Set clone handler based on `Clone` trait. /// /// If set as a handler for a component that is not the same as the one used to create this handler, it will panic. - pub fn clone_handler() -> Self { - Self(Some(component_clone_via_clone::)) + pub fn clone_handler() -> Self { + Self::new(ComponentCloneHandlerKind::Custom( + component_clone_via_clone::, + )) + } + + /// Set clone handler to use copy. + /// + /// If component does not implement `Copy`, this will be equivalent to [`ignore`](Self::ignore) + pub fn copy_handler() -> Self { + Self::new(ComponentCloneHandlerKind::Copy) } /// Set clone handler based on `Reflect` trait. + /// + /// If component does not implement `Reflect`, this will be equivalent to [`ignore`](Self::ignore) #[cfg(feature = "bevy_reflect")] pub fn reflect_handler() -> Self { - Self(Some(component_clone_via_reflect)) + Self::new(ComponentCloneHandlerKind::Custom( + component_clone_via_reflect, + )) } /// Set a custom handler for the component. pub fn custom_handler(handler: ComponentCloneFn) -> Self { - Self(Some(handler)) - } - - /// Get [`ComponentCloneFn`] representing this handler or `None` if set to default handler. - pub fn get_handler(&self) -> Option { - self.0 + Self::new(ComponentCloneHandlerKind::Custom(handler)) } -} - -/// 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>, - default_handler: ComponentCloneFn, -} -impl ComponentCloneHandlers { - /// Sets the default handler for this registry. All components with [`default`](ComponentCloneHandler::default_handler) handler, as well as any component that does not have an - /// explicitly registered clone function will use this handler. + /// Set a different component clone handler when performing world cloning instead of entity cloning. + /// If world clone handler is not set, entity clone handler will be used to clone component during world cloning instead. /// - /// 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); + /// Provided handler should generally be infallible and rely only on source component data. + pub fn with_world_clone_handler(self, handler: Self) -> Self { + Self { + entity_handler: self.entity_handler, + world_handler: Some(handler.entity_handler), } - self.handlers[id.0] = handler.0; } - /// 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 inner [`ComponentCloneHandlerKind`] describing how component should be cloned when performing entity cloning. + pub(crate) fn get_entity_handler(&self) -> ComponentCloneHandlerKind { + self.entity_handler } - /// 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). + /// Gets inner [`ComponentCloneHandlerKind`] describing how component should be cloned when performing world 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, - } + /// If [`ComponentCloneHandler`] does not have explicit world handler, use [`entity handler`](Self::get_entity_handler). + pub(crate) fn get_world_handler(&self) -> Option { + self.world_handler } } -impl Default for ComponentCloneHandlers { +/// Stores metadata associated with each kind of [`Component`] in a given [`World`]. +#[derive(Debug, Clone)] +pub struct Components { + components: Vec, + indices: TypeIdMap, + resource_indices: TypeIdMap, + default_component_clone_handler: ComponentCloneFn, +} + +impl Default for Components { fn default() -> Self { Self { - handlers: Default::default(), + components: Default::default(), + indices: Default::default(), + resource_indices: Default::default(), #[cfg(feature = "bevy_reflect")] - default_handler: component_clone_via_reflect, + default_component_clone_handler: component_clone_via_reflect, #[cfg(not(feature = "bevy_reflect"))] - default_handler: component_clone_ignore, + default_component_clone_handler: component_clone_ignore, } } } -/// Stores metadata associated with each kind of [`Component`] in a given [`World`]. -#[derive(Debug, Default)] -pub struct Components { - components: Vec, - indices: TypeIdMap, - resource_indices: TypeIdMap, - component_clone_handlers: ComponentCloneHandlers, -} - impl Components { /// Registers a [`Component`] of type `T` with this instance. /// If a component of this type has already been registered, this will return @@ -1148,9 +1185,6 @@ 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 } @@ -1513,16 +1547,6 @@ 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 { @@ -1677,6 +1701,37 @@ impl Components { pub fn iter(&self) -> impl Iterator + '_ { self.components.iter() } + + /// Sets the default handler for this registry. All components with [`default`](ComponentCloneHandler::default_handler) 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_clone_handler(&mut self, handler: ComponentCloneFn) { + self.default_component_clone_handler = handler; + } + + /// Returns the currently registered default handler. + pub fn get_default_clone_handler(&self) -> ComponentCloneFn { + self.default_component_clone_handler + } + + /// Sets [`ComponentCloneHandler`] for component with specified [`ComponentId`]. + /// + /// If component with [`ComponentId`] is not registered, this does nothing. + pub fn set_clone_handler(&mut self, component_id: ComponentId, handler: ComponentCloneHandler) { + if let Some(info) = self.components.get_mut(component_id.0) { + info.descriptor.clone_handler = handler; + } + } + + /// Gets [`ComponentCloneHandler`] for component with specified [`ComponentId`]. + /// + /// Returns `None` if component with provided [`ComponentId`] is not registered. + pub fn get_clone_handler(&self, component_id: ComponentId) -> Option { + self.components + .get(component_id.0) + .map(|info| info.descriptor.clone_handler) + } } /// A value that tracks when a system ran relative to other systems. @@ -2166,21 +2221,16 @@ pub fn enforce_no_required_components_recursion( } /// 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. +/// Can be [set](World::set_component_clone_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( - _world: &mut DeferredWorld, - ctx: &mut ComponentCloneCtx, -) { - if let Some(component) = ctx.read_source_component::() { +pub fn component_clone_via_clone(_world: &World, ctx: &mut ComponentCloneCtx) { + if let Some(component) = ctx.read_source_component::() { ctx.write_target_component(component.clone()); } } /// Component [clone handler function](ComponentCloneFn) implemented using reflect. -/// Can be [set](ComponentCloneHandlers::set_component_handler) as clone handler for any registered component, +/// Can be [set](World::set_component_clone_handler) as clone handler for any registered component, /// but only reflected components will be cloned. /// /// To clone a component using this handler, the following must be true: @@ -2195,7 +2245,7 @@ pub fn component_clone_via_clone( /// /// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. #[cfg(feature = "bevy_reflect")] -pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { +pub fn component_clone_via_reflect(_world: &World, ctx: &mut ComponentCloneCtx) { let Some(registry) = ctx.type_registry() else { return; }; @@ -2233,12 +2283,18 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen if let Some(reflect_from_world) = registry.get_type_data::(type_id) { + let Some(mut commands) = ctx.commands() else { + return; + }; + let Some(target) = ctx.entity_cloner().map(crate::entity::EntityCloner::target) else { + return; + }; let reflect_from_world = reflect_from_world.clone(); let source_component_cloned = source_component_reflect.clone_value(); + drop(registry); let component_layout = component_info.layout(); - let target = ctx.target(); let component_id = ctx.component_id(); - world.commands().queue(move |world: &mut World| { + commands.queue(move |world: &mut World| { let mut component = reflect_from_world.from_world(world); assert_eq!(type_id, (*component).type_id()); component.apply(source_component_cloned.as_partial_reflect()); @@ -2260,7 +2316,7 @@ pub fn component_clone_via_reflect(world: &mut DeferredWorld, ctx: &mut Componen /// Noop implementation of component clone handler function. /// /// See [`EntityCloneBuilder`](crate::entity::EntityCloneBuilder) for details. -pub fn component_clone_ignore(_world: &mut DeferredWorld, _ctx: &mut ComponentCloneCtx) {} +pub fn component_clone_ignore(_world: &World, _ctx: &mut ComponentCloneCtx) {} /// Wrapper for components clone specialization using autoderef. #[doc(hidden)] @@ -2277,7 +2333,7 @@ impl Default for ComponentCloneSpecializationWrapper { pub trait ComponentCloneBase { fn get_component_clone_handler(&self) -> ComponentCloneHandler; } -impl ComponentCloneBase for ComponentCloneSpecializationWrapper { +impl ComponentCloneBase for ComponentCloneSpecializationWrapper { fn get_component_clone_handler(&self) -> ComponentCloneHandler { ComponentCloneHandler::default_handler() } @@ -2288,8 +2344,82 @@ impl ComponentCloneBase for ComponentCloneSpecializationWrapper pub trait ComponentCloneViaClone { fn get_component_clone_handler(&self) -> ComponentCloneHandler; } -impl ComponentCloneViaClone for &ComponentCloneSpecializationWrapper { +impl ComponentCloneViaClone for &ComponentCloneSpecializationWrapper { fn get_component_clone_handler(&self) -> ComponentCloneHandler { - ComponentCloneHandler::clone_handler::() + ComponentCloneHandler::clone_handler::() + } +} + +/// Specialized trait for components clone specialization using autoderef. +#[doc(hidden)] +pub trait ComponentCloneViaCopy { + fn get_component_clone_handler(&self) -> ComponentCloneHandler; +} +impl ComponentCloneViaCopy for &&ComponentCloneSpecializationWrapper { + fn get_component_clone_handler(&self) -> ComponentCloneHandler { + ComponentCloneHandler::copy_handler() + } +} + +/// HACK: Determine if T is [`Copy`] by (ab)using array copy specialization. +/// This utilizes a maybe bug in std which is maybe unsound when used with lifetimes (see: ). +/// At the same time there doesn't seem to be any timeline on when (or if) this will be fixed, so maybe it is ok to use? +/// Either way, we can always fall back to autoderef-based specialization if this ever gets fixed +/// (or maybe proper specialization will be available by that time?). +/// This hack is used mostly to avoid forcing users to write unsafe code to indicate if component if actually `Copy` +/// when implementing [`Component`] trait by hand. +#[inline] +fn is_copy() -> bool { + struct MaybeCopy<'a, T>(&'a core::cell::Cell, PhantomData); + + impl Clone for MaybeCopy<'_, T> { + fn clone(&self) -> Self { + self.0.set(false); + Self(self.0, self.1) + } + } + + impl Copy for MaybeCopy<'_, T> {} + + let is_copy = core::cell::Cell::new(true); + + _ = [MaybeCopy::(&is_copy, Default::default()); 1].clone(); + + is_copy.get() +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn is_copy_working() { + struct A; + + #[derive(Clone, Copy)] + struct B; + + #[derive(Clone)] + struct C; + + impl Copy for C {} + + struct D; + + impl Clone for D { + fn clone(&self) -> Self { + *self + } + } + + impl Copy for D {} + + #[derive(Clone)] + struct E; + + assert!(!is_copy::()); + assert!(is_copy::()); + assert!(is_copy::()); + assert!(is_copy::()); + assert!(!is_copy::()); } } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 2b51c1f937b34..09e8833c76bd4 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -16,12 +16,26 @@ use alloc::sync::Arc; use crate::{ bundle::Bundle, - component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components}, - entity::Entity, + component::{ + Component, ComponentCloneHandler, ComponentCloneHandlerKind, ComponentId, ComponentInfo, + Components, + }, + entity::{Entities, Entity}, query::DebugCheckedUnwrap, - world::World, + system::Commands, + world::{command_queue::RawCommandQueue, CommandQueue, World}, }; +struct BumpWriteBuffer<'a, 'b> { + target_components_ptrs: &'a mut Vec>, + target_components_buffer: &'b Bump, +} + +enum ComponentWriteBuffer<'a, 'b> { + BumpWriteBuffer(BumpWriteBuffer<'a, 'b>), + Ptr(NonNull), +} + /// Context for component clone handlers. /// /// Provides fast access to useful resources like [`AppTypeRegistry`](crate::reflect::AppTypeRegistry) @@ -29,45 +43,45 @@ use crate::{ pub struct ComponentCloneCtx<'a, 'b> { component_id: ComponentId, source_component_ptr: Ptr<'a>, - target_component_written: bool, - target_components_ptrs: &'a mut Vec>, - target_components_buffer: &'b Bump, components: &'a Components, + target_write_buffer: ComponentWriteBuffer<'a, 'b>, component_info: &'a ComponentInfo, - entity_cloner: &'a EntityCloner, + target_component_written: bool, + entity_cloner: Option<&'a EntityCloner>, + command_queue: Option, + entities: &'a Entities, #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, - #[cfg(not(feature = "bevy_reflect"))] - #[expect(dead_code)] - type_registry: Option<()>, } impl<'a, 'b> ComponentCloneCtx<'a, 'b> { - /// Create a new instance of `ComponentCloneCtx` that can be passed to component clone handlers. + /// Create a new [`ComponentCloneCtx`] for specified component. /// /// # Safety - /// Caller must ensure that: - /// - `components` and `component_id` are from the same world. - /// - `source_component_ptr` points to a valid component of type represented by `component_id`. - unsafe fn new( - component_id: ComponentId, + /// - `component_info` must be from the `world`. + /// - `source_component_ptr` must point to a valid component described by `component_info`. + /// - `target_component_ptr` must have enough space allocated to write component described by `component_info`. + /// - `type_registry` must be from the `world`. + pub(crate) unsafe fn new_for_component( + component_info: &'a ComponentInfo, source_component_ptr: Ptr<'a>, - target_components_ptrs: &'a mut Vec>, - target_components_buffer: &'b Bump, - components: &'a Components, - entity_cloner: &'a EntityCloner, + target_component_ptr: NonNull, + world: &'a World, #[cfg(feature = "bevy_reflect")] type_registry: Option<&'a crate::reflect::AppTypeRegistry>, - #[cfg(not(feature = "bevy_reflect"))] type_registry: Option<()>, ) -> Self { + let components = world.components(); + let target_write_buffer = ComponentWriteBuffer::Ptr(target_component_ptr); Self { - component_id, + component_id: component_info.id(), source_component_ptr, - target_components_ptrs, + target_write_buffer, target_component_written: false, - target_components_buffer, + component_info, + entities: world.entities(), + command_queue: None, components, - component_info: components.get_info_unchecked(component_id), - entity_cloner, + entity_cloner: None, + #[cfg(feature = "bevy_reflect")] type_registry, } } @@ -77,16 +91,6 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.target_component_written } - /// Returns the current source entity. - pub fn source(&self) -> Entity { - self.entity_cloner.source - } - - /// Returns the current target entity. - pub fn target(&self) -> Entity { - self.entity_cloner.target - } - /// Returns the [`ComponentId`] of the component being cloned. pub fn component_id(&self) -> ComponentId { self.component_id @@ -97,10 +101,23 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { self.component_info } + /// Returns instance of [`Components`]. + pub fn components(&self) -> &Components { + self.components + } + + /// Returns an instance of [`Commands`] (if available for current context). + pub fn commands(&self) -> Option> { + self.command_queue.as_ref().map(|queue| { + // SAFETY: queue outlives 'a + unsafe { Commands::new_raw_from_entities(queue.clone(), self.entities) } + }) + } + /// Returns a reference to the component on the source entity. /// /// Will return `None` if `ComponentId` of requested component does not match `ComponentId` of source component - pub fn read_source_component(&self) -> Option<&T> { + pub fn read_source_component(&self) -> Option<&T> { if self .component_info .type_id() @@ -135,6 +152,28 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { unsafe { Some(reflect_from_ptr.as_reflect(self.source_component_ptr)) } } + /// # Safety + /// - Passed `write_fn` must initialize component at passed pointer and return `true`, otherwise it should return `false`. + unsafe fn write_target_component_impl(&mut self, write_fn: impl FnOnce(NonNull) -> bool) { + match &mut self.target_write_buffer { + ComponentWriteBuffer::BumpWriteBuffer(buffer) => { + let target_component_data_ptr = buffer + .target_components_buffer + .alloc_layout(self.component_info.layout()); + + if write_fn(target_component_data_ptr) { + buffer + .target_components_ptrs + .push(PtrMut::new(target_component_data_ptr)); + self.target_component_written = true; + } + } + ComponentWriteBuffer::Ptr(ptr) => { + self.target_component_written = write_fn(*ptr); + } + } + } + /// Writes component data to target entity. /// /// # Panics @@ -142,9 +181,9 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - Component has already been written once. /// - Component being written is not registered in the world. /// - `ComponentId` of component being written does not match expected `ComponentId`. - pub fn write_target_component(&mut self, component: T) { + pub fn write_target_component(&mut self, component: T) { let short_name = disqualified::ShortName::of::(); - if self.target_component_written { + if self.target_component_written() { panic!("Trying to write component '{short_name}' multiple times") } if self @@ -154,10 +193,14 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { { panic!("TypeId of component '{short_name}' does not match source component TypeId") }; - let component_ref = self.target_components_buffer.alloc(component); - self.target_components_ptrs - .push(PtrMut::from(component_ref)); - self.target_component_written = true; + // SAFETY: + // - target_ptr and component have layout described by component_info + unsafe { + self.write_target_component_impl(|target_ptr| { + target_ptr.cast::().write(component); + true + }); + } } /// Writes component data to target entity by providing a pointer to source component data and a pointer to uninitialized target component data. @@ -176,17 +219,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { &mut self, clone_fn: impl FnOnce(Ptr, NonNull) -> bool, ) { - if self.target_component_written { + if self.target_component_written() { panic!("Trying to write component multiple times") } - let layout = self.component_info.layout(); - let target_component_data_ptr = self.target_components_buffer.alloc_layout(layout); - - if clone_fn(self.source_component_ptr, target_component_data_ptr) { - self.target_components_ptrs - .push(PtrMut::new(target_component_data_ptr)); - self.target_component_written = true; - } + let source_component_ptr = self.source_component_ptr; + self.write_target_component_impl(|target_ptr| clone_fn(source_component_ptr, target_ptr)); } /// Writes component data to target entity. @@ -194,13 +231,12 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// # Panics /// This will panic if: /// - World does not have [`AppTypeRegistry`](`crate::reflect::AppTypeRegistry`). - /// - Component does not implement [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr). /// - Source component does not have [`TypeId`]. /// - Passed component's [`TypeId`] does not match source component [`TypeId`]. /// - Component has already been written once. #[cfg(feature = "bevy_reflect")] pub fn write_target_component_reflect(&mut self, component: Box) { - if self.target_component_written { + if self.target_component_written() { panic!("Trying to write component multiple times") } let source_type_id = self @@ -211,53 +247,48 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { if source_type_id != component_type_id { panic!("Passed component TypeId does not match source component TypeId") } - let component_layout = self.component_info.layout(); - + let layout = self.component_info.layout(); let component_data_ptr = Box::into_raw(component).cast::(); - let target_component_data_ptr = - self.target_components_buffer.alloc_layout(component_layout); // SAFETY: - // - target_component_data_ptr and component_data have the same data type. - // - component_data_ptr has layout of component_layout + // - component_data_ptr and target_ptr don't overlap + // - target_ptr and component_data_ptr point to data with layout described by component_info unsafe { - core::ptr::copy_nonoverlapping( - component_data_ptr, - target_component_data_ptr.as_ptr(), - component_layout.size(), - ); - self.target_components_ptrs - .push(PtrMut::new(target_component_data_ptr)); - alloc::alloc::dealloc(component_data_ptr, component_layout); + self.write_target_component_impl(|target_ptr| { + core::ptr::copy_nonoverlapping( + component_data_ptr, + target_ptr.as_ptr(), + layout.size(), + ); + alloc::alloc::dealloc(component_data_ptr, layout); + true + }); } - - self.target_component_written = true; } - /// Return a reference to this context's `EntityCloner` instance. + /// Return a reference to this context's `EntityCloner` instance (if available for current context). /// /// This can be used to issue clone commands using the same cloning configuration: /// ``` - /// # use bevy_ecs::world::{DeferredWorld, World}; + /// # use bevy_ecs::world::World; /// # use bevy_ecs::entity::ComponentCloneCtx; - /// fn clone_handler(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { - /// let another_target = world.commands().spawn_empty().id(); - /// let mut entity_cloner = ctx - /// .entity_cloner() - /// .with_source_and_target(ctx.source(), another_target); - /// world.commands().queue(move |world: &mut World| { + /// fn clone_handler(_world: &World, ctx: &mut ComponentCloneCtx) { + /// let Some(entity_cloner) = ctx.entity_cloner() else { + /// return + /// }; + /// let Some(mut commands) = ctx.commands() else { + /// return + /// }; + /// let another_target = commands.spawn_empty().id(); + /// let mut entity_cloner = entity_cloner.with_source_and_target(entity_cloner.source(), another_target); + /// commands.queue(move |world: &mut World| { /// entity_cloner.clone_entity(world); /// }); /// } /// ``` - pub fn entity_cloner(&self) -> &EntityCloner { + pub fn entity_cloner(&self) -> Option<&EntityCloner> { self.entity_cloner } - /// Returns instance of [`Components`]. - pub fn components(&self) -> &Components { - self.components - } - /// Returns [`AppTypeRegistry`](`crate::reflect::AppTypeRegistry`) if it exists in the world. /// /// NOTE: Prefer this method instead of manually reading the resource from the world. @@ -280,46 +311,56 @@ pub struct EntityCloner { 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) { - // SAFETY: - // - `source_entity` is read-only. - // - `type_registry` is read-only. - // - `components` is read-only. - // - `deferred_world` disallows structural ecs changes, which means all read-only resources above a not affected. - let (type_registry, source_entity, components, mut deferred_world) = unsafe { - let world = world.as_unsafe_world_cell(); - let source_entity = world - .get_entity(self.source) - .expect("Source entity must exist"); - - #[cfg(feature = "bevy_reflect")] - let app_registry = world.get_resource::(); - #[cfg(not(feature = "bevy_reflect"))] - let app_registry = Option::<()>::None; - - ( - app_registry, - source_entity, - world.components(), - world.into_deferred(), - ) - }; + let source_entity = world + .get_entity(self.source) + .expect("Source entity must exist"); + let components = world.components(); let archetype = source_entity.archetype(); + let mut command_queue = CommandQueue::default(); + #[cfg(feature = "bevy_reflect")] + let type_registry = world.get_resource::(); let component_data = Bump::new(); let mut component_ids: Vec = Vec::with_capacity(archetype.component_count()); let mut component_data_ptrs: Vec = Vec::with_capacity(archetype.component_count()); + fn copy_clone_handler(_world: &World, ctx: &mut ComponentCloneCtx) { + let count = ctx.component_info().layout().size(); + // SAFETY: + // - This handler is only used for copyable components + // - source_ptr and target_ptr don't overlap + // - function passed to write_target_component_ptr writes valid data of proper type to target_ptr + unsafe { + ctx.write_target_component_ptr(|source_ptr, target_ptr| { + core::ptr::copy_nonoverlapping(source_ptr.as_ptr(), target_ptr.as_ptr(), count); + true + }); + } + } + for component in archetype.components() { if !self.is_cloning_allowed(&component) { continue; } - - let global_handlers = components.get_component_clone_handlers(); - let handler = match self.clone_handlers_overrides.get(&component) { - Some(handler) => handler - .get_handler() - .unwrap_or_else(|| global_handlers.get_default_handler()), - None => global_handlers.get_handler(component), + // SAFETY: component is a valid component from the same world as components + let component_info = unsafe { components.get_info_unchecked(component) }; + + let handler = match self + .clone_handlers_overrides + .get(&component) + .unwrap_or_else(|| component_info.clone_handler()) + .get_entity_handler() + { + ComponentCloneHandlerKind::Default => components.get_default_clone_handler(), + ComponentCloneHandlerKind::Ignore => continue, + ComponentCloneHandlerKind::Copy => { + if component_info.is_copy() { + copy_clone_handler + } else { + continue; + } + } + ComponentCloneHandlerKind::Custom(handler) => handler, }; // SAFETY: @@ -328,38 +369,34 @@ impl EntityCloner { let source_component_ptr = unsafe { source_entity.get_by_id(component).debug_checked_unwrap() }; - // SAFETY: - // - `components` and `component` are from the same world - // - `source_component_ptr` is valid and points to the same type as represented by `component` - let mut ctx = unsafe { - ComponentCloneCtx::new( - component, - source_component_ptr, - &mut component_data_ptrs, - &component_data, - components, - self, - type_registry, - ) + let mut ctx = ComponentCloneCtx { + command_queue: Some(command_queue.get_raw()), + component_id: component, + component_info, + components, + entities: world.entities(), + entity_cloner: Some(self), + source_component_ptr, + target_component_written: false, + target_write_buffer: ComponentWriteBuffer::BumpWriteBuffer(BumpWriteBuffer { + target_components_buffer: &component_data, + target_components_ptrs: &mut component_data_ptrs, + }), + #[cfg(feature = "bevy_reflect")] + type_registry, }; - (handler)(&mut deferred_world, &mut ctx); + (handler)(world, &mut ctx); - if ctx.target_component_written { + if ctx.target_component_written() { component_ids.push(component); } } - world.flush(); - - if !world.entities.contains(self.target) { - panic!("Target entity does not exist"); - } - debug_assert_eq!(component_data_ptrs.len(), component_ids.len()); // SAFETY: - // - All `component_ids` are from the same world as `target` entity + // - All `component_ids` are from `source_world`, and caller ensures that `target_world` has same `Components` as `source_world` // - All `component_data_ptrs` are valid types represented by `component_ids` unsafe { world.entity_mut(self.target).insert_by_ids( @@ -368,6 +405,8 @@ impl EntityCloner { ); } + command_queue.apply(world); + if self.move_components { world.entity_mut(self.source).remove_by_ids(&component_ids); } @@ -388,6 +427,16 @@ impl EntityCloner { ..*self } } + + /// Returns the current source entity. + pub fn source(&self) -> Entity { + self.source + } + + /// Returns the current target entity. + pub fn target(&self) -> Entity { + self.target + } } /// Builder struct to clone an entity. Allows configuring which components to clone, as well as how to clone them. @@ -420,7 +469,7 @@ impl EntityCloner { /// /// 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). +/// the component will be cloned using the [default cloning strategy](World::get_default_component_clone_handler). /// To use `Clone`-based handler ([`ComponentCloneHandler::clone_handler`]) in this case it should be set manually using one /// of the methods mentioned in the [Handlers](#handlers) section /// @@ -444,9 +493,9 @@ impl EntityCloner { /// `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) +/// 2. global overrides using [`set_component_handler`](World::set_component_clone_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) +/// 4. default handler override using [`set_default_handler`](World::set_default_component_clone_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> { @@ -600,7 +649,7 @@ impl<'w> EntityCloneBuilder<'w> { } /// 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) + /// This handler will be used to clone the component instead of the global one [`World::get_default_component_clone_handler`]. /// /// See [Handlers section of `EntityCloneBuilder`](EntityCloneBuilder#handlers) to understand how this affects handler priority. pub fn override_component_clone_handler( @@ -669,7 +718,7 @@ mod tests { self as bevy_ecs, component::{Component, ComponentCloneHandler, ComponentDescriptor, StorageType}, entity::EntityCloneBuilder, - world::{DeferredWorld, World}, + world::World, }; use alloc::vec::Vec; use bevy_ecs_macros::require; @@ -698,9 +747,7 @@ mod tests { world.register_component::(); let id = world.component_id::().unwrap(); - world - .get_component_clone_handlers_mut() - .set_component_handler(id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(id, ComponentCloneHandler::reflect_handler()); let component = A { field: 5 }; @@ -748,10 +795,9 @@ mod tests { let a_id = world.register_component::(); let b_id = world.register_component::(); let c_id = world.register_component::(); - let handlers = world.get_component_clone_handlers_mut(); - handlers.set_component_handler(a_id, ComponentCloneHandler::reflect_handler()); - handlers.set_component_handler(b_id, ComponentCloneHandler::reflect_handler()); - handlers.set_component_handler(c_id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(a_id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(b_id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(c_id, ComponentCloneHandler::reflect_handler()); let component_a = A { field: 5, @@ -784,7 +830,7 @@ mod tests { #[derive(Component, Reflect)] struct B; - fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + fn test_handler(_world: &World, ctx: &mut ComponentCloneCtx) { assert!(ctx.read_source_component_reflect().is_none()); } @@ -801,9 +847,10 @@ mod tests { } let a_id = world.register_component::(); - let handlers = world.get_component_clone_handlers_mut(); - handlers - .set_component_handler(a_id, ComponentCloneHandler::custom_handler(test_handler)); + world.set_component_clone_handler( + a_id, + ComponentCloneHandler::custom_handler(test_handler), + ); let e = world.spawn(A).id(); let e_clone = world.spawn_empty().id(); @@ -857,9 +904,8 @@ mod tests { let mut world = World::default(); let a_id = world.register_component::(); let b_id = world.register_component::(); - let handlers = world.get_component_clone_handlers_mut(); - handlers.set_component_handler(a_id, ComponentCloneHandler::reflect_handler()); - handlers.set_component_handler(b_id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(a_id, ComponentCloneHandler::reflect_handler()); + world.set_component_clone_handler(b_id, ComponentCloneHandler::reflect_handler()); // No AppTypeRegistry let e = world.spawn((A, B)).id(); @@ -900,6 +946,35 @@ mod tests { assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } + #[test] + fn clone_entity_using_copy() { + #[derive(Component, Copy, PartialEq, Eq, Debug)] + struct A { + field: usize, + } + + impl Clone for A { + #[expect( + clippy::non_canonical_clone_impl, + reason = "This is required to check that Copy implementation is selected instead of Clone" + )] + fn clone(&self) -> Self { + Self { field: 1 } + } + } + + let mut world = World::default(); + + let component = A { field: 5 }; + + let e = world.spawn(component).id(); + let e_clone = world.spawn_empty().id(); + + EntityCloneBuilder::new(&mut world).clone_entity(e, e_clone); + + assert_eq!(world.get::(e_clone), Some(&component)); + } + #[test] fn clone_entity_with_allow_filter() { #[derive(Component, Clone, PartialEq, Eq)] @@ -1052,7 +1127,7 @@ mod tests { #[test] fn clone_entity_with_dynamic_components() { const COMPONENT_SIZE: usize = 10; - fn test_handler(_world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { + fn test_handler(_world: &World, ctx: &mut ComponentCloneCtx) { // SAFETY: this handler is only going to be used with a component represented by [u8; COMPONENT_SIZE] unsafe { ctx.write_target_component_ptr(move |source_ptr, target_ptr| { @@ -1079,16 +1154,12 @@ mod tests { layout, None, true, + true, + ComponentCloneHandler::custom_handler(test_handler), ) }; let component_id = world.register_component_with_descriptor(descriptor); - let handlers = world.get_component_clone_handlers_mut(); - handlers.set_component_handler( - component_id, - ComponentCloneHandler::custom_handler(test_handler), - ); - let mut entity = world.spawn_empty(); let data = [5u8; COMPONENT_SIZE]; diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 44e500a15bb36..417cc3fc65c79 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -579,6 +579,17 @@ pub struct Entities { len: u32, } +impl Clone for Entities { + fn clone(&self) -> Self { + Self { + meta: self.meta.clone(), + pending: self.pending.clone(), + free_cursor: AtomicIdCursor::new(self.free_cursor.load(Ordering::Relaxed)), + len: self.len, + } + } +} + impl Entities { pub(crate) const fn new() -> Self { Entities { diff --git a/crates/bevy_ecs/src/event/base.rs b/crates/bevy_ecs/src/event/base.rs index a7974c89e8dfd..9b5123317b5ad 100644 --- a/crates/bevy_ecs/src/event/base.rs +++ b/crates/bevy_ecs/src/event/base.rs @@ -119,7 +119,7 @@ impl Hash for EventId { } } -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] pub(crate) struct EventInstance { pub event_id: EventId, diff --git a/crates/bevy_ecs/src/event/collections.rs b/crates/bevy_ecs/src/event/collections.rs index d35c4743ee648..918e93da5ad4a 100644 --- a/crates/bevy_ecs/src/event/collections.rs +++ b/crates/bevy_ecs/src/event/collections.rs @@ -91,7 +91,7 @@ use { /// [`EventReader`]: super::EventReader /// [`EventWriter`]: super::EventWriter /// [`event_update_system`]: super::event_update_system -#[derive(Debug, Resource)] +#[derive(Clone, Debug, Resource)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource, Default))] pub struct Events { /// Holds the oldest still active events. @@ -331,7 +331,7 @@ impl Extend for Events { } } -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] pub(crate) struct EventSequence { pub(crate) events: Vec>, diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index e86f6814a8ff6..228dfbd8eaaeb 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -2,12 +2,12 @@ use crate::{ component::{Component, ComponentCloneHandler, ComponentHooks, Mutable, StorageType}, entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, observer::ObserverState, - world::{DeferredWorld, World}, + world::World, }; use alloc::vec::Vec; /// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to. -#[derive(Default)] +#[derive(Default, Clone)] pub(crate) struct ObservedBy(pub(crate) Vec); impl Component for ObservedBy { @@ -45,6 +45,7 @@ impl Component for ObservedBy { fn get_component_clone_handler() -> ComponentCloneHandler { ComponentCloneHandler::ignore() + .with_world_clone_handler(ComponentCloneHandler::clone_handler::()) } } @@ -66,11 +67,17 @@ impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { } } -fn component_clone_observed_by(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { - let target = ctx.target(); - let source = ctx.source(); +fn component_clone_observed_by(_world: &World, ctx: &mut ComponentCloneCtx) { + let Some(entity_cloner) = ctx.entity_cloner() else { + return; + }; + let Some(mut commands) = ctx.commands() else { + return; + }; + let target = entity_cloner.target(); + let source = entity_cloner.source(); - world.commands().queue(move |world: &mut World| { + commands.queue(move |world: &mut World| { let observed_by = world .get::(source) .map(|observed_by| observed_by.0.clone()) diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 8194c1e04ea65..570fbf3293969 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -325,7 +325,7 @@ impl ObserverTrigger { type ObserverMap = EntityHashMap; /// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger targeted at a specific component. -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct CachedComponentObservers { // Observers listening to triggers targeting this component map: ObserverMap, @@ -334,7 +334,7 @@ pub struct CachedComponentObservers { } /// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger. -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct CachedObservers { // Observers listening for any time this trigger is fired map: ObserverMap, @@ -345,7 +345,7 @@ pub struct CachedObservers { } /// Metadata for observers. Stores a cache mapping trigger ids to the registered observers. -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct Observers { // Cached ECS observers to save a lookup most common triggers. on_add: CachedObservers, diff --git a/crates/bevy_ecs/src/removal_detection.rs b/crates/bevy_ecs/src/removal_detection.rs index c81a5cfa1eb2c..0e837e7ee43c5 100644 --- a/crates/bevy_ecs/src/removal_detection.rs +++ b/crates/bevy_ecs/src/removal_detection.rs @@ -64,7 +64,7 @@ impl DerefMut for RemovedComponentReader { } /// Stores the [`RemovedComponents`] event buffers for all types of component in a given [`World`]. -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug)] pub struct RemovedComponentEvents { event_sets: SparseSet>, } diff --git a/crates/bevy_ecs/src/storage/blob_array.rs b/crates/bevy_ecs/src/storage/blob_array.rs index 86315386a8d1f..ed36239b8641a 100644 --- a/crates/bevy_ecs/src/storage/blob_array.rs +++ b/crates/bevy_ecs/src/storage/blob_array.rs @@ -475,6 +475,33 @@ impl BlobArray { drop(value); } } + + /// Copy data from other [`BlobArray`]. + /// + /// # Safety + /// Caller must ensure that: + /// - `other` stores data of the same layout as `self`. + /// - `other`'s length is `len`. + /// - `self` has enough `capacity` to store `len` elements. + pub unsafe fn copy_from_unchecked(&mut self, other: &Self, len: usize) { + if other.is_zst() { + return; + } + // SAFETY: + // - other is a valid BlobArray, so it must have a valid array layout + let num_bytes_to_copy = array_layout_unchecked(&other.layout(), len).size(); + #[cfg(debug_assertions)] + debug_assert!( + num_bytes_to_copy + <= array_layout(&other.layout(), self.capacity) + .expect("Calculating capacity to copy to BlobVec failed") + .size() + ); + debug_assert!(other.layout() == self.layout()); + let source_ptr = other.get_ptr().as_ptr(); + let target_ptr = self.get_ptr_mut().as_ptr(); + core::ptr::copy_nonoverlapping(source_ptr, target_ptr, num_bytes_to_copy); + } } #[cfg(test)] diff --git a/crates/bevy_ecs/src/storage/blob_vec.rs b/crates/bevy_ecs/src/storage/blob_vec.rs index 51a3d49e3c08d..0b23aac9d4255 100644 --- a/crates/bevy_ecs/src/storage/blob_vec.rs +++ b/crates/bevy_ecs/src/storage/blob_vec.rs @@ -184,6 +184,28 @@ impl BlobVec { core::ptr::copy_nonoverlapping::(value.as_ptr(), ptr.as_ptr(), self.item_layout.size()); } + /// Try to initialize a value at next index in this [`BlobVec`] using passed initialization function. + /// If `init_fn` returns `true`, the value will be considered initialized, `len` increased and this function will return `true`. + /// Otherwise, `len` will be left as it was before and this function will return `false` + /// + /// # Safety + /// - caller must ensure that if `init_fn` returns `true`, a valid value of the layout stored in this [`BlobVec`] + /// was written to [`NonNull`] passed to `init_fn`. + pub(crate) unsafe fn try_initialize_next( + &mut self, + init_fn: impl Fn(NonNull) -> bool, + ) -> bool { + self.reserve(1); + let index = self.len; + self.len += 1; + let ptr = self.get_unchecked_mut(index); + if init_fn(ptr.into()) { + return true; + } + self.len = index; + false + } + /// Replaces the value at `index` with `value`. This function does not do any bounds checking. /// /// # Safety @@ -391,6 +413,33 @@ impl BlobVec { } } } + + /// Copy data from other [`BlobVec`]. + /// + /// # Safety + /// Caller must ensure that: + /// - `other` stores data of the same layout as `self`. + /// - `self` has enough `capacity` to store `other.len` elements. + pub unsafe fn copy_from_unchecked(&mut self, other: &Self) { + // Skip ZSTs + if other.item_layout.size() == 0 { + return; + } + let len = other.len(); + // SAFETY: This must be valid because other is a valid BlobVec + let num_bytes_to_copy = array_layout_unchecked(&other.layout(), other.len()).size(); + debug_assert!( + num_bytes_to_copy + <= array_layout(&other.layout(), self.capacity) + .expect("Calculating capacity to copy to BlobVec failed") + .size() + ); + debug_assert!(other.layout() == self.layout()); + let source_ptr = other.get_ptr().as_ptr(); + let target_ptr = self.get_ptr_mut().as_ptr(); + core::ptr::copy_nonoverlapping(source_ptr, target_ptr, num_bytes_to_copy); + self.len = len; + } } impl Drop for BlobVec { diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index eaeff97a8cccb..fb53b3d194b11 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -31,7 +31,9 @@ pub use resource::*; pub use sparse_set::*; pub use table::*; -/// The raw data stores of a [`World`](crate::world::World) +use crate::world::{error::WorldCloneError, World}; + +/// The raw data stores of a [`World`] #[derive(Default)] pub struct Storages { /// Backing storage for [`SparseSet`] components. @@ -43,3 +45,39 @@ pub struct Storages { /// Backing storage for `!Send` resources. pub non_send_resources: Resources, } + +impl Storages { + /// Try to clone [`Storages`]. This is only possible if all components and resources can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// - Caller must ensure that [`Storages`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + Ok(Storages { + sparse_sets: self.sparse_sets.try_clone( + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + tables: self.tables.try_clone( + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + resources: self.resources.try_clone( + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + non_send_resources: self.non_send_resources.try_clone( + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + }) + } +} diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 501c6e80a535b..b0465ad012494 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,8 +1,13 @@ use crate::{ archetype::ArchetypeComponentId, change_detection::{MaybeLocation, MaybeUnsafeCellLocation, MutUntyped, TicksMut}, - component::{ComponentId, ComponentTicks, Components, Tick, TickCells}, + component::{ + ComponentCloneHandlerKind, ComponentId, ComponentInfo, ComponentTicks, Components, Tick, + TickCells, + }, + entity::ComponentCloneCtx, storage::{blob_vec::BlobVec, SparseSet}, + world::{error::WorldCloneError, World}, }; use alloc::string::String; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; @@ -22,10 +27,6 @@ pub struct ResourceData { data: ManuallyDrop, added_ticks: UnsafeCell, changed_ticks: UnsafeCell, - #[cfg_attr( - not(feature = "std"), - expect(dead_code, reason = "currently only used with the std feature") - )] type_name: String, id: ArchetypeComponentId, #[cfg(feature = "std")] @@ -69,24 +70,37 @@ impl ResourceData { /// If `SEND` is false, this will panic if called from a different thread than the one it was inserted from. #[inline] fn validate_access(&self) { - if SEND { + if self.try_validate_access() { return; } + // Panic in tests, as testing for aborting is nearly impossible #[cfg(feature = "std")] - if self.origin_thread_id != Some(std::thread::current().id()) { - // Panic in tests, as testing for aborting is nearly impossible - panic!( + panic!( "Attempted to access or drop non-send resource {} from thread {:?} on a thread {:?}. This is not allowed. Aborting.", self.type_name, self.origin_thread_id, std::thread::current().id() ); + } + + #[inline] + /// Returns `true` if access to `!Send` resources is done on the thread they were created from or resources are `Send`. + fn try_validate_access(&self) -> bool { + #[cfg(feature = "std")] + { + if SEND { + true + } else { + self.origin_thread_id == Some(std::thread::current().id()) + } } // TODO: Handle no_std non-send. // Currently, no_std is single-threaded only, so this is safe to ignore. // To support no_std multithreading, an alternative will be required. + #[cfg(not(feature = "std"))] + true } /// Returns true if the resource is populated. @@ -304,6 +318,77 @@ impl ResourceData { self.added_ticks.get_mut().check_tick(change_tick); self.changed_ticks.get_mut().check_tick(change_tick); } + + /// Try to clone [`ResourceData`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// Caller must ensure that: + /// - [`ComponentInfo`] is the same as the one used to create this [`ResourceData`]. + /// - [`ResourceData`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + component_info: &ComponentInfo, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + if !self.try_validate_access() { + return Err(WorldCloneError::NonSendResourceCloned(component_info.id())); + } + let mut data = BlobVec::new(component_info.layout(), component_info.drop(), 1); + + let handler = match component_info + .clone_handler() + .get_world_handler() + .unwrap_or_else(|| component_info.clone_handler().get_entity_handler()) + { + ComponentCloneHandlerKind::Default => { + Some(world.components.get_default_clone_handler()) + } + ComponentCloneHandlerKind::Ignore => { + return Err(WorldCloneError::ComponentCantBeCloned(component_info.id())) + } + ComponentCloneHandlerKind::Copy => { + if !component_info.is_copy() { + return Err(WorldCloneError::FailedToCloneResource(component_info.id())); + } + data.copy_from_unchecked(&self.data); + None + } + ComponentCloneHandlerKind::Custom(handler) => Some(handler), + }; + + if let Some(handler) = handler { + let is_initialized = data.try_initialize_next(|target_component_ptr| { + let source_component_ptr = self.data.get_unchecked(Self::ROW); + let mut ctx = ComponentCloneCtx::new_for_component( + component_info, + source_component_ptr, + target_component_ptr, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + ); + handler(world, &mut ctx); + ctx.target_component_written() + }); + if !is_initialized { + return Err(WorldCloneError::FailedToCloneComponent(component_info.id())); + } + } + + Ok(Self { + data: ManuallyDrop::new(data), + added_ticks: UnsafeCell::new(self.added_ticks.read()), + changed_ticks: UnsafeCell::new(self.changed_ticks.read()), + type_name: self.type_name.clone(), + id: self.id, + #[cfg(feature = "std")] + origin_thread_id: self.origin_thread_id, + #[cfg(feature = "track_location")] + changed_by: UnsafeCell::new(self.changed_by.read()), + }) + } } /// The backing store for all [`Resource`]s stored in the [`World`]. @@ -403,4 +488,31 @@ impl Resources { info.check_change_ticks(change_tick); } } + + /// Try to clone [`Resources`]. This is only possible if all resources can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// - Caller must ensure that [`Resources`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut resources = SparseSet::with_capacity(self.resources.len()); + let components = world.components(); + for (component_id, res) in self.resources.iter() { + resources.insert( + *component_id, + res.try_clone( + // SAFETY: component_id is valid because this Table is valid and from the same world as Components. + components.get_info_unchecked(*component_id), + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + ); + } + Ok(Self { resources }) + } } diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 518333fa272d8..efb321591b971 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -3,6 +3,7 @@ use crate::{ component::{ComponentId, ComponentInfo, ComponentTicks, Tick, TickCells}, entity::Entity, storage::{Column, TableRow}, + world::{error::WorldCloneError, World}, }; use alloc::{boxed::Box, vec::Vec}; use bevy_ptr::{OwningPtr, Ptr}; @@ -13,7 +14,7 @@ use nonmax::NonMaxUsize; type EntityIndex = u32; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct SparseArray { values: Vec>, marker: PhantomData, @@ -21,7 +22,7 @@ pub(crate) struct SparseArray { /// A space-optimized version of [`SparseArray`] that cannot be changed /// after construction. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct ImmutableSparseArray { values: Box<[Option]>, marker: PhantomData, @@ -140,6 +141,31 @@ impl ComponentSparseSet { } } + /// Try to clone [`ComponentSparseSet`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// Caller must ensure that: + /// - [`ComponentInfo`] is the same as the one used to create this [`ComponentSparseSet`]. + /// - [`ComponentSparseSet`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + component_info: &ComponentInfo, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + Ok(Self { + dense: self.dense.try_clone( + component_info, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + entities: self.entities.clone(), + sparse: self.sparse.clone(), + }) + } + /// Removes all of the values stored within. pub(crate) fn clear(&mut self) { self.dense.clear(); @@ -368,7 +394,7 @@ impl ComponentSparseSet { /// A data structure that blends dense and sparse storage /// /// `I` is the type of the indices, while `V` is the type of data stored in the dense storage. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct SparseSet { dense: Vec, indices: Vec, @@ -377,7 +403,7 @@ pub struct SparseSet { /// A space-optimized version of [`SparseSet`] that cannot be changed /// after construction. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct ImmutableSparseSet { dense: Box<[V]>, indices: Box<[I]>, @@ -610,6 +636,32 @@ impl SparseSets { self.sets.is_empty() } + /// Try to clone [`SparseSets`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// - Caller must ensure that [`SparseSets`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut sets = SparseSet::with_capacity(self.sets.len()); + let components = world.components(); + for (component_id, set) in self.sets.iter() { + // SAFETY: component_id is valid because this SparseSets is valid and from the same world as Components. + let component_info = components.get_info_unchecked(*component_id); + let set = set.try_clone( + component_info, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?; + sets.insert(*component_id, set); + } + Ok(SparseSets { sets }) + } + /// An Iterator visiting all ([`ComponentId`], [`ComponentSparseSet`]) pairs. /// NOTE: Order is not guaranteed. pub fn iter(&self) -> impl Iterator { diff --git a/crates/bevy_ecs/src/storage/table/column.rs b/crates/bevy_ecs/src/storage/table/column.rs index 4054b5c15fd54..72eff67b806d2 100644 --- a/crates/bevy_ecs/src/storage/table/column.rs +++ b/crates/bevy_ecs/src/storage/table/column.rs @@ -1,7 +1,9 @@ use super::*; use crate::{ - component::TickCells, + component::{ComponentCloneHandlerKind, TickCells}, + entity::ComponentCloneCtx, storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr}, + world::{error::WorldCloneError, World}, }; use alloc::vec::Vec; use bevy_ptr::PtrMut; @@ -326,6 +328,84 @@ impl ThinColumn { ) -> &[UnsafeCell<&'static Location<'static>>] { self.changed_by.as_slice(len) } + + /// Try to clone [`ThinColumn`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// Caller must ensure that: + /// - [`ComponentInfo`] is the same as the one used to create this [`ThinColumn`]. + /// - [`ThinColumn`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + component_info: &ComponentInfo, + len: usize, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut column = Self::with_capacity(component_info, len); + column.added_ticks = ThinArrayPtr::from( + self.added_ticks + .as_slice(len) + .iter() + .map(|cell| UnsafeCell::new(cell.read())) + .collect::>(), + ); + column.changed_ticks = ThinArrayPtr::from( + self.changed_ticks + .as_slice(len) + .iter() + .map(|cell| UnsafeCell::new(cell.read())) + .collect::>(), + ); + #[cfg(feature = "track_location")] + { + column.changed_by = ThinArrayPtr::from( + self.changed_by + .as_slice(len) + .iter() + .map(|tick| UnsafeCell::new(tick.read())) + .collect::>(), + ); + } + + let handler = match component_info + .clone_handler() + .get_world_handler() + .unwrap_or_else(|| component_info.clone_handler().get_entity_handler()) + { + ComponentCloneHandlerKind::Default => world.components.get_default_clone_handler(), + ComponentCloneHandlerKind::Ignore => { + return Err(WorldCloneError::ComponentCantBeCloned(component_info.id())) + } + ComponentCloneHandlerKind::Copy => { + if !component_info.is_copy() { + return Err(WorldCloneError::FailedToCloneComponent(component_info.id())); + } + column.data.copy_from_unchecked(&self.data, len); + return Ok(column); + } + ComponentCloneHandlerKind::Custom(handler) => handler, + }; + + for i in 0..len { + let source_component_ptr = self.data.get_unchecked(i); + let target_component_ptr = column.data.get_unchecked_mut(i).into(); + let mut ctx = ComponentCloneCtx::new_for_component( + component_info, + source_component_ptr, + target_component_ptr, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + ); + handler(world, &mut ctx); + if !ctx.target_component_written() { + return Err(WorldCloneError::FailedToCloneComponent(component_info.id())); + } + } + Ok(column) + } } /// A type-erased contiguous container for data of a homogeneous type. @@ -686,4 +766,81 @@ impl Column { debug_assert!(row.as_usize() < self.changed_by.len()); self.changed_by.get_unchecked(row.as_usize()) } + + /// Try to clone [`Column`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// Caller must ensure that: + /// - [`ComponentInfo`] is the same as the one used to create this [`Column`]. + /// - [`Column`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + component_info: &ComponentInfo, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut column = Self::with_capacity(component_info, self.len()); + column.added_ticks = self + .added_ticks + .iter() + .map(|cell| UnsafeCell::new(cell.read())) + .collect(); + column.changed_ticks = self + .changed_ticks + .iter() + .map(|cell| UnsafeCell::new(cell.read())) + .collect(); + #[cfg(feature = "track_location")] + { + column.changed_by = self + .changed_by + .iter() + .map(|tick| UnsafeCell::new(tick.read())) + .collect(); + } + + let handler = match component_info + .clone_handler() + .get_world_handler() + .unwrap_or_else(|| component_info.clone_handler().get_entity_handler()) + { + ComponentCloneHandlerKind::Default => world.components.get_default_clone_handler(), + ComponentCloneHandlerKind::Ignore => { + return Err(WorldCloneError::ComponentCantBeCloned(component_info.id())) + } + ComponentCloneHandlerKind::Copy => { + if !component_info.is_copy() { + return Err(WorldCloneError::FailedToCloneComponent(component_info.id())); + } + // SAFETY: + // - column.data layout is from the same ComponentInfo as the one used to create self + // - column.data has capacity of at least self.len + column.data.copy_from_unchecked(&self.data); + return Ok(column); + } + ComponentCloneHandlerKind::Custom(handler) => handler, + }; + + for i in 0..self.len() { + let row = TableRow::from_usize(i); + let source_component_ptr = self.get_data_unchecked(row); + let is_initialized = column.data.try_initialize_next(|target_component_ptr| { + let mut ctx = ComponentCloneCtx::new_for_component( + component_info, + source_component_ptr, + target_component_ptr, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + ); + handler(world, &mut ctx); + ctx.target_component_written() + }); + if !is_initialized { + return Err(WorldCloneError::FailedToCloneComponent(component_info.id())); + } + } + Ok(column) + } } diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index ce33890751ccd..b4622399f2612 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -4,6 +4,7 @@ use crate::{ entity::Entity, query::DebugCheckedUnwrap, storage::{blob_vec::BlobVec, ImmutableSparseSet, SparseSet}, + world::{error::WorldCloneError, World}, }; use alloc::{boxed::Box, vec, vec::Vec}; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; @@ -671,6 +672,38 @@ impl Table { self.get_column(component_id) .map(|col| col.data.get_unchecked(row.as_usize())) } + + /// Try to clone [`Table`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// - Caller must ensure that [`Table`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut columns = SparseSet::with_capacity(self.columns.len()); + let components = world.components(); + let column_len = self.entities.len(); + for (component_id, column) in self.columns.iter() { + columns.insert( + *component_id, + column.try_clone( + // SAFETY: component_id is valid because this Table is valid and from the same world as Components. + components.get_info_unchecked(*component_id), + column_len, + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?, + ); + } + Ok(Self { + entities: self.entities.clone(), + columns: columns.into_immutable(), + }) + } } /// A collection of [`Table`] storages, indexed by [`TableId`] @@ -781,6 +814,30 @@ impl Tables { table.check_change_ticks(change_tick); } } + + /// Try to clone [`Tables`]. This is only possible if all components can be cloned, + /// otherwise [`WorldCloneError`] will be returned. + /// + /// # Safety + /// - Caller must ensure that [`Tables`] and `AppTypeRegistry` are from `world`. + pub(crate) unsafe fn try_clone( + &self, + world: &World, + #[cfg(feature = "bevy_reflect")] type_registry: Option<&crate::reflect::AppTypeRegistry>, + ) -> Result { + let mut tables = Vec::with_capacity(self.tables.len()); + for table in &self.tables { + tables.push(table.try_clone( + world, + #[cfg(feature = "bevy_reflect")] + type_registry, + )?); + } + Ok(Self { + table_ids: self.table_ids.clone(), + tables, + }) + } } impl Index for Tables { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 94c78c85e936e..3ed1c96a1133a 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -3,7 +3,7 @@ use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, change_detection::{Ticks, TicksMut}, - component::{ComponentId, ComponentTicks, Components, Tick}, + component::{ComponentCloneHandler, ComponentId, ComponentTicks, Components, Tick}, entity::Entities, query::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError, @@ -839,7 +839,14 @@ all_tuples_enumerated!(impl_param_set, 1, 8, P, m, p); label = "invalid `Resource`", note = "consider annotating `{Self}` with `#[derive(Resource)]`" )] -pub trait Resource: Send + Sync + 'static {} +pub trait Resource: Send + Sync + 'static { + /// Called when registering this resource, allowing to override clone function (or disable cloning altogether) for this resource. + /// + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. + fn get_component_clone_handler() -> ComponentCloneHandler { + ComponentCloneHandler::default_handler() + } +} // SAFETY: Res only reads a single World resource unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} diff --git a/crates/bevy_ecs/src/world/error.rs b/crates/bevy_ecs/src/world/error.rs index 1c6b5043bcea8..93388056cb388 100644 --- a/crates/bevy_ecs/src/world/error.rs +++ b/crates/bevy_ecs/src/world/error.rs @@ -48,3 +48,26 @@ impl PartialEq for EntityFetchError { } impl Eq for EntityFetchError {} + +/// An error that occurs when cloning world failed. +#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)] +pub enum WorldCloneError { + /// World id allocation failed. + #[error("More `bevy` `World`s have been created than is supported.")] + WorldIdExhausted, + /// World has unapplied commands queued. + #[error("World cannot be cloned while there are unapplied commands queued.")] + UnappliedCommands, + /// Component clone handler failed to clone component. + #[error("Component clone handler for component with ID {0:?} failed to clone the component.")] + FailedToCloneComponent(ComponentId), + /// Component clone handler failed to clone resource. + #[error("Component clone handler for resource with ID {0:?} failed to clone the resource.")] + FailedToCloneResource(ComponentId), + /// Resource cloned from different thread than the one used to create it. + #[error("Tried to clone non-send resource with ID {0:?} from a different thread than the one it was created from.")] + NonSendResourceCloned(ComponentId), + /// Component clone handler is set to `Ignore`. + #[error("Component clone handler for component or resource with ID {0:?} is set to Ignore and can't be cloned.")] + ComponentCantBeCloned(ComponentId), +} diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 1037969dedca9..f8961c3d58a1e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -25,6 +25,7 @@ pub use entity_ref::{ DynamicComponentFetch, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Entry, FilteredEntityMut, FilteredEntityRef, OccupiedEntry, TryFromFilteredError, VacantEntry, }; +use error::WorldCloneError; pub use filtered_resource::*; pub use identifier::WorldId; pub use spawn_batch::*; @@ -34,8 +35,8 @@ use crate::{ bundle::{Bundle, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode}, change_detection::{MutUntyped, TicksMut}, component::{ - Component, ComponentCloneHandlers, ComponentDescriptor, ComponentHooks, ComponentId, - ComponentInfo, ComponentTicks, Components, Mutable, RequiredComponents, + Component, ComponentCloneFn, ComponentCloneHandler, ComponentDescriptor, ComponentHooks, + ComponentId, ComponentInfo, ComponentTicks, Components, Mutable, RequiredComponents, RequiredComponentsError, Tick, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, @@ -170,6 +171,46 @@ impl World { self.id } + /// Try to perform full world cloning. + /// + /// This function will return a clone if all components and resources in this world + /// can be cloned. If that is not possible, [`WorldCloneError`] will be returned instead. + pub fn try_clone(&self) -> Result { + // SAFETY: `self.command_queue` is only de-allocated in `World`'s `Drop` + if !unsafe { self.command_queue.is_empty() } { + return Err(WorldCloneError::UnappliedCommands); + } + + let id = WorldId::new().ok_or(WorldCloneError::WorldIdExhausted)?; + // SAFETY: + // - storages and type_registry is from self + let storages = unsafe { + self.storages.try_clone( + self, + #[cfg(feature = "bevy_reflect")] + self.get_resource::(), + )? + }; + + let world = World { + id, + entities: self.entities.clone(), + components: self.components.clone(), + archetypes: self.archetypes.clone(), + storages, + bundles: self.bundles.clone(), + observers: self.observers.clone(), + removed_components: self.removed_components.clone(), + change_tick: AtomicU32::new(self.change_tick.load(Ordering::Relaxed)), + last_change_tick: self.last_change_tick, + last_check_tick: self.last_check_tick, + last_trigger_id: self.last_trigger_id, + command_queue: RawCommandQueue::new(), + }; + + Ok(world) + } + /// Creates a new [`UnsafeWorldCell`] view with complete read+write access. #[inline] pub fn as_unsafe_world_cell(&mut self) -> UnsafeWorldCell<'_> { @@ -3120,33 +3161,41 @@ impl World { 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. + /// Sets custom [`ComponentCloneFn`] as default clone handler for this world's components. + /// All components with [`default`](ComponentCloneHandler::default_handler) handler, as well as any component that does not have an + /// explicitly registered clone function will use this handler. /// - /// ``` - /// # use bevy_ecs::prelude::*; - /// use bevy_ecs::component::{ComponentId, ComponentCloneHandler}; - /// use bevy_ecs::entity::ComponentCloneCtx; - /// use bevy_ecs::world::DeferredWorld; - /// - /// fn custom_clone_handler( - /// _world: &mut DeferredWorld, - /// _ctx: &mut ComponentCloneCtx, - /// ) { - /// // Custom cloning logic for component - /// } + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. + pub fn set_default_component_clone_handler(&mut self, handler: ComponentCloneFn) { + self.components.set_default_clone_handler(handler); + } + + /// Get default clone handler set for this world's components. + pub fn get_default_component_clone_handler(&self) -> ComponentCloneFn { + self.components.get_default_clone_handler() + } + + /// Sets custom [`ComponentCloneHandler`] as clone handler for component with provided [`ComponentId`]. /// - /// #[derive(Component)] - /// struct ComponentA; + /// If component with provided [`ComponentId`] is not registered, this does nothing. /// - /// let mut world = World::new(); - /// - /// let component_id = world.register_component::(); + /// See [Handlers section of `EntityCloneBuilder`](crate::entity::EntityCloneBuilder#handlers) to understand how this affects handler priority. + pub fn set_component_clone_handler( + &mut self, + component_id: ComponentId, + handler: ComponentCloneHandler, + ) { + self.components.set_clone_handler(component_id, handler); + } + + /// Get [`ComponentCloneHandler`] set for component with provided [`ComponentId`] /// - /// world.get_component_clone_handlers_mut() - /// .set_component_handler(component_id, ComponentCloneHandler::custom_handler(custom_clone_handler)) - /// ``` - pub fn get_component_clone_handlers_mut(&mut self) -> &mut ComponentCloneHandlers { - self.components.get_component_clone_handlers_mut() + /// Returns `None` if component with provided [`ComponentId`] is not registered. + pub fn get_component_clone_handler( + &self, + component_id: ComponentId, + ) -> Option { + self.components.get_clone_handler(component_id) } } @@ -3668,8 +3717,8 @@ mod tests { use super::{FromWorld, World}; use crate::{ change_detection::DetectChangesMut, - component::{ComponentDescriptor, ComponentInfo, StorageType}, - entity::EntityHashSet, + component::{ComponentCloneHandler, ComponentDescriptor, ComponentInfo, StorageType}, + entity::{Entity, EntityHashSet}, ptr::OwningPtr, system::Resource, world::error::EntityFetchError, @@ -3967,6 +4016,8 @@ mod tests { DROP_COUNT.fetch_add(1, Ordering::SeqCst); }), true, + false, + ComponentCloneHandler::default_handler(), ) }; @@ -4321,4 +4372,48 @@ mod tests { .map(|_| {}), Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1)); } + + #[test] + fn clone_world() { + #[derive(Component, Clone, PartialEq, Eq, Debug)] + struct Comp { + value: i32, + alloc_value: Vec, + } + + #[derive(Component, Clone, Copy, PartialEq, Eq, Debug)] + struct CopyComp { + value: i32, + } + + #[derive(Component, Clone, Copy, PartialEq, Eq, Debug)] + struct ZSTComp; + + #[derive(Resource, Clone, PartialEq, Eq, Debug)] + struct Res { + value: i32, + alloc_value: Vec, + } + + let comp = Comp { + value: 5, + alloc_value: vec![1, 2, 3, 4, 5], + }; + let copy_comp = CopyComp { value: 10 }; + let zst = ZSTComp; + let res = Res { + value: 1, + alloc_value: vec![7, 8, 9], + }; + + let mut world = World::default(); + let e_id = world.spawn((comp.clone(), copy_comp, zst)).id(); + world.insert_resource(res.clone()); + + let mut world2 = world.try_clone().unwrap(); + + let mut query = world2.query::<(Entity, &Comp, &CopyComp, &ZSTComp)>(); + assert_eq!(query.single(&world2), (e_id, &comp, ©_comp, &zst)); + assert_eq!(world2.get_resource::(), Some(&res)); + } } diff --git a/crates/bevy_hierarchy/src/components/children.rs b/crates/bevy_hierarchy/src/components/children.rs index 4780d31eb2e67..7b382b3d7fd0d 100644 --- a/crates/bevy_hierarchy/src/components/children.rs +++ b/crates/bevy_hierarchy/src/components/children.rs @@ -45,7 +45,15 @@ impl Component for Children { type Mutability = Mutable; fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::ignore() + // Custom handler to avoid adding `Clone` to Children + ComponentCloneHandler::ignore().with_world_clone_handler( + ComponentCloneHandler::custom_handler(|_world, ctx| { + let component = ctx.read_source_component::(); + if let Some(component) = component { + ctx.write_target_component(Self(component.0.clone())); + } + }), + ) } } diff --git a/crates/bevy_hierarchy/src/components/parent.rs b/crates/bevy_hierarchy/src/components/parent.rs index 4fc97aa914a24..43845796fd562 100644 --- a/crates/bevy_hierarchy/src/components/parent.rs +++ b/crates/bevy_hierarchy/src/components/parent.rs @@ -45,7 +45,15 @@ impl Component for Parent { type Mutability = Mutable; fn get_component_clone_handler() -> ComponentCloneHandler { - ComponentCloneHandler::ignore() + // Custom handler to avoid adding `Clone` to Parent + ComponentCloneHandler::ignore().with_world_clone_handler( + ComponentCloneHandler::custom_handler(|_world, ctx| { + let component = ctx.read_source_component::(); + if let Some(component) = component { + ctx.write_target_component(Self(component.0)); + } + }), + ) } } diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs index 5b89149154c0b..06f0dcc1f1c44 100644 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ b/crates/bevy_hierarchy/src/hierarchy.rs @@ -4,9 +4,9 @@ use crate::{ }; use bevy_ecs::{ component::ComponentCloneHandler, - entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, + entity::{ComponentCloneCtx, Entity, EntityCloneBuilder, EntityCloner}, system::{error_handler, EntityCommands}, - world::{DeferredWorld, EntityWorldMut, World}, + world::{EntityWorldMut, World}, }; use log::debug; @@ -242,18 +242,22 @@ impl CloneEntityHierarchyExt for EntityCloneBuilder<'_> { } /// Clone handler for the [`Children`] component. Allows to clone the entity recursively. -fn component_clone_children(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { +fn component_clone_children(_world: &World, ctx: &mut ComponentCloneCtx) { + let Some(mut commands) = ctx.commands() else { + return; + }; + let Some(entity_cloner) = ctx.entity_cloner() else { + return; + }; + let parent = entity_cloner.target(); let children = ctx .read_source_component::() .expect("Source entity must have Children component") .iter(); - let parent = ctx.target(); for child in children { - let child_clone = world.commands().spawn_empty().id(); - let mut clone_entity = ctx - .entity_cloner() - .with_source_and_target(*child, child_clone); - world.commands().queue(move |world: &mut World| { + let child_clone = commands.spawn_empty().id(); + let mut clone_entity = entity_cloner.with_source_and_target(*child, child_clone); + commands.queue(move |world: &mut World| { clone_entity.clone_entity(world); world.entity_mut(child_clone).set_parent(parent); }); @@ -261,12 +265,18 @@ fn component_clone_children(world: &mut DeferredWorld, ctx: &mut ComponentCloneC } /// Clone handler for the [`Parent`] component. Allows to add clone as a child to the parent entity. -fn component_clone_parent(world: &mut DeferredWorld, ctx: &mut ComponentCloneCtx) { +fn component_clone_parent(_world: &World, ctx: &mut ComponentCloneCtx) { + let Some(target) = ctx.entity_cloner().map(EntityCloner::target) else { + return; + }; + let Some(mut commands) = ctx.commands() else { + return; + }; let parent = ctx .read_source_component::() - .map(|p| p.0) + .map(Parent::get) .expect("Source entity must have Parent component"); - world.commands().entity(ctx.target()).set_parent(parent); + commands.entity(target).set_parent(parent); } #[cfg(test)] diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index b2138be85a7f7..fa896c8e5d40b 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -7,7 +7,9 @@ use std::{alloc::Layout, collections::HashMap, io::Write, ptr::NonNull}; use bevy::{ ecs::{ - component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType}, + component::{ + ComponentCloneHandler, ComponentDescriptor, ComponentId, ComponentInfo, StorageType, + }, query::QueryData, world::FilteredEntityMut, }, @@ -91,6 +93,8 @@ fn main() { Layout::array::(size).unwrap(), None, true, + false, + ComponentCloneHandler::default_handler(), ) }); let Some(info) = world.components().get_info(id) else { diff --git a/examples/ecs/immutable_components.rs b/examples/ecs/immutable_components.rs index 2e9b54522cd3d..f78bc0955de5e 100644 --- a/examples/ecs/immutable_components.rs +++ b/examples/ecs/immutable_components.rs @@ -2,7 +2,7 @@ use bevy::{ ecs::{ - component::{ComponentDescriptor, ComponentId, StorageType}, + component::{ComponentCloneHandler, ComponentDescriptor, ComponentId, StorageType}, world::DeferredWorld, }, prelude::*, @@ -149,6 +149,8 @@ fn demo_3(world: &mut World) { Layout::array::(size).unwrap(), None, false, + false, + ComponentCloneHandler::default_handler(), ) }; diff --git a/examples/stress_tests/many_components.rs b/examples/stress_tests/many_components.rs index 4bb87d322d5c7..05bd0716c181b 100644 --- a/examples/stress_tests/many_components.rs +++ b/examples/stress_tests/many_components.rs @@ -17,7 +17,7 @@ use bevy::{ DiagnosticPath, DiagnosticsPlugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin, }, ecs::{ - component::{ComponentDescriptor, ComponentId, StorageType}, + component::{ComponentCloneHandler, ComponentDescriptor, ComponentId, StorageType}, system::QueryParamBuilder, world::FilteredEntityMut, }, @@ -93,6 +93,8 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) { Layout::new::(), None, true, // is mutable + false, + ComponentCloneHandler::default_handler(), ) }, )