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 <>
This commit is contained in:
parent
e981bb8902
commit
131f99de23
@ -158,19 +158,21 @@ pub trait Relationship: Component + Sized {
|
|||||||
{
|
{
|
||||||
relationship_target.collection_mut_risky().remove(entity);
|
relationship_target.collection_mut_risky().remove(entity);
|
||||||
if relationship_target.len() == 0 {
|
if relationship_target.len() == 0 {
|
||||||
if let Ok(mut entity) = world.commands().get_entity(target_entity) {
|
let command = |mut entity: EntityWorldMut| {
|
||||||
// this "remove" operation must check emptiness because in the event that an identical
|
// this "remove" operation must check emptiness because in the event that an identical
|
||||||
// relationship is inserted on top, this despawn would result in the removal of that identical
|
// relationship is inserted on top, this despawn would result in the removal of that identical
|
||||||
// relationship ... not what we want!
|
// relationship ... not what we want!
|
||||||
entity.queue(|mut entity: EntityWorldMut| {
|
if entity
|
||||||
if entity
|
.get::<Self::RelationshipTarget>()
|
||||||
.get::<Self::RelationshipTarget>()
|
.is_some_and(RelationshipTarget::is_empty)
|
||||||
.is_some_and(RelationshipTarget::is_empty)
|
{
|
||||||
{
|
entity.remove::<Self::RelationshipTarget>();
|
||||||
entity.remove::<Self::RelationshipTarget>();
|
}
|
||||||
}
|
};
|
||||||
});
|
|
||||||
}
|
world
|
||||||
|
.commands()
|
||||||
|
.queue(command.with_entity(target_entity).handle_error_with(ignore));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,4 +426,63 @@ mod tests {
|
|||||||
|
|
||||||
// No assert necessary, looking to make sure compilation works with the macros
|
// No assert necessary, looking to make sure compilation works with the macros
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parent_child_relationship_with_custom_relationship() {
|
||||||
|
use crate::prelude::ChildOf;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[relationship(relationship_target = RelTarget)]
|
||||||
|
struct Rel(Entity);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[relationship_target(relationship = Rel)]
|
||||||
|
struct RelTarget(Entity);
|
||||||
|
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
// Rel on Parent
|
||||||
|
// Despawn Parent
|
||||||
|
let mut commands = world.commands();
|
||||||
|
let child = commands.spawn_empty().id();
|
||||||
|
let parent = commands.spawn(Rel(child)).add_child(child).id();
|
||||||
|
commands.entity(parent).despawn();
|
||||||
|
world.flush();
|
||||||
|
|
||||||
|
assert!(world.get_entity(child).is_err());
|
||||||
|
assert!(world.get_entity(parent).is_err());
|
||||||
|
|
||||||
|
// Rel on Parent
|
||||||
|
// Despawn Child
|
||||||
|
let mut commands = world.commands();
|
||||||
|
let child = commands.spawn_empty().id();
|
||||||
|
let parent = commands.spawn(Rel(child)).add_child(child).id();
|
||||||
|
commands.entity(child).despawn();
|
||||||
|
world.flush();
|
||||||
|
|
||||||
|
assert!(world.get_entity(child).is_err());
|
||||||
|
assert!(!world.entity(parent).contains::<Rel>());
|
||||||
|
|
||||||
|
// Rel on Child
|
||||||
|
// Despawn Parent
|
||||||
|
let mut commands = world.commands();
|
||||||
|
let parent = commands.spawn_empty().id();
|
||||||
|
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
|
||||||
|
commands.entity(parent).despawn();
|
||||||
|
world.flush();
|
||||||
|
|
||||||
|
assert!(world.get_entity(child).is_err());
|
||||||
|
assert!(world.get_entity(parent).is_err());
|
||||||
|
|
||||||
|
// Rel on Child
|
||||||
|
// Despawn Child
|
||||||
|
let mut commands = world.commands();
|
||||||
|
let parent = commands.spawn_empty().id();
|
||||||
|
let child = commands.spawn((ChildOf(parent), Rel(parent))).id();
|
||||||
|
commands.entity(child).despawn();
|
||||||
|
world.flush();
|
||||||
|
|
||||||
|
assert!(world.get_entity(child).is_err());
|
||||||
|
assert!(!world.entity(parent).contains::<RelTarget>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user