Skip to content

Commit 6e6f0ef

Browse files
committed
Add a faster version of replace_related
1 parent c7f135b commit 6e6f0ef

File tree

2 files changed

+371
-7
lines changed

2 files changed

+371
-7
lines changed

crates/bevy_ecs/src/hierarchy.rs

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
bundle::Bundle,
1313
component::{Component, HookContext},
1414
entity::Entity,
15-
relationship::{RelatedSpawner, RelatedSpawnerCommands},
15+
relationship::{RelatedSpawner, RelatedSpawnerCommands, Relationship},
1616
system::EntityCommands,
1717
world::{DeferredWorld, EntityWorldMut, FromWorld, World},
1818
};
@@ -179,11 +179,33 @@ impl<'w> EntityWorldMut<'w> {
179179
self.add_related::<ChildOf>(&[child])
180180
}
181181

182-
/// Replaces the children on this entity with a new list of children.
182+
/// Replaces all the related children with a new set of children.
183183
pub fn replace_children(&mut self, children: &[Entity]) -> &mut Self {
184184
self.replace_related::<ChildOf>(children)
185185
}
186186

187+
/// Replaces all the related children with a new set of children.
188+
///
189+
/// # Warning
190+
/// Not maintaining the function invariants may lead to erratic engine behavior including random crashes.
191+
/// See [`Self::replace_related_with_difference`] for invariants.
192+
///
193+
/// # Panics
194+
///
195+
/// In debug mode when function invariants are broken.
196+
pub fn replace_children_with_difference(
197+
&mut self,
198+
entities_to_unrelate: &[Entity],
199+
entities_to_relate: &[Entity],
200+
newly_related_entities: &[Entity],
201+
) -> &mut Self {
202+
self.replace_related_with_difference::<ChildOf>(
203+
entities_to_unrelate,
204+
entities_to_relate,
205+
newly_related_entities,
206+
)
207+
}
208+
187209
/// Spawns the passed bundle and adds it to this entity as a child.
188210
///
189211
/// For efficient spawning of multiple children, use [`with_children`].
@@ -240,6 +262,28 @@ impl<'a> EntityCommands<'a> {
240262
self.replace_related::<ChildOf>(children)
241263
}
242264

