---
# System prepended metadata

title: Basically SystemSets
tags: [ECS]

---

# Basically SystemSets

## Motivation

- [bevyengine/bevy#16432](https://github.com/bevyengine/bevy/issues/16432) - Back to Base Sets
- [bevyengine/bevy#9139](https://github.com/bevyengine/bevy/issues/9139) - Affinitize System Sets to Schedules
- [bevyengine/bevy#9792](https://github.com/bevyengine/bevy/issues/9792) - System sets do not share their configuration across schedules

## TL;DR

- Get rid of `Schedules` (plural).
- Move `Schedules::ignored_ambiguities` into a new `IgnoredAmbiguities` resource.
- Add `MainSchedule` resource that holds a `Schedule`.
- Add `FixedMainSchedule` resource that holds a `Schedule`.
- Add `StartupSchedule` resource that holds a `Schedule`.
- Remove `MainScheduleOrder` and `FixedMainScheduleOrder`.
    - Order is now defined in terms of normal system set ordering constraints:
        - `app.configure_sets((First, PreUpdate, Update, PostUpdate, Last).chain());`
        - `app.configure_fixed_sets((First, PreUpdate, Update, PostUpdate, Last).chain());`
        - `app.configure_startup_sets((First, PreUpdate, Update, PostUpdate, Last).chain());`

## Considerations

- Should we discard the `FixedX` and `XStartup` system sets, and instead use the `XUpdate` sets across all of the bevy-provided schedules?
    - I don't think we could do this immediately for backwards compatibility reasons, so this is more of a long-term thought. I am highly in favor of this, though.
- If a system is added to a set in `MainSchedule`, and that set is not a member of the `Main` system set (even transitively), should the system be ran? Should orphaned system sets be included in execution?

## Ideal API

*Note: With the understanding that we will need a backwards compatible transition stage, which is described below.*

```rust
// Extracted from Schedules::ignored_ambiguities
#[derive(Resource)]
pub struct IgnoredAmbiguities(BTreeSet<ComponentId>);

// First/PreUpdate/Update/PostUpdate/Last systems and sets go here.
#[derive(Resource)]
pub struct MainSchedule(Schedule);

// FixedFirst/FixedPreUpdate/FixedUpdate/FixedPostUpdate/FixedLast
// systems and sets go here.
#[derive(Resource)]
pub struct FixedMainSchedule(Schedule);

// PreStartup/Startup/PostStartup systems and sets go here.
#[derive(Resource)]
pub struct StartupSchedule(Schedule);

// The old ScheduleLabels are changed into SystemSets
// and merged into an enum for better discoverability.
// We would also use these sets for all of the schedules
// above, rather than each of them having their own.
#[derive(SystemSet)]
pub enum CoreSets {
    First,
    PreUpdate,
    Update,
    PostUpdate,
    Last
}

// Re-export them for good UX and backwards compat.
pub use CoreSets::{First, PreUpdate, Update, PostUpdate, Last};

impl App {
    // Systems registered here go into MainSchedule.
    pub fn add_systems(&mut self, set: impl SystemSet, systems: impl IntoSystemConfigs<ScheduleSystem>) -> &mut Self;
    
    // Sets configured here go into MainSchedule.
    pub fn configure_sets(&mut self, sets: impl IntoSystemConfigs<InternedSystemSet>) -> &mut Self;
    
    // Systems registered here go into FixedMainSchedule.
    pub fn add_fixed_systems(&mut self, set: impl SystemSet, systems: impl IntoSystemConfigs<ScheduleSystem>) -> &mut Self;
    
    // Sets configured here go in FixedMainSchedule.
    pub fn configure_fixed_sets(&mut self, sets: impl IntoSystemConfigs<InternedSystemSet>) -> &mut Self;
    
    // Systems registered here go into StartupSchedule.
    pub fn add_startup_systems(&mut self, set: impl SystemSet, systems: impl IntoSystemConfigs<ScheduleSystem>) -> &mut Self;
    
    // Sets configured here go in StartupSchedule.
    pub fn configure_startup_sets(&mut self, sets: impl IntoSystemConfigs<InternedSystemSet>) -> &mut Self;
}
```

### Notes

- `ScheduleLabel`s are solely used for debugging/visualization now.

## Backwards compatibility

To ease the transition we'll need to modify `add_systems` so that the old way of registering systems is still doable. 

```rust
pub trait TargetSchedule {
    fn schedule(world: &mut World) -> Mut<'_, Schedule>;
}

// And for First, PreUpdate, PostUpdate, Last
impl TargetSchedule for Update {
    fn schedule(world: &mut World) -> Mut<'_, Schedule> {
        world.get_resource_or_init::<MainSchedule>()
    }
}

// And for FixedFirst, FixedPreUpdate, FixedPostUpdate, FixedLast
impl TargetSchedule for FixedUpdate {
    fn schedule(world: &mut World) -> Mut<'_, Schedule> {
        world.get_resource_or_init::<FixedMainSchedule>()
    }
}

// And for PreStartup, PostStartup
impl TargetSchedule for Startup {
    fn schedule(world: &mut World) -> Mut<'_, Schedule> {
        world.get_resource_or_init::<StartupSchedule>()
    }
}

impl App {
    pub fn add_systems(&mut self, set: impl SystemSet + TargetSchedule, systems: impl IntoSystemConfigs<ScheduleSystem>);
    
    pub fn configure_sets(&mut self, set: impl SystemSet + TargetSchedule, sets: impl IntoSystemConfigs<InternedSystemSet>);
}
```

## `bevy_state`

```rust
// Add a new Schedule resource to store state transitions in. 
#[derive(Resource)]
pub struct StateTransitionSchedule(Schedule);

// These were previously ScheduleLabels, and separate structs.
#[derive(SystemSet)]
pub enum TransitionSets<S: States> {
    OnEnter(S),
    OnTransition {
        from: S,
        to: S
    },
    OnExit(S)
}

#[derive(SystemSet)]
pub struct FlushState<S: States>(pub S);

impl App {
    pub fn add_state<S: State>(&mut self) -> &mut Self;
    
    // The idea here is to let the user configure the order of their state transition sets.
    pub fn configure_state<S: State>(&mut self, transitions: impl IntoTransitionConfigs<S>) -> &mut Self;
    
    pub fn add_state_systems<S>(&mut self, transition: TransitionSets<S>, systems: impl IntoSystemConfigs<ScheduleSystem>) -> &mut Self;
    
    pub fn configure_state_sets(&mut self, sets: impl IntoSystemConfigs<ScheduleSystem>) -> &mut Self;
}
```