Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Panic calling DynamicScene::write_to_world with MapEntities and ComponentHooks #14300

Closed
mrchantey opened this issue Jul 14, 2024 · 3 comments · Fixed by #15405
Closed

Panic calling DynamicScene::write_to_world with MapEntities and ComponentHooks #14300

mrchantey opened this issue Jul 14, 2024 · 3 comments · Fixed by #15405
Labels
A-ECS Entities, components, systems, and events A-Scenes Serialized ECS data stored on the disk C-Bug An unexpected or incorrect behavior S-Needs-Design This issue requires design work to think about how it would best be accomplished
Milestone

Comments

@mrchantey
Copy link
Contributor

mrchantey commented Jul 14, 2024

Bevy version

0.14.0

What you did

  • call DynamicScene::write_to_world with two components:
    1. One that uses MapEntities
    2. One that calls world.commands().spawn_empty() in ComponentHooks::on_add

What went wrong

Panics at bevy_ecs::entity::Entities::verify_flushed, traced back to scene::write_to_world

thread 'main' panicked at C:\work-ref\bevy\crates\bevy_ecs\src\entity\mod.rs:582:9:
flush() needs to be called before this operation is legal

Additional information

I dont know enough about the ecs internals to understand whats happening, possibly commands from the DeferredWorld in ComponentHooks::on_add are being called at an inappropriate time?

Full reproducible

use bevy::ecs::entity::MapEntities;
use bevy::ecs::reflect::ReflectMapEntities;
use bevy::prelude::*;

#[derive(Default, Component, Reflect)]
#[reflect(Component)]
struct CompA;
#[derive(Component, Reflect)]
// #[reflect(Component)]             // OK
#[reflect(Component, MapEntities)]   // Breaks
struct CompB(pub Entity);

impl MapEntities for CompB {
  fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
    self.0 = entity_mapper.map_entity(self.0);
  }
}

fn main() {
  let mut app = App::new();
  app.register_type::<CompA>().register_type::<CompB>();
  let world = app.world_mut();
  world
    .register_component_hooks::<CompA>()
    .on_add(|mut world, _, _| {
      world
        .commands()
        .spawn_empty();
    });
  world.spawn((CompB(Entity::PLACEHOLDER), CompA));

  let scene = DynamicScene::from_world(world);
  scene.write_to_world(world, &mut default()).unwrap();
}
@mrchantey mrchantey added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Jul 14, 2024
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events A-Scenes Serialized ECS data stored on the disk S-Needs-Investigation This issue requires detective work to figure out what's going wrong and removed S-Needs-Triage This issue needs to be labelled labels Jul 14, 2024
@alice-i-cecile alice-i-cecile added this to the 0.14.1 milestone Jul 14, 2024
@mrchantey
Copy link
Contributor Author

looks related to #14465

@alice-i-cecile alice-i-cecile modified the milestones: 0.14.1, 0.14.2 Aug 2, 2024
@alice-i-cecile alice-i-cecile added S-Needs-Design This issue requires design work to think about how it would best be accomplished and removed S-Needs-Investigation This issue requires detective work to figure out what's going wrong labels Sep 2, 2024
@mockersf mockersf modified the milestones: 0.14.2, 0.14.3 Sep 5, 2024
@jrobsonchase
Copy link
Contributor

Can confirm - this is almost identical to #14465. In both cases, observers or hooks are able to create pending entities which aren't getting flushed before an operation is attempted which requires a flush in advance.

Still trying to figure out the best place to put the flush for this one.

@jrobsonchase
Copy link
Contributor

Maybe this is more of an issue of SceneEntityMapper not upholding the Entities flushing contract. Could stick a world.flush_entities() at the start of SceneEntityMapper and call it good?

Still seems like observers are able to put the Entities into an unexpected needs-flushing state that should be addressed though.

github-merge-queue bot pushed a commit that referenced this issue Sep 24, 2024
# Objective

- Add a test case for #14300 

Fixes #14300

## Solution

`SceneEntityMapper` relies on operations on `Entities` that require
flushing in advance, such as `alloc` and `free`. Previously, it wasn't
calling `world.flush_entities()` itself and relied on its caller having
flushed beforehand. This wasn't an issue before observers and hooks were
released, since entity reservation was happening at expected times. Now
that hooks and observers are a thing, they can introduce a need to
flush.

We have a few options:
* Flush after each observer/hook run
* Flush between each paired observer/hook and operation that requires a
flush
* Flush before operations requiring it

The first option for this case seemed trickier to reason about than I
wanted, since it involved the `BundleInserter` and its
`UnsafeWorldCell`, and the second is generally harder to track down. The
third seemed the most straightforward and conventional, since we can see
a flush occurring at the start of a number of `World` methods.
Therefore, we're letting `SceneEntityMapper` be in charge of upholding
its own invariants and calling `flush_entities` when it's created.

## Testing

Added a new test case modeled after #14300
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events A-Scenes Serialized ECS data stored on the disk C-Bug An unexpected or incorrect behavior S-Needs-Design This issue requires design work to think about how it would best be accomplished
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants