diff --git a/migration-guides/0.5-to-main.md b/migration-guides/0.5-to-main.md index d5e8a24c8..0965c96a7 100644 --- a/migration-guides/0.5-to-main.md +++ b/migration-guides/0.5-to-main.md @@ -6,12 +6,17 @@ since the latest release. These guides are evolving and may not be polished yet. See [migration-guides/README.md](./README.md) and existing entries for information about Avian's migration guide process and what to put here. -## `ReadRigidBody` and `WriteRigidBody` +## `ReadRigidBodyForces` and `WriteRigidBodyForces` -PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBody` and `WriteRigidBody`, and `RigidyBodyForces` is now defined as: +PR [#908](https://github.com/avianphysics/avian/pull/908) introduced two new traits: `ReadRigidBodyForces` and `WriteRigidBodyForces`, and `RigidyBodyForces` is now defined as: ```rust pub trait RigidBodyForces: ReadRigidBodyForces + WriteRigidBodyForces {} ``` 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. + +## `SleepBody` and `WakeBody` + +The `SleepBody` and `WakeBody` commands now return an error when applied for an entity that doesn't exist +or doesn't belong to an island. Previously, they logged a warning instead. diff --git a/src/dynamics/mod.rs b/src/dynamics/mod.rs index 862f1647f..27b47af49 100644 --- a/src/dynamics/mod.rs +++ b/src/dynamics/mod.rs @@ -111,7 +111,6 @@ pub mod prelude { PhysicsLengthUnit, SolverPlugin, SolverPlugins, islands::{ IslandPlugin, IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands, - WakeUpBody, }, schedule::{ SolverSchedulePlugin, SolverSet, SolverSystems, SubstepCount, SubstepSchedule, diff --git a/src/dynamics/solver/islands/mod.rs b/src/dynamics/solver/islands/mod.rs index 99e0cff97..d6fe4d264 100644 --- a/src/dynamics/solver/islands/mod.rs +++ b/src/dynamics/solver/islands/mod.rs @@ -43,11 +43,7 @@ // https://github.com/erincatto/box2d/blob/df9787b59e4480135fbd73d275f007b5d931a83f/src/island.c#L57 mod sleeping; - -#[expect(deprecated)] -pub use sleeping::{ - IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands, WakeUpBody, -}; +pub use sleeping::{IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands}; use bevy::{ ecs::{entity_disabling::Disabled, lifecycle::HookContext, world::DeferredWorld}, diff --git a/src/dynamics/solver/islands/sleeping.rs b/src/dynamics/solver/islands/sleeping.rs index b82443059..9f4aaf097 100644 --- a/src/dynamics/solver/islands/sleeping.rs +++ b/src/dynamics/solver/islands/sleeping.rs @@ -7,6 +7,7 @@ use bevy::{ ecs::{ entity::Entity, entity_disabling::Disabled, + error::Result, lifecycle::{HookContext, Insert, Replace}, observer::On, query::{Changed, Has, Or, With, Without}, @@ -21,7 +22,6 @@ use bevy::{ }, world::{DeferredWorld, Mut, Ref, World}, }, - log::warn, prelude::{Deref, DerefMut}, time::Time, }; @@ -100,7 +100,7 @@ fn sleep_on_add_sleeping(mut world: DeferredWorld, ctx: HookContext) { return; } - world.commands().queue(SleepBody(ctx.entity)); + world.commands().queue_silenced(SleepBody(ctx.entity)); } fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) { @@ -119,7 +119,7 @@ fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) { return; } - world.commands().queue(WakeBody(ctx.entity)); + world.commands().queue_silenced(WakeBody(ctx.entity)); } fn wake_on_replace_rigid_body( @@ -293,46 +293,52 @@ struct CachedBodySleepingSystemState( /// A [`Command`] that forces a [`RigidBody`] and its [`PhysicsIsland`][super::PhysicsIsland] to be [`Sleeping`]. pub struct SleepBody(pub Entity); -impl Command for SleepBody { - fn apply(self, world: &mut World) { - if let Some(island_id) = world - .get::(self.0) - .map(|node| node.island_id) - { - world.try_resource_scope(|world, mut state: Mut| { - let ( - mut body_islands, - body_colliders, - mut islands, - mut contact_graph, - mut joint_graph, - ) = state.0.get_mut(world); - - let Some(island) = islands.get_mut(island_id) else { - return; - }; - - // The island must be split before it can be woken up. - // Note that this is expensive. - if island.constraints_removed > 0 { - islands.split_island( - island_id, - &mut body_islands, - &body_colliders, - &mut contact_graph, - &mut joint_graph, - ); - } - - // The ID of the body's island might have changed due to the split, - // so we need to retrieve it again. - let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap(); +impl Command for SleepBody { + fn apply(self, world: &mut World) -> Result { + if let Ok(entity) = world.get_entity(self.0) { + if let Some(island_id) = entity.get::().map(|node| node.island_id) { + world.try_resource_scope(|world, mut state: Mut| { + let ( + mut body_islands, + body_colliders, + mut islands, + mut contact_graph, + mut joint_graph, + ) = state.0.get_mut(world); + + let Some(island) = islands.get_mut(island_id) else { + return; + }; + + // The island must be split before it can be woken up. + // Note that this is expensive. + if island.constraints_removed > 0 { + islands.split_island( + island_id, + &mut body_islands, + &body_colliders, + &mut contact_graph, + &mut joint_graph, + ); + } - // Sleep the island. - SleepIslands(vec![island_id]).apply(world); - }); + // The ID of the body's island might have changed due to the split, + // so we need to retrieve it again. + let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap(); + + // Sleep the island. + SleepIslands(vec![island_id]).apply(world); + }); + Ok(()) + } else { + Err(format!( + "Tried to sleep entity {:?} that is not a body or does not belong to an island", + self.0 + ) + .into()) + } } else { - warn!("Tried to sleep body {:?} that does not exist", self.0); + Err(format!("Tried to sleep entity {:?} that does not exist", self.0).into()) } } } @@ -442,27 +448,25 @@ struct CachedIslandWakingSystemState( /// A [`Command`] that wakes up a [`RigidBody`] and its [`PhysicsIsland`](super::PhysicsIsland) if it is [`Sleeping`]. pub struct WakeBody(pub Entity); -impl Command for WakeBody { - fn apply(self, world: &mut World) { - if let Some(body_island) = world.get::(self.0) { - WakeIslands(vec![body_island.island_id]).apply(world); +impl Command for WakeBody { + fn apply(self, world: &mut World) -> Result { + if let Ok(entity) = world.get_entity(self.0) { + if let Some(body_island) = entity.get::() { + WakeIslands(vec![body_island.island_id]).apply(world); + Ok(()) + } else { + Err(format!( + "Tried to wake entity {:?} that is not a body or does not belong to an island", + self.0 + ) + .into()) + } } else { - warn!("Tried to wake body {:?} that does not exist", self.0); + Err(format!("Tried to wake entity {:?} that does not exist", self.0).into()) } } } -/// A deprecated alias for [`WakeBody`]. -#[deprecated(since = "0.4.0", note = "Renamed to `WakeBody`.")] -pub struct WakeUpBody(pub Entity); - -#[expect(deprecated)] -impl Command for WakeUpBody { - fn apply(self, world: &mut World) { - WakeBody(self.0).apply(world); - } -} - /// A [`Command`] that wakes up the [`PhysicsIsland`](super::PhysicsIsland)s with the given IDs if they are sleeping. pub struct WakeIslands(pub Vec);