Fix unsound EntityMut::remove_children
. Add EntityMut::world_scope
(#6464)
`EntityMut::remove_children` does not call `self.update_location()` which is unsound. Verified by adding the following assertion, which fails when running the tests. ```rust let before = self.location(); self.update_location(); assert_eq!(before, self.location()); ``` I also removed incorrect messages like "parent entity is not modified" and the unhelpful "Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype" which might lead people to think that's the *only* thing that can change the entity's location. # Changelog Added `EntityMut::world_scope`. Co-authored-by: devil-ira <justthecooldude@gmail.com>
This commit is contained in:
parent
3a14ae40ee
commit
b0bd8722f3
@ -526,7 +526,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
|
|
||||||
/// Returns this `EntityMut`'s world.
|
/// Returns this `EntityMut`'s world.
|
||||||
///
|
///
|
||||||
/// See [`EntityMut::into_world_mut`] for a safe alternative.
|
/// See [`EntityMut::world_scope`] or [`EntityMut::into_world_mut`] for a safe alternative.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Caller must not modify the world in a way that changes the current entity's location
|
/// Caller must not modify the world in a way that changes the current entity's location
|
||||||
@ -543,6 +543,12 @@ impl<'w> EntityMut<'w> {
|
|||||||
self.world
|
self.world
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gives mutable access to this `EntityMut`'s [`World`] in a temporary scope.
|
||||||
|
pub fn world_scope(&mut self, f: impl FnOnce(&mut World)) {
|
||||||
|
f(self.world);
|
||||||
|
self.update_location();
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates the internal entity location to match the current location in the internal
|
/// Updates the internal entity location to match the current location in the internal
|
||||||
/// [`World`]. This is only needed if the user called [`EntityMut::world`], which enables the
|
/// [`World`]. This is only needed if the user called [`EntityMut::world`], which enables the
|
||||||
/// location to change.
|
/// location to change.
|
||||||
|
@ -456,31 +456,23 @@ pub trait BuildWorldChildren {
|
|||||||
|
|
||||||
impl<'w> BuildWorldChildren for EntityMut<'w> {
|
impl<'w> BuildWorldChildren for EntityMut<'w> {
|
||||||
fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self {
|
fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self {
|
||||||
{
|
let entity = self.id();
|
||||||
let entity = self.id();
|
self.world_scope(|world| {
|
||||||
let mut builder = WorldChildBuilder {
|
let mut builder = WorldChildBuilder {
|
||||||
current_entity: None,
|
current_entity: None,
|
||||||
parent_entities: vec![entity],
|
parent_entities: vec![entity],
|
||||||
// SAFETY: self.update_location() is called below. It is impossible to make EntityMut
|
world,
|
||||||
// function calls on `self` within the scope defined here
|
|
||||||
world: unsafe { self.world_mut() },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
spawn_children(&mut builder);
|
spawn_children(&mut builder);
|
||||||
}
|
});
|
||||||
self.update_location();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
|
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
{
|
self.world_scope(|world| {
|
||||||
// SAFETY: parent entity is not modified and its location is updated manually
|
|
||||||
let world = unsafe { self.world_mut() };
|
|
||||||
update_old_parents(world, parent, children);
|
update_old_parents(world, parent, children);
|
||||||
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
});
|
||||||
self.update_location();
|
|
||||||
}
|
|
||||||
if let Some(mut children_component) = self.get_mut::<Children>() {
|
if let Some(mut children_component) = self.get_mut::<Children>() {
|
||||||
children_component
|
children_component
|
||||||
.0
|
.0
|
||||||
@ -494,14 +486,9 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
|
|
||||||
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
|
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
{
|
self.world_scope(|world| {
|
||||||
// SAFETY: parent entity is not modified and its location is updated manually
|
|
||||||
let world = unsafe { self.world_mut() };
|
|
||||||
update_old_parents(world, parent, children);
|
update_old_parents(world, parent, children);
|
||||||
// Inserting a bundle in the children entities may change the parent entity's location if they were of the same archetype
|
});
|
||||||
self.update_location();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mut children_component) = self.get_mut::<Children>() {
|
if let Some(mut children_component) = self.get_mut::<Children>() {
|
||||||
children_component
|
children_component
|
||||||
.0
|
.0
|
||||||
@ -515,9 +502,9 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
|
|
||||||
fn remove_children(&mut self, children: &[Entity]) -> &mut Self {
|
fn remove_children(&mut self, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
// SAFETY: This doesn't change the parent's location
|
self.world_scope(|world| {
|
||||||
let world = unsafe { self.world_mut() };
|
remove_children(parent, children, world);
|
||||||
remove_children(parent, children, world);
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ impl<'w, 's, 'a> DespawnRecursiveExt for EntityCommands<'w, 's, 'a> {
|
|||||||
|
|
||||||
impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
||||||
/// Despawns the provided entity and its children.
|
/// Despawns the provided entity and its children.
|
||||||
fn despawn_recursive(mut self) {
|
fn despawn_recursive(self) {
|
||||||
let entity = self.id();
|
let entity = self.id();
|
||||||
|
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
@ -114,11 +114,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
|||||||
)
|
)
|
||||||
.entered();
|
.entered();
|
||||||
|
|
||||||
// SAFETY: EntityMut is consumed so even though the location is no longer
|
despawn_with_children_recursive(self.into_world_mut(), entity);
|
||||||
// valid, it cannot be accessed again with the invalid location.
|
|
||||||
unsafe {
|
|
||||||
despawn_with_children_recursive(self.world_mut(), entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn despawn_descendants(&mut self) {
|
fn despawn_descendants(&mut self) {
|
||||||
|
Loading…
Reference in New Issue
Block a user