From 524fb01457378096bf08e6bc90b085816cd24844 Mon Sep 17 00:00:00 2001 From: MiniaczQ Date: Mon, 15 Jul 2024 17:08:54 +0200 Subject: [PATCH] Make initial `StateTransition` run before `PreStartup` (#14208) # Objective - Fixes #14206 ## Solution - Run initial `StateTransition` as a startup schedule before `PreStartup`, instead of running it inside `Startup` as an exclusive system. Related discord discussion: https://discord.com/channels/691052431525675048/692572690833473578/1259543775668207678 ## Testing Reproduction now works correctly: ```rs use bevy::prelude::*; #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)] enum AppState { #[default] Menu, InGame, } fn main() { App::new() .add_plugins(DefaultPlugins) .init_state::() .add_systems(Startup, setup) .add_systems(OnEnter(AppState::Menu), enter_menu_state) .run(); } fn setup(mut next_state: ResMut>) { next_state.set(AppState::Menu); } fn enter_menu_state() { println!("Entered menu state"); } ``` ![image](https://github.com/bevyengine/bevy/assets/13040204/96d7a533-c439-4c0b-8f15-49f620903ce1) --- ## Changelog - Initial `StateTransition` runs before `PreStartup` instead of inside `Startup`. --- crates/bevy_state/src/app.rs | 11 ++++------ crates/bevy_state/src/state/mod.rs | 24 +++++++++------------- crates/bevy_state/src/state/transitions.rs | 15 ++------------ 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/crates/bevy_state/src/app.rs b/crates/bevy_state/src/app.rs index bf5698e811dfd..def0b7deef792 100644 --- a/crates/bevy_state/src/app.rs +++ b/crates/bevy_state/src/app.rs @@ -1,9 +1,5 @@ -use bevy_app::{App, MainScheduleOrder, Plugin, PreUpdate, Startup, SubApp}; -use bevy_ecs::{ - event::Events, - schedule::{IntoSystemConfigs, ScheduleLabel}, - world::FromWorld, -}; +use bevy_app::{App, MainScheduleOrder, Plugin, PreStartup, PreUpdate, SubApp}; +use bevy_ecs::{event::Events, schedule::IntoSystemConfigs, world::FromWorld}; use bevy_utils::{tracing::warn, warn_once}; use crate::state::{ @@ -215,7 +211,8 @@ impl Plugin for StatesPlugin { fn build(&self, app: &mut App) { let mut schedule = app.world_mut().resource_mut::(); schedule.insert_after(PreUpdate, StateTransition); - setup_state_transitions_in_world(app.world_mut(), Some(Startup.intern())); + schedule.insert_startup_before(PreStartup, StateTransition); + setup_state_transitions_in_world(app.world_mut()); } } diff --git a/crates/bevy_state/src/state/mod.rs b/crates/bevy_state/src/state/mod.rs index 5220b9f9152ad..5e5ea005df0d7 100644 --- a/crates/bevy_state/src/state/mod.rs +++ b/crates/bevy_state/src/state/mod.rs @@ -19,7 +19,6 @@ pub use transitions::*; mod tests { use bevy_ecs::event::EventRegistry; use bevy_ecs::prelude::*; - use bevy_ecs::schedule::ScheduleLabel; use bevy_state_macros::States; use bevy_state_macros::SubStates; @@ -64,7 +63,7 @@ mod tests { world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.run_schedule(StateTransition); assert_eq!(world.resource::>().0, SimpleState::A); @@ -120,7 +119,7 @@ mod tests { world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.run_schedule(StateTransition); assert_eq!(world.resource::>().0, SimpleState::A); @@ -180,7 +179,7 @@ mod tests { world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.run_schedule(StateTransition); assert_eq!(world.resource::>().0, SimpleState::A); @@ -275,7 +274,7 @@ mod tests { world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.run_schedule(StateTransition); assert_eq!(world.resource::>().0, SimpleState::A); @@ -354,9 +353,6 @@ mod tests { } } - #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)] - struct Startup; - #[test] fn computed_state_transitions_are_produced_correctly() { let mut world = World::new(); @@ -367,7 +363,7 @@ mod tests { world.init_resource::>(); world.init_resource::(); - setup_state_transitions_in_world(&mut world, Some(Startup.intern())); + setup_state_transitions_in_world(&mut world); let mut schedules = world .get_resource_mut::() @@ -431,7 +427,7 @@ mod tests { world.init_resource::(); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); assert_eq!(world.resource::>().0, SimpleState::A); assert_eq!(world.resource::>().0, SimpleState2::A1); @@ -508,7 +504,7 @@ mod tests { #[test] fn same_state_transition_should_emit_event_and_not_run_schedules() { let mut world = World::new(); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); EventRegistry::register_event::>(&mut world); world.init_resource::>(); let mut schedules = world.resource_mut::(); @@ -568,7 +564,7 @@ mod tests { SubState::register_sub_state_systems(&mut apply_changes); schedules.insert(apply_changes); world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.insert_resource(NextState::Pending(SimpleState::B(true))); world.run_schedule(StateTransition); @@ -599,7 +595,7 @@ mod tests { TestComputedState::register_computed_state_systems(&mut apply_changes); schedules.insert(apply_changes); world.insert_resource(schedules); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); world.insert_resource(NextState::Pending(SimpleState::B(true))); world.run_schedule(StateTransition); @@ -651,7 +647,7 @@ mod tests { #[test] fn check_transition_orders() { let mut world = World::new(); - setup_state_transitions_in_world(&mut world, None); + setup_state_transitions_in_world(&mut world); EventRegistry::register_event::>(&mut world); EventRegistry::register_event::>(&mut world); EventRegistry::register_event::>( diff --git a/crates/bevy_state/src/state/transitions.rs b/crates/bevy_state/src/state/transitions.rs index dc2764df42e19..226424e4a2d9b 100644 --- a/crates/bevy_state/src/state/transitions.rs +++ b/crates/bevy_state/src/state/transitions.rs @@ -2,9 +2,7 @@ use std::{marker::PhantomData, mem}; use bevy_ecs::{ event::{Event, EventReader, EventWriter}, - schedule::{ - InternedScheduleLabel, IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet, - }, + schedule::{IntoSystemSetConfigs, Schedule, ScheduleLabel, Schedules, SystemSet}, system::{Commands, In, ResMut}, world::World, }; @@ -181,10 +179,7 @@ pub(crate) fn internal_apply_state_transition( /// /// Runs automatically when using `App` to insert states, but needs to /// be added manually in other situations. -pub fn setup_state_transitions_in_world( - world: &mut World, - startup_label: Option, -) { +pub fn setup_state_transitions_in_world(world: &mut World) { let mut schedules = world.get_resource_or_insert_with(Schedules::default); if schedules.contains(StateTransition) { return; @@ -200,12 +195,6 @@ pub fn setup_state_transitions_in_world( .chain(), ); schedules.insert(schedule); - - if let Some(startup) = startup_label { - schedules.add_systems(startup, |world: &mut World| { - let _ = world.try_run_schedule(StateTransition); - }); - } } /// Returns the latest state transition event of type `S`, if any are available.