Skip to content

Commit ce57258

Browse files
Fix observers' access to relationship data during despawn_related (#20258)
# Objective - Fix issue where observers cannot access relationship data during `despawn_related` - Fixes #20106 ## Solution - Changed `despawn_related` to use `get()` instead of `take()` to preserve relationship components during the despawn process - Collect entities into a vector before despawning to ensure the relationship data remains accessible to observers and hooks ## Testing - Added test `despawn_related_observers_can_access_relationship_data` that reproduces the issue scenario --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
1 parent ba73f66 commit ce57258

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

crates/bevy_ecs/src/relationship/related_methods.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,15 @@ impl<'w> EntityWorldMut<'w> {
299299
/// Despawns entities that relate to this one via the given [`RelationshipTarget`].
300300
/// This entity will not be despawned.
301301
pub fn despawn_related<S: RelationshipTarget>(&mut self) -> &mut Self {
302-
if let Some(sources) = self.take::<S>() {
302+
if let Some(sources) = self.get::<S>() {
303+
// We have to collect here to defer removal, allowing observers and hooks to see this data
304+
// before it is finally removed.
305+
let sources = sources.iter().collect::<Vec<_>>();
303306
self.world_scope(|world| {
304-
for entity in sources.iter() {
307+
for entity in sources {
305308
if let Ok(entity_mut) = world.get_entity_mut(entity) {
306309
entity_mut.despawn();
307-
}
310+
};
308311
}
309312
});
310313
}
@@ -882,4 +885,41 @@ mod tests {
882885
let data = parent.get::<Parent>().unwrap().data;
883886
assert_eq!(data, 42);
884887
}
888+
889+
#[test]
890+
fn despawn_related_observers_can_access_relationship_data() {
891+
use crate::lifecycle::Replace;
892+
use crate::observer::On;
893+
use crate::prelude::Has;
894+
use crate::system::Query;
895+
896+
#[derive(Component)]
897+
struct MyComponent;
898+
899+
#[derive(Component, Default)]
900+
struct ObserverResult {
901+
success: bool,
902+
}
903+
904+
let mut world = World::new();
905+
let result_entity = world.spawn(ObserverResult::default()).id();
906+
907+
world.add_observer(
908+
move |trigger: On<Replace, MyComponent>,
909+
has_relationship: Query<Has<ChildOf>>,
910+
mut results: Query<&mut ObserverResult>| {
911+
let entity = trigger.target();
912+
if has_relationship.get(entity).unwrap_or(false) {
913+
results.get_mut(result_entity).unwrap().success = true;
914+
}
915+
},
916+
);
917+
918+
let parent = world.spawn_empty().id();
919+
let _child = world.spawn((MyComponent, ChildOf(parent))).id();
920+
921+
world.entity_mut(parent).despawn_related::<Children>();
922+
923+
assert!(world.get::<ObserverResult>(result_entity).unwrap().success);
924+
}
885925
}

0 commit comments

Comments
 (0)