bevy icon indicating copy to clipboard operation
bevy copied to clipboard

`NextState<T>` event gets silently dropped under specific conditions

Open Phoqinu opened this issue 1 year ago • 0 comments

Bevy version

bevy = "0.15.0"

What you did

Requested ResMut<NextState<T>> in system where T is a SubStates and this substate is not "active" yet.

What went wrong

System with ResMut<NextState<T>> will get executed, next state set but no state transition/warn will happen.

Additional information

This code

use std::any::type_name;

use bevy::{
    log::LogPlugin,
    prelude::*,
    state::{app::StatesPlugin, state::FreelyMutableState},
};


fn main() {
    let mut app = App::new();

    app.add_plugins((MinimalPlugins, StatesPlugin, LogPlugin::default()))

        .init_state::<OuterState>()
        .add_sub_state::<LeftInnerState>()
        .add_sub_state::<RightInnerState>()

        .add_systems(Update, ping(LeftInnerState::Right).run_if(run_once))
        .add_systems(OnEnter(LeftInnerState::Right), pong(LeftInnerState::Right))

        .add_systems(Update, ping(RightInnerState::Left).run_if(run_once))
        .add_systems(OnEnter(RightInnerState::Left),pong(RightInnerState::Left))

        .add_systems(Update, warn_me.run_if(run_once));

    app.update();
    app.update();
    app.update();
}

fn ping<S: FreelyMutableState>(next: S) -> impl FnMut(ResMut<NextState<S>>) {
    move |mut next_state| {
        next_state.set(next.clone());
        info!("ping: {}::{:?}", type_name::<S>(), next.clone())
    }
}

fn pong<S: FreelyMutableState>(from: S) -> impl FnMut() {
    move || info!("pong: {}::{:?}", type_name::<S>(), from.clone())
}

#[derive(Debug, Default, States, Hash, PartialEq, Eq, Clone)]
enum OuterState {
    #[default]
    Left,
    Right,
}

#[derive(Debug, Default, SubStates, Hash, PartialEq, Eq, Clone)]
#[source(OuterState = OuterState::Left)]
enum LeftInnerState {
    #[default]
    Left,
    Right,
}

#[derive(Debug, Default, SubStates, Hash, PartialEq, Eq, Clone)]
#[source(OuterState = OuterState::Right)]
enum RightInnerState {
    Left,
    #[default]
    Right,
}

fn warn_me(_: Res<State<RightInnerState>>) {}

produces this:

2024-12-02T17:07:47.826948Z  INFO bevy_silent_event_drop_test: ping: bevy_silent_event_drop_test::RightInnerState::Left
2024-12-02T17:07:47.826948Z  WARN bevy_ecs::system::function_system: bevy_silent_event_drop_test::warn_me did not run because it requested inaccessible system parameter Res<State<RightInnerState>>
2024-12-02T17:07:47.827291Z  INFO bevy_silent_event_drop_test: ping: bevy_silent_event_drop_test::LeftInnerState::Right
2024-12-02T17:07:47.827927Z  INFO bevy_silent_event_drop_test: pong: bevy_silent_event_drop_test::LeftInnerState::Right

From that 1 line is missing:

  1. INFO pong from bevy_silent_event_drop_test::RightInnerState::Left or
  2. WARN bevy_ecs::system::function_system: bevy_silent_event_drop_test::ping did not run because it requested inaccessible system parameter ResMut<NextState<RightInnerState>> or
  3. WARN received next state event for subsstate which is currently inaccessible due to it's parent not being in right state or something

It should be 2 or 3.

Phoqinu avatar Dec 02 '24 13:12 Phoqinu