Skip to content

Commit b4c2acf

Browse files
resource instead of field
1 parent ba6d4b5 commit b4c2acf

File tree

9 files changed

+125
-66
lines changed

9 files changed

+125
-66
lines changed

crates/bevy_app/src/app.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use alloc::{
1010
pub use bevy_derive::AppLabel;
1111
use bevy_ecs::{
1212
component::RequiredComponentsError,
13-
error::{panic, ErrorHandler},
13+
error::{DefaultErrorHandler, ErrorHandler},
1414
event::{event_update_system, EventCursor},
1515
intern::Interned,
1616
prelude::*,
@@ -86,7 +86,7 @@ pub struct App {
8686
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
8787
/// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
8888
pub(crate) runner: RunnerFn,
89-
default_error_handler: ErrorHandler,
89+
default_error_handler: Option<ErrorHandler>,
9090
}
9191

9292
impl Debug for App {
@@ -145,7 +145,7 @@ impl App {
145145
sub_apps: HashMap::default(),
146146
},
147147
runner: Box::new(run_once),
148-
default_error_handler: panic,
148+
default_error_handler: None,
149149
}
150150
}
151151

@@ -1119,9 +1119,11 @@ impl App {
11191119

11201120
/// Inserts a [`SubApp`] with the given label.
11211121
pub fn insert_sub_app(&mut self, label: impl AppLabel, mut sub_app: SubApp) {
1122-
sub_app
1123-
.world_mut()
1124-
.set_default_error_handler(self.default_error_handler);
1122+
if let Some(handler) = self.default_error_handler {
1123+
sub_app
1124+
.world_mut()
1125+
.get_resource_or_insert_with(|| DefaultErrorHandler(handler));
1126+
}
11251127
self.sub_apps.sub_apps.insert(label.intern(), sub_app);
11261128
}
11271129

@@ -1344,27 +1346,39 @@ impl App {
13441346
/// Gets the error handler to set for new supapps.
13451347
///
13461348
/// Note that the error handler of existing subapps may differ.
1347-
pub fn get_error_handler(&self) -> ErrorHandler {
1349+
pub fn get_error_handler(&self) -> Option<ErrorHandler> {
13481350
self.default_error_handler
13491351
}
13501352

13511353
/// Override the error handler for the all subapps (including the main one).
13521354
///
1353-
/// This handler will be called when an error is produced and not otherwise handled.
1355+
/// May only be called once and should be set by the application, not by libraries.
1356+
///
1357+
/// The handler will be called when an error is produced and not otherwise handled.
1358+
///
1359+
/// # Panics
1360+
/// Panics if called multiple times.
13541361
///
13551362
/// # Example
13561363
/// ```
13571364
/// # use bevy_app::*;
13581365
/// # use bevy_ecs::error::warn;
1366+
/// # fn MyPlugins() {}
13591367
/// App::new()
13601368
/// .set_error_handler(warn)
1361-
/// .add_plugins(DefaultPlugins)
1362-
/// .run()
1369+
/// .add_plugins(MyPlugins)
1370+
/// .run();
13631371
/// ```
13641372
pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
1365-
self.default_error_handler = handler;
1373+
assert!(
1374+
self.default_error_handler.is_none(),
1375+
"`set_error_handler` called multiple times on same `App`"
1376+
);
1377+
self.default_error_handler = Some(handler);
13661378
for sub_app in self.sub_apps.iter_mut() {
1367-
sub_app.world_mut().set_default_error_handler(handler);
1379+
sub_app
1380+
.world_mut()
1381+
.get_resource_or_insert_with(|| DefaultErrorHandler(handler));
13681382
}
13691383
self
13701384
}

crates/bevy_ecs/src/error/command_handling.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,15 @@ use crate::{
99

1010
use super::{BevyError, ErrorContext, ErrorHandler};
1111

12-
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
12+
/// Takes a [`Command`] that potentially returns a Result and uses a given error handler function to convert it into
1313
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
1414
pub trait HandleError<Out = ()>: Send + 'static {
1515
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
1616
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
1717
fn handle_error_with(self, error_handler: ErrorHandler) -> impl Command;
1818
/// Takes a [`Command`] that returns a Result and uses the default error handler function to convert it into
1919
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
20-
fn handle_error(self) -> impl Command
21-
where
22-
Self: Sized,
23-
{
24-
move |world: &mut World| {
25-
self.handle_error_with(world.default_error_handler)
26-
.apply(world);
27-
}
28-
}
20+
fn handle_error(self) -> impl Command;
2921
}
3022

3123
impl<C, T, E> HandleError<Result<T, E>> for C
@@ -44,6 +36,18 @@ where
4436
),
4537
}
4638
}
39+
40+
fn handle_error(self) -> impl Command {
41+
move |world: &mut World| match self.apply(world) {
42+
Ok(_) => {}
43+
Err(err) => world.default_error_handler()(
44+
err.into(),
45+
ErrorContext::Command {
46+
name: type_name::<C>().into(),
47+
},
48+
),
49+
}
50+
}
4751
}
4852

