Path: blob/main/crates/bevy_ecs/src/relationship/mod.rs
9408 views
//! This module provides functionality to link entities to each other using specialized components called "relationships". See the [`Relationship`] trait for more info.12mod related_methods;3mod relationship_query;4mod relationship_source_collection;56use alloc::boxed::Box;7use bevy_ptr::Ptr;8use core::marker::PhantomData;910use alloc::format;1112use bevy_utils::prelude::DebugName;13pub use related_methods::*;14pub use relationship_query::*;15pub use relationship_source_collection::*;1617use crate::{18component::{Component, ComponentCloneBehavior, Mutable},19entity::{ComponentCloneCtx, Entity},20error::CommandWithEntity,21lifecycle::HookContext,22world::{DeferredWorld, EntityWorldMut},23};24use log::warn;2526/// A [`Component`] on a "source" [`Entity`] that references another target [`Entity`], creating a "relationship" between them. Every [`Relationship`]27/// has a corresponding [`RelationshipTarget`] type (and vice-versa), which exists on the "target" entity of a relationship and contains the list of all28/// "source" entities that relate to the given "target"29///30/// The [`Relationship`] component is the "source of truth" and the [`RelationshipTarget`] component reflects that source of truth. When a [`Relationship`]31/// component is inserted on an [`Entity`], the corresponding [`RelationshipTarget`] component is immediately inserted on the target component if it does32/// not already exist, and the "source" entity is automatically added to the [`RelationshipTarget`] collection (this is done via "component hooks").33///34/// A common example of a [`Relationship`] is the parent / child relationship. Bevy ECS includes a canonical form of this via the [`ChildOf`](crate::hierarchy::ChildOf)35/// [`Relationship`] and the [`Children`](crate::hierarchy::Children) [`RelationshipTarget`].36///37/// [`Relationship`] and [`RelationshipTarget`] should always be derived via the [`Component`] trait to ensure the hooks are set up properly.38///39/// ## Derive40///41/// [`Relationship`] and [`RelationshipTarget`] can only be derived for structs with a single unnamed field, single named field42/// or for named structs where one field is annotated with `#[relationship]`.43/// If there are additional fields, they must all implement [`Default`].44///45/// [`RelationshipTarget`] also requires that the relationship field is private to prevent direct mutation,46/// ensuring the correctness of relationships.47/// ```48/// # use bevy_ecs::component::Component;49/// # use bevy_ecs::entity::Entity;50/// #[derive(Component)]51/// #[relationship(relationship_target = Children)]52/// pub struct ChildOf {53/// #[relationship]54/// pub parent: Entity,55/// internal: u8,56/// };57///58/// #[derive(Component)]59/// #[relationship_target(relationship = ChildOf)]60/// pub struct Children(Vec<Entity>);61/// ```62///63/// When deriving [`RelationshipTarget`] you can specify the `#[relationship_target(linked_spawn)]` attribute to64/// automatically despawn entities stored in an entity's [`RelationshipTarget`] when that entity is despawned:65///66/// ```67/// # use bevy_ecs::component::Component;68/// # use bevy_ecs::entity::Entity;69/// #[derive(Component)]70/// #[relationship(relationship_target = Children)]71/// pub struct ChildOf(pub Entity);72///73/// #[derive(Component)]74/// #[relationship_target(relationship = ChildOf, linked_spawn)]75/// pub struct Children(Vec<Entity>);76/// ```77///78/// By default, relationships cannot point to their own entity. If you want to allow self-referential79/// relationships, you can use the `allow_self_referential` attribute:80///81/// ```82/// # use bevy_ecs::component::Component;83/// # use bevy_ecs::entity::Entity;84/// #[derive(Component)]85/// #[relationship(relationship_target = PeopleILike, allow_self_referential)]86/// pub struct LikedBy(pub Entity);87///88/// #[derive(Component)]89/// #[relationship_target(relationship = LikedBy)]90/// pub struct PeopleILike(Vec<Entity>);91/// ```92pub trait Relationship: Component + Sized {93/// The [`Component`] added to the "target" entities of this [`Relationship`], which contains the list of all "source"94/// entities that relate to the "target".95type RelationshipTarget: RelationshipTarget<Relationship = Self>;9697/// If `true`, a relationship is allowed to point to its own entity.98///99/// Set this to `true` when self-relationships are semantically valid for your use case,100/// such as `Likes(self)`, `EmployedBy(self)`, or a `ColliderOf` relationship where101/// a collider can be attached to its own entity.102///103/// # Warning104///105/// When `ALLOW_SELF` is `true`, be careful when using recursive traversal methods106/// like `iter_ancestors` or `root_ancestor`, as they will loop infinitely if an entity107/// points to itself.108const ALLOW_SELF_REFERENTIAL: bool = false;109110/// Gets the [`Entity`] ID of the related entity.111fn get(&self) -> Entity;112113/// Creates this [`Relationship`] from the given `entity`.114fn from(entity: Entity) -> Self;115116/// Changes the current [`Entity`] ID of the entity containing the [`RelationshipTarget`] to another one.117///118/// This is useful for updating the relationship without overwriting other fields stored in `Self`.119///120/// # Warning121///122/// This should generally not be called by user code, as modifying the related entity could invalidate the123/// relationship. If this method is used, then the hooks [`on_replace`](Relationship::on_replace) have to124/// run before and [`on_insert`](Relationship::on_insert) after it.125/// This happens automatically when this method is called with [`EntityWorldMut::modify_component`].126///127/// Prefer to use regular means of insertions when possible.128fn set_risky(&mut self, entity: Entity);129130/// The `on_insert` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.131fn on_insert(132mut world: DeferredWorld,133HookContext {134entity,135caller,136relationship_hook_mode,137..138}: HookContext,139) {140match relationship_hook_mode {141RelationshipHookMode::Run => {}142RelationshipHookMode::Skip => return,143RelationshipHookMode::RunIfNotLinked => {144if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {145return;146}147}148}149let target_entity = world.entity(entity).get::<Self>().unwrap().get();150if !Self::ALLOW_SELF_REFERENTIAL && target_entity == entity {151warn!(152"{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.\nIf this is intended behavior self-referential relations can be enabled with the allow_self_referential attribute: #[relationship(allow_self_referential)]",153caller.map(|location|format!("{location}: ")).unwrap_or_default(),154DebugName::type_name::<Self>(),155DebugName::type_name::<Self>()156);157world.commands().entity(entity).remove::<Self>();158return;159}160// For one-to-one relationships, remove existing relationship before adding new one161let current_source_to_remove = world162.get_entity(target_entity)163.ok()164.and_then(|target_entity_ref| target_entity_ref.get::<Self::RelationshipTarget>())165.and_then(|relationship_target| {166relationship_target167.collection()168.source_to_remove_before_add()169});170171if let Some(current_source) = current_source_to_remove {172world.commands().entity(current_source).try_remove::<Self>();173}174175if let Ok(mut entity_commands) = world.commands().get_entity(target_entity) {176// Deferring is necessary for batch mode177entity_commands178.entry::<Self::RelationshipTarget>()179.and_modify(move |mut relationship_target| {180relationship_target.collection_mut_risky().add(entity);181})182.or_insert_with(move || {183let mut target = Self::RelationshipTarget::with_capacity(1);184target.collection_mut_risky().add(entity);185target186});187} else {188warn!(189"{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.",190caller.map(|location|format!("{location}: ")).unwrap_or_default(),191DebugName::type_name::<Self>(),192DebugName::type_name::<Self>()193);194world.commands().entity(entity).remove::<Self>();195}196}197198/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.199// note: think of this as "on_drop"200fn on_replace(201mut world: DeferredWorld,202HookContext {203entity,204relationship_hook_mode,205..206}: HookContext,207) {208match relationship_hook_mode {209RelationshipHookMode::Run => {}210RelationshipHookMode::Skip => return,211RelationshipHookMode::RunIfNotLinked => {212if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {213return;214}215}216}217let target_entity = world.entity(entity).get::<Self>().unwrap().get();218if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity)219&& let Some(mut relationship_target) =220target_entity_mut.get_mut::<Self::RelationshipTarget>()221{222relationship_target.collection_mut_risky().remove(entity);223if relationship_target.len() == 0 {224let command = |mut entity: EntityWorldMut| {225// this "remove" operation must check emptiness because in the event that an identical226// relationship is inserted on top, this despawn would result in the removal of that identical227// relationship ... not what we want!228if entity229.get::<Self::RelationshipTarget>()230.is_some_and(RelationshipTarget::is_empty)231{232entity.remove::<Self::RelationshipTarget>();233}234};235236world237.commands()238.queue_silenced(command.with_entity(target_entity));239}240}241}242}243244/// The iterator type for the source entities in a [`RelationshipTarget`] collection,245/// as defined in the [`RelationshipSourceCollection`] trait.246pub type SourceIter<'w, R> =247<<R as RelationshipTarget>::Collection as RelationshipSourceCollection>::SourceIter<'w>;248249/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type.250/// See the [`Relationship`] documentation for more information.251pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {252/// If this is true, when despawning or cloning (when [linked cloning is enabled](crate::entity::EntityClonerBuilder::linked_cloning)), the related entities targeting this entity will also be despawned or cloned.253///254/// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children).255/// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning).256///257/// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone.258/// This defaults to false when derived.259const LINKED_SPAWN: bool;260/// The [`Relationship`] that populates this [`RelationshipTarget`] collection.261type Relationship: Relationship<RelationshipTarget = Self>;262/// The collection type that stores the "source" entities for this [`RelationshipTarget`] component.263///264/// Check the list of types which implement [`RelationshipSourceCollection`] for the data structures that can be used inside of your component.265/// If you need a new collection type, you can implement the [`RelationshipSourceCollection`] trait266/// for a type you own which wraps the collection you want to use (to avoid the orphan rule),267/// or open an issue on the Bevy repository to request first-party support for your collection type.268type Collection: RelationshipSourceCollection;269270/// Returns a reference to the stored [`RelationshipTarget::Collection`].271fn collection(&self) -> &Self::Collection;272/// Returns a mutable reference to the stored [`RelationshipTarget::Collection`].273///274/// # Warning275/// This should generally not be called by user code, as modifying the internal collection could invalidate the relationship.276/// The collection should not contain duplicates.277fn collection_mut_risky(&mut self) -> &mut Self::Collection;278279/// Creates a new [`RelationshipTarget`] from the given [`RelationshipTarget::Collection`].280///281/// # Warning282/// This should generally not be called by user code, as constructing the internal collection could invalidate the relationship.283/// The collection should not contain duplicates.284fn from_collection_risky(collection: Self::Collection) -> Self;285286/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.287// note: think of this as "on_drop"288fn on_replace(289mut world: DeferredWorld,290HookContext {291entity,292relationship_hook_mode,293..294}: HookContext,295) {296match relationship_hook_mode {297RelationshipHookMode::Run => {}298// For RelationshipTarget we don't want to run this hook even if it isn't linked, but for Relationship we do.299RelationshipHookMode::Skip | RelationshipHookMode::RunIfNotLinked => return,300}301let (entities, mut commands) = world.entities_and_commands();302let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();303for source_entity in relationship_target.iter() {304commands305.entity(source_entity)306.try_remove::<Self::Relationship>();307}308}309310/// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when311/// that entity is despawned.312// note: think of this as "on_drop"313fn on_despawn(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {314let (entities, mut commands) = world.entities_and_commands();315let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();316for source_entity in relationship_target.iter() {317commands.entity(source_entity).try_despawn();318}319}320321/// Creates this [`RelationshipTarget`] with the given pre-allocated entity capacity.322fn with_capacity(capacity: usize) -> Self {323let collection =324<Self::Collection as RelationshipSourceCollection>::with_capacity(capacity);325Self::from_collection_risky(collection)326}327328/// Iterates the entities stored in this collection.329#[inline]330fn iter(&self) -> SourceIter<'_, Self> {331self.collection().iter()332}333334/// Returns the number of entities in this collection.335#[inline]336fn len(&self) -> usize {337self.collection().len()338}339340/// Returns true if this entity collection is empty.341#[inline]342fn is_empty(&self) -> bool {343self.collection().is_empty()344}345}346347/// The "clone behavior" for [`RelationshipTarget`]. The [`RelationshipTarget`] will be populated with the proper components348/// when the corresponding [`Relationship`] sources of truth are inserted. Cloning the actual entities349/// in the original [`RelationshipTarget`] would result in duplicates, so we don't do that!350///351/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured352/// to spawn recursively.353pub fn clone_relationship_target<T: RelationshipTarget>(354component: &T,355cloned: &mut T,356context: &mut ComponentCloneCtx,357) {358if context.linked_cloning() && T::LINKED_SPAWN {359let collection = cloned.collection_mut_risky();360for entity in component.iter() {361collection.add(entity);362context.queue_entity_clone(entity);363}364} else if context.moving() {365let target = context.target();366let collection = cloned.collection_mut_risky();367for entity in component.iter() {368collection.add(entity);369context.queue_deferred(move |world, _mapper| {370// We don't want relationships hooks to run because we are manually constructing the collection here371_ = DeferredWorld::from(world)372.modify_component_with_relationship_hook_mode::<T::Relationship, ()>(373entity,374RelationshipHookMode::Skip,375|r| r.set_risky(target),376);377});378}379}380}381382/// Configures the conditions under which the Relationship insert/replace hooks will be run.383#[derive(Copy, Clone, Debug)]384pub enum RelationshipHookMode {385/// Relationship insert/replace hooks will always run386Run,387/// Relationship insert/replace hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false388RunIfNotLinked,389/// Relationship insert/replace hooks will always be skipped390Skip,391}392393/// Wrapper for components clone specialization using autoderef.394#[doc(hidden)]395pub struct RelationshipCloneBehaviorSpecialization<T>(PhantomData<T>);396397impl<T> Default for RelationshipCloneBehaviorSpecialization<T> {398fn default() -> Self {399Self(PhantomData)400}401}402403/// Base trait for relationship clone specialization using autoderef.404#[doc(hidden)]405pub trait RelationshipCloneBehaviorBase {406fn default_clone_behavior(&self) -> ComponentCloneBehavior;407}408409impl<C> RelationshipCloneBehaviorBase for RelationshipCloneBehaviorSpecialization<C> {410fn default_clone_behavior(&self) -> ComponentCloneBehavior {411// Relationships currently must have `Clone`/`Reflect`-based handler for cloning/moving logic to properly work.412ComponentCloneBehavior::Ignore413}414}415416/// Specialized trait for relationship clone specialization using autoderef.417#[doc(hidden)]418pub trait RelationshipCloneBehaviorViaReflect {419fn default_clone_behavior(&self) -> ComponentCloneBehavior;420}421422#[cfg(feature = "bevy_reflect")]423impl<C: Relationship + bevy_reflect::Reflect> RelationshipCloneBehaviorViaReflect424for &RelationshipCloneBehaviorSpecialization<C>425{426fn default_clone_behavior(&self) -> ComponentCloneBehavior {427ComponentCloneBehavior::reflect()428}429}430431/// Specialized trait for relationship clone specialization using autoderef.432#[doc(hidden)]433pub trait RelationshipCloneBehaviorViaClone {434fn default_clone_behavior(&self) -> ComponentCloneBehavior;435}436437impl<C: Relationship + Clone> RelationshipCloneBehaviorViaClone438for &&RelationshipCloneBehaviorSpecialization<C>439{440fn default_clone_behavior(&self) -> ComponentCloneBehavior {441ComponentCloneBehavior::clone::<C>()442}443}444445/// Specialized trait for relationship target clone specialization using autoderef.446#[doc(hidden)]447pub trait RelationshipTargetCloneBehaviorViaReflect {448fn default_clone_behavior(&self) -> ComponentCloneBehavior;449}450451#[cfg(feature = "bevy_reflect")]452impl<C: RelationshipTarget + bevy_reflect::Reflect + bevy_reflect::TypePath>453RelationshipTargetCloneBehaviorViaReflect for &&&RelationshipCloneBehaviorSpecialization<C>454{455fn default_clone_behavior(&self) -> ComponentCloneBehavior {456ComponentCloneBehavior::Custom(|source, context| {457if let Some(component) = source.read::<C>()458&& let Ok(mut cloned) = component.reflect_clone_and_take::<C>()459{460cloned.collection_mut_risky().clear();461clone_relationship_target(component, &mut cloned, context);462context.write_target_component(cloned);463}464})465}466}467468/// Specialized trait for relationship target clone specialization using autoderef.469#[doc(hidden)]470pub trait RelationshipTargetCloneBehaviorViaClone {471fn default_clone_behavior(&self) -> ComponentCloneBehavior;472}473474impl<C: RelationshipTarget + Clone> RelationshipTargetCloneBehaviorViaClone475for &&&&RelationshipCloneBehaviorSpecialization<C>476{477fn default_clone_behavior(&self) -> ComponentCloneBehavior {478ComponentCloneBehavior::Custom(|source, context| {479if let Some(component) = source.read::<C>() {480let mut cloned = component.clone();481cloned.collection_mut_risky().clear();482clone_relationship_target(component, &mut cloned, context);483context.write_target_component(cloned);484}485})486}487}488489/// We know there's no additional data on Children, so this handler is an optimization to avoid cloning the entire Collection.490#[doc(hidden)]491pub trait RelationshipTargetCloneBehaviorHierarchy {492fn default_clone_behavior(&self) -> ComponentCloneBehavior;493}494495impl RelationshipTargetCloneBehaviorHierarchy496for &&&&&RelationshipCloneBehaviorSpecialization<crate::hierarchy::Children>497{498fn default_clone_behavior(&self) -> ComponentCloneBehavior {499ComponentCloneBehavior::Custom(|source, context| {500if let Some(component) = source.read::<crate::hierarchy::Children>() {501let mut cloned = crate::hierarchy::Children::with_capacity(component.len());502clone_relationship_target(component, &mut cloned, context);503context.write_target_component(cloned);504}505})506}507}508509/// This enum describes a way to access the entities of [`Relationship`] and [`RelationshipTarget`] components510/// in a type-erased context.511#[derive(Debug, Clone, Copy)]512pub enum RelationshipAccessor {513/// This component is a [`Relationship`].514Relationship {515/// Offset of the field containing [`Entity`] from the base of the component.516///517/// Dynamic equivalent of [`Relationship::get`].518entity_field_offset: usize,519/// Value of [`RelationshipTarget::LINKED_SPAWN`] for the [`Relationship::RelationshipTarget`] of this [`Relationship`].520linked_spawn: bool,521},522/// This component is a [`RelationshipTarget`].523RelationshipTarget {524/// Function that returns an iterator over all [`Entity`]s of this [`RelationshipTarget`]'s collection.525///526/// Dynamic equivalent of [`RelationshipTarget::iter`].527/// # Safety528/// Passed pointer must point to the value of the same component as the one that this accessor was registered to.529iter: for<'a> unsafe fn(Ptr<'a>) -> Box<dyn Iterator<Item = Entity> + 'a>,530/// Value of [`RelationshipTarget::LINKED_SPAWN`] of this [`RelationshipTarget`].531linked_spawn: bool,532},533}534535/// A type-safe convenience wrapper over [`RelationshipAccessor`].536pub struct ComponentRelationshipAccessor<C: ?Sized> {537pub(crate) accessor: RelationshipAccessor,538phantom: PhantomData<C>,539}540541impl<C> ComponentRelationshipAccessor<C> {542/// Create a new [`ComponentRelationshipAccessor`] for a [`Relationship`] component.543/// # Safety544/// `entity_field_offset` should be the offset from the base of this component and point to a field that stores value of type [`Entity`].545/// This value can be obtained using the [`core::mem::offset_of`] macro.546pub unsafe fn relationship(entity_field_offset: usize) -> Self547where548C: Relationship,549{550Self {551accessor: RelationshipAccessor::Relationship {552entity_field_offset,553linked_spawn: C::RelationshipTarget::LINKED_SPAWN,554},555phantom: Default::default(),556}557}558559/// Create a new [`ComponentRelationshipAccessor`] for a [`RelationshipTarget`] component.560pub fn relationship_target() -> Self561where562C: RelationshipTarget,563{564Self {565accessor: RelationshipAccessor::RelationshipTarget {566// Safety: caller ensures that `ptr` is of type `C`.567iter: |ptr| unsafe { Box::new(RelationshipTarget::iter(ptr.deref::<C>())) },568linked_spawn: C::LINKED_SPAWN,569},570phantom: Default::default(),571}572}573}574575#[cfg(test)]576mod tests {577use core::marker::PhantomData;578579use crate::prelude::{ChildOf, Children};580use crate::relationship::{Relationship, RelationshipAccessor};581use crate::world::World;582use crate::{component::Component, entity::Entity};583use alloc::vec::Vec;584585#[test]586fn custom_relationship() {587#[derive(Component)]588#[relationship(relationship_target = LikedBy)]589struct Likes(pub Entity);590591#[derive(Component)]592#[relationship_target(relationship = Likes)]593struct LikedBy(Vec<Entity>);594595let mut world = World::new();596let a = world.spawn_empty().id();597let b = world.spawn(Likes(a)).id();598let c = world.spawn(Likes(a)).id();599assert_eq!(world.entity(a).get::<LikedBy>().unwrap().0, &[b, c]);600}601602#[test]603fn self_relationship_fails_by_default() {604#[derive(Component)]605#[relationship(relationship_target = RelTarget)]606struct Rel(Entity);607608#[derive(Component)]609#[relationship_target(relationship = Rel)]610struct RelTarget(Vec<Entity>);611612let mut world = World::new();613let a = world.spawn_empty().id();614world.entity_mut(a).insert(Rel(a));615assert!(!world.entity(a).contains::<Rel>());616assert!(!world.entity(a).contains::<RelTarget>());617}618619#[test]620fn self_relationship_succeeds_with_allow_self_referential() {621#[derive(Component)]622#[relationship(relationship_target = RelTarget, allow_self_referential)]623struct Rel(Entity);624625#[derive(Component)]626#[relationship_target(relationship = Rel)]627struct RelTarget(Vec<Entity>);628629let mut world = World::new();630let a = world.spawn_empty().id();631world.entity_mut(a).insert(Rel(a));632assert!(world.entity(a).contains::<Rel>());633assert!(world.entity(a).contains::<RelTarget>());634assert_eq!(world.entity(a).get::<Rel>().unwrap().get(), a);635assert_eq!(&*world.entity(a).get::<RelTarget>().unwrap().0, &[a]);636}637638#[test]639fn self_relationship_removal_with_allow_self_referential() {640#[derive(Component)]641#[relationship(relationship_target = RelTarget, allow_self_referential)]642struct Rel(Entity);643644#[derive(Component)]645#[relationship_target(relationship = Rel)]646struct RelTarget(Vec<Entity>);647648let mut world = World::new();649let a = world.spawn_empty().id();650world.entity_mut(a).insert(Rel(a));651assert!(world.entity(a).contains::<Rel>());652assert!(world.entity(a).contains::<RelTarget>());653654// Remove the relationship and verify cleanup655world.entity_mut(a).remove::<Rel>();656assert!(!world.entity(a).contains::<Rel>());657assert!(!world.entity(a).contains::<RelTarget>());658}659660#[test]661fn relationship_with_missing_target_fails() {662#[derive(Component)]663#[relationship(relationship_target = RelTarget)]664struct Rel(Entity);665666#[derive(Component)]667#[relationship_target(relationship = Rel)]668struct RelTarget(Vec<Entity>);669670let mut world = World::new();671let a = world.spawn_empty().id();672world.despawn(a);673let b = world.spawn(Rel(a)).id();674assert!(!world.entity(b).contains::<Rel>());675assert!(!world.entity(b).contains::<RelTarget>());676}677678#[test]679fn relationship_with_multiple_non_target_fields_compiles() {680#[expect(681dead_code,682reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."683)]684#[derive(Component)]685#[relationship(relationship_target=Target)]686struct Source {687#[relationship]688target: Entity,689foo: u8,690bar: u8,691}692693#[expect(694dead_code,695reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."696)]697#[derive(Component)]698#[relationship_target(relationship=Source)]699struct Target(Vec<Entity>);700701// No assert necessary, looking to make sure compilation works with the macros702}703#[test]704fn relationship_target_with_multiple_non_target_fields_compiles() {705#[expect(706dead_code,707reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."708)]709#[derive(Component)]710#[relationship(relationship_target=Target)]711struct Source(Entity);712713#[expect(714dead_code,715reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."716)]717#[derive(Component)]718#[relationship_target(relationship=Source)]719struct Target {720#[relationship]721target: Vec<Entity>,722foo: u8,723bar: u8,724}725726// No assert necessary, looking to make sure compilation works with the macros727}728729#[test]730fn relationship_with_multiple_unnamed_non_target_fields_compiles() {731#[expect(732dead_code,733reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."734)]735#[derive(Component)]736#[relationship(relationship_target=Target<T>)]737struct Source<T: Send + Sync + 'static>(#[relationship] Entity, PhantomData<T>);738739#[expect(740dead_code,741reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."742)]743#[derive(Component)]744#[relationship_target(relationship=Source<T>)]745struct Target<T: Send + Sync + 'static>(#[relationship] Vec<Entity>, PhantomData<T>);746747// No assert necessary, looking to make sure compilation works with the macros748}749750#[test]751fn parent_child_relationship_with_custom_relationship() {752#[derive(Component)]753#[relationship(relationship_target = RelTarget)]754struct Rel(Entity);755756#[derive(Component)]757#[relationship_target(relationship = Rel)]758struct RelTarget(Entity);759760let mut world = World::new();761762// Rel on Parent763// Despawn Parent764let mut commands = world.commands();765let child = commands.spawn_empty().id();766let parent = commands.spawn(Rel(child)).add_child(child).id();767commands.entity(parent).despawn();768world.flush();769770assert!(world.get_entity(child).is_err());771assert!(world.get_entity(parent).is_err());772773// Rel on Parent774// Despawn Child775let mut commands = world.commands();776let child = commands.spawn_empty().id();777let parent = commands.spawn(Rel(child)).add_child(child).id();778commands.entity(child).despawn();779world.flush();780781assert!(world.get_entity(child).is_err());782assert!(!world.entity(parent).contains::<Rel>());783784// Rel on Child785// Despawn Parent786let mut commands = world.commands();787let parent = commands.spawn_empty().id();788let child = commands.spawn((ChildOf(parent), Rel(parent))).id();789commands.entity(parent).despawn();790world.flush();791792assert!(world.get_entity(child).is_err());793assert!(world.get_entity(parent).is_err());794795// Rel on Child796// Despawn Child797let mut commands = world.commands();798let parent = commands.spawn_empty().id();799let child = commands.spawn((ChildOf(parent), Rel(parent))).id();800commands.entity(child).despawn();801world.flush();802803assert!(world.get_entity(child).is_err());804assert!(!world.entity(parent).contains::<RelTarget>());805}806807#[test]808fn spawn_batch_with_relationship() {809let mut world = World::new();810let parent = world.spawn_empty().id();811let children = world812.spawn_batch((0..10).map(|_| ChildOf(parent)))813.collect::<Vec<_>>();814815for &child in &children {816assert!(world817.get::<ChildOf>(child)818.is_some_and(|child_of| child_of.parent() == parent));819}820assert!(world821.get::<Children>(parent)822.is_some_and(|children| children.len() == 10));823}824825#[test]826fn insert_batch_with_relationship() {827let mut world = World::new();828let parent = world.spawn_empty().id();829let child = world.spawn_empty().id();830world.insert_batch([(child, ChildOf(parent))]);831world.flush();832833assert!(world.get::<ChildOf>(child).is_some());834assert!(world.get::<Children>(parent).is_some());835}836837#[test]838fn dynamically_traverse_hierarchy() {839let mut world = World::new();840let child_of_id = world.register_component::<ChildOf>();841let children_id = world.register_component::<Children>();842843let parent = world.spawn_empty().id();844let child = world.spawn_empty().id();845world.entity_mut(child).insert(ChildOf(parent));846world.flush();847848let children_ptr = world.get_by_id(parent, children_id).unwrap();849let RelationshipAccessor::RelationshipTarget { iter, .. } = world850.components()851.get_info(children_id)852.unwrap()853.relationship_accessor()854.unwrap()855else {856unreachable!()857};858// Safety: `children_ptr` contains value of the same type as the one this accessor was registered for.859let children: Vec<_> = unsafe { iter(children_ptr).collect() };860assert_eq!(children, alloc::vec![child]);861862let child_of_ptr = world.get_by_id(child, child_of_id).unwrap();863let RelationshipAccessor::Relationship {864entity_field_offset,865..866} = world867.components()868.get_info(child_of_id)869.unwrap()870.relationship_accessor()871.unwrap()872else {873unreachable!()874};875// Safety:876// - offset is in bounds, aligned and has the same lifetime as the original pointer.877// - value at offset is guaranteed to be a valid Entity878let child_of_entity: Entity =879unsafe { *child_of_ptr.byte_add(*entity_field_offset).deref() };880assert_eq!(child_of_entity, parent);881}882}883884885