bevy/crates/bevy_ecs/src/hierarchy.rs
Freyja-moth 772524b3a6 Change with_related to work with a Bundle and added with_relationships method (#18699)
# Objective

Fixes #18678

## Solution

Moved the current `with_related` method to `with_relationships` and
added a new `with_related` that uses a bundle.

I'm not entirely sold on the name just yet, if anyone has any ideas let
me know.

## Testing

I wasn't able to test these changes because it crashed my computer every
time I tried (fun). But there don't seem to be any tests that use the
old `with_related` method so it should be fine, hopefully

## Showcase

```rust
commands.spawn_empty()
    .with_related::<Relationship>(Name::new("Related thingy"))
    .with_relationships(|rel| {
        rel.spawn(Name::new("Second related thingy"));
    });
```

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-04-10 01:19:33 +02:00

997 lines
34 KiB
Rust

//! The canonical "parent-child" [`Relationship`] for entities, driven by
//! the [`ChildOf`] [`Relationship`] and the [`Children`] [`RelationshipTarget`].
//!
//! See [`ChildOf`] for a full description of the relationship and how to use it.
//!
//! [`Relationship`]: crate::relationship::Relationship
//! [`RelationshipTarget`]: crate::relationship::RelationshipTarget
#[cfg(feature = "bevy_reflect")]
use crate::reflect::{ReflectComponent, ReflectFromWorld};
use crate::{
bundle::Bundle,
component::{Component, HookContext},
entity::Entity,
relationship::{RelatedSpawner, RelatedSpawnerCommands, Relationship},
system::EntityCommands,
world::{DeferredWorld, EntityWorldMut, FromWorld, World},
};
use alloc::{format, string::String, vec::Vec};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::std_traits::ReflectDefault;
use core::ops::Deref;
use core::slice;
use disqualified::ShortName;
use log::warn;
/// Stores the parent entity of this child entity with this component.
///
/// This is a [`Relationship`] component, and creates the canonical
/// "parent / child" hierarchy. This is the "source of truth" component, and it pairs with
/// the [`Children`] [`RelationshipTarget`](crate::relationship::RelationshipTarget).
///
/// This relationship should be used for things like:
///
/// 1. Organizing entities in a scene
/// 2. Propagating configuration or data inherited from a parent, such as "visibility" or "world-space global transforms".
/// 3. Ensuring a hierarchy is despawned when an entity is despawned.
///
/// [`ChildOf`] contains a single "target" [`Entity`]. When [`ChildOf`] is inserted on a "source" entity,
/// the "target" entity will automatically (and immediately, via a component hook) have a [`Children`]
/// component inserted, and the "source" entity will be added to that [`Children`] instance.
///
/// If the [`ChildOf`] component is replaced with a different "target" entity, the old target's [`Children`]
/// will be automatically (and immediately, via a component hook) be updated to reflect that change.
///
/// Likewise, when the [`ChildOf`] component is removed, the "source" entity will be removed from the old
/// target's [`Children`]. If this results in [`Children`] being empty, [`Children`] will be automatically removed.
///
/// When a parent is despawned, all children (and their descendants) will _also_ be despawned.
///
/// You can create parent-child relationships in a variety of ways. The most direct way is to insert a [`ChildOf`] component:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # let mut world = World::new();
/// let root = world.spawn_empty().id();
/// let child1 = world.spawn(ChildOf(root)).id();
/// let child2 = world.spawn(ChildOf(root)).id();
/// let grandchild = world.spawn(ChildOf(child1)).id();
///
/// assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1, child2]);
/// assert_eq!(&**world.entity(child1).get::<Children>().unwrap(), &[grandchild]);
///
/// world.entity_mut(child2).remove::<ChildOf>();
/// assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1]);
///
/// world.entity_mut(root).despawn();
/// assert!(world.get_entity(root).is_err());
/// assert!(world.get_entity(child1).is_err());
/// assert!(world.get_entity(grandchild).is_err());
/// ```
///
/// However if you are spawning many children, you might want to use the [`EntityWorldMut::with_children`] helper instead:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # let mut world = World::new();
/// let mut child1 = Entity::PLACEHOLDER;
/// let mut child2 = Entity::PLACEHOLDER;
/// let mut grandchild = Entity::PLACEHOLDER;
/// let root = world.spawn_empty().with_children(|p| {
/// child1 = p.spawn_empty().with_children(|p| {
/// grandchild = p.spawn_empty().id();
/// }).id();
/// child2 = p.spawn_empty().id();
/// }).id();
///
/// assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1, child2]);
/// assert_eq!(&**world.entity(child1).get::<Children>().unwrap(), &[grandchild]);
/// ```
#[derive(Component, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(
feature = "bevy_reflect",
reflect(Component, PartialEq, Debug, FromWorld, Clone)
)]
#[relationship(relationship_target = Children)]
#[doc(alias = "IsChild", alias = "Parent")]
pub struct ChildOf(pub Entity);
impl ChildOf {
/// The parent entity of this child entity.
#[inline]
pub fn parent(&self) -> Entity {
self.0
}
/// The parent entity of this child entity.
#[deprecated(since = "0.16.0", note = "Use child_of.parent() instead")]
#[inline]
pub fn get(&self) -> Entity {
self.0
}
}
// TODO: We need to impl either FromWorld or Default so ChildOf can be registered as Reflect.
// This is because Reflect deserialize by creating an instance and apply a patch on top.
// However ChildOf 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 ChildOf {
#[inline(always)]
fn from_world(_world: &mut World) -> Self {
ChildOf(Entity::PLACEHOLDER)
}
}
/// Tracks which entities are children of this parent entity.
///
/// A [`RelationshipTarget`] collection component that is populated
/// with entities that "target" this entity with the [`ChildOf`] [`Relationship`] component.
///
/// Together, these components form the "canonical parent-child hierarchy". See the [`ChildOf`] component for the full
/// description of this relationship and instructions on how to use it.
///
/// # Usage
///
/// Like all [`RelationshipTarget`] components, this data should not be directly manipulated to avoid desynchronization.
/// Instead, modify the [`ChildOf`] components on the "source" entities.
///
/// To access the children of an entity, you can iterate over the [`Children`] component,
/// using the [`IntoIterator`] trait.
/// For more complex access patterns, see the [`RelationshipTarget`] trait.
///
/// [`RelationshipTarget`]: crate::relationship::RelationshipTarget
#[derive(Component, Default, Debug, PartialEq, Eq)]
#[relationship_target(relationship = ChildOf, linked_spawn)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld, Default))]
#[doc(alias = "IsParent")]
pub struct Children(Vec<Entity>);
impl Children {
/// 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<F>(&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<K, F>(&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<K, F>(&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<F>(&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<K, F>(&mut self, compare: F)
where
F: FnMut(&Entity) -> K,
K: Ord,
{
self.0.sort_unstable_by_key(compare);
}
}
impl<'a> IntoIterator for &'a Children {
type Item = <Self::IntoIter as Iterator>::Item;
type IntoIter = slice::Iter<'a, Entity>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl Deref for Children {
type Target = [Entity];
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// A type alias over [`RelatedSpawner`] used to spawn child entities containing a [`ChildOf`] relationship.
pub type ChildSpawner<'w> = RelatedSpawner<'w, ChildOf>;
/// A type alias over [`RelatedSpawnerCommands`] used to spawn child entities containing a [`ChildOf`] relationship.
pub type ChildSpawnerCommands<'w> = RelatedSpawnerCommands<'w, ChildOf>;
impl<'w> EntityWorldMut<'w> {
/// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
/// See also [`with_related`](Self::with_related).
pub fn with_children(&mut self, func: impl FnOnce(&mut ChildSpawner)) -> &mut Self {
self.with_related_entities(func);
self
}
/// Adds the given children to this entity
/// See also [`add_related`](Self::add_related).
pub fn add_children(&mut self, children: &[Entity]) -> &mut Self {
self.add_related::<ChildOf>(children)
}
/// Insert children at specific index.
/// See also [`insert_related`](Self::insert_related).
pub fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
self.insert_related::<ChildOf>(index, children)
}
/// Adds the given child to this entity
/// See also [`add_related`](Self::add_related).
pub fn add_child(&mut self, child: Entity) -> &mut Self {
self.add_related::<ChildOf>(&[child])
}
/// Replaces all the related children with a new set of children.
pub fn replace_children(&mut self, children: &[Entity]) -> &mut Self {
self.replace_related::<ChildOf>(children)
}
/// Replaces all the related children with a new set of children.
///
/// # Warning
///
/// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
/// Refer to [`Self::replace_related_with_difference`] for a list of these invariants.
///
/// # Panics
///
/// Panics when debug assertions are enabled if an invariant is is broken and the command is executed.
pub fn replace_children_with_difference(
&mut self,
entities_to_unrelate: &[Entity],
entities_to_relate: &[Entity],
newly_related_entities: &[Entity],
) -> &mut Self {
self.replace_related_with_difference::<ChildOf>(
entities_to_unrelate,
entities_to_relate,
newly_related_entities,
)
}
/// Spawns the passed bundle and adds it to this entity as a child.
///
/// For efficient spawning of multiple children, use [`with_children`].
///
/// [`with_children`]: EntityWorldMut::with_children
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
let parent = self.id();
self.world_scope(|world| {
world.spawn((bundle, ChildOf(parent)));
});
self
}
/// Removes the [`ChildOf`] component, if it exists.
#[deprecated(since = "0.16.0", note = "Use entity_mut.remove::<ChildOf>()")]
pub fn remove_parent(&mut self) -> &mut Self {
self.remove::<ChildOf>();
self
}
/// Inserts the [`ChildOf`] component with the given `parent` entity, if it exists.
#[deprecated(since = "0.16.0", note = "Use entity_mut.insert(ChildOf(entity))")]
pub fn set_parent(&mut self, parent: Entity) -> &mut Self {
self.insert(ChildOf(parent));
self
}
}
impl<'a> EntityCommands<'a> {
/// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
pub fn with_children(
&mut self,
func: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>),
) -> &mut Self {
self.with_related_entities(func);
self
}
/// Adds the given children to this entity
pub fn add_children(&mut self, children: &[Entity]) -> &mut Self {
self.add_related::<ChildOf>(children)
}
/// Insert children at specific index.
/// See also [`insert_related`](Self::insert_related).
pub fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
self.insert_related::<ChildOf>(index, children)
}
/// Adds the given child to this entity
pub fn add_child(&mut self, child: Entity) -> &mut Self {
self.add_related::<ChildOf>(&[child])
}
/// Replaces the children on this entity with a new list of children.
pub fn replace_children(&mut self, children: &[Entity]) -> &mut Self {
self.replace_related::<ChildOf>(children)
}
/// Replaces all the related entities with a new set of entities.
///
/// # Warning
///
/// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
/// Refer to [`EntityWorldMut::replace_related_with_difference`] for a list of these invariants.
///
/// # Panics
///
/// Panics when debug assertions are enabled if an invariant is is broken and the command is executed.
pub fn replace_children_with_difference<R: Relationship>(
&mut self,
entities_to_unrelate: &[Entity],
entities_to_relate: &[Entity],
newly_related_entities: &[Entity],
) -> &mut Self {
self.replace_related_with_difference::<R>(
entities_to_unrelate,
entities_to_relate,
newly_related_entities,
)
}
/// Spawns the passed bundle and adds it to this entity as a child.
///
/// For efficient spawning of multiple children, use [`with_children`].
///
/// [`with_children`]: EntityCommands::with_children
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
self.with_related::<ChildOf>(bundle);
self
}
/// Removes the [`ChildOf`] component, if it exists.
#[deprecated(since = "0.16.0", note = "Use entity_commands.remove::<ChildOf>()")]
pub fn remove_parent(&mut self) -> &mut Self {
self.remove::<ChildOf>();
self
}
/// Inserts the [`ChildOf`] component with the given `parent` entity, if it exists.
#[deprecated(since = "0.16.0", note = "Use entity_commands.insert(ChildOf(entity))")]
pub fn set_parent(&mut self, parent: Entity) -> &mut Self {
self.insert(ChildOf(parent));
self
}
}
/// An `on_insert` component hook that when run, will validate that the parent of a given entity
/// contains component `C`. This will print a warning if the parent does not contain `C`.
pub fn validate_parent_has_component<C: Component>(
world: DeferredWorld,
HookContext { entity, caller, .. }: HookContext,
) {
let entity_ref = world.entity(entity);
let Some(child_of) = entity_ref.get::<ChildOf>() else {
return;
};
if !world
.get_entity(child_of.parent())
.is_ok_and(|e| e.contains::<C>())
{
// TODO: print name here once Name lives in bevy_ecs
let name: Option<String> = None;
warn!(
"warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\
This will cause inconsistent behaviors! See: https://bevyengine.org/learn/errors/b0004",
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
ty_name = ShortName::of::<C>(),
name = name.map_or_else(
|| format!("Entity {}", entity),
|s| format!("The {s} entity")
),
);
}
}
/// Returns a [`SpawnRelatedBundle`] that will insert the [`Children`] component, spawn a [`SpawnableList`] of entities with given bundles that
/// relate to the [`Children`] entity via the [`ChildOf`] component, and reserve space in the [`Children`] for each spawned entity.
///
/// Any additional arguments will be interpreted as bundles to be spawned.
///
/// Also see [`related`](crate::related) for a version of this that works with any [`RelationshipTarget`] type.
///
/// ```
/// # use bevy_ecs::hierarchy::Children;
/// # use bevy_ecs::name::Name;
/// # use bevy_ecs::world::World;
/// # use bevy_ecs::children;
/// # use bevy_ecs::spawn::{Spawn, SpawnRelated};
/// let mut world = World::new();
/// world.spawn((
/// Name::new("Root"),
/// children![
/// Name::new("Child1"),
/// (
/// Name::new("Child2"),
/// children![Name::new("Grandchild")]
/// )
/// ]
/// ));
/// ```
///
/// [`RelationshipTarget`]: crate::relationship::RelationshipTarget
/// [`SpawnRelatedBundle`]: crate::spawn::SpawnRelatedBundle
/// [`SpawnableList`]: crate::spawn::SpawnableList
#[macro_export]
macro_rules! children {
[$($child:expr),*$(,)?] => {
$crate::hierarchy::Children::spawn(($($crate::spawn::Spawn($child)),*))
};
}
#[cfg(test)]
mod tests {
use crate::{
entity::Entity,
hierarchy::{ChildOf, Children},
relationship::{RelationshipHookMode, RelationshipTarget},
spawn::{Spawn, SpawnRelated},
world::World,
};
use alloc::{vec, vec::Vec};
#[derive(PartialEq, Eq, Debug)]
struct Node {
entity: Entity,
children: Vec<Node>,
}
impl Node {
fn new(entity: Entity) -> Self {
Self {
entity,
children: Vec::new(),
}
}
fn new_with(entity: Entity, children: Vec<Node>) -> Self {
Self { entity, children }
}
}
fn get_hierarchy(world: &World, entity: Entity) -> Node {
Node {
entity,
children: world
.entity(entity)
.get::<Children>()
.map_or_else(Default::default, |c| {
c.iter().map(|e| get_hierarchy(world, e)).collect()
}),
}
}
#[test]
fn hierarchy() {
let mut world = World::new();
let root = world.spawn_empty().id();
let child1 = world.spawn(ChildOf(root)).id();
let grandchild = world.spawn(ChildOf(child1)).id();
let child2 = world.spawn(ChildOf(root)).id();
// Spawn
let hierarchy = get_hierarchy(&world, root);
assert_eq!(
hierarchy,
Node::new_with(
root,
vec![
Node::new_with(child1, vec![Node::new(grandchild)]),
Node::new(child2)
]
)
);
// Removal
world.entity_mut(child1).remove::<ChildOf>();
let hierarchy = get_hierarchy(&world, root);
assert_eq!(hierarchy, Node::new_with(root, vec![Node::new(child2)]));
// Insert
world.entity_mut(child1).insert(ChildOf(root));
let hierarchy = get_hierarchy(&world, root);
assert_eq!(
hierarchy,
Node::new_with(
root,
vec![
Node::new(child2),
Node::new_with(child1, vec![Node::new(grandchild)])
]
)
);
// Recursive Despawn
world.entity_mut(root).despawn();
assert!(world.get_entity(root).is_err());
assert!(world.get_entity(child1).is_err());
assert!(world.get_entity(child2).is_err());
assert!(world.get_entity(grandchild).is_err());
}
#[test]
fn with_children() {
let mut world = World::new();
let mut child1 = Entity::PLACEHOLDER;
let mut child2 = Entity::PLACEHOLDER;
let root = world
.spawn_empty()
.with_children(|p| {
child1 = p.spawn_empty().id();
child2 = p.spawn_empty().id();
})
.id();
let hierarchy = get_hierarchy(&world, root);
assert_eq!(
hierarchy,
Node::new_with(root, vec![Node::new(child1), Node::new(child2)])
);
}
#[test]
fn add_children() {
let mut world = World::new();
let child1 = world.spawn_empty().id();
let child2 = world.spawn_empty().id();
let root = world.spawn_empty().add_children(&[child1, child2]).id();
let hierarchy = get_hierarchy(&world, root);
assert_eq!(
hierarchy,
Node::new_with(root, vec![Node::new(child1), Node::new(child2)])
);
}
#[test]
fn insert_children() {
let mut world = World::new();
let child1 = world.spawn_empty().id();
let child2 = world.spawn_empty().id();
let child3 = world.spawn_empty().id();
let child4 = world.spawn_empty().id();
let mut entity_world_mut = world.spawn_empty();
let first_children = entity_world_mut.add_children(&[child1, child2]);
let root = first_children.insert_children(1, &[child3, child4]).id();
let hierarchy = get_hierarchy(&world, root);
assert_eq!(
hierarchy,
Node::new_with(
root,
vec![
Node::new(child1),
Node::new(child3),
Node::new(child4),
Node::new(child2)
]
)
);
}
#[test]
fn self_parenting_invalid() {
let mut world = World::new();
let id = world.spawn_empty().id();
world.entity_mut(id).insert(ChildOf(id));
assert!(
world.entity(id).get::<ChildOf>().is_none(),
"invalid ChildOf relationships should self-remove"
);
}
#[test]
fn missing_parent_invalid() {
let mut world = World::new();
let parent = world.spawn_empty().id();
world.entity_mut(parent).despawn();
let id = world.spawn(ChildOf(parent)).id();
assert!(
world.entity(id).get::<ChildOf>().is_none(),
"invalid ChildOf relationships should self-remove"
);
}
#[test]
fn reinsert_same_parent() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let id = world.spawn(ChildOf(parent)).id();
world.entity_mut(id).insert(ChildOf(parent));
assert_eq!(
Some(&ChildOf(parent)),
world.entity(id).get::<ChildOf>(),
"ChildOf should still be there"
);
}
#[test]
fn spawn_children() {
let mut world = World::new();
let id = world.spawn(Children::spawn((Spawn(()), Spawn(())))).id();
assert_eq!(world.entity(id).get::<Children>().unwrap().len(), 2,);
}
#[test]
fn replace_children() {
let mut world = World::new();
let parent = world.spawn(Children::spawn((Spawn(()), Spawn(())))).id();
let &[child_a, child_b] = &world.entity(parent).get::<Children>().unwrap().0[..] else {
panic!("Tried to spawn 2 children on an entity and didn't get 2 children");
};
let child_c = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children(&[child_a, child_c]);
let children = world.entity(parent).get::<Children>().unwrap();
assert!(children.contains(&child_a));
assert!(children.contains(&child_c));
assert!(!children.contains(&child_b));
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert!(world.entity(child_b).get::<ChildOf>().is_none());
}
#[test]
fn replace_children_with_nothing() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
world.entity_mut(parent).add_children(&[child_a, child_b]);
assert_eq!(world.entity(parent).get::<Children>().unwrap().len(), 2);
world.entity_mut(parent).replace_children(&[]);
assert!(world.entity(child_a).get::<ChildOf>().is_none());
assert!(world.entity(child_b).get::<ChildOf>().is_none());
}
#[test]
fn insert_same_child_twice() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child = world.spawn_empty().id();
world.entity_mut(parent).add_child(child);
world.entity_mut(parent).add_child(child);
let children = world.get::<Children>(parent).unwrap();
assert_eq!(children.0, [child]);
assert_eq!(
world.entity(child).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
}
#[test]
fn replace_with_difference() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
// Test inserting new relations
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a, child_b],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_b).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_a, child_b]
);
// Test replacing relations and changing order
world.entity_mut(parent).replace_children_with_difference(
&[child_b],
&[child_d, child_c, child_a],
&[child_c, child_d],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_d).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_d, child_c, child_a]
);
assert!(!world.entity(child_b).contains::<ChildOf>());
// Test removing relationships
world.entity_mut(parent).replace_children_with_difference(
&[child_a, child_d, child_c],
&[],
&[],
);
assert!(!world.entity(parent).contains::<Children>());
assert!(!world.entity(child_a).contains::<ChildOf>());
assert!(!world.entity(child_b).contains::<ChildOf>());
assert!(!world.entity(child_c).contains::<ChildOf>());
assert!(!world.entity(child_d).contains::<ChildOf>());
}
#[test]
fn replace_with_difference_on_empty() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[child_a], &[], &[]);
assert!(!world.entity(parent).contains::<Children>());
assert!(!world.entity(child_a).contains::<ChildOf>());
}
#[test]
fn replace_with_difference_totally_new_children() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
// Test inserting new relations
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a, child_b],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_b).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_a, child_b]
);
// Test replacing relations and changing order
world.entity_mut(parent).replace_children_with_difference(
&[child_b, child_a],
&[child_d, child_c],
&[child_c, child_d],
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(child_d).get::<ChildOf>().unwrap(),
&ChildOf(parent)
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_d, child_c]
);
assert!(!world.entity(child_a).contains::<ChildOf>());
assert!(!world.entity(child_b).contains::<ChildOf>());
}
#[test]
fn replace_children_order() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
let initial_order = [child_a, child_b, child_c, child_d];
world.entity_mut(parent).add_children(&initial_order);
assert_eq!(
world.entity_mut(parent).get::<Children>().unwrap().0,
initial_order
);
let new_order = [child_d, child_b, child_a, child_c];
world.entity_mut(parent).replace_children(&new_order);
assert_eq!(world.entity(parent).get::<Children>().unwrap().0, new_order);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_overlapping_unrelate_with_relate() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[], &[child_a], &[child_a]);
// This should panic
world
.entity_mut(parent)
.replace_children_with_difference(&[child_a], &[child_a], &[]);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_overlapping_unrelate_with_newly() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[], &[child_a], &[child_a]);
// This should panic
world.entity_mut(parent).replace_children_with_difference(
&[child_b],
&[child_a, child_b],
&[child_b],
);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_newly_not_subset() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
// This should panic
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a],
);
}
#[test]
fn child_replace_hook_skip() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let other = world.spawn_empty().id();
let child = world.spawn(ChildOf(parent)).id();
world
.entity_mut(child)
.insert_with_relationship_hook_mode(ChildOf(other), RelationshipHookMode::Skip);
assert_eq!(
&**world.entity(parent).get::<Children>().unwrap(),
&[child],
"Children should still have the old value, as on_insert/on_replace didn't run"
);
}
}