diff --git a/Cargo.toml b/Cargo.toml index 898ab0bbc3b8fd..8a3b9cf2b85e18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -255,6 +255,10 @@ path = "examples/ecs/ecs_guide.rs" name = "change_detection" path = "examples/ecs/change_detection.rs" +[[example]] +name = "command_error_handling" +path = "examples/ecs/command_error_handling.rs" + [[example]] name = "event" path = "examples/ecs/event.rs" diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index cdb3432494d674..61efd110355492 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -27,8 +27,9 @@ pub mod prelude { Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage, }, system::{ - Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, - NonSendMut, Query, QuerySet, RemovedComponents, Res, ResMut, System, + CommandError, Commands, FallibleCommand, In, IntoChainSystem, IntoExclusiveSystem, + IntoSystem, Local, NonSend, NonSendMut, Query, QuerySet, RemovedComponents, Res, + ResMut, System, }, world::{FromWorld, Mut, World}, }; diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 89c23a13f496fc..f6cc8df5e21b6b 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -9,8 +9,8 @@ use crate::{ }; use std::{fmt::Debug, marker::PhantomData}; -pub use config::{FallibleCommandConfig, FinalFallibleCommandConfig}; -pub use fallible::{CommandError, FallibleCommand}; +pub use config::*; +pub use fallible::*; /// A [`World`] mutation. /// If this could potentially fail, use [`FallibleCommand`]. diff --git a/examples/README.md b/examples/README.md index 13b791835ba65f..6dc4a75fc37812 100644 --- a/examples/README.md +++ b/examples/README.md @@ -150,6 +150,7 @@ Example | File | Description --- | --- | --- `ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS `change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components +`command_error_handling` | [`ecs/command_error_handling.rs](./ecs/command_error_handling.rs) | Error handling fallible commands `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities diff --git a/examples/ecs/command_error_handling.rs b/examples/ecs/command_error_handling.rs new file mode 100644 index 00000000000000..1d04f55f42aa35 --- /dev/null +++ b/examples/ecs/command_error_handling.rs @@ -0,0 +1,51 @@ +use bevy::prelude::*; + +fn main() { + App::build() + .add_startup_system(handle_command_error.system()) + .run(); +} + +#[derive(Debug)] +struct AComponent(usize); + +fn handle_command_error(mut commands: Commands) { + let e = commands.spawn().id(); + + // Immediately despawn the entity. + commands.entity(e).despawn(); + + // This `despawn` command will fail because the entity was already despawned! + // If no error handler is specified, the error will be logged. + commands.entity(e).despawn(); + + // Optionally, `on_failure` allows you to provide a custom error handler! + commands + .entity(e) + .insert(AComponent(0)) + .on_failure(|CommandError { error, world, .. }| { + // You'll notice that the `error` will also give you back the component you + // attempted to insert on the entity. + + println!( + "Sadly our component '{:?}' for entity '{:?}' didn't insert... :(", + error.component, error.entity + ); + + // error handlers have mutable access to `World` + world.insert_resource("🐦"); + }); + + // Some nice things: + // - You can still chain commands! + // - There are a slew of built-in error handlers + commands + .entity(e) + .insert(AComponent(1)) + .ignore() // `ignore` will neither log nor panic the error + .insert(AComponent(2)) + .log_on_failure(); // `log_on_failure` is the default behavior, and will log the error + + // Uncomment the below line to see the command error cause a panic due to `panic_on_failure` + // commands.entity(e).despawn().panic_on_failure(); +}