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);
|
||||
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
|
||||
// relationship is inserted on top, this despawn would result in the removal of that identical
|
||||
// relationship ... not what we want!
|
||||
entity.queue(|mut entity: EntityWorldMut| {
|
||||
if entity
|
||||
.get::<Self::RelationshipTarget>()
|
||||
.is_some_and(RelationshipTarget::is_empty)
|
||||
{
|
||||
entity.remove::<Self::RelationshipTarget>();
|
||||
}
|
||||
});
|
||||
}
|
||||
if entity
|
||||
.get::<Self::RelationshipTarget>()
|
||||
.is_some_and(RelationshipTarget::is_empty)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
#[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