Skip to content
6 changes: 3 additions & 3 deletions benches/benches/bevy_ecs/entity_cloning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type ComplexBundle = (C1, C2, C3, C4, C5, C6, C7, C8, C9, C10);
/// use the [`Reflect`] trait instead of [`Clone`].
fn reflection_cloner<B: Bundle + GetTypeRegistration>(
world: &mut World,
recursive: bool,
linked_cloning: bool,
) -> EntityCloner {
// Get mutable access to the type registry, creating it if it does not exist yet.
let registry = world.get_resource_or_init::<AppTypeRegistry>();
Expand All @@ -77,7 +77,7 @@ fn reflection_cloner<B: Bundle + GetTypeRegistration>(
for component in component_ids {
builder.override_clone_behavior_with_id(component, ComponentCloneBehavior::reflect());
}
builder.recursive(recursive);
builder.linked_cloning(linked_cloning);

builder.finish()
}
Expand Down Expand Up @@ -136,7 +136,7 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
reflection_cloner::<B>(&mut world, true)
} else {
let mut builder = EntityCloner::build(&mut world);
builder.recursive(true);
builder.linked_cloning(true);
builder.finish()
};

Expand Down
20 changes: 16 additions & 4 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
Err(err) => err.into_compile_error().into(),
};

let visit_entities = visit_entities(&ast.data, &bevy_ecs_path, relationship.is_some());
let visit_entities = visit_entities(
&ast.data,
&bevy_ecs_path,
relationship.is_some(),
relationship_target.is_some(),
);

let storage = storage_path(&bevy_ecs_path, attrs.storage);

