Skip to content

Commit 50f70eb

Browse files
HeartofPhosmockersf
authored andcommitted
Fix custom relations panics with parent/child relations (#19341)
# Objective Fixes #18905 ## Solution `world.commands().entity(target_entity).queue(command)` calls `commands.with_entity` without an error handler, instead queue on `Commands` with an error handler ## Testing Added unit test Co-authored-by: Heart <>
1 parent b14f94e commit 50f70eb

File tree

1 file changed

+71
-10
lines changed
  • crates/bevy_ecs/src/relationship

1 file changed

+71
-10
lines changed

crates/bevy_ecs/src/relationship/mod.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,21 @@ pub trait Relationship: Component + Sized {
158158
{
159159
relationship_target.collection_mut_risky().remove(entity);
160160
if relationship_target.len() == 0 {
161-
if let Ok(mut entity) = world.commands().get_entity(target_entity) {
161+
let command = |mut entity: EntityWorldMut| {
162162
// this "remove" operation must check emptiness because in the event that an identical
163163
// relationship is inserted on top, this despawn would result in the removal of that identical
164164
// relationship ... not what we want!
165-
entity.queue(|mut entity: EntityWorldMut| {
166-
if entity
167-
.get::<Self::RelationshipTarget>()
168-
.is_some_and(RelationshipTarget::is_empty)
169-
{
170-
entity.remove::<Self::RelationshipTarget>();
171-
}
172-
});
173-
}
165+
if entity
166+
.get::<Self::RelationshipTarget>()
167+
.is_some_and(RelationshipTarget::is_empty)
168+
{
169+
entity.remove::<Self::RelationshipTarget>();
170+
}
171+
};
172+
173+
world
174+
.commands()
175+
.queue(command.with_entity(target_entity).handle_error_with(ignore));
174176
}
175177
}
176178
}
@@ -424,4 +426,63 @@ mod tests {
424426

425427
// No assert necessary, looking to make sure compilation works with the macros
426428
}
429+
430+
#[test]
431+
fn parent_child_relationship_with_custom_relationship() {
432+
use crate::prelude::ChildOf;
433+
434+
#[derive(Component)]
435+
#[relationship(relationship_target = RelTarget)]
436+
struct Rel(Entity);
437+
438+
#[derive(Component)]
439+
#[relationship_target(relationship = Rel)]
440+
struct RelTarget(Entity);
441+
442+
let mut world = World::new();
443+
444+
// Rel on Parent
445+
// Despawn Parent
446+
let mut commands = world.commands();
447+
let child = commands.spawn_empty().id();
448+
let parent = commands.spawn(Rel(child)).add_child(child).id();
449+
commands.entity(parent).despawn();
450+
world.flush();
451+
452+
assert!(world.get_entity(child).is_err());
453+
assert!(world.get_entity(parent).is_err());
454+
455+
// Rel on Parent
456+
// Despawn Child
457+
let mut commands = world.commands();
458+
let child = commands.spawn_empty().id();
459+
let parent = commands.spawn(Rel(child)).add_child(child).id();
460+
commands.entity(child).despawn();
461+
world.flush();
462+
463+
assert!(world.get_entity(child).is_err());
464+
assert!(!world.entity(parent).contains::<Rel>());
465+
466+
// Rel on Child
467+
// Despawn Parent
468+
let mut commands = world.commands();
469+
let parent = commands.spawn_empty().id();
470+
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
471+
commands.entity(parent).despawn();
472+
world.flush();
473+
474+
assert!(world.get_entity(child).is_err());
475+
assert!(world.get_entity(parent).is_err());
476+
477+
// Rel on Child
478+
// Despawn Child
479+
let mut commands = world.commands();
480+
let parent = commands.spawn_empty().id();
481+
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
482+
commands.entity(child).despawn();
483+
world.flush();
484+
485+
assert!(world.get_entity(child).is_err());
486+
assert!(!world.entity(parent).contains::<RelTarget>());
487+
}
427488
}

0 commit comments

Comments
 (0)