Skip to content

Commit 9289194

Browse files
BD103alice-i-cecileBleachfuel
authored
Migration writing pass 3 (#2079)
Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Tim Overbeek <[email protected]>
1 parent e240686 commit 9289194

17 files changed

+160
-128
lines changed

content/learn/migration-guides/0.15-to-0.16.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ This guide is currently for a __Release Candidate__ and is subject to change as
1212

1313
The most important changes to be aware of this release are:
1414

15-
- Bevy has reworked its error handling to make it easier to handle `Result`s everywhere. As a result, `Query::single` and friends now return results, rather than panicking.
15+
- Bevy now prefers `Result`s over panicking, as applications can easily recover from `Result`s instead of crashing. As part of this decision, Bevy's error handling capabilities have been significantly improved. (Systems can easily return `Result`s and a new `BevyError` type has been introduced.) Additionally, several panicking functions have been switched to return a result (`Query::single()`) or have been deprecated completely (`Query::many()`).
1616

1717
{{ migration_guides(version="0.16") }}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
`Query::to_readonly` has been renamed to `Query::as_readonly`.
1+
`Query::to_readonly()` has been renamed to `Query::as_readonly()` to reflect that it is cheap to call.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Previously `EntityWorldMut` triggered command queue flushes in unpredictable places, which could interfere with hooks and observers. Now the command queue is flushed always immediately after any call in `EntityWorldMut` that spawns or despawns an entity, or adds, removes or replaces a component. This means hooks and observers will run their commands in the correct order.
1+
Previously, `EntityWorldMut` triggered command queue flushes in unpredictable places, which could interfere with hooks and observers. Now the command queue is always flushed immediately after `EntityWorldMut` spawns or despawns an entity, or adds, removes, or replaces a component.
22

3-
As a side effect, there is a possibility that a hook or observer could despawn the entity that is being referred to by `EntityWorldMut`. This could already currently happen if an observer was added while keeping an `EntityWorldMut` reference and would cause unsound behaviour. If the entity has been despawned, calling any methods which require the entity location will panic. This matches the behaviour that `Commands` will panic if called on an already despawned entity. In the extremely rare case where taking a new `EntityWorldMut` reference or otherwise restructuring the code so that this case does not happen is not possible, there’s a new `is_despawned` method that can be used to check if the referred entity has been despawned.
3+
As a side effect of this change, there is a new possibility that a hook or observer may despawn an entity that is being referred to by `EntityWorldMut`. If any of `EntityWorldMut`'s methods detect that the entity is despawned, they will panic. If you know this is a possibility and wish to avoid panicking, you may check that the entity is despawned with `EntityWorldMut::is_despawned()`.
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
Users who have implemented their own custom executor should use `ScheduleSystem` in place of `BoxedSystem<(), ()>` and import the `System` trait where needed.
1+
If you've written a custom executor, there are a few changes you will need to make in order to support fallible systems.
22

3-
Custom executors should:
3+
1. Many uses of `BoxedSystem<(), ()>` have been replaced with `ScheduleSystem`, which is a type alias to `BoxedSystem<(), Result>`.
4+
2. Executors should obey the `SystemParamValidationError` returned by `SystemParam::validate_param()` in order to determine whether to raise an error or skip the system.
5+
3. When an executor encounters an error, it should pass that error to `default_error_handler()`, whose behavior can be configured with the `GLOBAL_ERROR_HANDLER` static.
46

5-
- obey the `SystemParamValidationError` returned by `SystemParam::validate_param`, skipping systems as requested
6-
- use the `default_error_handler` for both validation errors and results returned from systems
7-
8-
See the source code of the first-party Bevy schedule executors for more details.
7+
For more information on fallible systems, please read the module docs for `bevy::ecs::error`.
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1-
<!-- TODO -->
1+
The `EntityCommands::apply()` method now takes a `EntityWorldMut`, which is an optimized version of the previous `Entity` and `&mut World` pair. `EntityWorldMut` has several existing methods for working with entities, although you may use `EntityWorldMut::id()` to access the `Entity` and `EntityWorldMut::world_scope()` to access the `&mut World`.
2+
3+
```rust
4+
struct MyCommand;
5+
6+
fn print_entity(In(entity): In<Entity>) {
7+
info!("Entity: {entity}");
8+
}
9+
10+
// 0.15
11+
impl EntityCommand for MyCommand {
12+
fn apply(self, entity: Entity, world: &mut World) {
13+
world
14+
.run_system_cached_with(print_entity, entity)
15+
.unwrap();
16+
}
17+
}
18+
19+
// 0.16
20+
impl EntityCommand for MyCommand {
21+
fn apply(self, entity_world: EntityWorldMut) {
22+
let entity = entity_world.id();
23+
24+
entity_world.world_scope(move |world: &mut World| {
25+
world.run_system_cached_with(print_entity, entity).unwrap();
26+
});
27+
}
28+
}
29+
```
30+
31+
Additionally, the method `EntityCommand::with_entity()` has been moved to a separate trait, `CommandWithEntity`, so that it can be generic over commands that return `Result`s.
Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
1-
Existing spawn patterns will continue to work as expected.
2-
3-
Manual Bundle implementations now require a `BundleEffect` associated type. Existing bundles would have no bundle effect, so use `()`. Additionally `Bundle::from_components` has been moved to the new `BundleFromComponents` trait.
1+
As part of improvements to the bundle spawning API, the `DynamicBundle` trait now has a new `Effect` associated type. If you manually implemented `DynamicBundle`, you likely want to set `Effect = ()`, which retains the same behavior as 0.15 bundles:
42

53
```rust
6-
// Before
7-
unsafe impl Bundle for X {
8-
unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self {
9-
}
10-
/* remaining bundle impl here */
4+
// 0.15
5+
impl DynamicBundle for MyBundle {
6+
// ...
117
}
128

13-
// After
14-
unsafe impl Bundle for X {
9+
// 0.16
10+
impl DynamicBundle for MyBundle {
1511
type Effect = ();
16-
/* remaining bundle impl here */
17-
}
1812

19-
unsafe impl BundleFromComponents for X {
20-
unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self {
21-
}
13+
// ...
2214
}
2315
```
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Methods like `Ref::changed_by()` that return a `&'static Location<'static>` will now be available even when the `track_location` feature is disabled, but they will return a new `MaybeLocation` type. `MaybeLocation` wraps a `&'static Location<'static>` when the feature is enabled, and is a ZST when the feature is disabled.
1+
Methods like `Ref::changed_by()` that used to return a `&'static Location<'static>` will now be available even when the `track_location` feature is disabled, but they will now return the new `MaybeLocation` type. `MaybeLocation` wraps a `&'static Location<'static>` when the feature is enabled, and is a ZST when the feature is disabled.
22

3-
Existing code that needs a `&Location` can call `into_option().unwrap()` to recover it. Many trait impls are forwarded, so if you only need `Display` then no changes will be necessary.
3+
Existing code that needs a `&Location` can call `MaybeLocation::into_option()` to recover it. Many trait impls are forwarded, so if you only need `Display` then no changes will be necessary.
44

55
If that code was conditionally compiled, you may instead want to use the methods on `MaybeLocation` to remove the need for conditional compilation.
66

7-
Code that constructs a `Ref`, `Mut`, `Res`, or `ResMut` will now need to provide location information unconditionally. If you are creating them from existing Bevy types, you can obtain a `MaybeLocation` from methods like `Table::get_changed_by_slice_for()` or `ComponentSparseSet::get_with_ticks`. Otherwise, you will need to store a `MaybeLocation` next to your data and use methods like `as_ref()` or `as_mut()` to obtain wrapped references.
7+
Code that constructs a `Ref`, `Mut`, `Res`, or `ResMut` will now need to provide location information unconditionally. If you are creating them from existing Bevy types, you can obtain a `MaybeLocation` from methods like `Table::get_changed_by_slice_for()` or `ComponentSparseSet::get_with_ticks`. Otherwise, you will need to store a `MaybeLocation` next to your data and use methods like `as_ref()` or `as_mut()` to obtain wrapped references.
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
- Remove storages from functions where it is no longer needed.
2-
- Note that SparseSets are no longer present for all registered sparse set components, only those that have been spawned.
1+
In order to decouple `Storages` from `Components`, the following methods no longer take a `&mut Storages` argument:
2+
3+
- `Components::register_component()`
4+
- `Components::register_component_with_descriptor()`
5+
- `Bundle::register_required_components()`
6+
- `Component::register_required_components()`
7+
8+
With this change, note that `SparseSets` will no longer be created when components are registered. Instead, they will only be constructed when those components are spawned.
Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
1-
The `sort` family of methods on `QueryIter` unsoundly gave access `L::Item<'w>` with the full `'w` lifetime. It has been shortened to `L::Item<'w>` so that items cannot escape the comparer. If you get lifetime errors using these methods, you will need to make the comparer generic in the new lifetime. Often this can be done by replacing named `'w` with `'_`, or by replacing the use of a function item with a closure.
1+
The `sort()` family of methods on `QueryIter` unsoundly gave access `L::Item<'w>` with the full world `'w` lifetime, meaning it was possible to smuggle items out of the compare closure. This has been fixed by shortening the lifetime so that items cannot escape the closure on the following methods on `QueryIter` and `QueryManyIter`:
2+
3+
- `sort()`
4+
- `sort_unstable()`
5+
- `sort_by()`
6+
- `sort_unstable_by()`
7+
- `sort_by_key()`
8+
- `sort_unstable_by_key()`
9+
- `sort_by_cached_key()`
10+
11+
This fix may cause your code to get lifetimes errors, such as:
12+
13+
```
14+
error: implementation of `FnMut` is not general enough
15+
```
16+
17+
To fix this, you will need to make the comparer generic over the new lifetime. Often this can be done by replacing named `'w` with `'_`, or by replacing the use of a function item with a closure:
218

319
```rust
4-
// Before: Now fails with "error: implementation of `FnMut` is not general enough"
20+
// 0.15
521
query.iter().sort_by::<&C>(Ord::cmp);
6-
// After: Wrap in a closure
22+
23+
// 0.16
724
query.iter().sort_by::<&C>(|l, r| Ord::cmp(l, r));
25+
```
26+
27+
```rust
28+
// 0.15
29+
fn comparer(left: &&'w C, right: &&'w C) -> Ordering {
30+
// ...
31+
}
32+
33+
query.iter().sort_by::<&C>(comparer);
34+
35+
// 0.16
36+
fn comparer(left: &&C, right: &&C) -> Ordering {
37+
// ...
38+
}
839

940
query.iter().sort_by::<&C>(comparer);
10-
// Before: Uses specific `'w` lifetime from some outer scope
11-
// now fails with "error: implementation of `FnMut` is not general enough"
12-
fn comparer(left: &&'w C, right: &&'w C) -> Ordering { /* ... */ }
13-
// After: Accepts any lifetime using inferred lifetime parameter
14-
fn comparer(left: &&C, right: &&C) -> Ordering { /* ... */ }
1541
```
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
SystemSetConfigs -> ScheduleConfigs<InternedSystemSet>
2-
SystemConfigs -> ScheduleConfigs<ScheduleSystem>
3-
IntoSystemSetConfigs -> IntoScheduleConfigs<InternedSystemSet, M>
4-
IntoSystemConfigs -> IntoScheduleConfigs<ScheduleSystem, M>
1+
In order to reduce internal duplication between scheduling systems and system sets, the new generic `ScheduleConfigs<T>` type and `IntoScheduleConfigs<T>` trait have been added. These take a generic parameter, `T`, that may be `ScheduleSystem` (for systems) or `InternedSystemSet` (for system sets).
2+
3+
|0.15 Item|0.16 Item|
4+
|-|-|
5+
|`SystemConfigs`|`ScheduleConfigs<ScheduleSystem>`|
6+
|`SystemSetConfigs`|`ScheduleConfigs<InternedSystemSet>`|
7+
|`IntoSystemConfigs`|`IntoScheduleConfigs<ScheduleSystem, M>`|
8+
|`IntoSystemSetConfigs`|`IntoScheduleConfigs<InternedSystemSet, M>`|

0 commit comments

Comments
 (0)