mod related_methods;
mod relationship_query;
mod relationship_source_collection;
use alloc::boxed::Box;
use bevy_ptr::Ptr;
use core::marker::PhantomData;
use alloc::format;
use bevy_utils::prelude::DebugName;
pub use related_methods::*;
pub use relationship_query::*;
pub use relationship_source_collection::*;
use crate::{
component::{Component, ComponentCloneBehavior, Mutable},
entity::{ComponentCloneCtx, Entity},
error::CommandWithEntity,
lifecycle::HookContext,
world::{DeferredWorld, EntityWorldMut},
};
use log::warn;
pub trait Relationship: Component + Sized {
type RelationshipTarget: RelationshipTarget<Relationship = Self>;
fn get(&self) -> Entity;
fn from(entity: Entity) -> Self;
fn set_risky(&mut self, entity: Entity);
fn on_insert(
mut world: DeferredWorld,
HookContext {
entity,
caller,
relationship_hook_mode,
..
}: HookContext,
) {
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
return;
}
}
}
let target_entity = world.entity(entity).get::<Self>().unwrap().get();
if target_entity == entity {
warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(),
DebugName::type_name::<Self>(),
DebugName::type_name::<Self>()
);
world.commands().entity(entity).remove::<Self>();
return;
}
let current_source_to_remove = world
.get_entity(target_entity)
.ok()
.and_then(|target_entity_ref| target_entity_ref.get::<Self::RelationshipTarget>())
.and_then(|relationship_target| {
relationship_target
.collection()
.source_to_remove_before_add()
});
if let Some(current_source) = current_source_to_remove {
world.commands().entity(current_source).try_remove::<Self>();
}
if let Ok(mut entity_commands) = world.commands().get_entity(target_entity) {
entity_commands
.entry::<Self::RelationshipTarget>()
.and_modify(move |mut relationship_target| {
relationship_target.collection_mut_risky().add(entity);
})
.or_insert_with(move || {
let mut target = Self::RelationshipTarget::with_capacity(1);
target.collection_mut_risky().add(entity);
target
});
} else {
warn!(
"{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(),
DebugName::type_name::<Self>(),
DebugName::type_name::<Self>()
);
world.commands().entity(entity).remove::<Self>();
}
}
fn on_replace(
mut world: DeferredWorld,
HookContext {
entity,
relationship_hook_mode,
..
}: HookContext,
) {
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
return;
}
}
}
let target_entity = world.entity(entity).get::<Self>().unwrap().get();
if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity)
&& let Some(mut relationship_target) =
target_entity_mut.get_mut::<Self::RelationshipTarget>()
{
relationship_target.collection_mut_risky().remove(entity);
if relationship_target.len() == 0 {
let command = |mut entity: EntityWorldMut| {
if entity
.get::<Self::RelationshipTarget>()
.is_some_and(RelationshipTarget::is_empty)
{
entity.remove::<Self::RelationshipTarget>();
}
};
world
.commands()
.queue_silenced(command.with_entity(target_entity));
}
}
}
}
pub type SourceIter<'w, R> =
<<R as RelationshipTarget>::Collection as RelationshipSourceCollection>::SourceIter<'w>;
pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
const LINKED_SPAWN: bool;
type Relationship: Relationship<RelationshipTarget = Self>;
type Collection: RelationshipSourceCollection;
fn collection(&self) -> &Self::Collection;
fn collection_mut_risky(&mut self) -> &mut Self::Collection;
fn from_collection_risky(collection: Self::Collection) -> Self;
fn on_replace(
mut world: DeferredWorld,
HookContext {
entity,
relationship_hook_mode,
..
}: HookContext,
) {
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip | RelationshipHookMode::RunIfNotLinked => return,
}
let (entities, mut commands) = world.entities_and_commands();
let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
for source_entity in relationship_target.iter() {
commands
.entity(source_entity)
.try_remove::<Self::Relationship>();
}
}
fn on_despawn(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
let (entities, mut commands) = world.entities_and_commands();
let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
for source_entity in relationship_target.iter() {
commands.entity(source_entity).try_despawn();
}
}
fn with_capacity(capacity: usize) -> Self {
let collection =
<Self::Collection as RelationshipSourceCollection>::with_capacity(capacity);
Self::from_collection_risky(collection)
}
#[inline]
fn iter(&self) -> SourceIter<'_, Self> {
self.collection().iter()
}
#[inline]
fn len(&self) -> usize {
self.collection().len()
}
#[inline]
fn is_empty(&self) -> bool {
self.collection().is_empty()
}
}
pub fn clone_relationship_target<T: RelationshipTarget>(
component: &T,
cloned: &mut T,
context: &mut ComponentCloneCtx,
) {
if context.linked_cloning() && T::LINKED_SPAWN {
let collection = cloned.collection_mut_risky();
for entity in component.iter() {
collection.add(entity);
context.queue_entity_clone(entity);
}
} else if context.moving() {
let target = context.target();
let collection = cloned.collection_mut_risky();
for entity in component.iter() {
collection.add(entity);
context.queue_deferred(move |world, _mapper| {
_ = DeferredWorld::from(world)
.modify_component_with_relationship_hook_mode::<T::Relationship, ()>(
entity,
RelationshipHookMode::Skip,
|r| r.set_risky(target),
);
});
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum RelationshipHookMode {
Run,
RunIfNotLinked,
Skip,
}
#[doc(hidden)]
pub struct RelationshipCloneBehaviorSpecialization<T>(PhantomData<T>);
impl<T> Default for RelationshipCloneBehaviorSpecialization<T> {
fn default() -> Self {
Self(PhantomData)
}
}
#[doc(hidden)]
pub trait RelationshipCloneBehaviorBase {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl<C> RelationshipCloneBehaviorBase for RelationshipCloneBehaviorSpecialization<C> {
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::Ignore
}
}
#[doc(hidden)]
pub trait RelationshipCloneBehaviorViaReflect {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
#[cfg(feature = "bevy_reflect")]
impl<C: Relationship + bevy_reflect::Reflect> RelationshipCloneBehaviorViaReflect
for &RelationshipCloneBehaviorSpecialization<C>
{
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::reflect()
}
}
#[doc(hidden)]
pub trait RelationshipCloneBehaviorViaClone {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl<C: Relationship + Clone> RelationshipCloneBehaviorViaClone
for &&RelationshipCloneBehaviorSpecialization<C>
{
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::clone::<C>()
}
}
#[doc(hidden)]
pub trait RelationshipTargetCloneBehaviorViaReflect {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
#[cfg(feature = "bevy_reflect")]
impl<C: RelationshipTarget + bevy_reflect::Reflect + bevy_reflect::TypePath>
RelationshipTargetCloneBehaviorViaReflect for &&&RelationshipCloneBehaviorSpecialization<C>
{
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::Custom(|source, context| {
if let Some(component) = source.read::<C>()
&& let Ok(mut cloned) = component.reflect_clone_and_take::<C>()
{
cloned.collection_mut_risky().clear();
clone_relationship_target(component, &mut cloned, context);
context.write_target_component(cloned);
}
})
}
}
#[doc(hidden)]
pub trait RelationshipTargetCloneBehaviorViaClone {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl<C: RelationshipTarget + Clone> RelationshipTargetCloneBehaviorViaClone
for &&&&RelationshipCloneBehaviorSpecialization<C>
{
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::Custom(|source, context| {
if let Some(component) = source.read::<C>() {
let mut cloned = component.clone();
cloned.collection_mut_risky().clear();
clone_relationship_target(component, &mut cloned, context);
context.write_target_component(cloned);
}
})
}
}
#[doc(hidden)]
pub trait RelationshipTargetCloneBehaviorHierarchy {
fn default_clone_behavior(&self) -> ComponentCloneBehavior;
}
impl RelationshipTargetCloneBehaviorHierarchy
for &&&&&RelationshipCloneBehaviorSpecialization<crate::hierarchy::Children>
{
fn default_clone_behavior(&self) -> ComponentCloneBehavior {
ComponentCloneBehavior::Custom(|source, context| {
if let Some(component) = source.read::<crate::hierarchy::Children>() {
let mut cloned = crate::hierarchy::Children::with_capacity(component.len());
clone_relationship_target(component, &mut cloned, context);
context.write_target_component(cloned);
}
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum RelationshipAccessor {
Relationship {
entity_field_offset: usize,
linked_spawn: bool,
},
RelationshipTarget {
iter: for<'a> unsafe fn(Ptr<'a>) -> Box<dyn Iterator<Item = Entity> + 'a>,
linked_spawn: bool,
},
}
pub struct ComponentRelationshipAccessor<C: ?Sized> {
pub(crate) accessor: RelationshipAccessor,
phantom: PhantomData<C>,
}
impl<C> ComponentRelationshipAccessor<C> {
pub unsafe fn relationship(entity_field_offset: usize) -> Self
where
C: Relationship,
{
Self {
accessor: RelationshipAccessor::Relationship {
entity_field_offset,
linked_spawn: C::RelationshipTarget::LINKED_SPAWN,
},
phantom: Default::default(),
}
}
pub fn relationship_target() -> Self
where
C: RelationshipTarget,
{
Self {
accessor: RelationshipAccessor::RelationshipTarget {
iter: |ptr| unsafe { Box::new(RelationshipTarget::iter(ptr.deref::<C>())) },
linked_spawn: C::LINKED_SPAWN,
},
phantom: Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use crate::prelude::{ChildOf, Children};
use crate::relationship::RelationshipAccessor;
use crate::world::World;
use crate::{component::Component, entity::Entity};
use alloc::vec::Vec;
#[test]
fn custom_relationship() {
#[derive(Component)]
#[relationship(relationship_target = LikedBy)]
struct Likes(pub Entity);
#[derive(Component)]
#[relationship_target(relationship = Likes)]
struct LikedBy(Vec<Entity>);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn(Likes(a)).id();
let c = world.spawn(Likes(a)).id();
assert_eq!(world.entity(a).get::<LikedBy>().unwrap().0, &[b, c]);
}
#[test]
fn self_relationship_fails() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel)]
struct RelTarget(Vec<Entity>);
let mut world = World::new();
let a = world.spawn_empty().id();
world.entity_mut(a).insert(Rel(a));
assert!(!world.entity(a).contains::<Rel>());
assert!(!world.entity(a).contains::<RelTarget>());
}
#[test]
fn relationship_with_missing_target_fails() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel)]
struct RelTarget(Vec<Entity>);
let mut world = World::new();
let a = world.spawn_empty().id();
world.despawn(a);
let b = world.spawn(Rel(a)).id();
assert!(!world.entity(b).contains::<Rel>());
assert!(!world.entity(b).contains::<RelTarget>());
}
#[test]
fn relationship_with_multiple_non_target_fields_compiles() {
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship(relationship_target=Target)]
struct Source {
#[relationship]
target: Entity,
foo: u8,
bar: u8,
}
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship_target(relationship=Source)]
struct Target(Vec<Entity>);
}
#[test]
fn relationship_target_with_multiple_non_target_fields_compiles() {
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship(relationship_target=Target)]
struct Source(Entity);
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship_target(relationship=Source)]
struct Target {
#[relationship]
target: Vec<Entity>,
foo: u8,
bar: u8,
}
}
#[test]
fn relationship_with_multiple_unnamed_non_target_fields_compiles() {
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship(relationship_target=Target<T>)]
struct Source<T: Send + Sync + 'static>(#[relationship] Entity, PhantomData<T>);
#[expect(
dead_code,
reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
)]
#[derive(Component)]
#[relationship_target(relationship=Source<T>)]
struct Target<T: Send + Sync + 'static>(#[relationship] Vec<Entity>, PhantomData<T>);
}
#[test]
fn parent_child_relationship_with_custom_relationship() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel)]
struct RelTarget(Entity);
let mut world = World::new();
let mut commands = world.commands();
let child = commands.spawn_empty().id();
let parent = commands.spawn(Rel(child)).add_child(child).id();
commands.entity(parent).despawn();
world.flush();
assert!(world.get_entity(child).is_err());
assert!(world.get_entity(parent).is_err());
let mut commands = world.commands();
let child = commands.spawn_empty().id();
let parent = commands.spawn(Rel(child)).add_child(child).id();
commands.entity(child).despawn();
world.flush();
assert!(world.get_entity(child).is_err());
assert!(!world.entity(parent).contains::<Rel>());
let mut commands = world.commands();
let parent = commands.spawn_empty().id();
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
commands.entity(parent).despawn();
world.flush();
assert!(world.get_entity(child).is_err());
assert!(world.get_entity(parent).is_err());
let mut commands = world.commands();
let parent = commands.spawn_empty().id();
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
commands.entity(child).despawn();
world.flush();
assert!(world.get_entity(child).is_err());
assert!(!world.entity(parent).contains::<RelTarget>());
}
#[test]
fn spawn_batch_with_relationship() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let children = world
.spawn_batch((0..10).map(|_| ChildOf(parent)))
.collect::<Vec<_>>();
for &child in &children {
assert!(world
.get::<ChildOf>(child)
.is_some_and(|child_of| child_of.parent() == parent));
}
assert!(world
.get::<Children>(parent)
.is_some_and(|children| children.len() == 10));
}
#[test]
fn insert_batch_with_relationship() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child = world.spawn_empty().id();
world.insert_batch([(child, ChildOf(parent))]);
world.flush();
assert!(world.get::<ChildOf>(child).is_some());
assert!(world.get::<Children>(parent).is_some());
}
#[test]
fn dynamically_traverse_hierarchy() {
let mut world = World::new();
let child_of_id = world.register_component::<ChildOf>();
let children_id = world.register_component::<Children>();
let parent = world.spawn_empty().id();
let child = world.spawn_empty().id();
world.entity_mut(child).insert(ChildOf(parent));
world.flush();
let children_ptr = world.get_by_id(parent, children_id).unwrap();
let RelationshipAccessor::RelationshipTarget { iter, .. } = world
.components()
.get_info(children_id)
.unwrap()
.relationship_accessor()
.unwrap()
else {
unreachable!()
};
let children: Vec<_> = unsafe { iter(children_ptr).collect() };
assert_eq!(children, alloc::vec![child]);
let child_of_ptr = world.get_by_id(child, child_of_id).unwrap();
let RelationshipAccessor::Relationship {
entity_field_offset,
..
} = world
.components()
.get_info(child_of_id)
.unwrap()
.relationship_accessor()
.unwrap()
else {
unreachable!()
};
let child_of_entity: Entity =
unsafe { *child_of_ptr.byte_add(*entity_field_offset).deref() };
assert_eq!(child_of_entity, parent);
}
}