bevyengine / bevyengine/bevy

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

Open
#16,608 0 comments 1 reaction 0 assignees View on GitHub
A-States C-Bug S-Needs-Design
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## Bevy version

bevy = "0.15.0"

## What you did

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

## What went wrong

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

## Additional information
This code

```rust
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::()
.add_sub_state::()
.add_sub_state::()

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

fn pong(from: S) -> impl FnMut() {
move || info!("pong: {}::{:?}", type_name::(), 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>) {}
```

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>
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>`
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.

Contributor guide

Open the contributing guide

Research direction

Start with the minimal App reproduction in the issue and trace StatesPlugin, add_sub_state, and ResMut> handling when the parent state is inactive. Done means the inaccessible substate request is no longer silently dropped and produces the expected transition or warning, with the reproduction confirming the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
game-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.