//! Showcases how fallible systems and observers can make use of Rust's powerful result handling1//! syntax.23use bevy::ecs::{error::warn, world::DeferredWorld};4use bevy::math::sampling::UniformMeshSampler;5use bevy::prelude::*;67use rand::distr::Distribution;8use rand::SeedableRng;9use rand_chacha::ChaCha8Rng;1011fn main() {12let mut app = App::new();13// By default, fallible systems that return an error will panic.14//15// We can change this by setting a custom error handler, which applies to the entire app16// (you can also set it for specific `World`s).17// Here we are using one of the built-in error handlers.18// Bevy provides built-in handlers for `panic`, `error`, `warn`, `info`,19// `debug`, `trace` and `ignore`.20app.set_error_handler(warn);2122app.add_plugins(DefaultPlugins);2324#[cfg(feature = "bevy_mesh_picking_backend")]25app.add_plugins(MeshPickingPlugin);2627// Fallible systems can be used the same way as regular systems. The only difference is they28// return a `Result<(), BevyError>` instead of a `()` (unit) type. Bevy will handle both29// types of systems the same way, except for the error handling.30app.add_systems(Startup, setup);3132// Commands can also return `Result`s, which are automatically handled by the global error handler33// if not explicitly handled by the user.34app.add_systems(Startup, failing_commands);3536// Individual systems can also be handled by piping the output result:37app.add_systems(38PostStartup,39failing_system.pipe(|result: In<Result>| {40let _ = result.0.inspect_err(|err| info!("captured error: {err}"));41}),42);4344// Fallible observers are also supported.45app.add_observer(fallible_observer);4647// If we run the app, we'll see the following output at startup:48//49// WARN Encountered an error in system `fallible_systems::failing_system`: Resource not initialized50// ERROR fallible_systems::failing_system failed: Resource not initialized51// INFO captured error: Resource not initialized52app.run();53}5455/// An example of a system that calls several fallible functions with the question mark operator.56///57/// See: <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>58fn setup(59mut commands: Commands,60mut meshes: ResMut<Assets<Mesh>>,61mut materials: ResMut<Assets<StandardMaterial>>,62) -> Result {63let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);6465// Make a plane for establishing space.66commands.spawn((67Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),68MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),69Transform::from_xyz(0.0, -2.5, 0.0),70));7172// Spawn a light:73commands.spawn((74PointLight {75shadows_enabled: true,76..default()77},78Transform::from_xyz(4.0, 8.0, 4.0),79));8081// Spawn a camera:82commands.spawn((83Camera3d::default(),84Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),85));8687// Create a new sphere mesh:88let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;89sphere_mesh.generate_tangents()?;9091// Spawn the mesh into the scene:92let mut sphere = commands.spawn((93Mesh3d(meshes.add(sphere_mesh.clone())),94MeshMaterial3d(materials.add(StandardMaterial::default())),95Transform::from_xyz(-1.0, 1.0, 0.0),96));9798// Generate random sample points:99let triangles = sphere_mesh.triangles()?;100let distribution = UniformMeshSampler::try_new(triangles)?;101102// Setup sample points:103let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);104let point_material = materials.add(StandardMaterial {105base_color: Srgba::RED.into(),106emissive: LinearRgba::rgb(1.0, 0.0, 0.0),107..default()108});109110// Add sample points as children of the sphere:111for point in distribution.sample_iter(&mut seeded_rng).take(10000) {112sphere.with_child((113Mesh3d(point_mesh.clone()),114MeshMaterial3d(point_material.clone()),115Transform::from_translation(point),116));117}118119// Indicate the system completed successfully:120Ok(())121}122123// Observer systems can also return a `Result`.124fn fallible_observer(125event: On<Pointer<Move>>,126mut world: DeferredWorld,127mut step: Local<f32>,128) -> Result {129let mut transform = world130.get_mut::<Transform>(event.entity())131.ok_or("No transform found.")?;132133*step = if transform.translation.x > 3. {134-0.1135} else if transform.translation.x < -3. || *step == 0. {1360.1137} else {138*step139};140141transform.translation.x += *step;142143Ok(())144}145146#[derive(Resource)]147struct UninitializedResource;148149fn failing_system(world: &mut World) -> Result {150world151// `get_resource` returns an `Option<T>`, so we use `ok_or` to convert it to a `Result` on152// which we can call `?` to propagate the error.153.get_resource::<UninitializedResource>()154// We can provide a `str` here because `BevyError` implements `From<&str>`.155.ok_or("Resource not initialized")?;156157Ok(())158}159160fn failing_commands(mut commands: Commands) {161commands162// This entity doesn't exist!163.entity(Entity::from_raw_u32(12345678).unwrap())164// Normally, this failed command would panic,165// but since we've set the global error handler to `warn`166// it will log a warning instead.167.insert(Transform::default());168169// The error handlers for commands can be set individually as well,170// by using the queue_handled method.171commands.queue_handled(172|world: &mut World| -> Result {173world174.get_resource::<UninitializedResource>()175.ok_or("Resource not initialized when accessed in a command")?;176177Ok(())178},179|error, context| {180error!("{error}, {context}");181},182);183}184185186