diff --git a/src/cooldown.rs b/src/cooldown.rs index 11ea71d..cd20724 100644 --- a/src/cooldown.rs +++ b/src/cooldown.rs @@ -29,6 +29,7 @@ impl Default for Cooldown { impl Cooldown { fn get(&self, action: &AvianPickupAction) -> &Timer { + // Safety: all actions are always present in the map as we initialize them in `default`. self.0.get(action).unwrap() } diff --git a/src/input.rs b/src/input.rs index be437ac..4a21baf 100644 --- a/src/input.rs +++ b/src/input.rs @@ -86,7 +86,7 @@ fn set_verbs_according_to_input( continue; }; - // Doing these checks now so that later systems can just call `unwrap` + // Doing these checks now so that we can report issues early. let checks = [ (has_global_transform, "GlobalTransform"), (has_shadow, "ShadowParams"), diff --git a/src/interaction/drop.rs b/src/interaction/drop.rs index bb35fd8..ec405ed 100644 --- a/src/interaction/drop.rs +++ b/src/interaction/drop.rs @@ -25,7 +25,10 @@ fn drop( }); // Safety: the prop is a dynamic rigid body and thus is guaranteed to have a // linvel and angvel. - let (mut velocity, mut angvel) = q_prop.get_mut(prop).unwrap(); + let Ok((mut velocity, mut angvel)) = q_prop.get_mut(prop) else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + continue; + }; // HL2 uses 190 inches per second, which is 4.826 meters per second. // let's round that to 5 m/s. const HL2_NORM_SPEED: Scalar = 5.0; diff --git a/src/interaction/hold/on_add_holding.rs b/src/interaction/hold/on_add_holding.rs index 03f7b12..927e187 100644 --- a/src/interaction/hold/on_add_holding.rs +++ b/src/interaction/hold/on_add_holding.rs @@ -25,15 +25,20 @@ pub fn on_add_holding( )>, ) { let actor = trigger.entity(); - let (config, mut state, mut hold_error, holding) = q_actor.get_mut(actor).unwrap(); + let Ok((config, mut state, mut hold_error, holding)) = q_actor.get_mut(actor) else { + error!("Actor entity was deleted or in an invalid state. Ignoring."); + return; + }; let actor_transform = q_actor_transform.get_best_global_transform(actor); let prop = holding.0; *state = AvianPickupActorState::Holding(prop); commands.entity(prop).insert(HeldProp); - // Safety: All props are rigid bodies, so they are guaranteed to have a - // `Rotation` and `Mass`. - let (rotation, mut mass, pickup_mass, non_pickup_mass, pre_pickup_rotation) = - q_prop.get_mut(prop).unwrap(); + let Ok((rotation, mut mass, pickup_mass, non_pickup_mass, pre_pickup_rotation)) = + q_prop.get_mut(prop) + else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + return; + }; let new_mass = pickup_mass .map(|m| m.0) .unwrap_or(config.hold.temporary_prop_mass); diff --git a/src/interaction/hold/on_remove_holding.rs b/src/interaction/hold/on_remove_holding.rs index 65817d6..e5f283a 100644 --- a/src/interaction/hold/on_remove_holding.rs +++ b/src/interaction/hold/on_remove_holding.rs @@ -10,10 +10,14 @@ fn on_remove_holding( q_actor: Query<&Holding>, mut q_prop: Query<(&mut Mass, Option<&NonPickupMass>, Has)>, ) { + // Safety: We are removing a `Holding` component, so we know that the entity has + // one. let holding = q_actor.get(trigger.entity()).unwrap(); let prop = holding.0; - // Safety: All props are rigid bodies, so they are guaranteed to have a `Mass`. - let (mut mass, non_pickup_mass, has_held_marker) = q_prop.get_mut(prop).unwrap(); + let Ok((mut mass, non_pickup_mass, has_held_marker)) = q_prop.get_mut(prop) else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + return; + }; if !has_held_marker { error!( "A held prop that is no longer being held was not actually marked as held. This is supremely weird. Ignoring." diff --git a/src/interaction/hold/set_velocities.rs b/src/interaction/hold/set_velocities.rs index 2b7623c..18aa954 100644 --- a/src/interaction/hold/set_velocities.rs +++ b/src/interaction/hold/set_velocities.rs @@ -29,9 +29,10 @@ fn set_velocities( let inv_dt = dt.recip(); for (shadow, holding, actor) in q_actor.iter_mut() { let prop = holding.0; - // Safety: All props are rigid bodies, so they are guaranteed to have a - // `Position`, `Rotation`, `LinearVelocity`, and `AngularVelocity`. - let (mut velocity, mut angvel, position, rotation) = q_prop.get_mut(prop).unwrap(); + let Ok((mut velocity, mut angvel, position, rotation)) = q_prop.get_mut(prop) else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + continue; + }; let delta_position = shadow.target_position - position.0; diff --git a/src/interaction/hold/update_error.rs b/src/interaction/hold/update_error.rs index 7c2f430..24df05a 100644 --- a/src/interaction/hold/update_error.rs +++ b/src/interaction/hold/update_error.rs @@ -21,9 +21,10 @@ pub fn update_error( if hold_error.error_time <= 0.0 { continue; } - // Safety: All props are rigid bodies, so they are guaranteed to have a - // `Position`. - let position = q_prop.get(prop).unwrap(); + let Ok(position) = q_prop.get(prop) else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + continue; + }; let mut error = (position.0 - shadow.target_position).length(); if hold_error.error_time > 1.0 { hold_error.error_time = 1.0; diff --git a/src/interaction/hold/update_targets.rs b/src/interaction/hold/update_targets.rs index 9e3c659..0c942f6 100644 --- a/src/interaction/hold/update_targets.rs +++ b/src/interaction/hold/update_targets.rs @@ -46,15 +46,16 @@ fn set_targets( } let actor_transform = q_actor_transform.get_best_global_transform(actor); - // Safety: All props are rigid bodies, so they are guaranteed to have a - // `Rotation`. - let ( + let Ok(( prop_rotation, pre_pickup_rotation, preferred_rotation, preferred_distance, clamp_pitch, - ) = q_prop.get_mut(prop).unwrap(); + )) = q_prop.get_mut(prop)else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + continue; + }; let pitch_range = clamp_pitch .map(|c| &c.0) .unwrap_or(&config.hold.pitch_range); diff --git a/src/interaction/pull/find_in_cone.rs b/src/interaction/pull/find_in_cone.rs index fbe2377..decaf81 100644 --- a/src/interaction/pull/find_in_cone.rs +++ b/src/interaction/pull/find_in_cone.rs @@ -25,8 +25,7 @@ pub(super) fn find_prop_in_cone( let mut canditate = None; for collider in colliders { - // Safety: this collection only contains entities that are contained in - // `q_collider` + // Safety: Pretty sure a `shape_intersection` will never return an entity without a `Position`. let object_translation = q_collider.get(collider).unwrap().0; // Closer than other objects diff --git a/src/interaction/pull/mod.rs b/src/interaction/pull/mod.rs index f7ea42f..8ba40d3 100644 --- a/src/interaction/pull/mod.rs +++ b/src/interaction/pull/mod.rs @@ -51,8 +51,11 @@ fn find_object( continue; }; - // Safety: all colliders have a `ColliderParent` - let rigid_body_entity = q_collider_parent.get(prop.entity).unwrap().get(); + let Ok(rigid_body_entity) = q_collider_parent.get(prop.entity) else { + error!("Collider entity was deleted or in an invalid state. Ignoring."); + continue; + }; + let rigid_body_entity = rigid_body_entity.get(); let Ok((&rigid_body, &mass, mut impulse, prop_position, is_already_being_held)) = q_rigid_body.get_mut(rigid_body_entity) diff --git a/src/interaction/throw.rs b/src/interaction/throw.rs index 54f2283..b57111b 100644 --- a/src/interaction/throw.rs +++ b/src/interaction/throw.rs @@ -37,8 +37,12 @@ fn throw( let actor_transform = q_actor_transform.get_best_global_transform(actor); // Safety: All props are rigid bodies, which are guaranteed to have a // `LinearVelocity`, `AngularVelocity`, and `Mass`. - let (mut velocity, mut angvel, mass, lin_speed_override, ang_speed_override) = - q_prop.get_mut(prop).unwrap(); + let Ok((mut velocity, mut angvel, mass, lin_speed_override, ang_speed_override)) = + q_prop.get_mut(prop) + else { + error!("Prop entity was deleted or in an invalid state. Ignoring."); + continue; + }; // The 2013 code now does a `continue` on // `prop_dist_sq > config.interaction_distance * config.interaction_distance` // but eh, that's fine. Better to respect players' input in such edge cases. diff --git a/src/math.rs b/src/math.rs index 620d58c..0a99792 100644 --- a/src/math.rs +++ b/src/math.rs @@ -68,7 +68,7 @@ impl GetBestGlobalTransform for Query<'_, '_, (&GlobalTransform, Option<&Position>, Option<&Rotation>)> { fn get_best_global_transform(&self, entity: Entity) -> Transform { - let (global_transform, position, rotation) = self.get(entity).unwrap(); + let (global_transform, position, rotation) = self.get(entity).expect("Got an entity without `GlobalTransform`"); if let Some(position) = position { if let Some(rotation) = rotation { return Transform::from_translation(position.0).with_rotation(rotation.0); diff --git a/src/verb.rs b/src/verb.rs index 89c369f..9a587f5 100644 --- a/src/verb.rs +++ b/src/verb.rs @@ -68,8 +68,10 @@ fn set_verb( mut commands: Commands, q_actor: Query<(Has, Has, Has, Has)>, ) { - // Safety: we are only querying optional components. - let (throwing, dropping, pulling, holding) = q_actor.get(actor).unwrap(); + let Ok((throwing, dropping, pulling, holding)) = q_actor.get(actor) else { + error!("Actor entity was deleted or in an invalid state. Ignoring."); + return; + }; let mut commands = commands.entity(actor); match verb { Some(Verb::Throw(prop)) => {