Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1494,12 +1494,26 @@ impl App {
/// # Example
/// ```
/// # use bevy_app::*;
/// # use bevy_ecs::error::warn;
/// # fn MyPlugins(_: &mut App) {}
/// App::new()
/// .set_error_handler(warn)
/// .add_plugins(MyPlugins)
/// .run();
/// # use bevy_ecs::{error::{BevyError, ErrorContext, Result}, system::Commands};
/// fn exit_on_error<'w, 's>(
/// _: BevyError,
/// _: ErrorContext,
/// mut commands: Commands<'w, 's>,
/// ) -> Commands<'w, 's> {
/// commands.write_message(AppExit::error());
/// commands
/// }
///
/// fn fallible_system() -> Result {
/// Err("The system failed".into())
/// }
///
/// let mut app = App::new();
/// app.set_error_handler(exit_on_error)
/// .add_systems(Update, fallible_system)
/// .update();
///
/// assert_eq!(app.should_exit(), Some(AppExit::error()));
/// ```
///
/// [fallback error handler]: bevy_ecs::error::FallbackErrorHandler
Expand Down
81 changes: 64 additions & 17 deletions crates/bevy_ecs/src/error/handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use core::fmt::Display;

use crate::{change_detection::Tick, error::BevyError, prelude::Resource};
use crate::{
change_detection::Tick,
error::BevyError,
prelude::{Commands, Resource},
};
use bevy_ecs::error::Severity;
use bevy_utils::prelude::DebugName;
use derive_more::derive::{Deref, DerefMut};
Expand Down Expand Up @@ -107,7 +111,11 @@ macro_rules! inner {
/// consider setting [`PANIC_ORIGINATES_FROM_ERROR_HANDLER`].
/// This lets the executor know that a panic doesn't need to be
/// converted back to a [`BevyError`] and passed to the [`FallbackErrorHandler`].
pub type ErrorHandler = fn(BevyError, ErrorContext);
///
/// Error handlers can queue deferred work using the provided [`Commands`].
/// The [`Commands`] must be returned, even if they are not used.
pub type ErrorHandler =
for<'w, 's> fn(BevyError, ErrorContext, Commands<'w, 's>) -> Commands<'w, 's>;

/// Fallback error handler to call when an error is not handled otherwise.
/// Defaults to [`match_severity()`].
Expand Down Expand Up @@ -138,22 +146,30 @@ std::thread_local! {
/// Error handler that defers to an error's [`Severity`].
#[track_caller]
#[inline]
pub fn match_severity(err: BevyError, ctx: ErrorContext) {
pub fn match_severity<'w, 's>(
err: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
match err.severity() {
Severity::Ignore => ignore(err, ctx),
Severity::Trace => trace(err, ctx),
Severity::Debug => debug(err, ctx),
Severity::Info => info(err, ctx),
Severity::Warning => warn(err, ctx),
Severity::Error => error(err, ctx),
Severity::Panic => panic(err, ctx),
Severity::Ignore => ignore(err, ctx, commands),
Severity::Trace => trace(err, ctx, commands),
Severity::Debug => debug(err, ctx, commands),
Severity::Info => info(err, ctx, commands),
Severity::Warning => warn(err, ctx, commands),
Severity::Error => error(err, ctx, commands),
Severity::Panic => panic(err, ctx, commands),
}
}

/// Error handler that panics with the system error.
#[track_caller]
#[inline]
pub fn panic(error: BevyError, ctx: ErrorContext) {
pub fn panic<'w, 's>(
error: BevyError,
ctx: ErrorContext,
_commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
#[cfg(feature = "std")]
PANIC_ORIGINATES_FROM_ERROR_HANDLER.set(true);
inner!(panic, error, ctx);
Expand All @@ -162,39 +178,70 @@ pub fn panic(error: BevyError, ctx: ErrorContext) {
/// Error handler that logs the system error at the `error` level.
#[track_caller]
#[inline]
pub fn error(error: BevyError, ctx: ErrorContext) {
pub fn error<'w, 's>(
error: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
inner!(log::error, error, ctx);
commands
}

/// Error handler that logs the system error at the `warn` level.
#[track_caller]
#[inline]
pub fn warn(error: BevyError, ctx: ErrorContext) {
pub fn warn<'w, 's>(
error: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
inner!(log::warn, error, ctx);
commands
}

/// Error handler that logs the system error at the `info` level.
#[track_caller]
#[inline]
pub fn info(error: BevyError, ctx: ErrorContext) {
pub fn info<'w, 's>(
error: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
inner!(log::info, error, ctx);
commands
}

/// Error handler that logs the system error at the `debug` level.
#[track_caller]
#[inline]
pub fn debug(error: BevyError, ctx: ErrorContext) {
pub fn debug<'w, 's>(
error: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
inner!(log::debug, error, ctx);
commands
}

/// Error handler that logs the system error at the `trace` level.
#[track_caller]
#[inline]
pub fn trace(error: BevyError, ctx: ErrorContext) {
pub fn trace<'w, 's>(
error: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
inner!(log::trace, error, ctx);
commands
}

/// Error handler that ignores the system error.
#[track_caller]
#[inline]
pub fn ignore(_: BevyError, _: ErrorContext) {}
pub fn ignore<'w, 's>(
_: BevyError,
_: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
commands
}
17 changes: 12 additions & 5 deletions crates/bevy_ecs/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,29 @@
//! signature:
//!
//! ```rust,ignore
//! fn(BevyError, ErrorContext)
//! for<'w, 's> fn(BevyError, ErrorContext, Commands<'w, 's>) -> Commands<'w, 's>
//! ```
//!
//! The [`ErrorContext`] allows you to access additional details relevant to providing
//! context surrounding the error – such as the system's [`name`] – in your error messages.
//!
//! ```rust, ignore
//! use bevy_ecs::error::{BevyError, ErrorContext, FallbackErrorHandler};
//! use bevy_ecs::{
//! error::{BevyError, ErrorContext, FallbackErrorHandler},
//! system::Commands,
//! };
//! use log::trace;
//!
//! fn my_error_handler(error: BevyError, ctx: ErrorContext) {
//! fn my_error_handler<'w, 's>(
//! error: BevyError,
//! ctx: ErrorContext,
//! commands: Commands<'w, 's>,
//! ) -> Commands<'w, 's> {
//! if ctx.name().ends_with("plz_ignore") {
//! trace!("Nothing to see here, move along.");
//! return;
//! return commands;
//! }
//! bevy_ecs::error::error(error, ctx);
//! bevy_ecs::error::error(error, ctx, commands)
//! }
//!
//! fn main() {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/observer/distributed_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use core::marker::PhantomData;

use crate::{
component::{ComponentCloneBehavior, ComponentId, Mutable, StorageType},
error::{ErrorContext, ErrorHandler},
error::ErrorHandler,
event::EventKey,
lifecycle::{ComponentHook, HookContext},
observer::{
Expand Down Expand Up @@ -338,7 +338,7 @@ impl Observer {
/// Sets the error handler to use for this observer.
///
/// See the [`error` module-level documentation](crate::error) for more information.
pub fn with_error_handler(mut self, error_handler: fn(BevyError, ErrorContext)) -> Self {
pub fn with_error_handler(mut self, error_handler: ErrorHandler) -> Self {
self.error_handler = Some(error_handler);
self
}
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_ecs/src/observer/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,15 @@ pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSyst
let handler = state
.error_handler
.unwrap_or_else(|| world.fallback_error_handler());
handler(
let mut deferred_world = world.into_deferred();
let commands = deferred_world.commands();
let _commands = handler(
err,
ErrorContext::Observer {
name: (*system).name(),
last_run: (*system).get_last_run(),
},
commands,
);
};
(*system).queue_deferred(world.into_deferred());
Expand Down
9 changes: 7 additions & 2 deletions crates/bevy_ecs/src/schedule/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1653,7 +1653,7 @@ mod tests {
message::Message,
query::With,
schedule::{IntoScheduleConfigs, Schedule},
system::{IntoSystem, Local, System},
system::{Commands, IntoSystem, Local, System},
world::World,
};
use bevy_ecs_macros::{Resource, SystemSet};
Expand Down Expand Up @@ -2052,12 +2052,17 @@ mod tests {
true
}

fn my_error_handler(_: BevyError, ctx: ErrorContext) {
fn my_error_handler<'w, 's>(
_: BevyError,
ctx: ErrorContext,
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
let a = IntoSystem::into_system(system_a);
let b = IntoSystem::into_system(system_b);
assert!(
matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
);
commands
}

fn system_a() {}
Expand Down
14 changes: 9 additions & 5 deletions crates/bevy_ecs/src/schedule/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use fixedbitset::FixedBitSet;

use crate::{
change_detection::{CheckChangeTicks, Tick},
error::{BevyError, ErrorContext, Result},
error::{ErrorHandler, Result},
prelude::{IntoSystemSet, SystemSet},
query::FilteredAccessSet,
schedule::{
Expand All @@ -36,7 +36,7 @@ pub trait SystemExecutor: Send + Sync {
schedule: &mut SystemSchedule,
world: &mut World,
skip_systems: Option<&FixedBitSet>,
error_handler: fn(BevyError, ErrorContext),
error_handler: ErrorHandler,
);
/// Sets whether deferred system buffers should be applied after all systems have run.
fn set_apply_final_deferred(&mut self, value: bool);
Expand Down Expand Up @@ -242,6 +242,8 @@ impl IntoSystemSet<()> for ApplyDeferred {
mod __rust_begin_short_backtrace {
use core::hint::black_box;

#[cfg(feature = "std")]
use crate::system::Commands;
#[cfg(feature = "std")]
use crate::world::unsafe_world_cell::UnsafeWorldCell;
use crate::{
Expand Down Expand Up @@ -314,13 +316,15 @@ mod __rust_begin_short_backtrace {

#[inline(never)]
#[cfg(feature = "std")]
pub(super) fn error_handler(
pub(super) fn error_handler<'w, 's>(
error_handler: crate::error::ErrorHandler,
err: crate::error::BevyError,
err_context: crate::error::ErrorContext,
) {
error_handler(err, err_context);
commands: Commands<'w, 's>,
) -> Commands<'w, 's> {
let commands = error_handler(err, err_context, commands);
black_box(());
commands
}
}

Expand Down
Loading