Path: blob/main/crates/bevy_ecs/src/relationship/related_methods.rs
6600 views
use crate::{1bundle::Bundle,2entity::{hash_set::EntityHashSet, Entity},3prelude::Children,4relationship::{5Relationship, RelationshipHookMode, RelationshipSourceCollection, RelationshipTarget,6},7system::{Commands, EntityCommands},8world::{DeferredWorld, EntityWorldMut, World},9};10use bevy_platform::prelude::{Box, Vec};11use core::{marker::PhantomData, mem};1213use super::OrderedRelationshipSourceCollection;1415impl<'w> EntityWorldMut<'w> {16/// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle17pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {18let parent = self.id();19self.world_scope(|world| {20world.spawn((bundle, R::from(parent)));21});22self23}2425/// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].26pub fn with_related_entities<R: Relationship>(27&mut self,28func: impl FnOnce(&mut RelatedSpawner<R>),29) -> &mut Self {30let parent = self.id();31self.world_scope(|world| {32func(&mut RelatedSpawner::new(world, parent));33});34self35}3637/// Relates the given entities to this entity with the relation `R`.38///39/// See [`add_one_related`](Self::add_one_related) if you want relate only one entity.40pub fn add_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {41let id = self.id();42self.world_scope(|world| {43for related in related {44world45.entity_mut(*related)46.modify_or_insert_relation_with_relationship_hook_mode::<R>(47id,48RelationshipHookMode::Run,49);50}51});52self53}5455/// Removes the relation `R` between this entity and all its related entities.56pub fn clear_related<R: Relationship>(&mut self) -> &mut Self {57self.remove::<R::RelationshipTarget>()58}5960/// Relates the given entities to this entity with the relation `R`, starting at this particular index.61///62/// If the `related` has duplicates, a related entity will take the index of its last occurrence in `related`.63/// If the indices go out of bounds, they will be clamped into bounds.64/// This will not re-order existing related entities unless they are in `related`.65///66/// # Example67///68/// ```69/// use bevy_ecs::prelude::*;70///71/// let mut world = World::new();72/// let e0 = world.spawn_empty().id();73/// let e1 = world.spawn_empty().id();74/// let e2 = world.spawn_empty().id();75/// let e3 = world.spawn_empty().id();76/// let e4 = world.spawn_empty().id();77///78/// let mut main_entity = world.spawn_empty();79/// main_entity.add_related::<ChildOf>(&[e0, e1, e2, e2]);80/// main_entity.insert_related::<ChildOf>(1, &[e0, e3, e4, e4]);81/// let main_id = main_entity.id();82///83/// let relationship_source = main_entity.get::<Children>().unwrap().collection();84/// assert_eq!(relationship_source, &[e1, e0, e3, e2, e4]);85/// ```86pub fn insert_related<R: Relationship>(&mut self, index: usize, related: &[Entity]) -> &mut Self87where88<R::RelationshipTarget as RelationshipTarget>::Collection:89OrderedRelationshipSourceCollection,90{91let id = self.id();92self.world_scope(|world| {93for (offset, related) in related.iter().enumerate() {94let index = index.saturating_add(offset);95if world96.get::<R>(*related)97.is_some_and(|relationship| relationship.get() == id)98{99world100.get_mut::<R::RelationshipTarget>(id)101.expect("hooks should have added relationship target")102.collection_mut_risky()103.place(*related, index);104} else {105world106.entity_mut(*related)107.modify_or_insert_relation_with_relationship_hook_mode::<R>(108id,109RelationshipHookMode::Run,110);111world112.get_mut::<R::RelationshipTarget>(id)113.expect("hooks should have added relationship target")114.collection_mut_risky()115.place_most_recent(index);116}117}118});119120self121}122123/// Removes the relation `R` between this entity and the given entities.124pub fn remove_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {125let id = self.id();126self.world_scope(|world| {127for related in related {128if world129.get::<R>(*related)130.is_some_and(|relationship| relationship.get() == id)131{132world.entity_mut(*related).remove::<R>();133}134}135});136137self138}139140/// Replaces all the related entities with a new set of entities.141pub fn replace_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {142type Collection<R> =143<<R as Relationship>::RelationshipTarget as RelationshipTarget>::Collection;144145if related.is_empty() {146self.remove::<R::RelationshipTarget>();147148return self;149}150151let Some(existing_relations) = self.get_mut::<R::RelationshipTarget>() else {152return self.add_related::<R>(related);153};154155// We replace the component here with a dummy value so we can modify it without taking it (this would create archetype move).156// SAFETY: We eventually return the correctly initialized collection into the target.157let mut relations = mem::replace(158existing_relations.into_inner(),159<R as Relationship>::RelationshipTarget::from_collection_risky(160Collection::<R>::with_capacity(0),161),162);163164let collection = relations.collection_mut_risky();165166let mut potential_relations = EntityHashSet::from_iter(related.iter().copied());167168let id = self.id();169self.world_scope(|world| {170for related in collection.iter() {171if !potential_relations.remove(related) {172world.entity_mut(related).remove::<R>();173}174}175176for related in potential_relations {177// SAFETY: We'll manually be adjusting the contents of the `RelationshipTarget` to fit the final state.178world179.entity_mut(related)180.modify_or_insert_relation_with_relationship_hook_mode::<R>(181id,182RelationshipHookMode::Skip,183);184}185});186187// SAFETY: The entities we're inserting will be the entities that were either already there or entities that we've just inserted.188collection.clear();189collection.extend_from_iter(related.iter().copied());190self.insert(relations);191192self193}194195/// Replaces all the related entities with a new set of entities.196///197/// This is a more efficient of [`Self::replace_related`] which doesn't allocate.198/// The passed in arguments must adhere to these invariants:199/// - `entities_to_unrelate`: A slice of entities to remove from the relationship source.200/// Entities need not be related to this entity, but must not appear in `entities_to_relate`201/// - `entities_to_relate`: A slice of entities to relate to this entity.202/// This must contain all entities that will remain related (i.e. not those in `entities_to_unrelate`) plus the newly related entities.203/// - `newly_related_entities`: A subset of `entities_to_relate` containing only entities not already related to this entity.204/// - Slices **must not** contain any duplicates205///206/// # Warning207///208/// Violating these invariants may lead to panics, crashes or unpredictable engine behavior.209///210/// # Panics211///212/// Panics when debug assertions are enabled and any invariants are broken.213///214// TODO: Consider making these iterators so users aren't required to allocate a separate buffers for the different slices.215pub fn replace_related_with_difference<R: Relationship>(216&mut self,217entities_to_unrelate: &[Entity],218entities_to_relate: &[Entity],219newly_related_entities: &[Entity],220) -> &mut Self {221#[cfg(debug_assertions)]222{223let entities_to_relate = EntityHashSet::from_iter(entities_to_relate.iter().copied());224let entities_to_unrelate =225EntityHashSet::from_iter(entities_to_unrelate.iter().copied());226let mut newly_related_entities =227EntityHashSet::from_iter(newly_related_entities.iter().copied());228assert!(229entities_to_relate.is_disjoint(&entities_to_unrelate),230"`entities_to_relate` ({entities_to_relate:?}) shared entities with `entities_to_unrelate` ({entities_to_unrelate:?})"231);232assert!(233newly_related_entities.is_disjoint(&entities_to_unrelate),234"`newly_related_entities` ({newly_related_entities:?}) shared entities with `entities_to_unrelate ({entities_to_unrelate:?})`"235);236assert!(237newly_related_entities.is_subset(&entities_to_relate),238"`newly_related_entities` ({newly_related_entities:?}) wasn't a subset of `entities_to_relate` ({entities_to_relate:?})"239);240241if let Some(target) = self.get::<R::RelationshipTarget>() {242let existing_relationships: EntityHashSet = target.collection().iter().collect();243244assert!(245existing_relationships.is_disjoint(&newly_related_entities),246"`newly_related_entities` contains an entity that wouldn't be newly related"247);248249newly_related_entities.extend(existing_relationships);250newly_related_entities -= &entities_to_unrelate;251}252253assert_eq!(newly_related_entities, entities_to_relate, "`entities_to_relate` ({entities_to_relate:?}) didn't contain all entities that would end up related");254};255256match self.get_mut::<R::RelationshipTarget>() {257None => {258self.add_related::<R>(entities_to_relate);259260return self;261}262Some(mut target) => {263// SAFETY: The invariants expected by this function mean we'll only be inserting entities that are already related.264let collection = target.collection_mut_risky();265collection.clear();266267collection.extend_from_iter(entities_to_relate.iter().copied());268}269}270271let this = self.id();272self.world_scope(|world| {273for unrelate in entities_to_unrelate {274world.entity_mut(*unrelate).remove::<R>();275}276277for new_relation in newly_related_entities {278// We changed the target collection manually so don't run the insert hook279world280.entity_mut(*new_relation)281.modify_or_insert_relation_with_relationship_hook_mode::<R>(282this,283RelationshipHookMode::Skip,284);285}286});287288self289}290291/// Relates the given entity to this with the relation `R`.292///293/// See [`add_related`](Self::add_related) if you want to relate more than one entity.294pub fn add_one_related<R: Relationship>(&mut self, entity: Entity) -> &mut Self {295self.add_related::<R>(&[entity])296}297298/// Despawns entities that relate to this one via the given [`RelationshipTarget`].299/// This entity will not be despawned.300pub fn despawn_related<S: RelationshipTarget>(&mut self) -> &mut Self {301if let Some(sources) = self.get::<S>() {302// We have to collect here to defer removal, allowing observers and hooks to see this data303// before it is finally removed.304let sources = sources.iter().collect::<Vec<_>>();305self.world_scope(|world| {306for entity in sources {307if let Ok(entity_mut) = world.get_entity_mut(entity) {308entity_mut.despawn();309};310}311});312}313self314}315316/// Despawns the children of this entity.317/// This entity will not be despawned.318///319/// This is a specialization of [`despawn_related`](EntityWorldMut::despawn_related), a more general method for despawning via relationships.320pub fn despawn_children(&mut self) -> &mut Self {321self.despawn_related::<Children>();322self323}324325/// Inserts a component or bundle of components into the entity and all related entities,326/// traversing the relationship tracked in `S` in a breadth-first manner.327///328/// # Warning329///330/// This method should only be called on relationships that form a tree-like structure.331/// Any cycles will cause this method to loop infinitely.332// We could keep track of a list of visited entities and track cycles,333// but this is not a very well-defined operation (or hard to write) for arbitrary relationships.334pub fn insert_recursive<S: RelationshipTarget>(335&mut self,336bundle: impl Bundle + Clone,337) -> &mut Self {338self.insert(bundle.clone());339if let Some(relationship_target) = self.get::<S>() {340let related_vec: Vec<Entity> = relationship_target.iter().collect();341for related in related_vec {342self.world_scope(|world| {343world344.entity_mut(related)345.insert_recursive::<S>(bundle.clone());346});347}348}349350self351}352353/// Removes a component or bundle of components of type `B` from the entity and all related entities,354/// traversing the relationship tracked in `S` in a breadth-first manner.355///356/// # Warning357///358/// This method should only be called on relationships that form a tree-like structure.359/// Any cycles will cause this method to loop infinitely.360pub fn remove_recursive<S: RelationshipTarget, B: Bundle>(&mut self) -> &mut Self {361self.remove::<B>();362if let Some(relationship_target) = self.get::<S>() {363let related_vec: Vec<Entity> = relationship_target.iter().collect();364for related in related_vec {365self.world_scope(|world| {366world.entity_mut(related).remove_recursive::<S, B>();367});368}369}370371self372}373374fn modify_or_insert_relation_with_relationship_hook_mode<R: Relationship>(375&mut self,376entity: Entity,377relationship_hook_mode: RelationshipHookMode,378) {379// Check if the relation edge holds additional data380if size_of::<R>() > size_of::<Entity>() {381self.assert_not_despawned();382383let this = self.id();384385let modified = self.world_scope(|world| {386let modified = DeferredWorld::from(&mut *world)387.modify_component_with_relationship_hook_mode::<R, _>(388this,389relationship_hook_mode,390|r| r.set_risky(entity),391)392.expect("entity access must be valid")393.is_some();394395world.flush();396397modified398});399400if modified {401return;402}403}404405self.insert_with_relationship_hook_mode(R::from(entity), relationship_hook_mode);406}407}408409impl<'a> EntityCommands<'a> {410/// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle411pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {412let parent = self.id();413self.commands.spawn((bundle, R::from(parent)));414self415}416417/// Spawns entities related to this entity (with the `R` relationship) by taking a function that operates on a [`RelatedSpawner`].418pub fn with_related_entities<R: Relationship>(419&mut self,420func: impl FnOnce(&mut RelatedSpawnerCommands<R>),421) -> &mut Self {422let id = self.id();423func(&mut RelatedSpawnerCommands::new(self.commands(), id));424self425}426427/// Relates the given entities to this entity with the relation `R`.428///429/// See [`add_one_related`](Self::add_one_related) if you want relate only one entity.430pub fn add_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {431let related: Box<[Entity]> = related.into();432433self.queue(move |mut entity: EntityWorldMut| {434entity.add_related::<R>(&related);435})436}437438/// Removes the relation `R` between this entity and all its related entities.439pub fn clear_related<R: Relationship>(&mut self) -> &mut Self {440self.queue(|mut entity: EntityWorldMut| {441entity.clear_related::<R>();442})443}444445/// Relates the given entities to this entity with the relation `R`, starting at this particular index.446///447/// If the `related` has duplicates, a related entity will take the index of its last occurrence in `related`.448/// If the indices go out of bounds, they will be clamped into bounds.449/// This will not re-order existing related entities unless they are in `related`.450pub fn insert_related<R: Relationship>(&mut self, index: usize, related: &[Entity]) -> &mut Self451where452<R::RelationshipTarget as RelationshipTarget>::Collection:453OrderedRelationshipSourceCollection,454{455let related: Box<[Entity]> = related.into();456457self.queue(move |mut entity: EntityWorldMut| {458entity.insert_related::<R>(index, &related);459})460}461462/// Relates the given entity to this with the relation `R`.463///464/// See [`add_related`](Self::add_related) if you want to relate more than one entity.465pub fn add_one_related<R: Relationship>(&mut self, entity: Entity) -> &mut Self {466self.add_related::<R>(&[entity])467}468469/// Removes the relation `R` between this entity and the given entities.470pub fn remove_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {471let related: Box<[Entity]> = related.into();472473self.queue(move |mut entity: EntityWorldMut| {474entity.remove_related::<R>(&related);475})476}477478/// Replaces all the related entities with the given set of new related entities.479pub fn replace_related<R: Relationship>(&mut self, related: &[Entity]) -> &mut Self {480let related: Box<[Entity]> = related.into();481482self.queue(move |mut entity: EntityWorldMut| {483entity.replace_related::<R>(&related);484})485}486487/// Replaces all the related entities with a new set of entities.488///489/// # Warning490///491/// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.492/// Refer to [`EntityWorldMut::replace_related_with_difference`] for a list of these invariants.493///494/// # Panics495///496/// Panics when debug assertions are enable, an invariant is are broken and the command is executed.497pub fn replace_related_with_difference<R: Relationship>(498&mut self,499entities_to_unrelate: &[Entity],500entities_to_relate: &[Entity],501newly_related_entities: &[Entity],502) -> &mut Self {503let entities_to_unrelate: Box<[Entity]> = entities_to_unrelate.into();504let entities_to_relate: Box<[Entity]> = entities_to_relate.into();505let newly_related_entities: Box<[Entity]> = newly_related_entities.into();506507self.queue(move |mut entity: EntityWorldMut| {508entity.replace_related_with_difference::<R>(509&entities_to_unrelate,510&entities_to_relate,511&newly_related_entities,512);513})514}515516/// Despawns entities that relate to this one via the given [`RelationshipTarget`].517/// This entity will not be despawned.518pub fn despawn_related<S: RelationshipTarget>(&mut self) -> &mut Self {519self.queue(move |mut entity: EntityWorldMut| {520entity.despawn_related::<S>();521})522}523524/// Despawns the children of this entity.525/// This entity will not be despawned.526///527/// This is a specialization of [`despawn_related`](EntityCommands::despawn_related), a more general method for despawning via relationships.528pub fn despawn_children(&mut self) -> &mut Self {529self.despawn_related::<Children>()530}531532/// Inserts a component or bundle of components into the entity and all related entities,533/// traversing the relationship tracked in `S` in a breadth-first manner.534///535/// # Warning536///537/// This method should only be called on relationships that form a tree-like structure.538/// Any cycles will cause this method to loop infinitely.539pub fn insert_recursive<S: RelationshipTarget>(540&mut self,541bundle: impl Bundle + Clone,542) -> &mut Self {543self.queue(move |mut entity: EntityWorldMut| {544entity.insert_recursive::<S>(bundle);545})546}547548/// Removes a component or bundle of components of type `B` from the entity and all related entities,549/// traversing the relationship tracked in `S` in a breadth-first manner.550///551/// # Warning552///553/// This method should only be called on relationships that form a tree-like structure.554/// Any cycles will cause this method to loop infinitely.555pub fn remove_recursive<S: RelationshipTarget, B: Bundle>(&mut self) -> &mut Self {556self.queue(move |mut entity: EntityWorldMut| {557entity.remove_recursive::<S, B>();558})559}560}561562/// Directly spawns related "source" entities with the given [`Relationship`], targeting563/// a specific entity.564pub struct RelatedSpawner<'w, R: Relationship> {565target: Entity,566world: &'w mut World,567_marker: PhantomData<R>,568}569570impl<'w, R: Relationship> RelatedSpawner<'w, R> {571/// Creates a new instance that will spawn entities targeting the `target` entity.572pub fn new(world: &'w mut World, target: Entity) -> Self {573Self {574world,575target,576_marker: PhantomData,577}578}579580/// Spawns an entity with the given `bundle` and an `R` relationship targeting the `target`581/// entity this spawner was initialized with.582pub fn spawn(&mut self, bundle: impl Bundle) -> EntityWorldMut<'_> {583self.world.spawn((R::from(self.target), bundle))584}585586/// Spawns an entity with an `R` relationship targeting the `target`587/// entity this spawner was initialized with.588pub fn spawn_empty(&mut self) -> EntityWorldMut<'_> {589self.world.spawn(R::from(self.target))590}591592/// Returns the "target entity" used when spawning entities with an `R` [`Relationship`].593pub fn target_entity(&self) -> Entity {594self.target595}596597/// Returns a reference to the underlying [`World`].598pub fn world(&self) -> &World {599self.world600}601602/// Returns a mutable reference to the underlying [`World`].603pub fn world_mut(&mut self) -> &mut World {604self.world605}606}607608/// Uses commands to spawn related "source" entities with the given [`Relationship`], targeting609/// a specific entity.610pub struct RelatedSpawnerCommands<'w, R: Relationship> {611target: Entity,612commands: Commands<'w, 'w>,613_marker: PhantomData<R>,614}615616impl<'w, R: Relationship> RelatedSpawnerCommands<'w, R> {617/// Creates a new instance that will spawn entities targeting the `target` entity.618pub fn new(commands: Commands<'w, 'w>, target: Entity) -> Self {619Self {620commands,621target,622_marker: PhantomData,623}624}625626/// Spawns an entity with the given `bundle` and an `R` relationship targeting the `target`627/// entity this spawner was initialized with.628pub fn spawn(&mut self, bundle: impl Bundle) -> EntityCommands<'_> {629self.commands.spawn((R::from(self.target), bundle))630}631632/// Spawns an entity with an `R` relationship targeting the `target`633/// entity this spawner was initialized with.634pub fn spawn_empty(&mut self) -> EntityCommands<'_> {635self.commands.spawn(R::from(self.target))636}637638/// Returns the "target entity" used when spawning entities with an `R` [`Relationship`].639pub fn target_entity(&self) -> Entity {640self.target641}642643/// Returns the underlying [`Commands`].644pub fn commands(&mut self) -> Commands<'_, '_> {645self.commands.reborrow()646}647648/// Returns a mutable reference to the underlying [`Commands`].649pub fn commands_mut(&mut self) -> &mut Commands<'w, 'w> {650&mut self.commands651}652}653654#[cfg(test)]655mod tests {656use super::*;657use crate::prelude::{ChildOf, Children, Component};658659#[derive(Component, Clone, Copy)]660struct TestComponent;661662#[test]663fn insert_and_remove_recursive() {664let mut world = World::new();665666let a = world.spawn_empty().id();667let b = world.spawn(ChildOf(a)).id();668let c = world.spawn(ChildOf(a)).id();669let d = world.spawn(ChildOf(b)).id();670671world672.entity_mut(a)673.insert_recursive::<Children>(TestComponent);674675for entity in [a, b, c, d] {676assert!(world.entity(entity).contains::<TestComponent>());677}678679world680.entity_mut(b)681.remove_recursive::<Children, TestComponent>();682683// Parent684assert!(world.entity(a).contains::<TestComponent>());685// Target686assert!(!world.entity(b).contains::<TestComponent>());687// Sibling688assert!(world.entity(c).contains::<TestComponent>());689// Child690assert!(!world.entity(d).contains::<TestComponent>());691692world693.entity_mut(a)694.remove_recursive::<Children, TestComponent>();695696for entity in [a, b, c, d] {697assert!(!world.entity(entity).contains::<TestComponent>());698}699}700701#[test]702fn remove_all_related() {703let mut world = World::new();704705let a = world.spawn_empty().id();706let b = world.spawn(ChildOf(a)).id();707let c = world.spawn(ChildOf(a)).id();708709world.entity_mut(a).clear_related::<ChildOf>();710711assert_eq!(world.entity(a).get::<Children>(), None);712assert_eq!(world.entity(b).get::<ChildOf>(), None);713assert_eq!(world.entity(c).get::<ChildOf>(), None);714}715716#[test]717fn replace_related_works() {718let mut world = World::new();719let child1 = world.spawn_empty().id();720let child2 = world.spawn_empty().id();721let child3 = world.spawn_empty().id();722723let mut parent = world.spawn_empty();724parent.add_children(&[child1, child2]);725let child_value = ChildOf(parent.id());726let some_child = Some(&child_value);727728parent.replace_children(&[child2, child3]);729let children = parent.get::<Children>().unwrap().collection();730assert_eq!(children, &[child2, child3]);731assert_eq!(parent.world().get::<ChildOf>(child1), None);732assert_eq!(parent.world().get::<ChildOf>(child2), some_child);733assert_eq!(parent.world().get::<ChildOf>(child3), some_child);734735parent.replace_children_with_difference(&[child3], &[child1, child2], &[child1]);736let children = parent.get::<Children>().unwrap().collection();737assert_eq!(children, &[child1, child2]);738assert_eq!(parent.world().get::<ChildOf>(child1), some_child);739assert_eq!(parent.world().get::<ChildOf>(child2), some_child);740assert_eq!(parent.world().get::<ChildOf>(child3), None);741}742743#[test]744fn add_related_keeps_relationship_data() {745#[derive(Component, PartialEq, Debug)]746#[relationship(relationship_target = Parent)]747struct Child {748#[relationship]749parent: Entity,750data: u8,751}752753#[derive(Component)]754#[relationship_target(relationship = Child)]755struct Parent(Vec<Entity>);756757let mut world = World::new();758let parent1 = world.spawn_empty().id();759let parent2 = world.spawn_empty().id();760let child = world761.spawn(Child {762parent: parent1,763data: 42,764})765.id();766767world.entity_mut(parent2).add_related::<Child>(&[child]);768assert_eq!(769world.get::<Child>(child),770Some(&Child {771parent: parent2,772data: 42773})774);775}776777#[test]778fn insert_related_keeps_relationship_data() {779#[derive(Component, PartialEq, Debug)]780#[relationship(relationship_target = Parent)]781struct Child {782#[relationship]783parent: Entity,784data: u8,785}786787#[derive(Component)]788#[relationship_target(relationship = Child)]789struct Parent(Vec<Entity>);790791let mut world = World::new();792let parent1 = world.spawn_empty().id();793let parent2 = world.spawn_empty().id();794let child = world795.spawn(Child {796parent: parent1,797data: 42,798})799.id();800801world802.entity_mut(parent2)803.insert_related::<Child>(0, &[child]);804assert_eq!(805world.get::<Child>(child),806Some(&Child {807parent: parent2,808data: 42809})810);811}812813#[test]814fn replace_related_keeps_relationship_data() {815#[derive(Component, PartialEq, Debug)]816#[relationship(relationship_target = Parent)]817struct Child {818#[relationship]819parent: Entity,820data: u8,821}822823#[derive(Component)]824#[relationship_target(relationship = Child)]825struct Parent(Vec<Entity>);826827let mut world = World::new();828let parent1 = world.spawn_empty().id();829let parent2 = world.spawn_empty().id();830let child = world831.spawn(Child {832parent: parent1,833data: 42,834})835.id();836837world838.entity_mut(parent2)839.replace_related_with_difference::<Child>(&[], &[child], &[child]);840assert_eq!(841world.get::<Child>(child),842Some(&Child {843parent: parent2,844data: 42845})846);847848world.entity_mut(parent1).replace_related::<Child>(&[child]);849assert_eq!(850world.get::<Child>(child),851Some(&Child {852parent: parent1,853data: 42854})855);856}857858#[test]859fn replace_related_keeps_relationship_target_data() {860#[derive(Component)]861#[relationship(relationship_target = Parent)]862struct Child(Entity);863864#[derive(Component)]865#[relationship_target(relationship = Child)]866struct Parent {867#[relationship]868children: Vec<Entity>,869data: u8,870}871872let mut world = World::new();873let child1 = world.spawn_empty().id();874let child2 = world.spawn_empty().id();875let mut parent = world.spawn_empty();876parent.add_related::<Child>(&[child1]);877parent.get_mut::<Parent>().unwrap().data = 42;878879parent.replace_related_with_difference::<Child>(&[child1], &[child2], &[child2]);880let data = parent.get::<Parent>().unwrap().data;881assert_eq!(data, 42);882883parent.replace_related::<Child>(&[child1]);884let data = parent.get::<Parent>().unwrap().data;885assert_eq!(data, 42);886}887888#[test]889fn despawn_related_observers_can_access_relationship_data() {890use crate::lifecycle::Replace;891use crate::observer::On;892use crate::prelude::Has;893use crate::system::Query;894895#[derive(Component)]896struct MyComponent;897898#[derive(Component, Default)]899struct ObserverResult {900success: bool,901}902903let mut world = World::new();904let result_entity = world.spawn(ObserverResult::default()).id();905906world.add_observer(907move |event: On<Replace, MyComponent>,908has_relationship: Query<Has<ChildOf>>,909mut results: Query<&mut ObserverResult>| {910let entity = event.entity();911if has_relationship.get(entity).unwrap_or(false) {912results.get_mut(result_entity).unwrap().success = true;913}914},915);916917let parent = world.spawn_empty().id();918let _child = world.spawn((MyComponent, ChildOf(parent))).id();919920world.entity_mut(parent).despawn_related::<Children>();921922assert!(world.get::<ObserverResult>(result_entity).unwrap().success);923}924}925926927