Skip to content

Commit cec1bbf

Browse files
Tests & migration guide
1 parent 675d8ba commit cec1bbf

File tree

7 files changed

+86
-28
lines changed

7 files changed

+86
-28
lines changed

crates/bevy_app/src/app.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,7 +1350,8 @@ impl App {
13501350
self.default_error_handler
13511351
}
13521352

1353-
/// Override the error handler for the all subapps (including the main one).
1353+
/// Set the [default error handler] for the all subapps (including the main one and future ones)
1354+
/// that do not have one.
13541355
///
13551356
/// May only be called once and should be set by the application, not by libraries.
13561357
///
@@ -1363,12 +1364,14 @@ impl App {
13631364
/// ```
13641365
/// # use bevy_app::*;
13651366
/// # use bevy_ecs::error::warn;
1366-
/// # fn MyPlugins() {}
1367+
/// # fn MyPlugins(_: &mut App) {}
13671368
/// App::new()
13681369
/// .set_error_handler(warn)
13691370
/// .add_plugins(MyPlugins)
13701371
/// .run();
13711372
/// ```
1373+
///
1374+
/// [default error handler]: bevy_ecs::error::DefaultErrorHandler
13721375
pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
13731376
assert!(
13741377
self.default_error_handler.is_none(),

crates/bevy_ecs/src/error/command_handling.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ where
5959
self.apply(world);
6060
}
6161
}
62+
63+
#[inline]
64+
fn handle_error(self) -> impl Command {
65+
move |world: &mut World| {
66+
self.apply(world);
67+
}
68+
}
6269
}
6370

6471
impl<C> HandleError for C

crates/bevy_ecs/src/error/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! [`panic`] error handler function is used, resulting in a panic with the error message attached.
99
//!
1010
//! You can change the default behavior by registering a custom error handler:
11-
//! Use [`World::set_default_error_handler`] to set a custom error handler function for a world,
11+
//! Use [`DefaultErrorHandler`] to set a custom error handler function for a world,
1212
//! or `App::set_error_handler` for a whole app.
1313
//! In practice, this is generally feature-flagged: panicking or loudly logging errors in development,
1414
//! and quietly logging or ignoring them in production to avoid crashing the app.
@@ -35,7 +35,7 @@
3535
//! context surrounding the error – such as the system's [`name`] – in your error messages.
3636
//!
3737
//! ```rust, ignore
38-
//! use bevy_ecs::error::{BevyError, ErrorContext};
38+
//! use bevy_ecs::error::{BevyError, ErrorContext, DefaultErrorHandler};
3939
//! use log::trace;
4040
//!
4141
//! fn my_error_handler(error: BevyError, ctx: ErrorContext) {
@@ -48,8 +48,8 @@
4848
//!
4949
//! fn main() {
5050
//! let mut world = World::new();
51-
//! world.set_default_error_handler(my_error_handler);
52-
//!
51+
//! world.insert_resource(DefaultErrorHandler(my_error_handler));
52+
//! // Use your world here
5353
//! }
5454
//! ```
5555
//!

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::any::Any;
33

