bevyengine / bevyengine/bevy

Hooks/observers for require components should be invoked in a predictable order.

Open
#20,001 0 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Feature C-Usability
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## What problem does this solve or what need does it fill?

In the current entity spawn process, hooks/observers for components that are added directly are executed in the order they written in code.
However, the hooks/observers of their require components are executed in a completely non-deterministic order.

## What solution would you like?

It might be better to invoke hook/observer according to declaration order of the `Require` components.

## What alternative(s) have you considered?

There's no way to control the execution order of hooks/observers for `require` components.

## Additional context

```rust
use std::fmt::Debug;

use bevy::ecs::{component::Component, lifecycle::HookContext, world::{DeferredWorld, World}};

fn main() {
let mut world = World::default();
world.spawn(ABC);
world.spawn(GFEDCBA);

}

fn print_hook(
world: DeferredWorld,
HookContext { entity, ..}: HookContext,
){
println!("on_add {:?}",world.get::(entity));
}

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct A;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct B;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct C;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct D;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct E;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct F;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
struct G;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
#[require(A,B,C)]
struct ABC;

#[derive(Component, Default, Debug)]
#[component(on_add = print_hook::)]
#[require(G,F,E,D,C,B,A)]
struct GFEDCBA;
```

output:

``` shell
on_add Some(ABC)
on_add Some(B)
on_add Some(C)
on_add Some(A)
on_add Some(GFEDCBA)
on_add Some(E)
on_add Some(B)
on_add Some(F)
on_add Some(C)
on_add Some(D)
on_add Some(G)
on_add Some(A)
```

https://github.com/bevyengine/bevy/blob/1fb5a622973fb5850c4b85f293fe25d1de06040a/crates/bevy_ecs/src/bundle.rs#L543-L555

The reason is that `BundleInfo::component_ids` gets required component IDs from `RequiredComponents`, which internally uses a `HashMap` for storage.

I'm not sure if this is a bug, so I'm posting issue here.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.