error_handling/
error_handling.rs1use bevy::ecs::{
8 error::{warn, GLOBAL_ERROR_HANDLER},
9 world::DeferredWorld,
10};
11use bevy::math::sampling::UniformMeshSampler;
12use bevy::prelude::*;
13
14use rand::distributions::Distribution;
15use rand::SeedableRng;
16use rand_chacha::ChaCha8Rng;
17
18fn main() {
19 GLOBAL_ERROR_HANDLER
26 .set(warn)
27 .expect("The error handler can only be set once, globally.");
28
29 let mut app = App::new();
30
31 app.add_plugins(DefaultPlugins);
32
33 #[cfg(feature = "bevy_mesh_picking_backend")]
34 app.add_plugins(MeshPickingPlugin);
35
36 app.add_systems(Startup, setup);
40
41 app.add_systems(Startup, failing_commands);
44
45 app.add_systems(
47 PostStartup,
48 failing_system.pipe(|result: In<Result>| {
49 let _ = result.0.inspect_err(|err| info!("captured error: {err}"));
50 }),
51 );
52
53 app.add_observer(fallible_observer);
55
56 app.run();
62}
63
64fn setup(
68 mut commands: Commands,
69 mut meshes: ResMut<Assets<Mesh>>,
70 mut materials: ResMut<Assets<StandardMaterial>>,
71) -> Result {
72 let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
73
74 commands.spawn((
76 Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
77 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
78 Transform::from_xyz(0.0, -2.5, 0.0),
79 ));
80
81 commands.spawn((
83 PointLight {
84 shadows_enabled: true,
85 ..default()
86 },
87 Transform::from_xyz(4.0, 8.0, 4.0),
88 ));
89
90 commands.spawn((
92 Camera3d::default(),
93 Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
94 ));
95
96 let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?;
98 sphere_mesh.generate_tangents()?;
99
100 let mut sphere = commands.spawn((
102 Mesh3d(meshes.add(sphere_mesh.clone())),
103 MeshMaterial3d(materials.add(StandardMaterial::default())),
104 Transform::from_xyz(-1.0, 1.0, 0.0),
105 ));
106
107 let triangles = sphere_mesh.triangles()?;
109 let distribution = UniformMeshSampler::try_new(triangles)?;
110
111 let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?);
113 let point_material = materials.add(StandardMaterial {
114 base_color: Srgba::RED.into(),
115 emissive: LinearRgba::rgb(1.0, 0.0, 0.0),
116 ..default()
117 });
118
119 for point in distribution.sample_iter(&mut seeded_rng).take(10000) {
121 sphere.with_child((
122 Mesh3d(point_mesh.clone()),
123 MeshMaterial3d(point_material.clone()),
124 Transform::from_translation(point),
125 ));
126 }
127
128 Ok(())
130}
131
132fn fallible_observer(
134 trigger: Trigger<Pointer<Move>>,
135 mut world: DeferredWorld,
136 mut step: Local<f32>,
137) -> Result {
138 let mut transform = world
139 .get_mut::<Transform>(trigger.target)
140 .ok_or("No transform found.")?;
141
142 *step = if transform.translation.x > 3. {
143 -0.1
144 } else if transform.translation.x < -3. || *step == 0. {
145 0.1
146 } else {
147 *step
148 };
149
150 transform.translation.x += *step;
151
152 Ok(())
153}
154
155#[derive(Resource)]
156struct UninitializedResource;
157
158fn failing_system(world: &mut World) -> Result {
159 world
160 .get_resource::<UninitializedResource>()
163 .ok_or("Resource not initialized")?;
165
166 Ok(())
167}
168
169fn failing_commands(mut commands: Commands) {
170 commands
171 .entity(Entity::from_raw(12345678))
173 .insert(Transform::default());
177
178 commands.queue_handled(
181 |world: &mut World| -> Result {
182 world
183 .get_resource::<UninitializedResource>()
184 .ok_or("Resource not initialized when accessed in a command")?;
185
186 Ok(())
187 },
188 |error, context| {
189 error!("{error}, {context}");
190 },
191 );
192}