bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.46k stars 3.6k forks source link

Runtime required components do not work correctly for components added through `#[require(...)]` #16406

Closed Jondolf closed 1 week ago

Jondolf commented 1 week ago

Bevy version

Bevy 0.15.0-rc.3

What you did

I have a component, let's say A, that requires another component B using #[require(B)]. B then requires another component C, but the requirement is registered in a plugin using app.register_required_components::<B, C>().

What went wrong

When I spawn an entity with A, C is not added!

Here's a simple test that fails:

#[test]
fn runtime_required_components() {
    // `A` requires `B` directly.
    #[derive(Component)]
    #[require(B)]
    struct A;

    #[derive(Component, Default)]
    struct B;

    #[derive(Component, Default)]
    struct C;

    let mut world = World::new();

    // `B` requires `C` with runtime registration.
    // This *fails*, but not when using `#[require(C)]` for `B`.
    world.register_required_components::<B, C>();

    // Adding runtime registration for `A -> B` also doesn't help if done after `B -> C`,
    // but it *does* work if done before.
    let _ = world.try_register_required_components::<A, B>();

    let entity = world.spawn(A).id();
    assert!(world.entity(entity).get::<C>().is_some());
}

If the entity is spawned with B directly, it does work:

#[test]
fn runtime_required_components_2() {
    #[derive(Component)]
    #[require(B)]
    struct A;

    #[derive(Component, Default)]
    struct B;

    #[derive(Component, Default)]
    struct C;

    let mut world = World::new();

    world.register_required_components::<B, C>();

    let _ = world.try_register_required_components::<A, B>();

    let id = world.spawn(B).id();
    assert!(world.entity(id).get::<C>().is_some());
}

This implies that the problem is related to runtime requirements not being properly propagated up the inheritance tree if the higher levels use #[require(...)], or if the higher level requirement is added after the lower level requirement.

BenjaminBrienen commented 1 week ago
        let mut world = World::new();
        world.register_required_components::<B, C>();
        let result = world.try_register_required_components::<A, B>();
        assert!(result.is_ok());
        let id = world.spawn(B).id();
        assert!(world.entity(id).get::<C>().is_some());

The first assertion actually fails, so the problem seems to be with the attempt to register the required component.

BenjaminBrienen commented 1 week ago

DuplicateRegistration(ComponentId(6), ComponentId(4))

Jondolf commented 1 week ago

That one is supposed to return an error (hence why I used try_) since the requirement is already defined earlier with #[require(B)]. It's not related to the issue where the requirement for A -> C isn't added correctly

cart commented 1 week ago

This isn't fully resolved by #16410:

    #[test]
    fn runtime_required_components_propagate_up_multiple() {
        #[derive(Component)]
        struct A;

        #[derive(Component, Default)]
        struct B;

        #[derive(Component, Default)]
        struct C;

        #[derive(Component, Default)]
        struct D;

        let mut world = World::new();

        world.register_required_components::<A, B>();
        world.register_required_components::<B, C>();
        world.register_required_components::<C, D>();

        let id = world.spawn(A).id();

        assert!(world.entity(id).get::<B>().is_some());
        assert!(world.entity(id).get::<C>().is_some());
        assert!(world.entity(id).get::<D>().is_some());
    }