diff --git a/benches/benches/bevy_ecs/world/despawn_recursive.rs b/benches/benches/bevy_ecs/world/despawn_recursive.rs index 482086ab17444..db2ba631c2d01 100644 --- a/benches/benches/bevy_ecs/world/despawn_recursive.rs +++ b/benches/benches/bevy_ecs/world/despawn_recursive.rs @@ -1,7 +1,5 @@ use bevy_ecs::prelude::*; -use bevy_hierarchy::despawn_with_children_recursive; -use bevy_hierarchy::BuildChildren; -use bevy_hierarchy::ChildBuild; +use bevy_hierarchy::{Children, DespawnRecursive, BuildChildren, ChildBuild}; use criterion::Criterion; use glam::*; @@ -29,7 +27,7 @@ pub fn world_despawn_recursive(criterion: &mut Criterion) { group.bench_function(format!("{}_entities", entity_count), |bencher| { bencher.iter(|| { ents.iter().for_each(|e| { - despawn_with_children_recursive(&mut world, *e, true); + DespawnRecursive::::new(*e).apply(&mut world); }); }); }); diff --git a/crates/bevy_hierarchy/src/child_builder.rs b/crates/bevy_hierarchy/src/child_builder.rs index ad5819479d049..6b1b9826a99ee 100644 --- a/crates/bevy_hierarchy/src/child_builder.rs +++ b/crates/bevy_hierarchy/src/child_builder.rs @@ -1,163 +1,11 @@ -use crate::{Children, HierarchyEvent, Parent}; +use crate::{Children, Parent}; use bevy_ecs::{ bundle::Bundle, entity::Entity, - prelude::Events, system::{Commands, EntityCommands}, world::{Command, EntityWorldMut, World}, }; -use smallvec::{smallvec, SmallVec}; - -// Do not use `world.send_event_batch` as it prints error message when the Events are not available in the world, -// even though it's a valid use case to execute commands on a world without events. Loading a GLTF file for example -fn push_events(world: &mut World, events: impl IntoIterator) { - if let Some(mut moved) = world.get_resource_mut::>() { - moved.extend(events); - } -} - -/// Adds `child` to `parent`'s [`Children`], without checking if it is already present there. -/// -/// This might cause unexpected results when removing duplicate children. -fn add_child_unchecked(world: &mut World, parent: Entity, child: Entity) { - let mut parent = world.entity_mut(parent); - if let Some(mut children) = parent.get_mut::() { - children.0.push(child); - } else { - parent.insert(Children(smallvec![child])); - } -} - -/// Sets [`Parent`] of the `child` to `new_parent`. Inserts [`Parent`] if `child` doesn't have one. -fn update_parent(world: &mut World, child: Entity, new_parent: Entity) -> Option { - let mut child = world.entity_mut(child); - if let Some(mut parent) = child.get_mut::() { - let previous = parent.0; - *parent = Parent(new_parent); - Some(previous) - } else { - child.insert(Parent(new_parent)); - None - } -} - -/// Remove child from the parent's [`Children`] component. -/// -/// Removes the [`Children`] component from the parent if it's empty. -fn remove_from_children(world: &mut World, parent: Entity, child: Entity) { - let Ok(mut parent) = world.get_entity_mut(parent) else { - return; - }; - let Some(mut children) = parent.get_mut::() else { - return; - }; - children.0.retain(|x| *x != child); - if children.is_empty() { - parent.remove::(); - } -} - -/// Update the [`Parent`] component of the `child`. -/// Removes the `child` from the previous parent's [`Children`]. -/// -/// Does not update the new parents [`Children`] component. -/// -/// Does nothing if `child` was already a child of `parent`. -/// -/// Sends [`HierarchyEvent`]'s. -fn update_old_parent(world: &mut World, child: Entity, parent: Entity) { - let previous = update_parent(world, child, parent); - if let Some(previous_parent) = previous { - // Do nothing if the child was already parented to this entity. - if previous_parent == parent { - return; - } - remove_from_children(world, previous_parent, child); - - push_events( - world, - [HierarchyEvent::ChildMoved { - child, - previous_parent, - new_parent: parent, - }], - ); - } else { - push_events(world, [HierarchyEvent::ChildAdded { child, parent }]); - } -} - -/// Update the [`Parent`] components of the `children`. -/// Removes the `children` from their previous parent's [`Children`]. -/// -/// Does not update the new parents [`Children`] component. -/// -/// Does nothing for a child if it was already a child of `parent`. -/// -/// Sends [`HierarchyEvent`]'s. -fn update_old_parents(world: &mut World, parent: Entity, children: &[Entity]) { - let mut events: SmallVec<[HierarchyEvent; 8]> = SmallVec::with_capacity(children.len()); - for &child in children { - if let Some(previous) = update_parent(world, child, parent) { - // Do nothing if the entity already has the correct parent. - if parent == previous { - continue; - } - - remove_from_children(world, previous, child); - events.push(HierarchyEvent::ChildMoved { - child, - previous_parent: previous, - new_parent: parent, - }); - } else { - events.push(HierarchyEvent::ChildAdded { child, parent }); - } - } - push_events(world, events); -} - -/// Removes entities in `children` from `parent`'s [`Children`], removing the component if it ends up empty. -/// Also removes [`Parent`] component from `children`. -fn remove_children(parent: Entity, children: &[Entity], world: &mut World) { - let mut events: SmallVec<[HierarchyEvent; 8]> = SmallVec::new(); - if let Some(parent_children) = world.get::(parent) { - for &child in children { - if parent_children.contains(&child) { - events.push(HierarchyEvent::ChildRemoved { child, parent }); - } - } - } else { - return; - } - for event in &events { - if let &HierarchyEvent::ChildRemoved { child, .. } = event { - world.entity_mut(child).remove::(); - } - } - push_events(world, events); - - let mut parent = world.entity_mut(parent); - if let Some(mut parent_children) = parent.get_mut::() { - parent_children - .0 - .retain(|parent_child| !children.contains(parent_child)); - - if parent_children.is_empty() { - parent.remove::(); - } - } -} - -/// Removes all children from `parent` by removing its [`Children`] component, as well as removing -/// [`Parent`] component from its children. -fn clear_children(parent: Entity, world: &mut World) { - if let Some(children) = world.entity_mut(parent).take::() { - for &child in &children.0 { - world.entity_mut(child).remove::(); - } - } -} +use smallvec::SmallVec; /// Command that adds a child to an entity. #[derive(Debug)] @@ -211,7 +59,17 @@ pub struct RemoveChildren { impl Command for RemoveChildren { fn apply(self, world: &mut World) { - remove_children(self.parent, &self.children, world); + for child in self.children { + let Ok(mut child) = world.get_entity_mut(child) else { + continue; + }; + if child + .get::() + .is_some_and(|parent| parent.get() == self.parent) + { + child.remove::(); + } + } } } @@ -223,7 +81,9 @@ pub struct ClearChildren { impl Command for ClearChildren { fn apply(self, world: &mut World) { - clear_children(self.parent, world); + if let Ok(mut parent) = world.get_entity_mut(self.parent) { + parent.remove::(); + } } } @@ -235,8 +95,17 @@ pub struct ReplaceChildren { impl Command for ReplaceChildren { fn apply(self, world: &mut World) { - clear_children(self.parent, world); - world.entity_mut(self.parent).add_children(&self.children); + if let Ok(mut parent) = world.get_entity_mut(self.parent) { + parent.remove::(); + } + + world.flush(); + + for child in self.children { + if let Ok(mut child) = world.get_entity_mut(child) { + child.insert(Parent::new(self.parent)); + } + } } } @@ -544,28 +413,12 @@ impl ChildBuild for WorldChildBuilder<'_> { Self: 'a; fn spawn(&mut self, bundle: impl Bundle) -> EntityWorldMut { - let entity = self.world.spawn((bundle, Parent(self.parent))).id(); - add_child_unchecked(self.world, self.parent, entity); - push_events( - self.world, - [HierarchyEvent::ChildAdded { - child: entity, - parent: self.parent, - }], - ); + let entity = self.world.spawn((bundle, Parent::new(self.parent))).id(); self.world.entity_mut(entity) } fn spawn_empty(&mut self) -> EntityWorldMut { - let entity = self.world.spawn(Parent(self.parent)).id(); - add_child_unchecked(self.world, self.parent, entity); - push_events( - self.world, - [HierarchyEvent::ChildAdded { - child: entity, - parent: self.parent, - }], - ); + let entity = self.world.spawn(Parent::new(self.parent)).id(); self.world.entity_mut(entity) } @@ -584,38 +437,41 @@ impl BuildChildren for EntityWorldMut<'_> { fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self { let parent = self.id(); + self.world_scope(|world| { spawn_children(&mut WorldChildBuilder { world, parent }); }); + + self.world_scope(World::flush); + self } fn with_child(&mut self, bundle: B) -> &mut Self { let parent = self.id(); - let child = self.world_scope(|world| world.spawn((bundle, Parent(parent))).id()); - if let Some(mut children_component) = self.get_mut::() { - children_component.0.retain(|value| child != *value); - children_component.0.push(child); - } else { - self.insert(Children::from_entities(&[child])); - } + + self.world_scope(|world| world.spawn((bundle, Parent::new(parent))).id()); + + self.world_scope(World::flush); + self } fn add_child(&mut self, child: Entity) -> &mut Self { let parent = self.id(); + if child == parent { panic!("Cannot add entity as a child of itself."); } + self.world_scope(|world| { - update_old_parent(world, child, parent); + if let Ok(mut child) = world.get_entity_mut(child) { + child.insert(Parent::new(parent)); + } }); - if let Some(mut children_component) = self.get_mut::() { - children_component.0.retain(|value| child != *value); - children_component.0.push(child); - } else { - self.insert(Children::from_entities(&[child])); - } + + self.world_scope(World::flush); + self } @@ -628,17 +484,16 @@ impl BuildChildren for EntityWorldMut<'_> { if children.contains(&parent) { panic!("Cannot push entity as a child of itself."); } - self.world_scope(|world| { - update_old_parents(world, parent, children); + self.world_scope(move |world| { + for &child in children { + if let Ok(mut child) = world.get_entity_mut(child) { + child.insert(Parent::new(parent)); + } + } }); - if let Some(mut children_component) = self.get_mut::() { - children_component - .0 - .retain(|value| !children.contains(value)); - children_component.0.extend(children.iter().cloned()); - } else { - self.insert(Children::from_entities(children)); - } + + self.world_scope(World::flush); + self } @@ -647,52 +502,74 @@ impl BuildChildren for EntityWorldMut<'_> { if children.contains(&parent) { panic!("Cannot insert entity as a child of itself."); } - self.world_scope(|world| { - update_old_parents(world, parent, children); - }); - if let Some(mut children_component) = self.get_mut::() { - children_component - .0 - .retain(|value| !children.contains(value)); - children_component.0.insert_from_slice(index, children); - } else { - self.insert(Children::from_entities(children)); - } + + let component = self.take::().unwrap_or_default(); + + let iter_first = component.iter().take(index); + let iter_second = component.iter().skip(index); + + let component = iter_first + .chain(children) + .chain(iter_second) + .copied() + .collect::(); + + self.insert(component); + + self.world_scope(World::flush); + self } fn remove_children(&mut self, children: &[Entity]) -> &mut Self { let parent = self.id(); - self.world_scope(|world| { - remove_children(parent, children, world); + + self.world_scope(move |world| { + for &child in children { + if let Ok(mut child) = world.get_entity_mut(child) { + if child.get::().is_some_and(|p| p.get() == parent) { + child.remove::(); + } + } + } }); + + self.world_scope(World::flush); + self } fn set_parent(&mut self, parent: Entity) -> &mut Self { let child = self.id(); + self.world_scope(|world| { world.entity_mut(parent).add_child(child); }); + + self.world_scope(World::flush); + self } fn remove_parent(&mut self) -> &mut Self { - let child = self.id(); - if let Some(parent) = self.take::().map(|p| p.get()) { - self.world_scope(|world| { - remove_from_children(world, parent, child); - push_events(world, [HierarchyEvent::ChildRemoved { child, parent }]); - }); - } + self.remove::(); + + self.world_scope(World::flush); + self } fn clear_children(&mut self) -> &mut Self { let parent = self.id(); + self.world_scope(|world| { - clear_children(parent, world); + if let Ok(mut parent) = world.get_entity_mut(parent) { + parent.remove::(); + } }); + + self.world_scope(World::flush); + self } @@ -704,10 +581,7 @@ impl BuildChildren for EntityWorldMut<'_> { #[cfg(test)] mod tests { use super::{BuildChildren, ChildBuild}; - use crate::{ - components::{Children, Parent}, - HierarchyEvent::{self, ChildAdded, ChildMoved, ChildRemoved}, - }; + use crate::{Children, HierarchyEvent, Parent}; use smallvec::{smallvec, SmallVec}; use bevy_ecs::{ @@ -762,25 +636,13 @@ mod tests { assert_parent(world, b, Some(a)); assert_children(world, a, Some(&[b])); - assert_events( - world, - &[ChildAdded { - child: b, - parent: a, - }], - ); + assert_events(world, &[HierarchyEvent::added(b, a)]); world.entity_mut(a).add_child(c); assert_children(world, a, Some(&[b, c])); assert_parent(world, c, Some(a)); - assert_events( - world, - &[ChildAdded { - child: c, - parent: a, - }], - ); + assert_events(world, &[HierarchyEvent::added(c, a)]); // Children component should be removed when it's empty. world.entity_mut(d).add_child(b).add_child(c); assert_children(world, a, None); @@ -797,13 +659,7 @@ mod tests { assert_parent(world, a, Some(b)); assert_children(world, b, Some(&[a])); - assert_events( - world, - &[ChildAdded { - child: a, - parent: b, - }], - ); + assert_events(world, &[HierarchyEvent::added(a, b)]); world.entity_mut(a).set_parent(c); @@ -812,11 +668,7 @@ mod tests { assert_children(world, c, Some(&[a])); assert_events( world, - &[ChildMoved { - child: a, - previous_parent: b, - new_parent: c, - }], + &[HierarchyEvent::removed(a, b), HierarchyEvent::added(a, c)], ); } @@ -850,25 +702,13 @@ mod tests { assert_parent(world, b, None); assert_parent(world, c, Some(a)); assert_children(world, a, Some(&[c])); - omit_events(world, 2); // Omit ChildAdded events. - assert_events( - world, - &[ChildRemoved { - child: b, - parent: a, - }], - ); + omit_events(world, 2); // Omit Added events. + assert_events(world, &[HierarchyEvent::removed(b, a)]); world.entity_mut(c).remove_parent(); assert_parent(world, c, None); assert_children(world, a, None); - assert_events( - world, - &[ChildRemoved { - child: c, - parent: a, - }], - ); + assert_events(world, &[HierarchyEvent::removed(c, a)]); } #[allow(dead_code)] @@ -893,14 +733,26 @@ mod tests { queue.apply(&mut world); assert_eq!( - world.get::(parent).unwrap().0.as_slice(), + world.get::(parent).unwrap().as_slice(), children.as_slice(), ); - assert_eq!(*world.get::(children[0]).unwrap(), Parent(parent)); - assert_eq!(*world.get::(children[1]).unwrap(), Parent(parent)); + assert_eq!( + *world.get::(children[0]).unwrap(), + Parent::new(parent) + ); + assert_eq!( + *world.get::(children[1]).unwrap(), + Parent::new(parent) + ); - assert_eq!(*world.get::(children[0]).unwrap(), Parent(parent)); - assert_eq!(*world.get::(children[1]).unwrap(), Parent(parent)); + assert_eq!( + *world.get::(children[0]).unwrap(), + Parent::new(parent) + ); + assert_eq!( + *world.get::(children[1]).unwrap(), + Parent::new(parent) + ); } #[test] @@ -913,7 +765,7 @@ mod tests { commands.entity(parent).with_child(C(2)); queue.apply(&mut world); - assert_eq!(world.get::(parent).unwrap().0.len(), 1); + assert_eq!(world.get::(parent).unwrap().len(), 1); } #[test] @@ -938,14 +790,14 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); { let mut commands = Commands::new(&mut queue, &world); @@ -955,11 +807,11 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child3, child4, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child3).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child4).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child3).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child4).unwrap(), Parent::new(parent)); let remove_children = [child1, child4]; { @@ -970,8 +822,8 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child3, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); assert!(world.get::(child1).is_none()); assert!(world.get::(child4).is_none()); @@ -997,11 +849,11 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); { let mut commands = Commands::new(&mut queue, &world); @@ -1036,11 +888,11 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); let replace_children = [child1, child4]; { @@ -1051,11 +903,11 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child4]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child4).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child4).unwrap(), Parent::new(parent)); assert!(world.get::(child2).is_none()); } @@ -1076,27 +928,27 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); world.entity_mut(parent).insert_children(1, &entities[3..]); let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child3, child4, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child3).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child4).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child3).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child4).unwrap(), Parent::new(parent)); let remove_children = [child1, child4]; world.entity_mut(parent).remove_children(&remove_children); let expected_children: SmallVec<[Entity; 8]> = smallvec![child3, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); assert!(world.get::(child1).is_none()); assert!(world.get::(child4).is_none()); @@ -1117,11 +969,11 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); world.entity_mut(parent).clear_children(); assert!(world.get::(parent).is_none()); @@ -1146,22 +998,22 @@ mod tests { let expected_children: SmallVec<[Entity; 8]> = smallvec![child1, child2]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); - assert_eq!(*world.get::(child1).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child1).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); world.entity_mut(parent).replace_children(&entities[2..]); let expected_children: SmallVec<[Entity; 8]> = smallvec![child2, child3, child4]; assert_eq!( - world.get::(parent).unwrap().0.clone(), - expected_children + world.get::(parent).unwrap().as_slice(), + expected_children.as_slice() ); assert!(world.get::(child1).is_none()); - assert_eq!(*world.get::(child2).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child3).unwrap(), Parent(parent)); - assert_eq!(*world.get::(child4).unwrap(), Parent(parent)); + assert_eq!(*world.get::(child2).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child3).unwrap(), Parent::new(parent)); + assert_eq!(*world.get::(child4).unwrap(), Parent::new(parent)); } /// Tests what happens when all children are removed from a parent using world functions @@ -1178,10 +1030,7 @@ mod tests { // add child into parent1 world.entity_mut(parent1).add_children(&[child]); - assert_eq!( - world.get::(parent1).unwrap().0.as_slice(), - &[child] - ); + assert_eq!(world.get::(parent1).unwrap().as_slice(), &[child]); // move only child from parent1 with `add_children` world.entity_mut(parent2).add_children(&[child]); @@ -1216,10 +1065,7 @@ mod tests { commands.entity(parent1).add_children(&[child]); queue.apply(&mut world); } - assert_eq!( - world.get::(parent1).unwrap().0.as_slice(), - &[child] - ); + assert_eq!(world.get::(parent1).unwrap().as_slice(), &[child]); // move only child from parent1 with `add_children` { diff --git a/crates/bevy_hierarchy/src/components/children.rs b/crates/bevy_hierarchy/src/components/children.rs deleted file mode 100644 index 18ad943ee3637..0000000000000 --- a/crates/bevy_hierarchy/src/components/children.rs +++ /dev/null @@ -1,168 +0,0 @@ -#[cfg(feature = "reflect")] -use bevy_ecs::reflect::{ - ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities, - ReflectVisitEntitiesMut, -}; -use bevy_ecs::{ - component::Component, - entity::{Entity, VisitEntitiesMut}, - prelude::FromWorld, - world::World, -}; -use core::{ops::Deref, slice}; -use smallvec::SmallVec; - -/// Contains references to the child entities of this entity. -/// -/// Each child must contain a [`Parent`] component that points back to this entity. -/// This component rarely needs to be created manually, -/// consider using higher level utilities like [`BuildChildren::with_children`] -/// which are safer and easier to use. -/// -/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`]. -/// -/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt -/// [`Query`]: bevy_ecs::system::Query -/// [`Parent`]: crate::components::parent::Parent -/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children -#[derive(Component, Debug, VisitEntitiesMut)] -#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] -#[cfg_attr( - feature = "reflect", - reflect( - Component, - MapEntities, - VisitEntities, - VisitEntitiesMut, - Debug, - FromWorld - ) -)] -pub struct Children(pub(crate) SmallVec<[Entity; 8]>); - -// TODO: We need to impl either FromWorld or Default so Children can be registered as Reflect. -// This is because Reflect deserialize by creating an instance and apply a patch on top. -// However Children should only ever be set with a real user-defined entities. Its worth looking -// into better ways to handle cases like this. -impl FromWorld for Children { - #[inline] - fn from_world(_world: &mut World) -> Self { - Children(SmallVec::new()) - } -} - -impl Children { - /// Constructs a [`Children`] component with the given entities. - #[inline] - pub(crate) fn from_entities(entities: &[Entity]) -> Self { - Self(SmallVec::from_slice(entities)) - } - - /// Swaps the child at `a_index` with the child at `b_index`. - #[inline] - pub fn swap(&mut self, a_index: usize, b_index: usize) { - self.0.swap(a_index, b_index); - } - - /// Sorts children [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) - /// in place using the provided comparator function. - /// - /// For the underlying implementation, see [`slice::sort_by`]. - /// - /// For the unstable version, see [`sort_unstable_by`](Children::sort_unstable_by). - /// - /// See also [`sort_by_key`](Children::sort_by_key), [`sort_by_cached_key`](Children::sort_by_cached_key). - #[inline] - pub fn sort_by(&mut self, compare: F) - where - F: FnMut(&Entity, &Entity) -> core::cmp::Ordering, - { - self.0.sort_by(compare); - } - - /// Sorts children [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) - /// in place using the provided key extraction function. - /// - /// For the underlying implementation, see [`slice::sort_by_key`]. - /// - /// For the unstable version, see [`sort_unstable_by_key`](Children::sort_unstable_by_key). - /// - /// See also [`sort_by`](Children::sort_by), [`sort_by_cached_key`](Children::sort_by_cached_key). - #[inline] - pub fn sort_by_key(&mut self, compare: F) - where - F: FnMut(&Entity) -> K, - K: Ord, - { - self.0.sort_by_key(compare); - } - - /// Sorts children [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) - /// in place using the provided key extraction function. Only evaluates each key at most - /// once per sort, caching the intermediate results in memory. - /// - /// For the underlying implementation, see [`slice::sort_by_cached_key`]. - /// - /// See also [`sort_by`](Children::sort_by), [`sort_by_key`](Children::sort_by_key). - #[inline] - pub fn sort_by_cached_key(&mut self, compare: F) - where - F: FnMut(&Entity) -> K, - K: Ord, - { - self.0.sort_by_cached_key(compare); - } - - /// Sorts children [unstably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) - /// in place using the provided comparator function. - /// - /// For the underlying implementation, see [`slice::sort_unstable_by`]. - /// - /// For the stable version, see [`sort_by`](Children::sort_by). - /// - /// See also [`sort_unstable_by_key`](Children::sort_unstable_by_key). - #[inline] - pub fn sort_unstable_by(&mut self, compare: F) - where - F: FnMut(&Entity, &Entity) -> core::cmp::Ordering, - { - self.0.sort_unstable_by(compare); - } - - /// Sorts children [unstably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) - /// in place using the provided key extraction function. - /// - /// For the underlying implementation, see [`slice::sort_unstable_by_key`]. - /// - /// For the stable version, see [`sort_by_key`](Children::sort_by_key). - /// - /// See also [`sort_unstable_by`](Children::sort_unstable_by). - #[inline] - pub fn sort_unstable_by_key(&mut self, compare: F) - where - F: FnMut(&Entity) -> K, - K: Ord, - { - self.0.sort_unstable_by_key(compare); - } -} - -impl Deref for Children { - type Target = [Entity]; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0[..] - } -} - -impl<'a> IntoIterator for &'a Children { - type Item = ::Item; - - type IntoIter = slice::Iter<'a, Entity>; - - #[inline(always)] - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} diff --git a/crates/bevy_hierarchy/src/components/mod.rs b/crates/bevy_hierarchy/src/components/mod.rs deleted file mode 100644 index 3c8b544850382..0000000000000 --- a/crates/bevy_hierarchy/src/components/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod children; -mod parent; - -pub use children::Children; -pub use parent::Parent; diff --git a/crates/bevy_hierarchy/src/components/parent.rs b/crates/bevy_hierarchy/src/components/parent.rs deleted file mode 100644 index b369447734206..0000000000000 --- a/crates/bevy_hierarchy/src/components/parent.rs +++ /dev/null @@ -1,91 +0,0 @@ -#[cfg(feature = "reflect")] -use bevy_ecs::reflect::{ - ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities, - ReflectVisitEntitiesMut, -}; -use bevy_ecs::{ - component::Component, - entity::{Entity, VisitEntities, VisitEntitiesMut}, - traversal::Traversal, - world::{FromWorld, World}, -}; -use core::ops::Deref; - -/// Holds a reference to the parent entity of this entity. -/// This component should only be present on entities that actually have a parent entity. -/// -/// Parent entity must have this entity stored in its [`Children`] component. -/// It is hard to set up parent/child relationships manually, -/// consider using higher level utilities like [`BuildChildren::with_children`]. -/// -/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`]. -/// -/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt -/// [`Query`]: bevy_ecs::system::Query -/// [`Children`]: super::children::Children -/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children -#[derive(Component, Debug, Eq, PartialEq, VisitEntities, VisitEntitiesMut)] -#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] -#[cfg_attr( - feature = "reflect", - reflect( - Component, - MapEntities, - VisitEntities, - VisitEntitiesMut, - PartialEq, - Debug, - FromWorld - ) -)] -pub struct Parent(pub(crate) Entity); - -impl Parent { - /// Gets the [`Entity`] ID of the parent. - #[inline(always)] - pub fn get(&self) -> Entity { - self.0 - } - - /// Gets the parent [`Entity`] as a slice of length 1. - /// - /// Useful for making APIs that require a type or homogeneous storage - /// for both [`Children`] & [`Parent`] that is agnostic to edge direction. - /// - /// [`Children`]: super::children::Children - #[inline(always)] - pub fn as_slice(&self) -> &[Entity] { - core::slice::from_ref(&self.0) - } -} - -// TODO: We need to impl either FromWorld or Default so Parent can be registered as Reflect. -// This is because Reflect deserialize by creating an instance and apply a patch on top. -// However Parent should only ever be set with a real user-defined entity. Its worth looking into -// better ways to handle cases like this. -impl FromWorld for Parent { - #[inline(always)] - fn from_world(_world: &mut World) -> Self { - Parent(Entity::PLACEHOLDER) - } -} - -impl Deref for Parent { - type Target = Entity; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// This provides generalized hierarchy traversal for use in [event propagation]. -/// -/// `Parent::traverse` will never form loops in properly-constructed hierarchies. -/// -/// [event propagation]: bevy_ecs::observer::Trigger::propagate -impl Traversal for &Parent { - fn traverse(item: Self::Item<'_>) -> Option { - Some(item.0) - } -} diff --git a/crates/bevy_hierarchy/src/despawn_recursive/command.rs b/crates/bevy_hierarchy/src/despawn_recursive/command.rs new file mode 100644 index 0000000000000..db72e482f9a2a --- /dev/null +++ b/crates/bevy_hierarchy/src/despawn_recursive/command.rs @@ -0,0 +1,139 @@ +use core::marker::PhantomData; + +use bevy_ecs::{ + component::Component, + entity::{Entity, VisitEntities}, + world::{Command, EntityWorldMut, World}, +}; +use bevy_utils::tracing::debug; + +/// Despawns all [entities](Entity) found using [`VisitEntities`] on the +/// [`Component`] `C` for the provided [`Entity`], including said [`Entity`]. +/// If an [`Entity`] cannot be found, a warning will be emitted. +/// +/// The target [`Entity`] can be excluded from being despawned using +/// [`without_inclusion`](`DespawnRecursive::without_inclusion`). +/// +/// Warnings can be disabled using [`with_warn`](`DespawnRecursive::with_warn`). +/// +/// Note that the [`Component`] `C` is _removed_ from the [`Entity`] even if it isn't despawned. +/// +/// # Examples +/// +/// ```rust +/// # use bevy_hierarchy::{DespawnRecursive, Children, Parent}; +/// # use bevy_ecs::world::{Command, World}; +/// # +/// # let mut world = World::new(); +/// # let parent = world.spawn_empty().id(); +/// # let child = world.spawn(Parent::new(parent)).id(); +/// # +/// # let mut commands = world.commands(); +/// # +/// # let command = { +/// // Despawn all Children from a parent +/// DespawnRecursive::::new(parent) +/// # }; +/// # +/// # commands.queue(command); +/// # world.flush(); +/// # +/// # assert!(world.get_entity(child).is_err()); +/// # assert!(world.get_entity(parent).is_err()); +/// ``` +#[derive(Debug)] +pub struct DespawnRecursive { + /// Target entity + entity: Entity, + /// Whether or not this command should output a warning if the entity does not exist + warn: bool, + /// Whether this command will despawn the provided entity (`inclusive`) or just + /// its descendants (`exclusive`). + inclusive: bool, + /// Marker for the relationship type to be despawned. + _phantom: PhantomData, +} + +impl DespawnRecursive { + /// Create a new [`DespawnRecursive`] [`Command`]. + pub const fn new(entity: Entity) -> Self { + Self { + entity, + warn: true, + inclusive: true, + _phantom: PhantomData, + } + } + + /// Control whether this [`Command`] should emit a warning when attempting to despawn + /// a nonexistent [`Entity`]. + pub const fn with_warn(mut self, warn: bool) -> Self { + self.warn = warn; + self + } + + /// Control whether this [`Command`] should exclude the target [`Entity`], only despawning + /// its descendants. + pub const fn without_inclusion(mut self) -> Self { + self.inclusive = false; + self + } +} + +impl Command for DespawnRecursive { + fn apply(self, world: &mut World) { + #[cfg(feature = "trace")] + let _span = bevy_utils::tracing::info_span!( + "command", + name = "DespawnRecursive", + entity = bevy_utils::tracing::field::debug(self.entity), + warn = bevy_utils::tracing::field::debug(self.warn) + ) + .entered(); + + let f = if self.warn { + despawn:: + } else { + despawn:: + }; + + if self.inclusive { + visit_recursive_depth_first::(world, self.entity, f); + } else { + visit_recursive_depth_first::(world, self.entity, f); + } + } +} + +fn visit_recursive_depth_first( + world: &mut World, + entity: Entity, + f: fn(&mut World, Entity), +) { + if let Some(component) = world + .get_entity_mut(entity) + .ok() + .as_mut() + .and_then(EntityWorldMut::take::) + { + component.visit_entities(|e| { + visit_recursive_depth_first::(world, e, f); + }); + } + + if INCLUSIVE { + f(world, entity); + } +} + +fn despawn(world: &mut World, entity: Entity) { + let succeeded = if WARN { + world.despawn(entity) + } else { + world.try_despawn(entity) + }; + + if !succeeded { + debug!("Failed to despawn entity {:?}", entity); + } +} diff --git a/crates/bevy_hierarchy/src/despawn_recursive/ext.rs b/crates/bevy_hierarchy/src/despawn_recursive/ext.rs new file mode 100644 index 0000000000000..519fd5ac451b5 --- /dev/null +++ b/crates/bevy_hierarchy/src/despawn_recursive/ext.rs @@ -0,0 +1,86 @@ +use bevy_ecs::{ + component::Component, + entity::VisitEntities, + system::EntityCommands, + world::{Command, EntityWorldMut}, +}; + +use crate::{Children, DespawnRecursive}; + +/// Trait that holds functions for despawning recursively down the a hierarchy. +pub trait DespawnRecursiveExt: Sized { + /// Despawns the provided [`Entity`]((bevy_ecs::entity::Entity)) alongside all descendants. + fn despawn_recursive(self) { + self.despawn_recursive_with_option::(true); + } + + /// Despawns all descendants of the given [`Entity`](bevy_ecs::entity::Entity). + fn despawn_descendants(&mut self) -> &mut Self { + self.despawn_descendants_with_option::(true) + } + + /// Similar to [`despawn_recursive`](`DespawnRecursiveExt::despawn_recursive`) but does not emit warnings + fn try_despawn_recursive(self) { + self.despawn_recursive_with_option::(false); + } + + /// Similar to [`despawn_descendants`](`DespawnRecursiveExt::despawn_descendants`) but does not emit warnings + fn try_despawn_descendants(&mut self) -> &mut Self { + self.despawn_descendants_with_option::(false) + } + + /// Despawns the provided [`Entity`](bevy_ecs::entity::Entity) alongside all descendants as related via `C`. + /// Optionally warns when attempting to despawn a nonexistent [`Entity`](bevy_ecs::entity::Entity). + fn despawn_recursive_with_option(self, warn: bool); + + /// Despawns from [`Entity`](bevy_ecs::entity::Entity) all descendants as related via `C`. + /// Optionally warns when attempting to despawn a nonexistent [`Entity`](bevy_ecs::entity::Entity). + fn despawn_descendants_with_option( + &mut self, + warn: bool, + ) -> &mut Self; +} + +impl DespawnRecursiveExt for EntityCommands<'_> { + fn despawn_recursive_with_option(mut self, warn: bool) { + let entity = self.id(); + self.commands() + .queue(DespawnRecursive::::new(entity).with_warn(warn)); + } + + fn despawn_descendants_with_option( + &mut self, + warn: bool, + ) -> &mut Self { + let entity = self.id(); + self.commands().queue( + DespawnRecursive::::new(entity) + .with_warn(warn) + .without_inclusion(), + ); + self + } +} + +impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> { + fn despawn_recursive_with_option(self, warn: bool) { + DespawnRecursive::::new(self.id()) + .with_warn(warn) + .apply(self.into_world_mut()); + } + + fn despawn_descendants_with_option( + &mut self, + warn: bool, + ) -> &mut Self { + let entity = self.id(); + + self.world_scope(|world| { + DespawnRecursive::::new(entity) + .with_warn(warn) + .without_inclusion() + .apply(world); + }); + self + } +} diff --git a/crates/bevy_hierarchy/src/despawn_recursive/mod.rs b/crates/bevy_hierarchy/src/despawn_recursive/mod.rs new file mode 100644 index 0000000000000..eff8133247214 --- /dev/null +++ b/crates/bevy_hierarchy/src/despawn_recursive/mod.rs @@ -0,0 +1,151 @@ +mod command; +pub use command::DespawnRecursive; + +mod ext; +pub use ext::DespawnRecursiveExt; + +#[cfg(test)] +mod tests { + use bevy_ecs::{ + component::Component, + system::Commands, + world::{CommandQueue, World}, + }; + + use super::DespawnRecursiveExt; + use crate::{ + child_builder::{BuildChildren, ChildBuild}, + Children, + }; + + #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)] + struct Idx(u32); + + #[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] + struct N(String); + + #[test] + fn despawn_recursive() { + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let grandparent_entity; + { + let mut commands = Commands::new(&mut queue, &world); + + commands + .spawn((N("Another parent".to_owned()), Idx(0))) + .with_children(|parent| { + parent.spawn((N("Another child".to_owned()), Idx(1))); + }); + + // Create a grandparent entity which will _not_ be deleted + grandparent_entity = commands.spawn((N("Grandparent".to_owned()), Idx(2))).id(); + commands.entity(grandparent_entity).with_children(|parent| { + // Add a child to the grandparent (the "parent"), which will get deleted + parent + .spawn((N("Parent, to be deleted".to_owned()), Idx(3))) + // All descendants of the "parent" should also be deleted. + .with_children(|parent| { + parent + .spawn((N("First Child, to be deleted".to_owned()), Idx(4))) + .with_children(|parent| { + // child + parent.spawn(( + N("First grand child, to be deleted".to_owned()), + Idx(5), + )); + }); + parent.spawn((N("Second child, to be deleted".to_owned()), Idx(6))); + }); + }); + + commands.spawn((N("An innocent bystander".to_owned()), Idx(7))); + } + queue.apply(&mut world); + + let parent_entity = world.get::(grandparent_entity).unwrap()[0]; + + { + let mut commands = Commands::new(&mut queue, &world); + commands.entity(parent_entity).despawn_recursive(); + // despawning the same entity twice should not panic + commands.entity(parent_entity).despawn_recursive(); + } + queue.apply(&mut world); + + let mut results = world + .query::<(&N, &Idx)>() + .iter(&world) + .map(|(a, b)| (a.clone(), *b)) + .collect::>(); + results.sort_unstable_by_key(|(_, index)| *index); + + { + let children = world.get::(grandparent_entity); + assert!( + children.is_none(), + "grandparent should no longer know about its child which has been removed" + ); + } + + assert_eq!( + results, + vec![ + (N("Another parent".to_owned()), Idx(0)), + (N("Another child".to_owned()), Idx(1)), + (N("Grandparent".to_owned()), Idx(2)), + (N("An innocent bystander".to_owned()), Idx(7)) + ] + ); + } + + #[test] + fn despawn_descendants() { + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + let parent = commands.spawn_empty().id(); + let child = commands.spawn_empty().id(); + + commands + .entity(parent) + .add_child(child) + .despawn_descendants(); + + queue.apply(&mut world); + + // The parent's Children component should be removed. + assert!(world.entity(parent).get::().is_none()); + // The child should be despawned. + assert!(world.get_entity(child).is_err()); + } + + #[test] + fn spawn_children_after_despawn_descendants() { + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + let parent = commands.spawn_empty().id(); + let child = commands.spawn_empty().id(); + + commands + .entity(parent) + .add_child(child) + .despawn_descendants() + .with_children(|parent| { + parent.spawn_empty(); + parent.spawn_empty(); + }); + + queue.apply(&mut world); + + // The parent's Children component should still have two children. + let children = world.entity(parent).get::(); + assert!(children.is_some()); + assert_eq!(children.unwrap().len(), 2_usize); + // The original child should be despawned. + assert!(world.get_entity(child).is_err()); + } +} diff --git a/crates/bevy_hierarchy/src/events.rs b/crates/bevy_hierarchy/src/events.rs deleted file mode 100644 index c397488c08434..0000000000000 --- a/crates/bevy_hierarchy/src/events.rs +++ /dev/null @@ -1,33 +0,0 @@ -use bevy_ecs::{event::Event, prelude::Entity}; -use bevy_reflect::Reflect; - -/// An [`Event`] that is fired whenever there is a change in the world's hierarchy. -/// -/// [`Event`]: bevy_ecs::event::Event -#[derive(Event, Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Debug, PartialEq))] -pub enum HierarchyEvent { - /// Fired whenever an [`Entity`] is added as a child to a parent. - ChildAdded { - /// The child that was added - child: Entity, - /// The parent the child was added to - parent: Entity, - }, - /// Fired whenever a child [`Entity`] is removed from its parent. - ChildRemoved { - /// The child that was removed - child: Entity, - /// The parent the child was removed from - parent: Entity, - }, - /// Fired whenever a child [`Entity`] is moved to a new parent. - ChildMoved { - /// The child that was moved - child: Entity, - /// The parent the child was removed from - previous_parent: Entity, - /// The parent the child was added to - new_parent: Entity, - }, -} diff --git a/crates/bevy_hierarchy/src/family.rs b/crates/bevy_hierarchy/src/family.rs new file mode 100644 index 0000000000000..a6f4d0fede3bd --- /dev/null +++ b/crates/bevy_hierarchy/src/family.rs @@ -0,0 +1,40 @@ +use crate::{many_to_one::ManyToOne, one_to_many::OneToMany, relationship::RelationshipEvent}; + +/// A familial relationship +#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] +pub struct Family; + +/// Holds a reference to the parent entity of this entity. +/// This component should only be present on entities that actually have a parent entity. +/// +/// Parent entity must have this entity stored in its [`Children`] component. +/// This invariant will be upheld using component hooks, but will only be valid after a sync point, +/// when deferred commands are applied. +/// To avoid this delay, consider using higher level utilities like [`BuildChildren::with_children`]. +/// +/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`]. +/// +/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt +/// [`Query`]: bevy_ecs::system::Query +/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children +pub type Parent = ManyToOne; + +/// Contains references to the child entities of this entity. +/// +/// Each child must contain a [`Parent`] component that points back to this entity. +/// This component rarely needs to be created manually, the recommended way to +/// work with this component is to insert [`Parent`] on all child entities, as +/// component hooks will ensure this component is available. +/// You may also consider using higher level utilities like [`BuildChildren::with_children`]. +/// +/// See [`HierarchyQueryExt`] for hierarchy related methods on [`Query`]. +/// +/// [`HierarchyQueryExt`]: crate::query_extension::HierarchyQueryExt +/// [`Query`]: bevy_ecs::system::Query +/// [`BuildChildren::with_children`]: crate::child_builder::BuildChildren::with_children +pub type Children = OneToMany; + +/// An [`Event`] that is fired whenever there is a change in the world's hierarchy. +/// +/// [`Event`]: bevy_ecs::event::Event +pub type HierarchyEvent = RelationshipEvent; diff --git a/crates/bevy_hierarchy/src/hierarchy.rs b/crates/bevy_hierarchy/src/hierarchy.rs deleted file mode 100644 index 4a9d71ace7d13..0000000000000 --- a/crates/bevy_hierarchy/src/hierarchy.rs +++ /dev/null @@ -1,345 +0,0 @@ -use crate::components::{Children, Parent}; -use bevy_ecs::{ - entity::Entity, - system::EntityCommands, - world::{Command, EntityWorldMut, World}, -}; -use bevy_utils::tracing::debug; - -/// Despawns the given entity and all its children recursively -#[derive(Debug)] -pub struct DespawnRecursive { - /// Target entity - pub entity: Entity, - /// Whether or not this command should output a warning if the entity does not exist - pub warn: bool, -} - -/// Despawns the given entity's children recursively -#[derive(Debug)] -pub struct DespawnChildrenRecursive { - /// Target entity - pub entity: Entity, - /// Whether or not this command should output a warning if the entity does not exist - pub warn: bool, -} - -/// Function for despawning an entity and all its children -pub fn despawn_with_children_recursive(world: &mut World, entity: Entity, warn: bool) { - // first, make the entity's own parent forget about it - if let Some(parent) = world.get::(entity).map(|parent| parent.0) { - if let Some(mut children) = world.get_mut::(parent) { - children.0.retain(|c| *c != entity); - } - } - - // then despawn the entity and all of its children - despawn_with_children_recursive_inner(world, entity, warn); -} - -// Should only be called by `despawn_with_children_recursive` and `try_despawn_with_children_recursive`! -fn despawn_with_children_recursive_inner(world: &mut World, entity: Entity, warn: bool) { - if let Some(mut children) = world.get_mut::(entity) { - for e in core::mem::take(&mut children.0) { - despawn_with_children_recursive_inner(world, e, warn); - } - } - - if warn { - if !world.despawn(entity) { - debug!("Failed to despawn entity {:?}", entity); - } - } else if !world.try_despawn(entity) { - debug!("Failed to despawn entity {:?}", entity); - } -} - -fn despawn_children_recursive(world: &mut World, entity: Entity, warn: bool) { - if let Some(children) = world.entity_mut(entity).take::() { - for e in children.0 { - despawn_with_children_recursive_inner(world, e, warn); - } - } -} - -impl Command for DespawnRecursive { - fn apply(self, world: &mut World) { - #[cfg(feature = "trace")] - let _span = bevy_utils::tracing::info_span!( - "command", - name = "DespawnRecursive", - entity = bevy_utils::tracing::field::debug(self.entity), - warn = bevy_utils::tracing::field::debug(self.warn) - ) - .entered(); - despawn_with_children_recursive(world, self.entity, self.warn); - } -} - -impl Command for DespawnChildrenRecursive { - fn apply(self, world: &mut World) { - #[cfg(feature = "trace")] - let _span = bevy_utils::tracing::info_span!( - "command", - name = "DespawnChildrenRecursive", - entity = bevy_utils::tracing::field::debug(self.entity), - warn = bevy_utils::tracing::field::debug(self.warn) - ) - .entered(); - - despawn_children_recursive(world, self.entity, self.warn); - } -} - -/// Trait that holds functions for despawning recursively down the transform hierarchy -pub trait DespawnRecursiveExt { - /// Despawns the provided entity alongside all descendants. - fn despawn_recursive(self); - - /// Despawns all descendants of the given entity. - fn despawn_descendants(&mut self) -> &mut Self; - - /// Similar to [`Self::despawn_recursive`] but does not emit warnings - fn try_despawn_recursive(self); - - /// Similar to [`Self::despawn_descendants`] but does not emit warnings - fn try_despawn_descendants(&mut self) -> &mut Self; -} - -impl DespawnRecursiveExt for EntityCommands<'_> { - /// Despawns the provided entity and its children. - /// This will emit warnings for any entity that does not exist. - fn despawn_recursive(mut self) { - let entity = self.id(); - self.commands() - .queue(DespawnRecursive { entity, warn: true }); - } - - fn despawn_descendants(&mut self) -> &mut Self { - let entity = self.id(); - self.commands() - .queue(DespawnChildrenRecursive { entity, warn: true }); - self - } - - /// Despawns the provided entity and its children. - /// This will never emit warnings. - fn try_despawn_recursive(mut self) { - let entity = self.id(); - self.commands().queue(DespawnRecursive { - entity, - warn: false, - }); - } - - fn try_despawn_descendants(&mut self) -> &mut Self { - let entity = self.id(); - self.commands().queue(DespawnChildrenRecursive { - entity, - warn: false, - }); - self - } -} - -fn despawn_recursive_inner(world: EntityWorldMut, warn: bool) { - let entity = world.id(); - - #[cfg(feature = "trace")] - let _span = bevy_utils::tracing::info_span!( - "despawn_recursive", - entity = bevy_utils::tracing::field::debug(entity), - warn = bevy_utils::tracing::field::debug(warn) - ) - .entered(); - - despawn_with_children_recursive(world.into_world_mut(), entity, warn); -} - -fn despawn_descendants_inner<'v, 'w>( - world: &'v mut EntityWorldMut<'w>, - warn: bool, -) -> &'v mut EntityWorldMut<'w> { - let entity = world.id(); - - #[cfg(feature = "trace")] - let _span = bevy_utils::tracing::info_span!( - "despawn_descendants", - entity = bevy_utils::tracing::field::debug(entity), - warn = bevy_utils::tracing::field::debug(warn) - ) - .entered(); - - world.world_scope(|world| { - despawn_children_recursive(world, entity, warn); - }); - world -} - -impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> { - /// Despawns the provided entity and its children. - /// This will emit warnings for any entity that does not exist. - fn despawn_recursive(self) { - despawn_recursive_inner(self, true); - } - - fn despawn_descendants(&mut self) -> &mut Self { - despawn_descendants_inner(self, true) - } - - /// Despawns the provided entity and its children. - /// This will not emit warnings. - fn try_despawn_recursive(self) { - despawn_recursive_inner(self, false); - } - - fn try_despawn_descendants(&mut self) -> &mut Self { - despawn_descendants_inner(self, false) - } -} - -#[cfg(test)] -mod tests { - use bevy_ecs::{ - component::Component, - system::Commands, - world::{CommandQueue, World}, - }; - - use super::DespawnRecursiveExt; - use crate::{ - child_builder::{BuildChildren, ChildBuild}, - components::Children, - }; - - #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)] - struct Idx(u32); - - #[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] - struct N(String); - - #[test] - fn despawn_recursive() { - let mut world = World::default(); - let mut queue = CommandQueue::default(); - let grandparent_entity; - { - let mut commands = Commands::new(&mut queue, &world); - - commands - .spawn((N("Another parent".to_owned()), Idx(0))) - .with_children(|parent| { - parent.spawn((N("Another child".to_owned()), Idx(1))); - }); - - // Create a grandparent entity which will _not_ be deleted - grandparent_entity = commands.spawn((N("Grandparent".to_owned()), Idx(2))).id(); - commands.entity(grandparent_entity).with_children(|parent| { - // Add a child to the grandparent (the "parent"), which will get deleted - parent - .spawn((N("Parent, to be deleted".to_owned()), Idx(3))) - // All descendants of the "parent" should also be deleted. - .with_children(|parent| { - parent - .spawn((N("First Child, to be deleted".to_owned()), Idx(4))) - .with_children(|parent| { - // child - parent.spawn(( - N("First grand child, to be deleted".to_owned()), - Idx(5), - )); - }); - parent.spawn((N("Second child, to be deleted".to_owned()), Idx(6))); - }); - }); - - commands.spawn((N("An innocent bystander".to_owned()), Idx(7))); - } - queue.apply(&mut world); - - let parent_entity = world.get::(grandparent_entity).unwrap()[0]; - - { - let mut commands = Commands::new(&mut queue, &world); - commands.entity(parent_entity).despawn_recursive(); - // despawning the same entity twice should not panic - commands.entity(parent_entity).despawn_recursive(); - } - queue.apply(&mut world); - - let mut results = world - .query::<(&N, &Idx)>() - .iter(&world) - .map(|(a, b)| (a.clone(), *b)) - .collect::>(); - results.sort_unstable_by_key(|(_, index)| *index); - - { - let children = world.get::(grandparent_entity).unwrap(); - assert!( - !children.iter().any(|&i| i == parent_entity), - "grandparent should no longer know about its child which has been removed" - ); - } - - assert_eq!( - results, - vec![ - (N("Another parent".to_owned()), Idx(0)), - (N("Another child".to_owned()), Idx(1)), - (N("Grandparent".to_owned()), Idx(2)), - (N("An innocent bystander".to_owned()), Idx(7)) - ] - ); - } - - #[test] - fn despawn_descendants() { - let mut world = World::default(); - let mut queue = CommandQueue::default(); - let mut commands = Commands::new(&mut queue, &world); - - let parent = commands.spawn_empty().id(); - let child = commands.spawn_empty().id(); - - commands - .entity(parent) - .add_child(child) - .despawn_descendants(); - - queue.apply(&mut world); - - // The parent's Children component should be removed. - assert!(world.entity(parent).get::().is_none()); - // The child should be despawned. - assert!(world.get_entity(child).is_err()); - } - - #[test] - fn spawn_children_after_despawn_descendants() { - let mut world = World::default(); - let mut queue = CommandQueue::default(); - let mut commands = Commands::new(&mut queue, &world); - - let parent = commands.spawn_empty().id(); - let child = commands.spawn_empty().id(); - - commands - .entity(parent) - .add_child(child) - .despawn_descendants() - .with_children(|parent| { - parent.spawn_empty(); - parent.spawn_empty(); - }); - - queue.apply(&mut world); - - // The parent's Children component should still have two children. - let children = world.entity(parent).get::(); - assert!(children.is_some()); - assert_eq!(children.unwrap().len(), 2_usize); - // The original child should be despawned. - assert!(world.get_entity(child).is_err()); - } -} diff --git a/crates/bevy_hierarchy/src/lib.rs b/crates/bevy_hierarchy/src/lib.rs index ced37bd154f64..ea4746a5ed18a 100755 --- a/crates/bevy_hierarchy/src/lib.rs +++ b/crates/bevy_hierarchy/src/lib.rs @@ -5,7 +5,7 @@ html_favicon_url = "https://bevyengine.org/assets/icon.png" )] -//! Parent-child relationships for Bevy entities. +//! Relationships for Bevy entities. //! //! You should use the tools in this crate //! whenever you want to organize your entities in a hierarchical fashion, @@ -37,6 +37,14 @@ //! Similarly, unassigning a child in the parent //! will always unassign the parent in the child. //! +//! These components will use component hooks to ensure both members of a relationship +//! have appropriate data for that relationship. +//! +//! Note that to maintain the invariants of a relationship, you must not mutate the component +//! in-place using methods like [`swap`](core::mem::swap), as these bypass the currently +//! available component hooks. To mutate a relationship, instead replace the component with an +//! updated value. +//! //! ## Despawning entities //! //! The commands and methods provided by `bevy_ecs` to despawn entities @@ -53,18 +61,19 @@ extern crate alloc; -mod components; -pub use components::*; +pub(crate) mod many_to_one; +pub(crate) mod one_to_many; +pub(crate) mod relationship; -mod hierarchy; -pub use hierarchy::*; +mod family; +pub use family::*; + +mod despawn_recursive; +pub use despawn_recursive::*; mod child_builder; pub use child_builder::*; -mod events; -pub use events::*; - mod valid_parent_check_plugin; pub use valid_parent_check_plugin::*; @@ -76,7 +85,7 @@ pub use query_extension::*; /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { #[doc(hidden)] - pub use crate::{child_builder::*, components::*, hierarchy::*, query_extension::*}; + pub use crate::{child_builder::*, despawn_recursive::*, family::*, query_extension::*}; #[doc(hidden)] #[cfg(feature = "bevy_app")] diff --git a/crates/bevy_hierarchy/src/many_to_one.rs b/crates/bevy_hierarchy/src/many_to_one.rs new file mode 100644 index 0000000000000..c2291635963b6 --- /dev/null +++ b/crates/bevy_hierarchy/src/many_to_one.rs @@ -0,0 +1,158 @@ +#[cfg(feature = "reflect")] +use bevy_ecs::reflect::{ + ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities, + ReflectVisitEntitiesMut, +}; +use bevy_ecs::{ + component::Component, + entity::{Entity, VisitEntities, VisitEntitiesMut}, + traversal::Traversal, + world::{FromWorld, World}, +}; +use core::{fmt::Debug, marker::PhantomData, ops::Deref}; + +use crate::{one_to_many::OneToMany, relationship::Relationship}; + +/// Represents one half of a one-to-many relationship between an [`Entity`] and some number of other [entities](Entity). +/// +/// The type of relationship is denoted by the parameter `R`. +#[derive(Component)] +#[component( + on_insert = ::associate, + on_replace = ::disassociate, +)] +#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] +#[cfg_attr( + feature = "reflect", + reflect( + Component, + MapEntities, + VisitEntities, + VisitEntitiesMut, + PartialEq, + Debug, + FromWorld + ) +)] +pub struct ManyToOne { + entity: Entity, + #[cfg_attr(feature = "reflect", reflect(ignore))] + _phantom: PhantomData, +} + +impl Relationship for ManyToOne { + type Other = OneToMany; + + fn has(&self, entity: Entity) -> bool { + self.entity == entity + } + + fn new(entity: Entity) -> Self { + Self { + entity, + _phantom: PhantomData, + } + } + + fn with(self, entity: Entity) -> Self { + Self::new(entity) + } + + fn without(self, entity: Entity) -> Option { + (self.entity != entity).then_some(self) + } + + fn iter(&self) -> impl ExactSizeIterator { + [self.entity].into_iter() + } +} + +impl PartialEq for ManyToOne { + fn eq(&self, other: &Self) -> bool { + self.entity == other.entity + } +} + +impl Eq for ManyToOne {} + +impl Debug for ManyToOne { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Has One {:?} ({}) With Many ({})", + self.entity, + core::any::type_name::(), + core::any::type_name::() + ) + } +} + +impl VisitEntities for ManyToOne { + fn visit_entities(&self, mut f: F) { + f(self.entity); + } +} + +impl VisitEntitiesMut for ManyToOne { + fn visit_entities_mut(&mut self, mut f: F) { + f(&mut self.entity); + } +} + +// TODO: We need to impl either FromWorld or Default so OneToOne can be registered as Reflect. +// This is because Reflect deserialize by creating an instance and apply a patch on top. +// However OneToOne should only ever be set with a real user-defined entity. It's worth looking into +// better ways to handle cases like this. +impl FromWorld for ManyToOne { + #[inline(always)] + fn from_world(_world: &mut World) -> Self { + Self { + entity: Entity::PLACEHOLDER, + _phantom: PhantomData, + } + } +} + +impl Deref for ManyToOne { + type Target = Entity; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.entity + } +} + +/// This provides generalized hierarchy traversal for use in [event propagation]. +/// +/// `ManyToOne::traverse` will never form loops in properly-constructed hierarchies. +/// +/// [event propagation]: bevy_ecs::observer::Trigger::propagate +impl Traversal for &ManyToOne { + fn traverse(item: Self::Item<'_>) -> Option { + Some(item.entity) + } +} + +impl ManyToOne { + /// Gets the [`Entity`] ID of the other member of this one-to-many relationship. + #[inline(always)] + pub fn get(&self) -> Entity { + self.entity + } + + /// Gets the other [`Entity`] as a slice. + #[inline(always)] + pub fn as_slice(&self) -> &[Entity] { + core::slice::from_ref(&self.entity) + } + + /// Create a new relationship with the provided [`Entity`]. + #[inline(always)] + #[must_use] + pub fn new(other: Entity) -> Self { + Self { + entity: other, + _phantom: PhantomData, + } + } +} diff --git a/crates/bevy_hierarchy/src/one_to_many.rs b/crates/bevy_hierarchy/src/one_to_many.rs new file mode 100644 index 0000000000000..20b5687217f57 --- /dev/null +++ b/crates/bevy_hierarchy/src/one_to_many.rs @@ -0,0 +1,438 @@ +#[cfg(feature = "reflect")] +use bevy_ecs::reflect::{ + ReflectComponent, ReflectFromWorld, ReflectMapEntities, ReflectVisitEntities, + ReflectVisitEntitiesMut, +}; +use bevy_ecs::{ + component::Component, + entity::{Entity, VisitEntitiesMut}, +}; +use core::{fmt::Debug, marker::PhantomData, ops::Deref}; +use smallvec::{smallvec, SmallVec}; + +use crate::{many_to_one::ManyToOne, relationship::Relationship}; + +/// Represents one half of a one-to-many relationship between an [`Entity`] and some number of other [entities](Entity). +/// +/// The type of relationship is denoted by the parameter `R`. +#[derive(Component)] +#[component( + on_insert = ::associate, + on_replace = ::disassociate, +)] +#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))] +#[cfg_attr( + feature = "reflect", + reflect( + Component, + MapEntities, + VisitEntities, + VisitEntitiesMut, + PartialEq, + Debug, + FromWorld + ) +)] +pub struct OneToMany { + // [Entity; 7] chosen to keep entities at 64 bytes on 64 bit platforms. + entities: SmallVec<[Entity; 7]>, + #[cfg_attr(feature = "reflect", reflect(ignore))] + _phantom: PhantomData, +} + +impl Relationship for OneToMany { + type Other = ManyToOne; + + fn has(&self, entity: Entity) -> bool { + self.entities.contains(&entity) + } + + fn new(entity: Entity) -> Self { + Self { + entities: smallvec![entity], + _phantom: PhantomData, + } + } + + fn with(mut self, entity: Entity) -> Self { + if !self.has(entity) { + self.entities.push(entity); + } + + self + } + + fn without(mut self, entity: Entity) -> Option { + self.entities.retain(|&mut id| id != entity); + + (!self.entities.is_empty()).then_some(self) + } + + fn iter(&self) -> impl ExactSizeIterator { + self.entities.iter().copied() + } +} + +impl PartialEq for OneToMany { + fn eq(&self, other: &Self) -> bool { + self.entities == other.entities + } +} + +impl Eq for OneToMany {} + +impl Debug for OneToMany { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Has Many {:?} ({}) With One ({})", + self.entities, + core::any::type_name::(), + core::any::type_name::() + ) + } +} + +impl VisitEntitiesMut for OneToMany { + fn visit_entities_mut(&mut self, mut f: F) { + for entity in &mut self.entities { + f(entity); + } + } +} + +impl Deref for OneToMany { + type Target = [Entity]; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.entities + } +} + +impl<'a, R> IntoIterator for &'a OneToMany { + type Item = ::Item; + + type IntoIter = core::slice::Iter<'a, Entity>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + self.entities.iter() + } +} + +impl FromIterator for OneToMany { + fn from_iter>(iter: T) -> Self { + Self::from_smallvec(iter.into_iter().collect()) + } +} + +impl Default for OneToMany { + fn default() -> Self { + Self::new() + } +} + +impl OneToMany { + /// Gets the other [`Entity`] as a slice of length 1. + #[inline(always)] + pub fn as_slice(&self) -> &[Entity] { + &self.entities + } + + /// Create a new relationship. + #[inline(always)] + #[must_use] + pub fn new() -> Self { + Self::from_smallvec(SmallVec::new()) + } + + #[inline(always)] + #[must_use] + fn from_smallvec(entities: SmallVec<[Entity; 7]>) -> Self { + Self { + entities, + _phantom: PhantomData, + } + } + + /// Ensures the provided [`Entity`] is present in this relationship. + #[inline(always)] + #[must_use] + pub fn with(mut self, other: Entity) -> Self { + if !self.entities.contains(&other) { + self.entities.push(other); + } + self + } + + /// Ensures the provided [`Entity`] is _not_ present in this relationship. + #[inline(always)] + #[must_use] + pub fn without(mut self, other: Entity) -> Self { + self.entities.retain(|&mut e| e != other); + self + } + + /// Swaps the entity at `a_index` with the entity at `b_index`. + /// + /// # Panics + /// + /// Will panic if either index is out-of-bounds. + #[inline] + pub fn swap(&mut self, a_index: usize, b_index: usize) { + self.entities.swap(a_index, b_index); + } + + /// Sorts entities [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) + /// in place using the provided comparator function. + /// + /// For the underlying implementation, see [`slice::sort_by`]. + /// + /// For the unstable version, see [`sort_unstable_by`](OneToMany::sort_unstable_by). + /// + /// See also [`sort_by_key`](OneToMany::sort_by_key), [`sort_by_cached_key`](OneToMany::sort_by_cached_key). + #[inline] + pub fn sort_by(&mut self, compare: F) + where + F: FnMut(&Entity, &Entity) -> core::cmp::Ordering, + { + self.entities.sort_by(compare); + } + + /// Sorts entities [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) + /// in place using the provided key extraction function. + /// + /// For the underlying implementation, see [`slice::sort_by_key`]. + /// + /// For the unstable version, see [`sort_unstable_by_key`](OneToMany::sort_unstable_by_key). + /// + /// See also [`sort_by`](OneToMany::sort_by), [`sort_by_cached_key`](OneToMany::sort_by_cached_key). + #[inline] + pub fn sort_by_key(&mut self, compare: F) + where + F: FnMut(&Entity) -> K, + K: Ord, + { + self.entities.sort_by_key(compare); + } + + /// Sorts entities [stably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) + /// in place using the provided key extraction function. Only evaluates each key at most + /// once per sort, caching the intermediate results in memory. + /// + /// For the underlying implementation, see [`slice::sort_by_cached_key`]. + /// + /// See also [`sort_by`](OneToMany::sort_by), [`sort_by_key`](OneToMany::sort_by_key). + #[inline] + pub fn sort_by_cached_key(&mut self, compare: F) + where + F: FnMut(&Entity) -> K, + K: Ord, + { + self.entities.sort_by_cached_key(compare); + } + + /// Sorts entities [unstably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) + /// in place using the provided comparator function. + /// + /// For the underlying implementation, see [`slice::sort_unstable_by`]. + /// + /// For the stable version, see [`sort_by`](OneToMany::sort_by). + /// + /// See also [`sort_unstable_by_key`](OneToMany::sort_unstable_by_key). + #[inline] + pub fn sort_unstable_by(&mut self, compare: F) + where + F: FnMut(&Entity, &Entity) -> core::cmp::Ordering, + { + self.entities.sort_unstable_by(compare); + } + + /// Sorts entities [unstably](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) + /// in place using the provided key extraction function. + /// + /// For the underlying implementation, see [`slice::sort_unstable_by_key`]. + /// + /// For the stable version, see [`sort_by_key`](OneToMany::sort_by_key). + /// + /// See also [`sort_unstable_by`](OneToMany::sort_unstable_by). + #[inline] + pub fn sort_unstable_by_key(&mut self, compare: F) + where + F: FnMut(&Entity) -> K, + K: Ord, + { + self.entities.sort_unstable_by_key(compare); + } +} + +#[cfg(test)] +mod tests { + use bevy_ecs::{event::Events, world::World}; + + use crate::{many_to_one::ManyToOne, relationship::RelationshipEvent}; + + use super::OneToMany; + + /// A familial relationship + struct Family; + + /// Shorthand for a Parent in a Family relationship + type Parent = ManyToOne; + + /// Shorthand for a Parent in a Family relationship + type Children = OneToMany; + + #[test] + fn simple_add_then_remove() { + let mut world = World::new(); + + world.register_component::(); + world.register_component::(); + + let a = world.spawn_empty().id(); + let b = world.spawn(Parent::new(a)).id(); + let c = world.spawn(Parent::new(a)).id(); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b, c]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + assert_eq!(world.get::(c), Some(&Parent::new(a))); + + world.entity_mut(a).remove::(); + + world.flush(); + + assert_eq!(world.get::(a), None); + assert_eq!(world.get::(b), None); + assert_eq!(world.get::(c), None); + } + + #[test] + fn partial_add_then_remove() { + let mut world = World::new(); + + world.register_component::(); + world.register_component::(); + + let a = world.spawn_empty().id(); + let b = world.spawn(Parent::new(a)).id(); + let c = world.spawn(Parent::new(a)).id(); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b, c]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + assert_eq!(world.get::(c), Some(&Parent::new(a))); + + world.entity_mut(c).remove::(); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + assert_eq!(world.get::(c), None); + } + + #[test] + fn take_and_return() { + let mut world = World::new(); + + world.register_component::(); + world.register_component::(); + + let a = world.spawn_empty().id(); + let b = world.spawn(Parent::new(a)).id(); + let c = world.spawn_empty().id(); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + assert_eq!(world.get::(c), None); + + let component = world.entity_mut(a).take::().unwrap(); + + let component = component.with(c); + + world.entity_mut(a).insert(component); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b, c]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + assert_eq!(world.get::(c), Some(&Parent::new(a))); + } + + #[test] + fn event_testing() { + let mut world = World::new(); + + world.register_component::(); + world.register_component::(); + world.init_resource::>>(); + + let a = world.spawn_empty().id(); + let b = world.spawn(Parent::new(a)).id(); + + world.flush(); + + assert_eq!( + world + .get::(a) + .map(|c| c.iter().copied().collect::>()), + Some(vec![b]) + ); + assert_eq!(world.get::(b), Some(&Parent::new(a))); + + assert_eq!( + world + .resource_mut::>>() + .drain() + .collect::>(), + vec![RelationshipEvent::::added(b, a)] + ); + + world.entity_mut(b).remove::(); + + world.flush(); + + assert_eq!(world.get::(a), None); + assert_eq!(world.get::(b), None); + + assert_eq!( + world + .resource_mut::>>() + .drain() + .collect::>(), + vec![RelationshipEvent::::removed(b, a)] + ); + } +} diff --git a/crates/bevy_hierarchy/src/relationship.rs b/crates/bevy_hierarchy/src/relationship.rs new file mode 100644 index 0000000000000..fba2758197390 --- /dev/null +++ b/crates/bevy_hierarchy/src/relationship.rs @@ -0,0 +1,267 @@ +use bevy_ecs::{ + component::{Component, ComponentId}, + entity::Entity, + event::{Event, Events}, + world::{DeferredWorld, World}, +}; +use core::marker::PhantomData; +use smallvec::SmallVec; + +/// Trait representing a relationship [`Component`]. +/// +/// A relationship consists of two [entities](Entity), one with this [`Component`], +/// and the other with the [`Other`](Relationship::Other). +/// These entities are referred to as `primary` and `foreign` to align with typical +/// relational database terminology. +/// The `primary` owns a component which contains some number of `foreign` entities. +/// This trait is designed to ensure that those `foreign` entities also own a component of type +/// [Other](Relationship::Other), where its `foreign` entities include the aforementioned `primary`. +pub(crate) trait Relationship: Component + Sized { + /// The other [`Component`] used to form this relationship. + type Other: Relationship; + + /// Whether this [`Relationship`] [`Component`] has the provided [`Entity`]. + fn has(&self, entity: Entity) -> bool; + + /// Create a new [`Relationship`] [`Component`] with the provided [`Entity`]. + fn new(entity: Entity) -> Self; + + /// Modify an existing [`Relationship`] [`Component`] to ensure it includes + /// the provided [`Entity`]. + fn with(self, entity: Entity) -> Self; + + /// Modify an existing [`Relationship`] [`Component`] to ensure it does not + /// include the provided [`Entity`]. + /// + /// Returns [`None`] if this [`Entity`] is the last member of this relationship. + fn without(self, entity: Entity) -> Option; + + /// Iterate over all [entities](Entity) this [`Relationship`] [`Component`] contains. + fn iter(&self) -> impl ExactSizeIterator; + + fn len(&self) -> usize { + self.iter().len() + } + + fn get(&self, index: usize) -> Option { + self.iter().nth(index) + } + + fn associate(mut world: DeferredWorld<'_>, primary_id: Entity, _component: ComponentId) { + world.commands().queue(move |world: &mut World| { + let foreign_ids_len = world + .get_entity(primary_id) + .ok() + .and_then(|a| a.get::()) + .map(Self::len); + + let Some(foreign_ids_len) = foreign_ids_len else { + return; + }; + + for foreign_id_index in 0..foreign_ids_len { + let foreign = world + .get_entity(primary_id) + .ok() + .and_then(|primary| primary.get::()) + .map(|primary_relationship| primary_relationship.get(foreign_id_index).unwrap()) + .and_then(|foreign_id| world.get_entity_mut(foreign_id).ok()); + + let Some(mut foreign) = foreign else { return }; + + let foreign_id = foreign.id(); + + let foreign_points_to_primary = foreign + .get::() + .is_some_and(|foreign_relationship| foreign_relationship.has(primary_id)); + + if !foreign_points_to_primary { + let other = foreign + .take::() + .unwrap_or(Self::Other::new(primary_id)) + .with(primary_id); + + foreign.insert(other); + + if let Some(mut events) = + world.get_resource_mut::>>() + { + events.send(RelationshipEvent::::added(primary_id, foreign_id)); + } + } + } + }); + } + + fn disassociate(mut world: DeferredWorld<'_>, primary_id: Entity, _component: ComponentId) { + let Some(primary_relationship) = world.get::(primary_id) else { + unreachable!("component hook should only be called when component is available"); + }; + + // Cloning to allow a user to `take` the component for modification + // [Entity; 7] chosen to keep b_ids at 64 bytes on 64 bit platforms. + let foreign_ids = primary_relationship + .iter() + .collect::>(); + + world.commands().queue(move |world: &mut World| { + for foreign_id in foreign_ids { + let primary_points_to_foreign = world + .get_entity(primary_id) + .ok() + .and_then(|primary| primary.get::()) + .is_some_and(|primary_relationship| primary_relationship.has(foreign_id)); + + let foreign_points_to_primary = world + .get_entity(foreign_id) + .ok() + .and_then(|foreign| foreign.get::()) + .is_some_and(|foreign_relationship| foreign_relationship.has(primary_id)); + + if foreign_points_to_primary && !primary_points_to_foreign { + if let Ok(mut foreign) = world.get_entity_mut(foreign_id) { + // Using a placeholder relationship to avoid triggering on_remove and on_insert + // hooks erroneously. + let mut placeholder = Self::Other::new(Entity::PLACEHOLDER); + let mut other = foreign.get_mut::().unwrap(); + let other = other.as_mut(); + + core::mem::swap(&mut placeholder, other); + + if let Some(mut new_other) = placeholder.without(primary_id) { + core::mem::swap(&mut new_other, other); + } else { + foreign.remove::(); + } + } + + if let Some(mut events) = + world.get_resource_mut::>>() + { + events.send(RelationshipEvent::::removed(primary_id, foreign_id)); + } + } + } + }); + } +} + +/// A relationship event. +#[derive(Event)] +pub enum RelationshipEvent { + /// A relationship was added between two [entities](Entity). + Added(RelationshipEventDetails), + /// A relationship was removed from two [entities](Entity). + Removed(RelationshipEventDetails), +} + +impl RelationshipEvent { + /// Create a new [`Added`](RelationshipEvent::Added) [`Event`] + pub const fn added(primary: Entity, foreign: Entity) -> Self { + Self::Added(RelationshipEventDetails::new(primary, foreign)) + } + + /// Create a new [`Removed`](RelationshipEvent::Removed) [`Event`] + pub const fn removed(primary: Entity, foreign: Entity) -> Self { + Self::Removed(RelationshipEventDetails::new(primary, foreign)) + } + + /// Get the primary [`Entity`] in this [`Event`]. + /// The primary is the _cause_ of the event, while the foreign is the relation. + pub const fn primary(&self) -> Entity { + match self { + Self::Added(details) | Self::Removed(details) => details.primary(), + } + } + + /// Get the foreign [`Entity`] in this [`Event`]. + /// The primary is the _cause_ of the event, while the foreign is the relation. + pub const fn foreign(&self) -> Entity { + match self { + Self::Added(details) | Self::Removed(details) => details.foreign(), + } + } +} + +impl core::fmt::Debug for RelationshipEvent { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Added(arg0) => f.debug_tuple("Added").field(arg0).finish(), + Self::Removed(arg0) => f.debug_tuple("Removed").field(arg0).finish(), + } + } +} + +impl PartialEq for RelationshipEvent { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Added(l0), Self::Added(r0)) | (Self::Removed(l0), Self::Removed(r0)) => l0 == r0, + _ => false, + } + } +} + +impl Eq for RelationshipEvent {} + +impl Clone for RelationshipEvent { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for RelationshipEvent {} + +/// The details of a [`RelationshipEvent`]. +pub struct RelationshipEventDetails { + primary: Entity, + foreign: Entity, + phantom_data: PhantomData, +} + +impl RelationshipEventDetails { + /// Create a new [`RelationshipEventDetails`] for a `primary` and a `foreign` [`Entity`]. + /// The `primary` [`Entity`] is the cause of the [`Event`], while the `foreign` + /// is the other member of the relationship. + pub const fn new(primary: Entity, foreign: Entity) -> Self { + Self { + primary, + foreign, + phantom_data: PhantomData, + } + } + + /// Get the [`Entity`] that caused this [`Event`] to be triggered. + pub const fn primary(&self) -> Entity { + self.primary + } + + /// Get the [`Entity`] related to the `primary` [`Entity`]. + pub const fn foreign(&self) -> Entity { + self.foreign + } +} + +impl core::fmt::Debug for RelationshipEventDetails { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("RelationshipEventDetails") + .field("primary", &self.primary) + .field("foreign", &self.foreign) + .finish() + } +} + +impl PartialEq for RelationshipEventDetails { + fn eq(&self, other: &Self) -> bool { + self.primary == other.primary && self.foreign == other.foreign + } +} + +impl Eq for RelationshipEventDetails {} + +impl Clone for RelationshipEventDetails { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for RelationshipEventDetails {} diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 3cb680e15e25d..a4be815241081 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -488,11 +488,9 @@ mod tests { query::Without, schedule::{apply_deferred, IntoSystemConfigs, Schedule}, system::RunSystemOnce, - world::World, - }; - use bevy_hierarchy::{ - despawn_with_children_recursive, BuildChildren, ChildBuild, Children, Parent, + world::{Command, World}, }; + use bevy_hierarchy::{BuildChildren, ChildBuild, Children, DespawnRecursive, Parent}; use bevy_math::{vec2, Rect, UVec2, Vec2}; use bevy_render::{ camera::{ManualTextureViews, OrthographicProjection}, @@ -803,7 +801,9 @@ mod tests { } // despawn the parent entity and its descendants - despawn_with_children_recursive(&mut world, ui_parent_entity, true); + DespawnRecursive::::new(ui_parent_entity) + .with_warn(true) + .apply(&mut world); ui_schedule.run(&mut world);