44
use crate::{
55
component::{ComponentHook, ComponentId, HookContext, Mutable, StorageType},
6-
error::ErrorContext,
6+
error::{ErrorContext, ErrorHandler},
77
observer::{ObserverDescriptor, ObserverTrigger},
88
prelude::*,
99
query::DebugCheckedUnwrap,
@@ -273,7 +273,7 @@ pub struct Observer {
273273
system: Box<dyn Any + Send + Sync + 'static>,
274274
descriptor: ObserverDescriptor,
275275
hook_on_add: ComponentHook,
276-
error_handler: Option<fn(BevyError, ErrorContext)>,
276+
error_handler: Option<ErrorHandler>,
277277
}
278278

279279
impl Observer {
@@ -380,7 +380,6 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
380380
.get::<Observer>()
381381
.debug_checked_unwrap()
382382
.error_handler
383-
.debug_checked_unwrap()
384383
};
385384

386385
let trigger: Trigger<E, B> = Trigger::new(
@@ -409,7 +408,8 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
409408
match (*system).validate_param_unsafe(world) {
410409
Ok(()) => {
411410
if let Err(err) = (*system).run_unsafe(trigger, world) {
412-
error_handler(
411+
let handler = error_handler.unwrap_or_else(|| world.default_error_handler());
412+
handler(
413413
err,
414414
ErrorContext::Observer {
415415
name: (*system).name(),
@@ -421,7 +421,8 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
421421
}
422422
Err(e) => {
423423
if !e.skipped {
424-
error_handler(
424+
let handler = error_handler.unwrap_or_else(|| world.default_error_handler());
425+
handler(
425426
e.into(),
426427
ErrorContext::Observer {
427428
name: (*system).name(),
@@ -458,15 +459,10 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
458459
..Default::default()
459460
};
460461

461-
let default_error_handler = world.default_error_handler();
462-
463462
// Initialize System
464463
let system: *mut dyn ObserverSystem<E, B> =
465464
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
466465
descriptor.merge(&observe.descriptor);
467-
if observe.error_handler.is_none() {
468-
observe.error_handler = Some(default_error_handler);
469-
}
470466
let system = observe.system.downcast_mut::<S>().unwrap();
471467
&mut *system
472468
} else {
@@ -493,7 +489,11 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
493489
#[cfg(test)]
494490
mod tests {
495491
use super::*;
496-
use crate::{event::Event, observer::Trigger};
492+
use crate::{
493+
error::{ignore, DefaultErrorHandler},
494+
event::Event,
495+
observer::Trigger,
496+
};
497497

498498
#[derive(Event)]
499499
struct TriggerEvent;
@@ -521,11 +521,20 @@ mod tests {
521521
Err("I failed!".into())
522522
}
523523

524+
// Using observer error handler
524525
let mut world = World::default();
525526
world.init_resource::<Ran>();
526-
let observer = Observer::new(system).with_error_handler(crate::error::ignore);
527-
world.spawn(observer);
528-
Schedule::default().run(&mut world);
527+
world.spawn(Observer::new(system).with_error_handler(ignore));
528+
world.trigger(TriggerEvent);
529+
assert!(world.resource::<Ran>().0);
530+
531+
// Using world error handler
532+
let mut world = World::default();
533+
world.init_resource::<Ran>();
534+
world.spawn(Observer::new(system));
535+
// Test that the correct handler is used when the observer was added
536+
// before the default handler
537+
world.insert_resource(DefaultErrorHandler(ignore));
529538
world.trigger(TriggerEvent);
530539
assert!(world.resource::<Ran>().0);
531540
}

crates/bevy_ecs/src/schedule/schedule.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,7 @@ mod tests {
20592059
use bevy_ecs_macros::ScheduleLabel;
20602060

20612061
use crate::{
2062+
error::{ignore, panic, DefaultErrorHandler, Result},
20622063
prelude::{ApplyDeferred, Res, Resource},
20632064
schedule::{
20642065
tests::ResMut, IntoScheduleConfigs, Schedule, ScheduleBuildSettings, SystemSet,
@@ -2808,4 +2809,32 @@ mod tests {
28082809
.expect("CheckSystemRan Resource Should Exist");
28092810
assert_eq!(value.0, 2);
28102811
}
2812+
2813+
#[test]
2814+
fn test_default_error_handler() {
2815+
#[derive(Resource, Default)]
2816+
struct Ran(bool);
2817+
2818+
fn system(mut ran: ResMut<Ran>) -> Result {
2819+
ran.0 = true;
2820+
Err("I failed!".into())
2821+
}
2822+
2823+
// Test that the default error handler is used
2824+
let mut world = World::default();
2825+
world.init_resource::<Ran>();
2826+
world.insert_resource(DefaultErrorHandler(ignore));
2827+
let mut schedule = Schedule::default();
2828+
schedule.add_systems(system).run(&mut world);
2829+
assert!(world.resource::<Ran>().0);
2830+
2831+
// Test that the handler doesn't change within the schedule
2832+
schedule.add_systems(
2833+
(|world: &mut World| {
2834+
world.insert_resource(DefaultErrorHandler(panic));
2835+
})
2836+
.before(system),
2837+
);
2838+
schedule.run(&mut world);
2839+
}
28112840
}

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ use crate::{
8989
/// A [`Command`] can return a [`Result`](crate::error::Result),
9090
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
9191
///
92-
/// The [default error handler](crate::error::default_error_handler) panics.
93-
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
92+
/// The default error handler panics. It can be configured via
93+
/// the [`DefaultErrorHandler`](crate::error::DefaultErrorHandler) resource.
9494
///
9595
/// Alternatively, you can customize the error handler for a specific command
9696
/// by calling [`Commands::queue_handled`].
@@ -509,7 +509,7 @@ impl<'w, 's> Commands<'w, 's> {
509509
/// Pushes a generic [`Command`] to the command queue.
510510
///
511511
/// If the [`Command`] returns a [`Result`],
512-
/// it will be handled using the [default error handler](crate::error::default_error_handler).
512+
/// it will be handled using the [default error handler](crate::error::DefaultErrorHandler).
513513
///
514514
/// To use a custom error handler, see [`Commands::queue_handled`].
515515
///
@@ -695,7 +695,7 @@ impl<'w, 's> Commands<'w, 's> {
695695
/// This command will fail if any of the given entities do not exist.
696696
///
697697
/// It will internally return a [`TryInsertBatchError`](crate::world::error::TryInsertBatchError),
698-
/// which will be handled by the [default error handler](crate::error::default_error_handler).
698+
/// which will be handled by the [default error handler](crate::error::DefaultErrorHandler).
699699
#[track_caller]
700700
pub fn insert_batch<I, B>(&mut self, batch: I)
701701
where
@@ -726,7 +726,7 @@ impl<'w, 's> Commands<'w, 's> {
726726
/// This command will fail if any of the given entities do not exist.
727727
///
728728
/// It will internally return a [`TryInsertBatchError`](crate::world::error::TryInsertBatchError),
729-
/// which will be handled by the [default error handler](crate::error::default_error_handler).
729+
/// which will be handled by the [default error handler](crate::error::DefaultErrorHandler).
730730
#[track_caller]
731731
pub fn insert_batch_if_new<I, B>(&mut self, batch: I)
732732
where
@@ -1223,8 +1223,8 @@ impl<'w, 's> Commands<'w, 's> {
12231223
/// An [`EntityCommand`] can return a [`Result`](crate::error::Result),
12241224
/// which will be passed to an [error handler](crate::error) if the `Result` is an error.
12251225
///
1226-
/// The [default error handler](crate::error::default_error_handler) panics.
1227-
/// It can be configured via [`set_global_default_error_handler`](crate::error::set_global_default_error_handler).
1226+
/// The default error handler panics. It can be configured via
1227+
/// the [`DefaultErrorHandler`](crate::error::DefaultErrorHandler) resource.
12281228
///
12291229
/// Alternatively, you can customize the error handler for a specific command
12301230
/// by calling [`EntityCommands::queue_handled`].
@@ -1767,7 +1767,7 @@ impl<'a> EntityCommands<'a> {
17671767
/// Pushes an [`EntityCommand`] to the queue,
17681768
/// which will get executed for the current [`Entity`].
17691769
///
1770-
/// The [default error handler](crate::error::default_error_handler)
1770+
/// The [default error handler](crate::error::DefaultErrorHandler)
17711771
/// will be used to handle error cases.
17721772
/// Every [`EntityCommand`] checks whether the entity exists at the time of execution
17731773
/// and returns an error if it does not.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Global default error handler
3+
pull_requests: [18810]
4+
---
5+
6+
Worlds can now have different default error handlers, so there no longer is a global handler.
7+
8+
Replace uses of `GLOBAL_ERROR_HANDLER` with `App`'s `.set_error_handler(handler)`.
9+
For worlds that do not directly belong to an `App`/`SubApp`,
10+
insert the `DefaultErrorHandler(handler)` resource.

0 commit comments

Comments
 (0)