Expand Down Expand Up @@ -207,7 +212,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
.unwrap_or(quote! { #bevy_ecs_path::component::Mutable });

let clone_behavior = if relationship_target.is_some() {
quote!(#bevy_ecs_path::component::ComponentCloneBehavior::RelationshipTarget(#bevy_ecs_path::relationship::clone_relationship_target::<Self>))
quote!(#bevy_ecs_path::component::ComponentCloneBehavior::Custom(#bevy_ecs_path::relationship::clone_relationship_target::<Self>))
} else {
quote!(
use #bevy_ecs_path::component::{DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone};
Expand Down Expand Up @@ -255,7 +260,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
})
}

fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> TokenStream2 {
fn visit_entities(
data: &Data,
bevy_ecs_path: &Path,
is_relationship: bool,
is_relationship_target: bool,
) -> TokenStream2 {
match data {
Data::Struct(DataStruct { fields, .. }) => {
let mut visited_fields = Vec::new();
Expand Down Expand Up @@ -288,7 +298,9 @@ fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> T
}
Fields::Unnamed(fields) => {
for (index, field) in fields.unnamed.iter().enumerate() {
if field
if index == 0 && is_relationship_target {
visited_indices.push(Index::from(0));
} else if field
.attrs
.iter()
.any(|a| a.meta.path().is_ident(ENTITIES_ATTR))
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
observer::Observers,
prelude::World,
query::DebugCheckedUnwrap,
relationship::RelationshipInsertHookMode,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, EntityWorldMut, ON_ADD, ON_INSERT, ON_REPLACE},
};
Expand Down Expand Up @@ -1100,6 +1101,7 @@ impl<'w> BundleInserter<'w> {
bundle: T,
insert_mode: InsertMode,
caller: MaybeLocation,
relationship_insert_hook_mode: RelationshipInsertHookMode,
) -> (EntityLocation, T::Effect) {
let bundle_info = self.bundle_info.as_ref();
let archetype_after_insert = self.archetype_after_insert.as_ref();
Expand Down Expand Up @@ -1312,6 +1314,7 @@ impl<'w> BundleInserter<'w> {
entity,
archetype_after_insert.iter_inserted(),
caller,
relationship_insert_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
Expand All @@ -1330,6 +1333,7 @@ impl<'w> BundleInserter<'w> {
entity,
archetype_after_insert.iter_added(),
caller,
relationship_insert_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
Expand Down Expand Up @@ -1474,6 +1478,7 @@ impl<'w> BundleSpawner<'w> {
entity,
bundle_info.iter_contributed_components(),
caller,
RelationshipInsertHookMode::Run,
);
if archetype.has_insert_observer() {
deferred_world.trigger_observers(
Expand Down
35 changes: 21 additions & 14 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use crate::{
archetype::ArchetypeFlags,
bundle::BundleInfo,
change_detection::{MaybeLocation, MAX_CHANGE_AGE},
entity::{ComponentCloneCtx, Entity},
entity::{ComponentCloneCtx, Entity, SourceComponent},
query::DebugCheckedUnwrap,
relationship::RelationshipInsertHookMode,
resource::Resource,
storage::{SparseSetIndex, SparseSets, Table, TableRow},
system::{Commands, Local, SystemParam},
Expand Down Expand Up @@ -544,6 +545,8 @@ pub struct HookContext {
pub component_id: ComponentId,
/// The caller location is `Some` if the `track_caller` feature is enabled.
pub caller: MaybeLocation,
/// Configures how relationship hooks will run
pub relationship_insert_hook_mode: RelationshipInsertHookMode,
}

/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
Expand Down Expand Up @@ -1085,7 +1088,7 @@ impl ComponentDescriptor {
}

/// Function type that can be used to clone an entity.
pub type ComponentCloneFn = fn(&mut Commands, &mut ComponentCloneCtx);
pub type ComponentCloneFn = fn(&mut Commands, &SourceComponent, &mut ComponentCloneCtx);

/// The clone behavior to use when cloning a [`Component`].
#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand All @@ -1097,11 +1100,6 @@ pub enum ComponentCloneBehavior {
Ignore,
/// Uses a custom [`ComponentCloneFn`].
Custom(ComponentCloneFn),
/// Uses a [`ComponentCloneFn`] that produces an empty version of the given relationship target.
// TODO: this exists so that the current scene spawning code can know when to skip these components.
// When we move to actually cloning entities in scene spawning code, this should be removed in favor of Custom, as the
// distinction will no longer be necessary.
RelationshipTarget(ComponentCloneFn),
}

impl ComponentCloneBehavior {
Expand Down Expand Up @@ -1132,8 +1130,7 @@ impl ComponentCloneBehavior {
match self {
ComponentCloneBehavior::Default => default,
ComponentCloneBehavior::Ignore => component_clone_ignore,
ComponentCloneBehavior::Custom(custom)
| ComponentCloneBehavior::RelationshipTarget(custom) => *custom,
ComponentCloneBehavior::Custom(custom) => *custom,
}
}
}
Expand Down Expand Up @@ -2166,9 +2163,10 @@ pub fn enforce_no_required_components_recursion(
///
pub fn component_clone_via_clone<C: Clone + Component>(
_commands: &mut Commands,
source: &SourceComponent,
ctx: &mut ComponentCloneCtx,
) {
if let Some(component) = ctx.read_source_component::<C>() {
if let Some(component) = source.read::<C>() {
ctx.write_target_component(component.clone());
}
}
Expand All @@ -2189,17 +2187,21 @@ pub fn component_clone_via_clone<C: Clone + Component>(
///
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
#[cfg(feature = "bevy_reflect")]
pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentCloneCtx) {
pub fn component_clone_via_reflect(
commands: &mut Commands,
source: &SourceComponent,
ctx: &mut ComponentCloneCtx,
) {
let Some(app_registry) = ctx.type_registry().cloned() else {
return;
};
let Some(source_component_reflect) = ctx.read_source_component_reflect() else {
let registry = app_registry.read();
let Some(source_component_reflect) = source.read_reflect(&registry) else {
return;
};
let component_info = ctx.component_info();
// checked in read_source_component_reflect
let type_id = component_info.type_id().unwrap();
let registry = app_registry.read();

// Try to clone using ReflectFromReflect
if let Some(reflect_from_reflect) =
Expand Down Expand Up @@ -2284,7 +2286,12 @@ pub fn component_clone_via_reflect(commands: &mut Commands, ctx: &mut ComponentC
/// Noop implementation of component clone handler function.
///
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
pub fn component_clone_ignore(_commands: &mut Commands, _ctx: &mut ComponentCloneCtx) {}
pub fn component_clone_ignore(
_commands: &mut Commands,
_source: &SourceComponent,
_ctx: &mut ComponentCloneCtx,
) {
}

/// Wrapper for components clone specialization using autoderef.
#[doc(hidden)]
Expand Down
Loading