# Systems as Entities ## Reasoning The primary reasion for systems-as-entities, is that it is a soft blocker for queries-as-entities. This is because systems hold on to `QueryState`, so dropping a system would also drop the `QueryState`. If, however, we store `QueryState` as a component on an entity, dropping a system would drop the `Entity`, but not remove the related `QueryState` from the world. Additionally, storing systems on entities would make developer introspection tools more powerfull, since you can query scheduled systems in the world. ## Scheduling v3 Currently, Bevy uses **Schedule V3** which was introduced in [0.10](https://bevy.org/news/bevy-0-10/#adding-systems). Very briefly, **Schedule V3** has two basic elements: [Systems](https://docs.rs/bevy/latest/bevy/prelude/trait.System.html) and [system sets](https://docs.rs/bevy/latest/bevy/ecs/schedule/trait.SystemSet.html). Systems are the rust functions that actually get run, while system sets are organizational tools that help a developer structure their systems. They may contain both other system sets as well as systems. *A system can be in two different system sets at the same time, so this does **not** correspond to a tree structure.* After creating the sets and systems, you can add constraints to them. Say that you want `foo()` to run before `bar()` and `SetA` to run after `SetB`, you can add these relations: `foo -> bar` and `setB -> setA`. This is, in fact, often necessary as two systems that have conflicting access **cannot** run at the same time. So what does this look like in practice? What actually happens if you call `App::add_systems(Update, (foo, bar))`? Currently, everything is stored in the [`Schedules`](https://docs.rs/bevy/latest/bevy/ecs/prelude/struct.Schedules.html) resource, which contains all the [`Schedule`](https://docs.rs/bevy/latest/bevy/prelude/struct.Schedule.html)s relevant for that world. This is just a container, so there's not much to write home about there. We need to look at `Schedule::add_systems((foo, bar))`, which parses the `(foo, bar)` into `ScheduleConfigs<T>` and updates the `ScheduleGraph`. Note that this does not yet result in a change in the systems that get actually run. This happens in `Schedule::run` which calls `Schedule::initialize` which calls `ScheduleGraph::update_schedule` *only* if the graph was actually changed. This updates `SystemSchedule`, which can be viewed as the optimized schedule that actually gets run. ## Levels of Difficulty Fundamentally, we'd like to marry **Schedule v3** with one-shot systems, which is the way we currently have systems-as-entities. There are several ways we could do this, each ranging in difficulty. ### The Most Lazy `RegisteredSystem` which is the component that stores systems for one-shot systems, looks as follows: ```rust pub(crate) struct RegisteredSystem<I, O> { initialized: bool, system: Option<BoxedSystem<I, O>>, } ``` The reason why `system` is wrapped in an `Option` is that the `BoxedSystem` should sometimes be `take()`en to actually run. To run it safely you need ownership. We *could* just spawn a `RegisteredSystem { initialized: true, system: None }` entity, during `Schedule::initialize`. Since `SystemSchedule` *will* take ownership of the system anyway so it can be run, we might as well initialize the system with `None`. The upside of this is that it works. The downside is that it does next to nothing. Since there's no link between the system entity and the `Schedule`, removing the entity does not change the schedule. ### Systems as Entities We could instead spawn the `RegisteredSystem` during `App::add_systems` and instead of using `SystemKey` in `ScheduleGraph` we use `Entity` to identify the systems. All of the relations and constrains will still be held in `Schedule` and `ScheduleGraph`, but the system data will always be stored in a component on an entity. The primary problem with this, is that it's hard to decouple systems from system sets, as they use largely an identical API. Moreover, since this API is very nice, I do not want to mess with it in any way. ### Systems and System Sets as Entities Additionally, we could also turn system sets into entities. With this approach we could model systems being an element of a set as a relationship between the system set entity and the system entity. If we go this direction, `ScheduleGraph` would start to contain less and less data, but not nothing. The constraints that specify that `foo` should follow `bar` and `SetA` should follow `SetB` would still be stored in the `Schedules` resource. As it seems impractical to model everything with relationships as they currently exist. This approach might already be blocked by a lack of any-to-any relations as *systems can be in two different system sets at the same time*. ### While in Rome Just model everything as entities and relationships. Go nuts. ## A Note on Performance With this exploration I am not concerning myself with performance that much. I am making the assumption that changes to a schedule are relatively rare. As long as we're not updating `SystemSchedule` every single frame, the performance should stay largely the same. In every iteration of the plan, `SystemSchedule` stays the same.