bevyengine / bevyengine/bevy

`SystemRunner` param - run systems inside other systems

Open
#16,680 14 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Feature D-Modest S-Nominated-To-Close X-Needs-SME
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## What problem does this solve or what need does it fill?

Currently ways of running systems from other systems are very limited. This proposal aims to provide a way to easily run system as a `SystemParam`

## What solution would you like?

Add a `SystemParam` - `SystemRunner`

```rust
#[derive(Default)]
struct MySystem;
impl System for MySystem {
In = In;
Out = String;
//...
}

fn running_my_system(mut runner: SystemRunner) {
let output = runner.run(50);
info!("Output of MySystem: {output}");
}
```

### Unergonomic `System` implementation

To use this param, user would have to explicitly specify the type of the system they will use. Implementing `System` trait on it's own is hard - unsafe code is not something users want to deal with. [`FunctionSystem`](https://dev-docs.bevyengine.org/bevy/ecs/system/struct.FunctionSystem.html)s are the main way people are used to working with the bevy systems, but they don't implement [`FromWorld`](https://dev-docs.bevyengine.org/bevy/ecs/prelude/trait.FromWorld.html), and even if it did, you would have to either specify the marker or box every system.

So, it would be nice to have a macro that labels a function system with a specified type name.

```rust
#[system(MySystem)]
pub fn my_system(query: Query<&mut MyComponent>, resourse: Res) -> i32 {
//...
}
```
expands to
```rust
pub struct MySystem(SystemState<(Query<'static, 'static, &'static mut MyComponent>, Res<'static, MyRes>)>);

impl FromWorld for MySystem {
fn from_world(world: &mut World) -> Self {
MySystem(SystemState::new(world))
}
}

impl System for MySystem {
//... Basically the same as FunctionSystem implementation
}
```

### Needed controversial change

There is a conflict between [`System::update_archetype_component_access`](https://dev-docs.bevyengine.org/bevy/ecs/prelude/trait.System.html#tymethod.update_archetype_component_access) and [`SystemParam::new_archetype`](https://dev-docs.bevyengine.org/bevy/ecs/system/trait.SystemParam.html#method.new_archetype). You can't call `update_archetype_component_access` from `new_archetype`. Without that you wouldn't be able to call `update_archetype_component_access` on an inner system and simultaneously have access rights to run the system.

We can add a `new_archetype` method to `System` trait or `update_archetype_component_access` to `SystemParam` trait. We can pass `UnsafeWorldCell` to the `new_archetype`.

The best option in my opinion - add a `new_archetype` method to the `System` trait. There will be system-specific code for updating access. `update_archetype_component_access` will have a default implementation common for all systems. It will be basically the same as the current `FunctionSystem`'s implementation. Systems would also need a method that exposes system's archetype generation for `update_archetype_component_access` to have default implementation. Method that exposes archetype generation should be fallible so that ZST systems can say that they don't need to update archetype access.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.