diff --git a/examples/ecs/command_error_handling.rs b/examples/ecs/command_error_handling.rs index 1d04f55f42aa35..65a0a479dcece6 100644 --- a/examples/ecs/command_error_handling.rs +++ b/examples/ecs/command_error_handling.rs @@ -1,51 +1,74 @@ -use bevy::prelude::*; +use bevy::{core::FixedTimestep, prelude::*}; fn main() { App::build() - .add_startup_system(handle_command_error.system()) + .add_startup_system(setup.system()) + .add_system_set( + SystemSet::new() + .with_run_criteria(FixedTimestep::step(0.5)) + .with_system(remove_components.system()), + ) + .add_system_set( + SystemSet::new() + .with_run_criteria(FixedTimestep::step(2.0)) + .with_system(despawn_all_entities.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(); +struct A(usize); + +#[derive(Bundle, Default)] +struct B { + value: usize, +} + +struct FailedDespawnAttempts(usize); + +fn setup(mut commands: Commands) { + for i in 0..3 { + // Note that `insert` and `insert_bundle` are fallible functions. + // If no error handler is specified, the default behavior is to log the error, and continue. + // These calls to `insert` and `insert_bundle` will not fail, since the entity is valid. + commands.spawn().insert(A(i)).insert_bundle(B::default()); + } + + commands.insert_resource(FailedDespawnAttempts(0)); +} + +fn despawn_all_entities(mut commands: Commands, query: Query) { + for e in query.iter() { + // `on_failure` allows you to provide a custom error handler! + commands + .entity(e) + .despawn() + .on_failure(|CommandError { error, world, .. }| { + // You'll notice that the `error` will also give you back the entity + // you tried to despawn. + let entity = error.entity; + + println!("Sadly our entity '{:?}' didn't despawned... :(", entity); + + // error handlers have mutable access to `World` + if let Some(mut failed_despawns) = world.get_resource_mut::() + { + failed_despawns.0 += 1; + } + }); + } +} + +fn remove_components(mut commands: Commands, query: Query) { + for e in query.iter() { + // Some nice things: + // - You can still chain commands! + // - There are a slew of built-in error handlers + commands + .entity(e) + .remove::() + .ignore() // `ignore` will neither log nor panic the error + .remove_bundle::() + .log_on_failure(); // `log_on_failure` is the default behavior, and will log the error. + // `panic_on_failure` is another alternative which will panic on the error. + } }