Skip to content

Commit ee44141

Browse files
authored
Add Result handling to Commands and EntityCommands (#17043)
## Objective Fixes #2004 Fixes #3845 Fixes #7118 Fixes #10166 ## Solution - The crux of this PR is the new `Command::with_error_handling` method. This wraps the relevant command in another command that, when applied, will apply the original command and handle any resulting errors. - To enable this, `Command::apply` and `EntityCommand::apply` now return `Result`. - `Command::with_error_handling` takes as a parameter an error handler of the form `fn(&mut World, CommandError)`, which it passes the error to. - `CommandError` is an enum that can be either `NoSuchEntity(Entity)` or `CommandFailed(Box<dyn Error>)`. ### Closures - Closure commands can now optionally return `Result`, which will be passed to `with_error_handling`. ### Commands - Fallible commands can be queued with `Commands::queue_fallible` and `Commands::queue_fallible_with`, which call `with_error_handling` before queuing them (using `Commands::queue` will queue them without error handling). - `Commands::queue_fallible_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `command` submodule provides unqueued forms of built-in fallible commands so that you can use them with `queue_fallible_with`. - There is also an `error_handler` submodule that provides simple error handlers for convenience. ### Entity Commands - `EntityCommand` now automatically checks if the entity exists before executing the command, and returns `NoSuchEntity` if it doesn't. - Since all entity commands might need to return an error, they are always queued with error handling. - `EntityCommands::queue_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `entity_command` submodule provides unqueued forms of built-in entity commands so that you can use them with `queue_with`. ### Defaults - In the future, commands should all fail according to the global error handling setting. That doesn't exist yet though. - For this PR, commands all fail the way they do on `main`. - Both now and in the future, the defaults can be overridden by `Commands::override_error_handler` (or equivalent methods on `EntityCommands` and `EntityEntryCommands`). - `override_error_handler` takes an error handler (`fn(&mut World, CommandError)`) and passes it to every subsequent command queued with `Commands::queue_fallible` or `EntityCommands::queue`. - The `_with` variants of the queue methods will still provide an error handler directly to the command. - An override can be reset with `reset_error_handler`. ## Future Work - After a universal error handling mode is added, we can change all commands to fail that way by default. - Once we have all commands failing the same way (which would require either the full removal of `try` variants or just making them useless while they're deprecated), `queue_fallible_with_default` could be removed, since its only purpose is to enable commands having different defaults.
1 parent 5faff84 commit ee44141

File tree

12 files changed

+1158
-453
lines changed

12 files changed

+1158
-453
lines changed

benches/benches/bevy_ecs/world/commands.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use core::hint::black_box;
22

33
use bevy_ecs::{
44
component::Component,
5-
system::Commands,
6-
world::{Command, CommandQueue, World},
5+
result::Result,
6+
system::{Command, Commands},
7+
world::{CommandQueue, World},
78
};
89
use criterion::Criterion;
910

@@ -136,16 +137,18 @@ struct FakeCommandA;
136137
struct FakeCommandB(u64);
137138

138139
impl Command for FakeCommandA {
139-
fn apply(self, world: &mut World) {
140+
fn apply(self, world: &mut World) -> Result {
140141
black_box(self);
141142
black_box(world);
143+
Ok(())
142144
}
143145
}
144146

145147
impl Command for FakeCommandB {
146-
fn apply(self, world: &mut World) {
148+
fn apply(self, world: &mut World) -> Result {
147149
black_box(self);
148150
black_box(world);
151+
Ok(())
149152
}
150153
}
151154

@@ -180,9 +183,10 @@ pub fn fake_commands(criterion: &mut Criterion) {
180183
struct SizedCommand<T: Default + Send + Sync + 'static>(T);
181184

182185
impl<T: Default + Send + Sync + 'static> Command for SizedCommand<T> {
183-
fn apply(self, world: &mut World) {
186+
fn apply(self, world: &mut World) -> Result {
184187
black_box(self);
185188
black_box(world);
189+
Ok(())
186190
}
187191
}
188192

crates/bevy_ecs/src/bundle.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,12 @@ impl SparseSetIndex for BundleId {
336336
}
337337
}
338338

339-
// What to do on insertion if component already exists
339+
/// What to do on insertion if a component already exists.
340340
#[derive(Clone, Copy, Eq, PartialEq)]
341-
pub(crate) enum InsertMode {
341+
pub enum InsertMode {
342342
/// Any existing components of a matching type will be overwritten.
343343
Replace,
344-
/// Any existing components of a matching type will kept unchanged.
344+
/// Any existing components of a matching type will be left unchanged.
345345
Keep,
346346
}
347347

crates/bevy_ecs/src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ pub mod prelude {
7373
IntoSystemSet, IntoSystemSetConfigs, Schedule, Schedules, SystemSet,
7474
},
7575
system::{
76-
Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef, IntoSystem, Local,
77-
NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem, Res, ResMut, Resource,
78-
Single, System, SystemIn, SystemInput, SystemParamBuilder, SystemParamFunction,
79-
WithParamWarnPolicy,
76+
Command, Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef,
77+
IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem,
78+
Res, ResMut, Resource, Single, System, SystemIn, SystemInput, SystemParamBuilder,
79+
SystemParamFunction, WithParamWarnPolicy,
8080
},
8181
world::{
82-
Command, EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut,
82+
EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut,
8383
FromWorld, OnAdd, OnInsert, OnRemove, OnReplace, World,
8484
},
8585
};

0 commit comments

Comments
 (0)