-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
Bevy version and features
What you did
use bevy_ecs::{lifecycle::HookContext, prelude::*, system::SystemId, world::DeferredWorld};
fn main() {
let mut world = World::new();
let system_cleanup = world.spawn_empty().id();
let system_id = world.register_system(move |mut commands: Commands| {
commands.entity(system_cleanup).despawn();
});
world
.entity_mut(system_cleanup)
.insert(SystemCleanup(system_id));
world.run_system(system_id).unwrap();
}
#[derive(Component)]
#[component(on_replace = on_replace)]
struct SystemCleanup(SystemId);
fn on_replace(mut world: DeferredWorld, context: HookContext) {
let system = world.get_mut::<SystemCleanup>(context.entity).unwrap().0;
world.commands().unregister_system(system);
}While this may seem contrived at first glance, it's a pretty useful pattern in my code base for ephemeral, self-cleaning event handlers.
What went wrong
thread 'main' (22524110) panicked at /Users/corvus/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_ecs-0.18.0-rc.2/src/world/entity_access/world_mut.rs:1806:14:
Attempted to update the location of a despawned entity, which is impossible. This was the result of performing an operation on this EntityWorldMut that queued a despawn command: InvalidEntityError { entity: 1v0, current_generation: EntityGeneration(1) }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Discussion
As far as I can tell, the main contributing factor to this bug was introduced in #19451, where update_location can now panic. I tried a trivial fix for the offending insert:
if let Ok(mut entity) = self.get_entity_mut(id.entity)
&& !entity.is_despawned()
{
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
initialized,
system,
});
}But this can't actually determine whether the queued commands will despawn it. I believe the despawning commands are flushed here, so there's no opportunity to check before updating the location.
A proper fix isn't obvious to me. Without larger changes to update_location such as making it fallible, the only solution I can see is simply not panicking (assuming it's not preserving any safety invariants). Naturally, not panicking poses its own issues.