Skip to content

Commit cbc9a21

Browse files
authored
Fix error for waking non-existent bodies (#924)
# Objective Fixes #922. If you try to despawn a sleeping body, you might occasionally get a warning like "Tried to wake body 3127v2 that does not exist". This is because we have a hook that applies the `WakeBody` command when `Sleeping` is removed, and that command emits a warning for non-existent bodies. ## Solution Use proper error handling for the command, and use `queue_silenced` in the hook. ## Testing Despawned some sleeping bodies.
1 parent 2c488eb commit cbc9a21

File tree

4 files changed

+69
-65
lines changed

4 files changed

+69
-65
lines changed

migration-guides/0.5-to-main.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ since the latest release. These guides are evolving and may not be polished yet.
66
See [migration-guides/README.md](./README.md) and existing entries for information about Avian's
77
migration guide process and what to put here.
88

9-
## `ReadRigidBody` and `WriteRigidBody`
9+
## `ReadRigidBodyForces` and `WriteRigidBodyForces`
1010

11-
PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBody` and `WriteRigidBody`, and `RigidyBodyForces` is now defined as:
11+
PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBodyForces` and `WriteRigidBodyForces`, and `RigidyBodyForces` is now defined as:
1212

1313
```rust
1414
pub trait RigidBodyForces: ReadRigidBodyForces + WriteRigidBodyForces {}
1515
```
1616

1717
In most cases this should just work, but if it doesn't, you can replace your implementation for `RigidBodyForces` with both `ReadRigidBodyForces` and `WriteRigidBodyForces` where it is used / needed. Both traits are required to implement `RigidBodyForces`, but you can implement them separately.
18+
19+
## `SleepBody` and `WakeBody`
20+
21+
The `SleepBody` and `WakeBody` commands now return an error when applied for an entity that doesn't exist
22+
or doesn't belong to an island. Previously, they logged a warning instead.

src/dynamics/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ pub mod prelude {
111111
PhysicsLengthUnit, SolverPlugin, SolverPlugins,
112112
islands::{
113113
IslandPlugin, IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands,
114-
WakeUpBody,
115114
},
116115
schedule::{
117116
SolverSchedulePlugin, SolverSet, SolverSystems, SubstepCount, SubstepSchedule,

src/dynamics/solver/islands/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@
4343
// https://github.com/erincatto/box2d/blob/df9787b59e4480135fbd73d275f007b5d931a83f/src/island.c#L57
4444

4545
mod sleeping;
46-
47-
#[expect(deprecated)]
48-
pub use sleeping::{
49-
IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands, WakeUpBody,
50-
};
46+
pub use sleeping::{IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands};
5147

5248
use bevy::{
5349
ecs::{entity_disabling::Disabled, lifecycle::HookContext, world::DeferredWorld},

src/dynamics/solver/islands/sleeping.rs

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use bevy::{
77
ecs::{
88
entity::Entity,
99
entity_disabling::Disabled,
10+
error::Result,
1011
lifecycle::{HookContext, Insert, Replace},
1112
observer::On,
1213
query::{Changed, Has, Or, With, Without},
@@ -21,7 +22,6 @@ use bevy::{
2122
},
2223
world::{DeferredWorld, Mut, Ref, World},
2324
},
24-
log::warn,
2525
prelude::{Deref, DerefMut},
2626
time::Time,
2727
};
@@ -100,7 +100,7 @@ fn sleep_on_add_sleeping(mut world: DeferredWorld, ctx: HookContext) {
100100
return;
101101
}
102102

103-
world.commands().queue(SleepBody(ctx.entity));
103+
world.commands().queue_silenced(SleepBody(ctx.entity));
104104
}
105105

106106
fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) {
@@ -119,7 +119,7 @@ fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) {
119119
return;
120120
}
121121

122-
world.commands().queue(WakeBody(ctx.entity));
122+
world.commands().queue_silenced(WakeBody(ctx.entity));
123123
}
124124

125125
fn wake_on_replace_rigid_body(
@@ -293,46 +293,52 @@ struct CachedBodySleepingSystemState(
293293
/// A [`Command`] that forces a [`RigidBody`] and its [`PhysicsIsland`][super::PhysicsIsland] to be [`Sleeping`].
294294
pub struct SleepBody(pub Entity);
295295

296-
impl Command for SleepBody {
297-
fn apply(self, world: &mut World) {
298-
if let Some(island_id) = world
299-
.get::<BodyIslandNode>(self.0)
300-
.map(|node| node.island_id)
301-
{
302-
world.try_resource_scope(|world, mut state: Mut<CachedBodySleepingSystemState>| {
303-
let (
304-
mut body_islands,
305-
body_colliders,
306-
mut islands,
307-
mut contact_graph,
308-
mut joint_graph,
309-
) = state.0.get_mut(world);
310-
311-
let Some(island) = islands.get_mut(island_id) else {
312-
return;
313-
};
314-
315-
// The island must be split before it can be woken up.
316-
// Note that this is expensive.
317-
if island.constraints_removed > 0 {
318-
islands.split_island(
319-
island_id,
320-
&mut body_islands,
321-
&body_colliders,
322-
&mut contact_graph,
323-
&mut joint_graph,
324-
);
325-
}
326-
327-
// The ID of the body's island might have changed due to the split,
328-
// so we need to retrieve it again.
329-
let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
296+
impl Command<Result> for SleepBody {
297+
fn apply(self, world: &mut World) -> Result {
298+
if let Ok(entity) = world.get_entity(self.0) {
299+
if let Some(island_id) = entity.get::<BodyIslandNode>().map(|node| node.island_id) {
300+
world.try_resource_scope(|world, mut state: Mut<CachedBodySleepingSystemState>| {
301+
let (
302+
mut body_islands,
303+
body_colliders,
304+
mut islands,
305+
mut contact_graph,
306+
mut joint_graph,
307+
) = state.0.get_mut(world);
308+
309+
let Some(island) = islands.get_mut(island_id) else {
310+
return;
311+
};
312+
313+
// The island must be split before it can be woken up.
314+
// Note that this is expensive.
315+
if island.constraints_removed > 0 {
316+
islands.split_island(
317+
island_id,
318+
&mut body_islands,
319+
&body_colliders,
320+
&mut contact_graph,
321+
&mut joint_graph,
322+
);
323+
}
330324

331-
// Sleep the island.
332-
SleepIslands(vec![island_id]).apply(world);
333-
});
325+
// The ID of the body's island might have changed due to the split,
326+
// so we need to retrieve it again.
327+
let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
328+
329+
// Sleep the island.
330+
SleepIslands(vec![island_id]).apply(world);
331+
});
332+
Ok(())
333+
} else {
334+
Err(format!(
335+
"Tried to sleep entity {:?} that is not a body or does not belong to an island",
336+
self.0
337+
)
338+
.into())
339+
}
334340
} else {
335-
warn!("Tried to sleep body {:?} that does not exist", self.0);
341+
Err(format!("Tried to sleep entity {:?} that does not exist", self.0).into())
336342
}
337343
}
338344
}
@@ -442,27 +448,25 @@ struct CachedIslandWakingSystemState(
442448
/// A [`Command`] that wakes up a [`RigidBody`] and its [`PhysicsIsland`](super::PhysicsIsland) if it is [`Sleeping`].
443449
pub struct WakeBody(pub Entity);
444450

445-
impl Command for WakeBody {
446-
fn apply(self, world: &mut World) {
447-
if let Some(body_island) = world.get::<BodyIslandNode>(self.0) {
448-
WakeIslands(vec![body_island.island_id]).apply(world);
451+
impl Command<Result> for WakeBody {
452+
fn apply(self, world: &mut World) -> Result {
453+
if let Ok(entity) = world.get_entity(self.0) {
454+
if let Some(body_island) = entity.get::<BodyIslandNode>() {
455+
WakeIslands(vec![body_island.island_id]).apply(world);
456+
Ok(())
457+
} else {
458+
Err(format!(
459+
"Tried to wake entity {:?} that is not a body or does not belong to an island",
460+
self.0
461+
)
462+
.into())
463+
}
449464
} else {
450-
warn!("Tried to wake body {:?} that does not exist", self.0);
465+
Err(format!("Tried to wake entity {:?} that does not exist", self.0).into())
451466
}
452467
}
453468
}
454469

455-
/// A deprecated alias for [`WakeBody`].
456-
#[deprecated(since = "0.4.0", note = "Renamed to `WakeBody`.")]
457-
pub struct WakeUpBody(pub Entity);
458-
459-
#[expect(deprecated)]
460-
impl Command for WakeUpBody {
461-
fn apply(self, world: &mut World) {
462-
WakeBody(self.0).apply(world);
463-
}
464-
}
465-
466470
/// A [`Command`] that wakes up the [`PhysicsIsland`](super::PhysicsIsland)s with the given IDs if they are sleeping.
467471
pub struct WakeIslands(pub Vec<IslandId>);
468472

0 commit comments

Comments
 (0)