# Objective
- Fixes#12377
## Solution
Added simple `#[diagnostic::on_unimplemented(...)]` attributes to some
critical public traits providing a more approachable initial error
message. Where appropriate, a `note` is added indicating that a `derive`
macro is available.
## Examples
<details>
<summary>Examples hidden for brevity</summary>
Below is a collection of examples showing the new error messages
produced by this change. In general, messages will start with a more
Bevy-centric error message (e.g., _`MyComponent` is not a `Component`_),
and a note directing the user to an available derive macro where
appropriate.
### Missing `#[derive(Resource)]`
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
struct MyResource;
fn main() {
App::new()
.insert_resource(MyResource)
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `MyResource` is not a `Resource`
--> examples/app/empty.rs:7:26
|
7 | .insert_resource(MyResource)
| --------------- ^^^^^^^^^^ invalid `Resource`
| |
| required by a bound introduced by this call
|
= help: the trait `Resource` is not implemented for `MyResource`
= note: consider annotating `MyResource` with `#[derive(Resource)]`
= help: the following other types implement trait `Resource`:
AccessibilityRequested
ManageAccessibilityUpdates
bevy::bevy_a11y::Focus
DiagnosticsStore
FrameCount
bevy::prelude::State<S>
SystemInfo
bevy::prelude::Axis<T>
and 141 others
note: required by a bound in `bevy::prelude::App::insert_resource`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:419:31
|
419 | pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
| ^^^^^^^^ required by this bound in `App::insert_resource`
```
</details>
### Putting A `QueryData` in a `QueryFilter` Slot
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
#[derive(Component)]
struct A;
#[derive(Component)]
struct B;
fn my_system(_query: Query<&A, &B>) {}
fn main() {
App::new()
.add_systems(Update, my_system)
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `&B` is not a valid `Query` filter
--> examples/app/empty.rs:9:22
|
9 | fn my_system(_query: Query<&A, &B>) {}
| ^^^^^^^^^^^^^ invalid `Query` filter
|
= help: the trait `QueryFilter` is not implemented for `&B`
= help: the following other types implement trait `QueryFilter`:
With<T>
Without<T>
bevy::prelude::Or<()>
bevy::prelude::Or<(F0,)>
bevy::prelude::Or<(F0, F1)>
bevy::prelude::Or<(F0, F1, F2)>
bevy::prelude::Or<(F0, F1, F2, F3)>
bevy::prelude::Or<(F0, F1, F2, F3, F4)>
and 28 others
note: required by a bound in `bevy::prelude::Query`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\query.rs:349:51
|
349 | pub struct Query<'world, 'state, D: QueryData, F: QueryFilter = ()> {
| ^^^^^^^^^^^ required by this bound in `Query`
```
</details>
### Missing `#[derive(Component)]`
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
struct A;
fn my_system(mut commands: Commands) {
commands.spawn(A);
}
fn main() {
App::new()
.add_systems(Startup, my_system)
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `A` is not a `Bundle`
--> examples/app/empty.rs:6:20
|
6 | commands.spawn(A);
| ----- ^ invalid `Bundle`
| |
| required by a bound introduced by this call
|
= help: the trait `bevy::prelude::Component` is not implemented for `A`, which is required by `A: Bundle`
= note: consider annotating `A` with `#[derive(Component)]` or `#[derive(Bundle)]`
= help: the following other types implement trait `Bundle`:
TransformBundle
SceneBundle
DynamicSceneBundle
AudioSourceBundle<Source>
SpriteBundle
SpriteSheetBundle
Text2dBundle
MaterialMesh2dBundle<M>
and 34 others
= note: required for `A` to implement `Bundle`
note: required by a bound in `bevy::prelude::Commands::<'w, 's>::spawn`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\commands\mod.rs:243:21
|
243 | pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands {
| ^^^^^^ required by this bound in `Commands::<'w, 's>::spawn`
```
</details>
### Missing `#[derive(Asset)]`
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
struct A;
fn main() {
App::new()
.init_asset::<A>()
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `A` is not an `Asset`
--> examples/app/empty.rs:7:23
|
7 | .init_asset::<A>()
| ---------- ^ invalid `Asset`
| |
| required by a bound introduced by this call
|
= help: the trait `Asset` is not implemented for `A`
= note: consider annotating `A` with `#[derive(Asset)]`
= help: the following other types implement trait `Asset`:
Font
AnimationGraph
DynamicScene
Scene
AudioSource
Pitch
bevy::bevy_gltf::Gltf
GltfNode
and 17 others
note: required by a bound in `init_asset`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_asset\src\lib.rs:307:22
|
307 | fn init_asset<A: Asset>(&mut self) -> &mut Self;
| ^^^^^ required by this bound in `AssetApp::init_asset`
```
</details>
### Mismatched Input and Output on System Piping
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
fn producer() -> u32 {
123
}
fn consumer(_: In<u16>) {}
fn main() {
App::new()
.add_systems(Update, producer.pipe(consumer))
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `fn(bevy::prelude::In<u16>) {consumer}` is not a valid system with input `u32` and output `_`
--> examples/app/empty.rs:11:44
|
11 | .add_systems(Update, producer.pipe(consumer))
| ---- ^^^^^^^^ invalid system
| |
| required by a bound introduced by this call
|
= help: the trait `bevy::prelude::IntoSystem<u32, _, _>` is not implemented for fn item `fn(bevy::prelude::In<u16>) {consumer}`
= note: expecting a system which consumes `u32` and produces `_`
note: required by a bound in `pipe`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\mod.rs:168:12
|
166 | fn pipe<B, Final, MarkerB>(self, system: B) -> PipeSystem<Self::System, B::System>
| ---- required by a bound in this associated function
167 | where
168 | B: IntoSystem<Out, Final, MarkerB>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `IntoSystem::pipe`
```
</details>
### Missing Reflection
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
#[derive(Component)]
struct MyComponent;
fn main() {
App::new()
.register_type::<MyComponent>()
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `MyComponent` does not provide type registration information
--> examples/app/empty.rs:8:26
|
8 | .register_type::<MyComponent>()
| ------------- ^^^^^^^^^^^ the trait `GetTypeRegistration` is not implemented for `MyComponent`
| |
| required by a bound introduced by this call
|
= note: consider annotating `MyComponent` with `#[derive(Reflect)]`
= help: the following other types implement trait `GetTypeRegistration`:
bool
char
isize
i8
i16
i32
i64
i128
and 443 others
note: required by a bound in `bevy::prelude::App::register_type`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:619:29
|
619 | pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::register_type`
```
</details>
### Missing `#[derive(States)]` Implementation
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)]
enum AppState {
#[default]
Menu,
InGame {
paused: bool,
turbo: bool,
},
}
fn main() {
App::new()
.init_state::<AppState>()
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: the trait bound `AppState: FreelyMutableState` is not satisfied
--> examples/app/empty.rs:15:23
|
15 | .init_state::<AppState>()
| ---------- ^^^^^^^^ the trait `FreelyMutableState` is not implemented for `AppState`
| |
| required by a bound introduced by this call
|
= note: consider annotating `AppState` with `#[derive(States)]`
note: required by a bound in `bevy::prelude::App::init_state`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:282:26
|
282 | pub fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self {
| ^^^^^^^^^^^^^^^^^^ required by this bound in `App::init_state`
```
</details>
### Adding a `System` with Unhandled Output
<details>
<summary>Example Code</summary>
```rust
use bevy::prelude::*;
fn producer() -> u32 {
123
}
fn main() {
App::new()
.add_systems(Update, consumer)
.run();
}
```
</details>
<details>
<summary>Error Generated</summary>
```error
error[E0277]: `fn() -> u32 {producer}` does not describe a valid system configuration
--> examples/app/empty.rs:9:30
|
9 | .add_systems(Update, producer)
| ----------- ^^^^^^^^ invalid system configuration
| |
| required by a bound introduced by this call
|
= help: the trait `IntoSystem<(), (), _>` is not implemented for fn item `fn() -> u32 {producer}`, which is required by `fn() -> u32 {producer}: IntoSystemConfigs<_>`
= help: the following other types implement trait `IntoSystemConfigs<Marker>`:
<Box<(dyn bevy::prelude::System<In = (), Out = ()> + 'static)> as IntoSystemConfigs<()>>
<NodeConfigs<Box<(dyn bevy::prelude::System<In = (), Out = ()> + 'static)>> as IntoSystemConfigs<()>>
<(S0,) as IntoSystemConfigs<(SystemConfigTupleMarker, P0)>>
<(S0, S1) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1)>>
<(S0, S1, S2) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2)>>
<(S0, S1, S2, S3) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3)>>
<(S0, S1, S2, S3, S4) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3, P4)>>
<(S0, S1, S2, S3, S4, S5) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3, P4, P5)>>
and 14 others
= note: required for `fn() -> u32 {producer}` to implement `IntoSystemConfigs<_>`
note: required by a bound in `bevy::prelude::App::add_systems`
--> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:342:23
|
339 | pub fn add_systems<M>(
| ----------- required by a bound in this associated function
...
342 | systems: impl IntoSystemConfigs<M>,
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::add_systems`
```
</details>
</details>
## Testing
CI passed locally.
## Migration Guide
Upgrade to version 1.78 (or higher) of Rust.
## Future Work
- Currently, hints are not supported in this diagnostic. Ideally,
suggestions like _"consider using ..."_ would be in a hint rather than a
note, but that is the best option for now.
- System chaining and other `all_tuples!(...)`-based traits have bad
error messages due to the slightly different error message format.
---------
Co-authored-by: Jamie Ridding <Themayu@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
# Objective
- Part of #11590
- Fix `unsafe_op_in_unsafe_fn` for trivial cases in bevy_ecs
## Solution
Fix `unsafe_op_in_unsafe_fn` in bevy_ecs for trivial cases, i.e., add an
`unsafe` block when the safety comment already exists or add a comment
like "The invariants are uphold by the caller".
---------
Co-authored-by: James Liu <contact@jamessliu.com>
# Objective
- Fixes#11679
## Solution
- Added `IntoSystem::system_type_id` which returns the equivalent of
`system.into_system().type_id()` without construction. This allows for
getting the `TypeId` of functions (a function is an unnamed type and
therefore you cannot call `TypeId::of::<apply_deferred::System>()`)
- Added default implementation of `System::type_id` to ensure
consistency between implementations. Some returned `Self`, while others
were returning an inner value instead. This ensures consistency with
`IntoSystem::system_type_id`.
## Migration Guide
If you use `System::type_id()` on function systems (exclusive or not),
ensure you are comparing its value to other `System::type_id()` calls,
or `IntoSystem::system_type_id()`.
This code wont require any changes, because `IntoSystem`'s are directly
compared to each other.
```rust
fn test_system() {}
let type_id = test_system.type_id();
// ...
// No change required
assert_eq!(test_system.type_id(), type_id);
```
Likewise, this code wont, because `System`'s are directly compared.
```rust
fn test_system() {}
let type_id = IntoSystem::into_system(test_system).type_id();
// ...
// No change required
assert_eq!(IntoSystem::into_system(test_system).type_id(), type_id);
```
The below _does_ require a change, since you're comparing a `System`
type to a `IntoSystem` type.
```rust
fn test_system() {}
// Before
assert_eq!(test_system.type_id(), IntoSystem::into_system(test_system).type_id());
// After
assert_eq!(test_system.system_type_id(), IntoSystem::into_system(test_system).type_id());
```
# Objective
- Users are often confused when their command effects are not visible in
the next system. This PR auto inserts sync points if there are deferred
buffers on a system and there are dependents on that system (systems
with after relationships).
- Manual sync points can lead to users adding more than needed and it's
hard for the user to have a global understanding of their system graph
to know which sync points can be merged. However we can easily calculate
which sync points can be merged automatically.
## Solution
1. Add new edge types to allow opting out of new behavior
2. Insert an sync point for each edge whose initial node has deferred
system params.
3. Reuse nodes if they're at the number of sync points away.
* add opt outs for specific edges with `after_ignore_deferred`,
`before_ignore_deferred` and `chain_ignore_deferred`. The
`auto_insert_apply_deferred` boolean on `ScheduleBuildSettings` can be
set to false to opt out for the whole schedule.
## Perf
This has a small negative effect on schedule build times.
```text
group auto-sync main-for-auto-sync
----- ----------- ------------------
build_schedule/1000_schedule 1.06 2.8±0.15s ? ?/sec 1.00 2.7±0.06s ? ?/sec
build_schedule/1000_schedule_noconstraints 1.01 26.2±0.88ms ? ?/sec 1.00 25.8±0.36ms ? ?/sec
build_schedule/100_schedule 1.02 13.1±0.33ms ? ?/sec 1.00 12.9±0.28ms ? ?/sec
build_schedule/100_schedule_noconstraints 1.08 505.3±29.30µs ? ?/sec 1.00 469.4±12.48µs ? ?/sec
build_schedule/500_schedule 1.00 485.5±6.29ms ? ?/sec 1.00 485.5±9.80ms ? ?/sec
build_schedule/500_schedule_noconstraints 1.00 6.8±0.10ms ? ?/sec 1.02 6.9±0.16ms ? ?/sec
```
---
## Changelog
- Auto insert sync points and added `after_ignore_deferred`,
`before_ignore_deferred`, `chain_no_deferred` and
`auto_insert_apply_deferred` APIs to opt out of this behavior
## Migration Guide
- `apply_deferred` points are added automatically when there is ordering
relationship with a system that has deferred parameters like `Commands`.
If you want to opt out of this you can switch from `after`, `before`,
and `chain` to the corresponding `ignore_deferred` API,
`after_ignore_deferred`, `before_ignore_deferred` or
`chain_ignore_deferred` for your system/set ordering.
- You can also set `ScheduleBuildSettings::auto_insert_sync_points` to
`false` if you want to do it for the whole schedule. Note that in this
mode you can still add `apply_deferred` points manually.
- For most manual insertions of `apply_deferred` you should remove them
as they cannot be merged with the automatically inserted points and
might reduce parallelizability of the system graph.
## TODO
- [x] remove any apply_deferred used in the engine
- [x] ~~decide if we should deprecate manually using apply_deferred.~~
We'll still allow inserting manual sync points for now for whatever edge
cases users might have.
- [x] Update migration guide
- [x] rerun schedule build benchmarks
---------
Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com>
# Objective
- Make the implementation order consistent between all sources to fit
the order in the trait.
## Solution
- Change the implementation order.
# Objective
First of all, this PR took heavy inspiration from #7760 and #5715. It
intends to also fix#5569, but with a slightly different approach.
This also fixes#9335 by reexporting `DynEq`.
## Solution
The advantage of this API is that we can intern a value without
allocating for zero-sized-types and for enum variants that have no
fields. This PR does this automatically in the `SystemSet` and
`ScheduleLabel` derive macros for unit structs and fieldless enum
variants. So this should cover many internal and external use cases of
`SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory
will be allocated.
- The interning returns a `Interned<dyn SystemSet>`, which is just a
wrapper around a `&'static dyn SystemSet`.
- `Hash` and `Eq` are implemented in terms of the pointer value of the
reference, similar to my first approach of anonymous system sets in
#7676.
- Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`.
- The debug output of `Interned<T>` is the same as the interned value.
Edit:
- `AppLabel` is now also interned and the old
`derive_label`/`define_label` macros were replaced with the new
interning implementation.
- Anonymous set ids are reused for different `Schedule`s, reducing the
amount of leaked memory.
### Pros
- `InternedSystemSet` and `InternedScheduleLabel` behave very similar to
the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied
without an allocation.
- Many use cases don't allocate at all.
- Very fast lookups and comparisons when using `InternedSystemSet` and
`InternedScheduleLabel`.
- The `intern` module might be usable in other areas.
- `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement
`{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics.
### Cons
- Implementors of `SystemSet` and `ScheduleLabel` still need to
implement `Hash` and `Eq` (and `Clone`) for it to work.
## Changelog
### Added
- Added `intern` module to `bevy_utils`.
- Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`.
### Changed
- Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with
`InternedSystemSet` and `InternedScheduleLabel`.
- Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`.
- Replaced `AppLabelId` with `InternedAppLabel`.
- Changed `AppLabel` to use `Debug` for error messages.
- Changed `AppLabel` to use interning.
- Changed `define_label`/`derive_label` to use interning.
- Replaced `define_boxed_label`/`derive_boxed_label` with
`define_label`/`derive_label`.
- Changed anonymous set ids to be only unique inside a schedule, not
globally.
- Made interned label types implement their label trait.
### Removed
- Removed `define_boxed_label` and `derive_boxed_label`.
## Migration guide
- Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with
`InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`.
- Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with
`InternedSystemSet` or `Interned<dyn SystemSet>`.
- Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn
AppLabel>`.
- Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet`
need to implement:
- `dyn_hash` directly instead of implementing `DynHash`
- `as_dyn_eq`
- Pass labels to `World::try_schedule_scope`, `World::schedule_scope`,
`World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`,
`Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and
`Schedules::get_mut` by value instead of by reference.
---------
Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Move schedule name into `Schedule` to allow the schedule name to be
used for errors and tracing in Schedule methods
- Fixes#9510
## Solution
- Move label onto `Schedule` and adjust api's on `World` and `Schedule`
to not pass explicit label where it makes sense to.
- add name to errors and tracing.
- `Schedule::new` now takes a label so either add the label or use
`Schedule::default` which uses a default label. `default` is mostly used
in doc examples and tests.
---
## Changelog
- move label onto `Schedule` to improve error message and logging for
schedules.
## Migration Guide
`Schedule::new` and `App::add_schedule`
```rust
// old
let schedule = Schedule::new();
app.add_schedule(MyLabel, schedule);
// new
let schedule = Schedule::new(MyLabel);
app.add_schedule(schedule);
```
if you aren't using a label and are using the schedule struct directly
you can use the default constructor.
```rust
// old
let schedule = Schedule::new();
schedule.run(world);
// new
let schedule = Schedule::default();
schedule.run(world);
```
`Schedules:insert`
```rust
// old
let schedule = Schedule::new();
schedules.insert(MyLabel, schedule);
// new
let schedule = Schedule::new(MyLabel);
schedules.insert(schedule);
```
`World::add_schedule`
```rust
// old
let schedule = Schedule::new();
world.add_schedule(MyLabel, schedule);
// new
let schedule = Schedule::new(MyLabel);
world.add_schedule(schedule);
```
# Objective
Make a combined system cloneable if both systems are cloneable on their
own. This is necessary for using chained conditions (e.g
`cond1.and_then(cond2)`) with `distributive_run_if()`.
## Solution
Implement `Clone` for `CombinatorSystem<Func, A, B>` where `A, B:
Clone`.
# Objective
Title.
---------
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: James Liu <contact@jamessliu.com>
# Objective
- `apply_system_buffers` is an unhelpful name: it introduces a new
internal-only concept
- this is particularly rough for beginners as reasoning about how
commands work is a critical stumbling block
## Solution
- rename `apply_system_buffers` to the more descriptive `apply_deferred`
- rename related fields, arguments and methods in the internals fo
bevy_ecs for consistency
- update the docs
## Changelog
`apply_system_buffers` has been renamed to `apply_deferred`, to more
clearly communicate its intent and relation to `Deferred` system
parameters like `Commands`.
## Migration Guide
- `apply_system_buffers` has been renamed to `apply_deferred`
- the `apply_system_buffers` method on the `System` trait has been
renamed to `apply_deferred`
- the `is_apply_system_buffers` function has been replaced by
`is_apply_deferred`
- `Executor::set_apply_final_buffers` is now
`Executor::set_apply_final_deferred`
- `Schedule::apply_system_buffers` is now `Schedule::apply_deferred`
---------
Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com>
# Objective
Fix#7833.
Safety comments in the multi-threaded executor don't really talk about
system world accesses, which makes it unclear if the code is actually
valid.
## Solution
Update the `System` trait to use `UnsafeWorldCell`. This type's API is
written in a way that makes it much easier to cleanly maintain safety
invariants. Use this type throughout the multi-threaded executor, with a
liberal use of safety comments.
---
## Migration Guide
The `System` trait now uses `UnsafeWorldCell` instead of `&World`. This
type provides a robust API for interior mutable world access.
- The method `run_unsafe` uses this type to manage world mutations
across multiple threads.
- The method `update_archetype_component_access` uses this type to
ensure that only world metadata can be used.
```rust
let mut system = IntoSystem::into_system(my_system);
system.initialize(&mut world);
// Before:
system.update_archetype_component_access(&world);
unsafe { system.run_unsafe(&world) }
// After:
system.update_archetype_component_access(world.as_unsafe_world_cell_readonly());
unsafe { system.run_unsafe(world.as_unsafe_world_cell()) }
```
---------
Co-authored-by: James Liu <contact@jamessliu.com>
# Objective
Follow-up to #8377.
As the system module has been refactored, there are many types that no
longer make sense to live in the files that they do:
- The `IntoSystem` trait is in `function_system.rs`, even though this
trait is relevant to all kinds of systems. Same for the `In<T>` type.
- `PipeSystem` is now just an implementation of `CombinatorSystem`, so
`system_piping.rs` no longer needs its own file.
## Solution
- Move `IntoSystem`, `In<T>`, and system piping combinators & tests into
the top-level `mod.rs` file for `bevy_ecs::system`.
- Move `PipeSystem` into `combinator.rs`.
# Objective
The implementation of `System::run_unsafe` for `FunctionSystem` requires
that the world is the same one used to initialize the system. However,
the `System` trait has no requirements that the world actually matches,
which makes this implementation unsound.
This was previously mentioned in
https://github.com/bevyengine/bevy/pull/7605#issuecomment-1426491871
Fixes part of #7833.
## Solution
Add the safety invariant that
`System::update_archetype_component_access` must be called prior to
`System::run_unsafe`. Since
`FunctionSystem::update_archetype_component_access` properly validates
the world, this ensures that `run_unsafe` is not called with a
mismatched world.
Most exclusive systems are not required to be run on the same world that
they are initialized with, so this is not a concern for them. Systems
formed by combining an exclusive system with a regular system *do*
require the world to match, however the validation is done inside of
`System::run` when needed.
# Objective
Fix#7584.
## Solution
Add an abstraction for creating custom system combinators with minimal boilerplate. Use this to implement AND/OR combinators. Use this to simplify the implementation of `PipeSystem`.
## Example
Feel free to bikeshed on the syntax.
I chose the names `and_then`/`or_else` to emphasize the fact that these short-circuit, while I chose method syntax to empasize that the arguments are *not* treated equally.
```rust
app.add_systems((
my_system.run_if(resource_exists::<R>().and_then(resource_equals(R(0)))),
our_system.run_if(resource_exists::<R>().or_else(resource_exists::<S>())),
));
```
---
## Todo
- [ ] Decide on a syntax
- [x] Write docs
- [x] Write tests
## Changelog
+ Added the extension methods `.and_then(...)` and `.or_else(...)` to run conditions, which allows combining run conditions with short-circuiting behavior.
+ Added the trait `Combine`, which can be used with the new `CombinatorSystem` to create system combinators with custom behavior.