4953
impl<C> HandleError<Never> for C
@@ -66,10 +70,7 @@ where
6670
self
6771
}
6872
#[inline]
69-
fn handle_error(self) -> impl Command
70-
where
71-
Self: Sized,
72-
{
73+
fn handle_error(self) -> impl Command {
7374
self
7475
}
7576
}

crates/bevy_ecs/src/error/handler.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use core::fmt::Display;
22

3-
use crate::{component::Tick, error::BevyError};
3+
use crate::{component::Tick, error::BevyError, prelude::Resource};
44
use alloc::borrow::Cow;
5+
use derive_more::derive::{Deref, DerefMut};
56

67
/// Context for a [`BevyError`] to aid in debugging.
78
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -89,6 +90,22 @@ macro_rules! inner {
8990
/// Defines how Bevy reacts to errors.
9091
pub type ErrorHandler = fn(BevyError, ErrorContext);
9192

93+
/// Error handler to call when an error is not handled otherwise.
94+
/// Defaults to [`panic()`].
95+
///
96+
/// When updated while a [`Schedule`] is running, it doesn't take effect for
97+
/// that schedule until it's completed.
98+
///
99+
/// [`Schedule`]: crate::schedule::Schedule
100+
#[derive(Resource, Deref, DerefMut, Copy, Clone)]
101+
pub struct DefaultErrorHandler(pub ErrorHandler);
102+
103+
impl Default for DefaultErrorHandler {
104+
fn default() -> Self {
105+
Self(panic)
106+
}
107+
}
108+
92109
/// Error handler that panics with the system error.
93110
#[track_caller]
94111
#[inline]

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ impl Observer {
232232
system: Box::new(|| {}),
233233
descriptor: Default::default(),
234234
hook_on_add: |mut world, hook_context| {
235-
let default_error_handler = world.default_error_handler;
235+
let default_error_handler = world.default_error_handler();
236236
world.commands().queue(move |world: &mut World| {
237237
let entity = hook_context.entity;
238238
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
@@ -421,7 +421,7 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
421421
B::component_ids(&mut world.components_registrator(), &mut |id| {
422422
components.push(id);
423423
});
424-
let default_error_handler = world.default_error_handler;
424+
let default_error_handler = world.default_error_handler();
425425
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
426426
observe.descriptor.events.push(event_id);
427427
observe.descriptor.components.extend(components);

crates/bevy_ecs/src/schedule/executor/multi_threaded.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ impl ExecutorState {
484484
system,
485485
conditions,
486486
context.environment.world_cell,
487+
context.error_handler,
487488
)
488489
} {
489490
self.skip_system_and_signal_dependents(system_index);
@@ -584,6 +585,7 @@ impl ExecutorState {
584585
system: &mut ScheduleSystem,
585586
conditions: &mut Conditions,
586587
world: UnsafeWorldCell,
588+
error_handler: ErrorHandler,
587589
) -> bool {
588590
let mut should_run = !self.skipped_systems.contains(system_index);
589591

@@ -598,7 +600,11 @@ impl ExecutorState {
598600
// required by the conditions.
599601
// - `update_archetype_component_access` has been called for each run condition.
600602
let set_conditions_met = unsafe {
601-
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world)
603+
evaluate_and_fold_conditions(
604+
&mut conditions.set_conditions[set_idx],
605+
world,
606+
error_handler,
607+
)
602608
};
603609

604610
if !set_conditions_met {
@@ -616,7 +622,11 @@ impl ExecutorState {
616622
// required by the conditions.
617623
// - `update_archetype_component_access` has been called for each run condition.
618624
let system_conditions_met = unsafe {
619-
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world)
625+
evaluate_and_fold_conditions(
626+
&mut conditions.system_conditions[system_index],
627+
world,
628+
error_handler,
629+
)
620630
};
621631

622632
if !system_conditions_met {
@@ -634,7 +644,7 @@ impl ExecutorState {
634644
Ok(()) => true,
635645
Err(e) => {
636646
if !e.skipped {
637-
world.default_error_handler()(
647+
error_handler(
638648
e.into(),
639649
ErrorContext::System {
640650
name: system.name(),
@@ -821,6 +831,7 @@ fn apply_deferred(
821831
unsafe fn evaluate_and_fold_conditions(
822832
conditions: &mut [BoxedCondition],
823833
world: UnsafeWorldCell,
834+
error_handler: ErrorHandler,
824835
) -> bool {
825836
#[expect(
826837
clippy::unnecessary_fold,
@@ -837,7 +848,7 @@ unsafe fn evaluate_and_fold_conditions(
837848
Ok(()) => (),
838849
Err(e) => {
839850
if !e.skipped {
840-
world.default_error_handler()(
851+
error_handler(
841852
e.into(),
842853
ErrorContext::System {
843854
name: condition.name(),

crates/bevy_ecs/src/schedule/executor/simple.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ impl SystemExecutor for SimpleExecutor {
7373
}
7474

7575
// evaluate system set's conditions
76-
let set_conditions_met =
77-
evaluate_and_fold_conditions(&mut schedule.set_conditions[set_idx], world);
76+
let set_conditions_met = evaluate_and_fold_conditions(
77+
&mut schedule.set_conditions[set_idx],
78+
world,
79+
error_handler,
80+
);
7881

7982
if !set_conditions_met {
8083
self.completed_systems
@@ -86,8 +89,11 @@ impl SystemExecutor for SimpleExecutor {
8689
}
8790

8891
// evaluate system's conditions
89-
let system_conditions_met =
90-
evaluate_and_fold_conditions(&mut schedule.system_conditions[system_index], world);
92+
let system_conditions_met = evaluate_and_fold_conditions(
93+
&mut schedule.system_conditions[system_index],
94+
world,
95+
error_handler,
96+
);
9197

9298
should_run &= system_conditions_met;
9399

@@ -175,7 +181,11 @@ impl SimpleExecutor {
175181
since = "0.17.0",
176182
note = "Use SingleThreadedExecutor instead. See https://github.com/bevyengine/bevy/issues/18453 for motivation."
177183
)]
178-
fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
184+
fn evaluate_and_fold_conditions(
185+
conditions: &mut [BoxedCondition],
186+
world: &mut World,
187+
error_handler: ErrorHandler,
188+
) -> bool {
179189
#[expect(
180190
clippy::unnecessary_fold,
181191
reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
@@ -187,7 +197,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W
187197
Ok(()) => (),
188198
Err(e) => {
189199
if !e.skipped {
190-
world.default_error_handler()(
200+
error_handler(
191201
e.into(),
192202
ErrorContext::System {
193203
name: condition.name(),

crates/bevy_ecs/src/schedule/executor/single_threaded.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ impl SystemExecutor for SingleThreadedExecutor {
7373
}
7474

7575
// evaluate system set's conditions
76-
let set_conditions_met =
77-
evaluate_and_fold_conditions(&mut schedule.set_conditions[set_idx], world);
76+
let set_conditions_met = evaluate_and_fold_conditions(
77+
&mut schedule.set_conditions[set_idx],
78+
world,
79+
error_handler,
80+
);
7881

7982
if !set_conditions_met {
8083
self.completed_systems
@@ -86,8 +89,11 @@ impl SystemExecutor for SingleThreadedExecutor {
8689
}
8790

8891
// evaluate system's conditions
89-
let system_conditions_met =
90-
evaluate_and_fold_conditions(&mut schedule.system_conditions[system_index], world);
92+
let system_conditions_met = evaluate_and_fold_conditions(
93+
&mut schedule.system_conditions[system_index],
94+
world,
95+
error_handler,
96+
);
9197

9298
should_run &= system_conditions_met;
9399

@@ -193,7 +199,11 @@ impl SingleThreadedExecutor {
193199
}
194200
}
195201

196-
fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
202+
fn evaluate_and_fold_conditions(
203+
conditions: &mut [BoxedCondition],
204+
world: &mut World,
205+
error_handler: ErrorHandler,
206+
) -> bool {
197207
#[expect(
198208
clippy::unnecessary_fold,
199209
reason = "Short-circuiting here would prevent conditions from mutating their own state as needed."
@@ -205,7 +215,7 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W
205215
Ok(()) => (),
206216
Err(e) => {
207217
if !e.skipped {
208-
world.default_error_handler()(
218+
error_handler(
209219
e.into(),
210220
ErrorContext::System {
211221
name: condition.name(),

0 commit comments

Comments
 (0)