diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index d3199c0909..e87a4b650b 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -251,6 +251,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let clone_behavior = if relationship_target.is_some() { quote!(#bevy_ecs_path::component::ComponentCloneBehavior::Custom(#bevy_ecs_path::relationship::clone_relationship_target::)) + } else if let Some(behavior) = attrs.clone_behavior { + quote!(#bevy_ecs_path::component::ComponentCloneBehavior::#behavior) } else { quote!( use #bevy_ecs_path::component::{DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone}; @@ -396,6 +398,7 @@ pub const ON_REMOVE: &str = "on_remove"; pub const ON_DESPAWN: &str = "on_despawn"; pub const IMMUTABLE: &str = "immutable"; +pub const CLONE_BEHAVIOR: &str = "clone_behavior"; /// All allowed attribute value expression kinds for component hooks #[derive(Debug)] @@ -458,6 +461,7 @@ struct Attrs { relationship: Option, relationship_target: Option, immutable: bool, + clone_behavior: Option, } #[derive(Clone, Copy)] @@ -496,6 +500,7 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { relationship: None, relationship_target: None, immutable: false, + clone_behavior: None, }; let mut require_paths = HashSet::new(); @@ -531,6 +536,9 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { } else if nested.path.is_ident(IMMUTABLE) { attrs.immutable = true; Ok(()) + } else if nested.path.is_ident(CLONE_BEHAVIOR) { + attrs.clone_behavior = Some(nested.value()?.parse()?); + Ok(()) } else { Err(nested.error("Unsupported attribute")) } @@ -560,6 +568,13 @@ fn parse_component_attr(ast: &DeriveInput) -> Result { } } + if attrs.relationship_target.is_some() && attrs.clone_behavior.is_some() { + return Err(syn::Error::new( + attrs.clone_behavior.span(), + "A Relationship Target already has its own clone behavior, please remove `clone_behavior = ...`", + )); + } + Ok(attrs) } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index f439ae6cde..8f0ec84225 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -418,6 +418,21 @@ use thiserror::Error; /// println!("{message}"); /// } /// } +/// +/// ``` +/// # Setting the clone behavior +/// +/// You can specify how the [`Component`] is cloned when deriving it. +/// +/// Your options are the functions and variants of [`ComponentCloneBehavior`] +/// See [Handlers section of `EntityClonerBuilder`](crate::entity::EntityClonerBuilder#handlers) to understand how this affects handler priority. +/// ``` +/// # use bevy_ecs::prelude::*; +/// +/// #[derive(Component)] +/// #[component(clone_behavior = Ignore)] +/// struct MyComponent; +/// /// ``` /// /// # Implementing the trait for foreign types diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index bd8eb2b4bd..5328eb1d3a 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -324,16 +324,10 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// ``` /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::component::{StorageType, ComponentCloneBehavior, Mutable}; -/// #[derive(Clone)] +/// #[derive(Clone, Component)] +/// #[component(clone_behavior = clone::())] /// struct SomeComponent; /// -/// impl Component for SomeComponent { -/// const STORAGE_TYPE: StorageType = StorageType::Table; -/// type Mutability = Mutable; -/// fn clone_behavior() -> ComponentCloneBehavior { -/// ComponentCloneBehavior::clone::() -/// } -/// } /// ``` /// /// # Clone Behaviors diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ec0b8cdfd4..44acc052de 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -2751,4 +2751,27 @@ mod tests { )] #[derive(Component)] struct MyEntitiesTuple(#[entities] Vec, #[entities] Entity, usize); + + #[test] + fn clone_entities() { + use crate::entity::{ComponentCloneCtx, SourceComponent}; + + #[derive(Component)] + #[component(clone_behavior = Ignore)] + struct IgnoreClone; + + #[derive(Component)] + #[component(clone_behavior = Default)] + struct DefaultClone; + + #[derive(Component)] + #[component(clone_behavior = Custom(custom_clone))] + struct CustomClone; + + #[derive(Component, Clone)] + #[component(clone_behavior = clone::())] + struct CloneFunction; + + fn custom_clone(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {} + } } diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index ce04088333..f15f3c4003 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -1,6 +1,5 @@ use bevy_app::Plugin; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::component::{ComponentCloneBehavior, Mutable, StorageType}; use bevy_ecs::entity::EntityHash; use bevy_ecs::{ component::Component, @@ -127,7 +126,8 @@ pub struct SyncToRenderWorld; /// Component added on the main world entities that are synced to the Render World in order to keep track of the corresponding render world entity. /// /// Can also be used as a newtype wrapper for render world entities. -#[derive(Deref, Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, Component)] +#[component(clone_behavior = Ignore)] pub struct RenderEntity(Entity); impl RenderEntity { #[inline] @@ -136,16 +136,6 @@ impl RenderEntity { } } -impl Component for RenderEntity { - const STORAGE_TYPE: StorageType = StorageType::Table; - - type Mutability = Mutable; - - fn clone_behavior() -> ComponentCloneBehavior { - ComponentCloneBehavior::Ignore - } -} - impl From for RenderEntity { fn from(entity: Entity) -> Self { RenderEntity(entity)