From 8480cb4a7a9c6728a8d93e859420693e19639c48 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 22 Jun 2023 10:43:02 +0100 Subject: [PATCH] Apply client physics once per server tick --- server/src/sys/msg/in_game.rs | 153 ++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 73 deletions(-) diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 0c5f6a7a7e..28fab7830a 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -58,8 +58,6 @@ impl Sys { healths: &ReadStorage<'_, Health>, rare_writes: &parking_lot::Mutex>, position: Option<&mut Pos>, - velocity: Option<&mut Vel>, - orientation: Option<&mut Ori>, controller: Option<&mut Controller>, settings: &Read<'_, Settings>, build_areas: &Read<'_, AreasContainer>, @@ -67,6 +65,7 @@ impl Sys { maybe_admin: &Option<&Admin>, time_for_vd_changes: Instant, msg: ClientGeneral, + player_physics: &mut Option<(Pos, Vel, Ori)>, ) -> Result<(), crate::error::Error> { let presence = match maybe_presence.as_deref_mut() { Some(g) => g, @@ -129,75 +128,7 @@ impl Sys { .as_ref() .map_or(true, |s| s.client_authoritative()) { - enum Rejection { - TooFar { old: Vec3, new: Vec3 }, - TooFast { vel: Vec3 }, - } - - let rejection = if maybe_admin.is_some() { - None - } else if let Some(mut setting) = player_physics_setting { - // If we detect any thresholds being exceeded, force server-authoritative - // physics for that player. This doesn't detect subtle hacks, but it - // prevents blatant ones and forces people to not debug physics hacks on the - // live server (and also mitigates some floating-point overflow crashes) - let rejection = None - // Check position - .or_else(|| { - if let Some(prev_pos) = &position { - if prev_pos.0.distance_squared(pos.0) > (500.0f32).powf(2.0) { - Some(Rejection::TooFar { old: prev_pos.0, new: pos.0 }) - } else { - None - } - } else { - None - } - }) - // Check velocity - .or_else(|| { - if vel.0.magnitude_squared() > (500.0f32).powf(2.0) { - Some(Rejection::TooFast { vel: vel.0 }) - } else { - None - } - }); - - // Force a client-side physics update if rejectable physics data is - // received. - if rejection.is_some() { - // We skip this for `TooFar` because false positives can occur when - // using server-side teleportation commands - // that the client doesn't know about (leading to the client sending - // physics state that disagree with the server). In the future, - // client-authoritative physics will be gone - // and this will no longer be necessary. - setting.server_force = - !matches!(rejection, Some(Rejection::TooFar { .. })); // true; - } - - rejection - } else { - None - }; - - match rejection { - Some(Rejection::TooFar { old, new }) => warn!( - "Rejected player physics update (new position {:?} is too far from \ - old position {:?})", - new, old - ), - Some(Rejection::TooFast { vel }) => warn!( - "Rejected player physics update (new velocity {:?} is too fast)", - vel - ), - None => { - // Don't insert unless the component already exists - position.map(|p| *p = pos); - velocity.map(|v| *v = vel); - orientation.map(|o| *o = ori); - }, - } + *player_physics = Some((pos, vel, ori)); } }, ClientGeneral::BreakBlock(pos) => { @@ -422,6 +353,7 @@ impl<'a> System<'a> for Sys { // ingame messages to be ignored. let mut clearable_maybe_presence = maybe_presence.as_deref_mut(); let mut skill_set = skill_set.map(Cow::Borrowed); + let mut player_physics = None; let _ = super::try_recv_all(client, 2, |client, msg| { Self::handle_client_in_game_msg( server_emitter, @@ -437,8 +369,6 @@ impl<'a> System<'a> for Sys { &healths, &rare_writes, pos.as_deref_mut(), - vel.as_deref_mut(), - ori.as_deref_mut(), controller.as_deref_mut(), &settings, &build_areas, @@ -446,9 +376,86 @@ impl<'a> System<'a> for Sys { &maybe_admin, time_for_vd_changes, msg, + &mut player_physics, ) }); + if let Some((new_pos, new_vel, new_ori)) = player_physics { + let old_pos = pos.as_deref_mut(); + let old_vel = vel.as_deref_mut(); + let old_ori = ori.as_deref_mut(); + + enum Rejection { + TooFar { old: Vec3, new: Vec3 }, + TooFast { vel: Vec3 }, + } + + let rejection = if maybe_admin.is_some() { + None + } else if let Some(mut setting) = new_player_physics_setting.as_mut() { + // If we detect any thresholds being exceeded, force server-authoritative + // physics for that player. This doesn't detect subtle hacks, but it + // prevents blatant ones and forces people to not debug physics hacks on the + // live server (and also mitigates some floating-point overflow crashes) + let rejection = None + // Check position + .or_else(|| { + if let Some(Pos(prev_pos)) = &old_pos { + if prev_pos.distance_squared(new_pos.0) > (500.0f32).powf(2.0) { + Some(Rejection::TooFar { old: *prev_pos, new: new_pos.0 }) + } else { + None + } + } else { + None + } + }) + // Check velocity + .or_else(|| { + if new_vel.0.magnitude_squared() > (500.0f32).powf(2.0) { + Some(Rejection::TooFast { vel: new_vel.0 }) + } else { + None + } + }); + + // Force a client-side physics update if rejectable physics data is + // received. + if rejection.is_some() { + // We skip this for `TooFar` because false positives can occur when + // using server-side teleportation commands + // that the client doesn't know about (leading to the client sending + // physics state that disagree with the server). In the future, + // client-authoritative physics will be gone + // and this will no longer be necessary. + setting.server_force = + !matches!(rejection, Some(Rejection::TooFar { .. })); // true; + } + + rejection + } else { + None + }; + + match rejection { + Some(Rejection::TooFar { old, new }) => warn!( + "Rejected player physics update (new position {:?} is too far from \ + old position {:?})", + new, old + ), + Some(Rejection::TooFast { vel }) => warn!( + "Rejected player physics update (new velocity {:?} is too fast)", + vel + ), + None => { + // Don't insert unless the component already exists + old_pos.map(|p| *p = new_pos); + old_vel.map(|v| *v = new_vel); + old_ori.map(|o| *o = new_ori); + }, + } + } + // Ensure deferred view distance changes are applied (if the // requsite time has elapsed). if let Some(presence) = maybe_presence {