265+
/// Replaces all the related entities with a new set of entities.
266+
///
267+
/// # Warning
268+
/// Not maintaining the function invariants may lead to erratic engine behavior including random crashes.
269+
/// See [`EntityWorldMut::replace_related_with_difference`] for invariants.
270+
///
271+
/// # Panics
272+
///
273+
/// In debug mode when function invariants are broken.
274+
pub fn replace_children_with_difference<R: Relationship>(
275+
&mut self,
276+
entities_to_unrelate: &[Entity],
277+
entities_to_relate: &[Entity],
278+
newly_related_entities: &[Entity],
279+
) -> &mut Self {
280+
self.replace_related_with_difference::<R>(
281+
entities_to_unrelate,
282+
entities_to_relate,
283+
newly_related_entities,
284+
)
285+
}
286+
243287
/// Spawns the passed bundle and adds it to this entity as a child.
244288
///
245289
/// For efficient spawning of multiple children, use [`with_children`].
@@ -336,6 +380,8 @@ macro_rules! children {
336380

337381
#[cfg(test)]
338382
mod tests {
383+
use core::ops::{Deref, DerefMut};
384+
339385
use crate::{
340386
entity::Entity,
341387
hierarchy::{ChildOf, Children},
@@ -533,4 +579,169 @@ mod tests {
533579

534580
assert!(world.entity(parent).get::<Children>().is_none());
535581
}
582+
583+
#[test]
584+
fn insert_same_child_twice() {
585+
let mut world = World::new();
586+
587+
let parent = world.spawn_empty().id();
588+
let child = world.spawn_empty().id();
589+
590+
world.entity_mut(parent).add_child(child);
591+
world.entity_mut(parent).add_child(child);
592+
593+
let children = world.get::<Children>(parent).unwrap();
594+
assert_eq!(children.0, [child]);
595+
}
596+
597+
#[test]
598+
fn replace_with_difference() {
599+
let mut world = World::new();
600+
601+
let parent = world.spawn_empty().id();
602+
let child_a = world.spawn_empty().id();
603+
let child_b = world.spawn_empty().id();
604+
let child_c = world.spawn_empty().id();
605+
let child_d = world.spawn_empty().id();
606+
607+
// Test inserting new relations
608+
world.entity_mut(parent).replace_children_with_difference(
609+
&[],
610+
&[child_a, child_b],
611+
&[child_a, child_b],
612+
);
613+
614+
assert_eq!(
615+
world.entity(child_a).get::<ChildOf>().unwrap(),
616+
&ChildOf { parent }
617+
);
618+
assert_eq!(
619+
world.entity(child_b).get::<ChildOf>().unwrap(),
620+
&ChildOf { parent }
621+
);
622+
assert_eq!(
623+
world.entity(parent).get::<Children>().unwrap().0,
624+
[child_a, child_b]
625+
);
626+
627+
// Test replacing relations and changing order
628+
world.entity_mut(parent).replace_children_with_difference(
629+
&[child_b],
630+
&[child_d, child_c, child_a],
631+
&[child_c, child_d],
632+
);
633+
assert_eq!(
634+
world.entity(child_a).get::<ChildOf>().unwrap(),
635+
&ChildOf { parent }
636+
);
637+
assert_eq!(
638+
world.entity(child_c).get::<ChildOf>().unwrap(),
639+
&ChildOf { parent }
640+
);
641+
assert_eq!(
642+
world.entity(child_d).get::<ChildOf>().unwrap(),
643+
&ChildOf { parent }
644+
);
645+
assert_eq!(
646+
world.entity(parent).get::<Children>().unwrap().0,
647+
[child_d, child_c, child_a]
648+
);
649+
assert!(!world.entity(child_b).contains::<ChildOf>());
650+
651+
// Test removing relationships
652+
world.entity_mut(parent).replace_children_with_difference(
653+
&[child_a, child_d, child_c],
654+
&[],
655+
&[],
656+
);
657+
assert!(!world.entity(parent).contains::<Children>());
658+
assert!(!world.entity(child_a).contains::<ChildOf>());
659+
assert!(!world.entity(child_b).contains::<ChildOf>());
660+
assert!(!world.entity(child_c).contains::<ChildOf>());
661+
assert!(!world.entity(child_d).contains::<ChildOf>());
662+
}
663+
664+
#[test]
665+
fn replace_with_difference_on_empty() {
666+
let mut world = World::new();
667+
668+
let parent = world.spawn_empty().id();
669+
let child_a = world.spawn_empty().id();
670+
671+
world
672+
.entity_mut(parent)
673+
.replace_children_with_difference(&[child_a], &[], &[]);
674+
675+
assert!(!world.entity(parent).contains::<Children>());
676+
assert!(!world.entity(child_a).contains::<ChildOf>());
677+
}
678+
679+
#[test]
680+
#[should_panic]
681+
#[cfg_attr(
682+
not(debug_assertions),
683+
ignore = "we don't check invariants if debug assertions are off"
684+
)]
685+
fn replace_diff_invariant_overlapping_unrelate_with_relate() {
686+
let mut world = World::new();
687+
688+
let parent = world.spawn_empty().id();
689+
let child_a = world.spawn_empty().id();
690+
691+
world
692+
.entity_mut(parent)
693+
.replace_children_with_difference(&[], &[child_a], &[child_a]);
694+
695+
// This should panic
696+
world
697+
.entity_mut(parent)
698+
.replace_children_with_difference(&[child_a], &[child_a], &[]);
699+
}
700+
701+
#[test]
702+
#[should_panic]
703+
#[cfg_attr(
704+
not(debug_assertions),
705+
ignore = "we don't check invariants if debug assertions are off"
706+
)]
707+
708+
fn replace_diff_invariant_overlapping_unrelate_with_newly() {
709+
let mut world = World::new();
710+
711+
let parent = world.spawn_empty().id();
712+
let child_a = world.spawn_empty().id();
713+
let child_b = world.spawn_empty().id();
714+
715+
world
716+
.entity_mut(parent)
717+
.replace_children_with_difference(&[], &[child_a], &[child_a]);
718+
719+
// This should panic
720+
world.entity_mut(parent).replace_children_with_difference(
721+
&[child_b],
722+
&[child_a, child_b],
723+
&[child_b],
724+
);
725+
}
726+
727+
#[test]
728+
#[should_panic]
729+
#[cfg_attr(
730+
not(debug_assertions),
731+
ignore = "we don't check invariants if debug assertions are off"
732+
)]
733+
fn replace_diff_invariant_newly_not_subset() {
734+
let mut world = World::new();
735+
736+
let parent = world.spawn_empty().id();
737+
let child_a = world.spawn_empty().id();
738+
let child_b = world.spawn_empty().id();
739+
740+
// This should panic
741+
world.entity_mut(parent).replace_children_with_difference(
742+
&[],
743+
&[child_a, child_b],
744+
&[child_a],
745+
);
746+
}
536747
}

0 commit comments

Comments
 (0)