enforce server physics

This commit is contained in:
Marcel Märtens 2021-09-28 23:40:23 +02:00
parent 665e9b9378
commit 8a68daf8d3
12 changed files with 43 additions and 216 deletions

View File

@ -39,7 +39,7 @@ use common::{
mounting::Rider,
outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook},
resources::{GameMode, PlayerEntity, ServerTime, Time, TimeOfDay},
resources::{DeltaTime, GameMode, PlayerEntity, ServerTime, Time, TimeOfDay},
spiral::Spiral2d,
terrain::{
block::Block, map::MapConfig, neighbors, site::DungeonKindMeta, BiomeKind,
@ -852,10 +852,8 @@ impl Client {
| ClientGeneral::BreakBlock(_)
| ClientGeneral::PlaceBlock(_, _)
| ClientGeneral::ExitInGame
| ClientGeneral::PlayerPhysics { .. }
| ClientGeneral::UnlockSkill(_)
| ClientGeneral::RequestSiteInfo(_)
| ClientGeneral::RequestPlayerPhysics { .. }
| ClientGeneral::RequestLossyTerrainCompression { .. }
| ClientGeneral::UpdateMapMarker(_)
| ClientGeneral::SpectatePosition(_) => {
@ -890,12 +888,6 @@ impl Client {
}
}
pub fn request_player_physics(&mut self, server_authoritative: bool) {
self.send_msg(ClientGeneral::RequestPlayerPhysics {
server_authoritative,
})
}
pub fn request_lossy_terrain_compression(&mut self, lossy_terrain_compression: bool) {
self.send_msg(ClientGeneral::RequestLossyTerrainCompression {
lossy_terrain_compression,
@ -1806,22 +1798,6 @@ impl Client {
self.last_server_ping = self.state.get_time();
}
// 6) Update the server about the player's physics attributes.
if self.presence.is_some() {
if let (Some(pos), Some(vel), Some(ori)) = (
self.state.read_storage().get(self.entity()).cloned(),
self.state.read_storage().get(self.entity()).cloned(),
self.state.read_storage().get(self.entity()).cloned(),
) {
self.in_game_stream.send(ClientGeneral::PlayerPhysics {
pos,
vel,
ori,
force_counter: self.force_update_counter,
})?;
}
}
/*
// Output debug metrics
if log_enabled!(Level::Info) && self.tick % 600 == 0 {
@ -2180,6 +2156,7 @@ impl Client {
match msg {
ServerGeneral::TimeSync(time) => {
self.state.ecs().write_resource::<ServerTime>().0 = time.0;
let dt = self.state.ecs().read_resource::<DeltaTime>().0 as f64;
let latency = self
.state
.ecs()
@ -2187,7 +2164,8 @@ impl Client {
.get(self.entity())
.map(|rc| rc.avg_latency())
.unwrap_or_default();
self.state.ecs().write_resource::<Time>().0 = time.0 + latency.as_secs_f64();
//remove dt as it is applied in state.tick again
self.state.ecs().write_resource::<Time>().0 = time.0 + latency.as_secs_f64() - dt;
},
ServerGeneral::AckControl(acked_ids, time) => {
if let Some(remote_controller) = self

View File

@ -63,12 +63,6 @@ pub enum ClientGeneral {
BreakBlock(Vec3<i32>),
PlaceBlock(Vec3<i32>, Block),
ExitInGame,
PlayerPhysics {
pos: comp::Pos,
vel: comp::Vel,
ori: comp::Ori,
force_counter: u64,
},
UnlockSkill(Skill),
RequestSiteInfo(SiteId),
UpdateMapMarker(comp::MapMarkerChange),
@ -85,9 +79,6 @@ pub enum ClientGeneral {
ChatMsg(String),
Command(String, Vec<String>),
Terminate,
RequestPlayerPhysics {
server_authoritative: bool,
},
RequestLossyTerrainCompression {
lossy_terrain_compression: bool,
},
@ -121,12 +112,10 @@ impl ClientMsg {
| ClientGeneral::BreakBlock(_)
| ClientGeneral::PlaceBlock(_, _)
| ClientGeneral::ExitInGame
| ClientGeneral::PlayerPhysics { .. }
| ClientGeneral::TerrainChunkRequest { .. }
| ClientGeneral::LodZoneRequest { .. }
| ClientGeneral::UnlockSkill(_)
| ClientGeneral::RequestSiteInfo(_)
| ClientGeneral::RequestPlayerPhysics { .. }
| ClientGeneral::RequestLossyTerrainCompression { .. }
| ClientGeneral::UpdateMapMarker(_)
| ClientGeneral::SpectatePosition(_) => {

View File

@ -296,7 +296,6 @@ pub enum ServerChatCommand {
RevokeBuildAll,
Safezone,
Say,
ServerPhysics,
SetMotd,
Ship,
Site,
@ -607,14 +606,6 @@ impl ServerChatCommand {
"Send messages to everyone within shouting distance",
None,
),
ServerChatCommand::ServerPhysics => cmd(
vec![
PlayerName(Required),
Boolean("enabled", "true".to_string(), Optional),
],
"Set/unset server-authoritative physics for an account",
Some(Moderator),
),
ServerChatCommand::SetMotd => cmd(
vec![Message(Optional)],
"Set the server description",
@ -783,7 +774,6 @@ impl ServerChatCommand {
ServerChatCommand::RevokeBuildAll => "revoke_build_all",
ServerChatCommand::Safezone => "safezone",
ServerChatCommand::Say => "say",
ServerChatCommand::ServerPhysics => "server_physics",
ServerChatCommand::SetMotd => "set_motd",
ServerChatCommand::Ship => "ship",
ServerChatCommand::Site => "site",

View File

@ -188,6 +188,20 @@ impl RemoteController {
}
last_start = local_start;
}
if result.actions.iter().any(|e| {
if let crate::comp::ControlAction::StartInput {
input,
target_entity: _,
select_pos: _,
} = e
{
input == &crate::comp::InputKind::Jump
} else {
false
}
}) {
tracing::error!("jump detencted");
}
result.inputs.move_dir /= dt.as_secs_f32();
result.inputs.move_z /= dt.as_secs_f32();
result.inputs.look_dir = Dir::new(look_dir.normalized());

View File

@ -62,31 +62,6 @@ pub enum GameMode {
#[derive(Copy, Clone, Default, Debug)]
pub struct PlayerEntity(pub Option<Entity>);
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct PlayerPhysicsSetting {
/// true if the client wants server-authoratative physics (e.g. to use
/// airships properly)
pub client_optin: bool,
/// true if the server is forcing server-authoratative physics (e.g. as
/// punishment for wallhacking)
pub server_force: bool,
}
impl PlayerPhysicsSetting {
pub fn server_authoritative(&self) -> bool { self.client_optin || self.server_force }
pub fn client_authoritative(&self) -> bool { !self.server_authoritative() }
}
/// List of which players are using client-authoratative vs server-authoratative
/// physics, as a stop-gap until we can use server-authoratative physics for
/// everyone
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Default, Debug)]
pub struct PlayerPhysicsSettings {
pub settings: hashbrown::HashMap<uuid::Uuid, PlayerPhysicsSetting>,
}
/// Describe how players interact with other players.
///
/// May be removed when we will discover better way

View File

@ -13,8 +13,7 @@ use common::{
outcome::Outcome,
region::RegionMap,
resources::{
DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, PlayerPhysicsSettings, ServerTime,
Time, TimeOfDay,
DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, ServerTime, Time, TimeOfDay,
},
slowjob::SlowJobPool,
terrain::{Block, MapSizeLg, TerrainChunk, TerrainGrid},
@ -283,7 +282,6 @@ impl State {
ecs.insert(SysMetrics::default());
ecs.insert(PhysicsMetrics::default());
ecs.insert(Trades::default());
ecs.insert(PlayerPhysicsSettings::default());
// Load plugins from asset directory
#[cfg(feature = "plugins")]

View File

@ -42,7 +42,7 @@ use common::{
npc::{self, get_npc_name},
outcome::Outcome,
parse_cmd_args,
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay},
resources::{BattleMode, Secs, Time, TimeOfDay},
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
uid::{Uid, UidAllocator},
vol::ReadVol,
@ -173,7 +173,6 @@ fn do_command(
ServerChatCommand::RevokeBuildAll => handle_revoke_build_all,
ServerChatCommand::Safezone => handle_safezone,
ServerChatCommand::Say => handle_say,
ServerChatCommand::ServerPhysics => handle_server_physics,
ServerChatCommand::SetMotd => handle_set_motd,
ServerChatCommand::Ship => handle_spawn_ship,
ServerChatCommand::Site => handle_site,
@ -3480,38 +3479,6 @@ fn handle_unban(
}
}
fn handle_server_physics(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: Vec<String>,
action: &ServerChatCommand,
) -> CmdResult<()> {
if let (Some(username), enabled_opt) = parse_cmd_args!(args, String, bool) {
let uuid = find_username(server, &username)?;
let server_force = enabled_opt.unwrap_or(true);
let mut player_physics_settings =
server.state.ecs().write_resource::<PlayerPhysicsSettings>();
let entry = player_physics_settings.settings.entry(uuid).or_default();
entry.server_force = server_force;
server.notify_client(
client,
ServerGeneral::server_msg(
ChatType::CommandInfo,
format!(
"Updated physics settings for {} ({}): {:?}",
username, uuid, entry
),
),
);
Ok(())
} else {
Err(action.help_string())
}
}
fn handle_buff(
server: &mut Server,
_client: EcsEntity,

View File

@ -6,11 +6,11 @@ use crate::{
};
use common::{
calendar::Calendar,
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Player, Pos, Vel},
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Pos, Vel},
event::EventBus,
outcome::Outcome,
region::{Event as RegionEvent, RegionMap},
resources::{PlayerPhysicsSettings, Time, TimeOfDay},
resources::{Time, TimeOfDay},
terrain::TerrainChunkSize,
uid::Uid,
vol::RectVolSize,
@ -30,7 +30,6 @@ impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, Tick>,
Read<'a, PlayerPhysicsSettings>,
TrackedStorages<'a>,
ReadExpect<'a, TimeOfDay>,
ReadExpect<'a, Time>,
@ -41,7 +40,6 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Vel>,
ReadStorage<'a, Ori>,
ReadStorage<'a, RegionSubscription>,
ReadStorage<'a, Player>,
ReadStorage<'a, Presence>,
ReadStorage<'a, Client>,
WriteStorage<'a, Last<Pos>>,
@ -62,7 +60,6 @@ impl<'a> System<'a> for Sys {
(
entities,
tick,
player_physics_settings,
tracked_storages,
time_of_day,
time,
@ -73,7 +70,6 @@ impl<'a> System<'a> for Sys {
velocities,
orientations,
subscriptions,
players,
presences,
clients,
mut last_pos,
@ -247,17 +243,10 @@ impl<'a> System<'a> for Sys {
{
// Decide how regularly to send physics updates.
let send_now = if client_entity == &entity {
let player_physics_setting = players
.get(entity)
.and_then(|p| {
player_physics_settings.settings.get(&p.uuid()).copied()
})
.unwrap_or_default();
// Don't send client physics updates about itself unless force update is
// set or the client is subject to
// server-authoritative physics
force_update.map_or(false, |f| f.is_forced())
|| player_physics_setting.server_authoritative()
true || force_update.map_or(false, |f| f.is_forced())
|| is_rider.get(entity).is_some()
} else if matches!(collider, Some(Collider::Voxel { .. })) {
// Things with a voxel collider (airships, etc.) need to have very

View File

@ -2,14 +2,9 @@
use crate::TerrainPersistence;
use crate::{client::Client, presence::Presence, Settings};
use common::{
comp::{
Admin, AdminRole, CanBuild, ForceUpdate, Health, Ori, Player, Pos, RemoteController,
SkillSet, Vel,
},
comp::{Admin, AdminRole, CanBuild, Pos, RemoteController, SkillSet},
event::{EventBus, ServerEvent},
link::Is,
mounting::Rider,
resources::{PlayerPhysicsSetting, PlayerPhysicsSettings, Time},
resources::Time,
slowjob::SlowJobPool,
terrain::TerrainGrid,
vol::ReadVol,
@ -20,9 +15,11 @@ use common_state::{BlockChange, BuildAreas};
use core::mem;
use rayon::prelude::*;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage};
use std::{borrow::Cow, time::Instant, time::Duration};
use tracing::{debug, trace, warn};
use vek::*;
use std::{
borrow::Cow,
time::{Duration, Instant},
};
use tracing::{debug, trace};
#[cfg(feature = "persistent_world")]
pub type TerrainPersistenceData<'a> = Option<Write<'a, TerrainPersistence>>;
@ -52,18 +49,12 @@ impl Sys {
maybe_presence: &mut Option<&mut Presence>,
terrain: &ReadExpect<'_, TerrainGrid>,
can_build: &ReadStorage<'_, CanBuild>,
is_rider: &ReadStorage<'_, Is<Rider>>,
force_updates: &ReadStorage<'_, ForceUpdate>,
skill_set: &mut Option<Cow<'_, SkillSet>>,
healths: &ReadStorage<'_, Health>,
rare_writes: &parking_lot::Mutex<RareWrites<'_, '_>>,
position: Option<&mut Pos>,
velocity: Option<&mut Vel>,
orientation: Option<&mut Ori>,
remote_controller: &mut RemoteController,
settings: &Read<'_, Settings>,
build_areas: &Read<'_, BuildAreas>,
player_physics_setting: Option<&mut PlayerPhysicsSetting>,
maybe_admin: &Option<&Admin>,
time_for_vd_changes: Instant,
msg: ClientGeneral,
@ -123,6 +114,7 @@ impl Sys {
*/
}
},
/*
ClientGeneral::PlayerPhysics { pos, vel, ori, force_counter } => {
if presence.kind.controlling_char()
&& force_updates.get(entity).map_or(true, |force_update| force_update.counter() == force_counter)
@ -203,6 +195,7 @@ impl Sys {
}
}
},
*/
ClientGeneral::BreakBlock(pos) => {
if let Some(comp_can_build) = can_build.get(entity) {
if comp_can_build.enabled {
@ -267,13 +260,6 @@ impl Sys {
ClientGeneral::RequestSiteInfo(id) => {
server_emitter.emit(ServerEvent::RequestSiteInfo { entity, id });
},
ClientGeneral::RequestPlayerPhysics {
server_authoritative,
} => {
if let Some(setting) = player_physics_setting {
setting.client_optin = server_authoritative;
}
},
ClientGeneral::RequestLossyTerrainCompression {
lossy_terrain_compression,
} => {
@ -323,22 +309,15 @@ impl<'a> System<'a> for Sys {
ReadExpect<'a, TerrainGrid>,
ReadExpect<'a, SlowJobPool>,
ReadStorage<'a, CanBuild>,
ReadStorage<'a, ForceUpdate>,
ReadStorage<'a, Is<Rider>>,
WriteStorage<'a, SkillSet>,
ReadStorage<'a, Health>,
Write<'a, BlockChange>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
Write<'a, BlockChange>,
WriteStorage<'a, Presence>,
WriteStorage<'a, Client>,
WriteStorage<'a, RemoteController>,
Read<'a, Settings>,
Read<'a, BuildAreas>,
Write<'a, PlayerPhysicsSettings>,
TerrainPersistenceData<'a>,
ReadStorage<'a, Player>,
ReadStorage<'a, Admin>,
);
@ -355,22 +334,15 @@ impl<'a> System<'a> for Sys {
terrain,
slow_jobs,
can_build,
force_updates,
is_rider,
mut skill_sets,
healths,
mut block_changes,
mut positions,
mut velocities,
mut orientations,
mut block_changes,
mut presences,
mut clients,
mut remote_controllers,
settings,
build_areas,
mut player_physics_settings_,
mut terrain_persistence,
players,
admins,
): Self::SystemData,
) {
@ -383,17 +355,13 @@ impl<'a> System<'a> for Sys {
_terrain_persistence: &mut terrain_persistence,
});
let player_physics_settings = &*player_physics_settings_;
let mut deferred_updates = (
&entities,
&mut clients,
(&mut presences).maybe(),
players.maybe(),
admins.maybe(),
(&skill_sets).maybe(),
(&mut positions).maybe(),
(&mut velocities).maybe(),
(&mut orientations).maybe(),
(&mut remote_controllers).maybe(),
)
.join()
@ -405,22 +373,11 @@ impl<'a> System<'a> for Sys {
entity,
client,
mut maybe_presence,
maybe_player,
maybe_admin,
skill_set,
ref mut pos,
ref mut vel,
ref mut ori,
remote_controller,
)| {
let old_player_physics_setting = maybe_player.map(|p| {
player_physics_settings
.settings
.get(&p.uuid())
.copied()
.unwrap_or_default()
});
let mut new_player_physics_setting = old_player_physics_setting;
// If an `ExitInGame` message is received this is set to `None` allowing further
// ingame messages to be ignored.
let mut clearable_maybe_presence = maybe_presence.as_deref_mut();
@ -442,18 +399,12 @@ impl<'a> System<'a> for Sys {
&mut clearable_maybe_presence,
&terrain,
&can_build,
&is_rider,
&force_updates,
&mut skill_set,
&healths,
&rare_writes,
pos.as_deref_mut(),
vel.as_deref_mut(),
ori.as_deref_mut(),
remote_controller,
&settings,
&build_areas,
new_player_physics_setting.as_mut(),
&maybe_admin,
time_for_vd_changes,
msg,
@ -473,24 +424,16 @@ impl<'a> System<'a> for Sys {
Cow::Borrowed(_) => None,
Cow::Owned(skill_set) => Some((entity, skill_set)),
});
// NOTE: Since we pass Option<&mut _> rather than &mut Option<_> to
// handle_client_in_game_msg, and the new player was initialized to the same
// value as the old setting , we know that either both the new and old setting
// are Some, or they are both None.
let physics_update = maybe_player.map(|p| p.uuid())
.zip(new_player_physics_setting
.filter(|_| old_player_physics_setting != new_player_physics_setting));
(skill_set_update, physics_update, new_remote_controller.map(|c| (entity, c)))
(skill_set_update, new_remote_controller.map(|c| (entity, c)))
},
)
// NOTE: Would be nice to combine this with the map_init somehow, but I'm not sure if
// that's possible.
.filter(|(x, y, z)| x.is_some() || y.is_some() || z.is_some())
.filter(|(x, y)| x.is_some() || y.is_some())
// NOTE: I feel like we shouldn't actually need to allocate here, but hopefully this
// doesn't turn out to be important as there shouldn't be that many connected clients.
// The reason we can't just use unzip is that the two sides might be different lengths.
.collect::<Vec<_>>();
let player_physics_settings = &mut *player_physics_settings_;
// Deferred updates to skillsets and player physics and new_remote_controller.
//
// NOTE: It is an invariant that there is at most one client entry per player
@ -498,8 +441,9 @@ impl<'a> System<'a> for Sys {
// per uuid, so the physics update is sound and doesn't depend on evaluation
// order, even though we're not updating directly by entity or uid (note that
// for a given entity, we process messages serially).
deferred_updates.iter_mut().for_each(
|(skill_set_update, physics_update, new_remote_controller)| {
deferred_updates
.iter_mut()
.for_each(|(skill_set_update, new_remote_controller)| {
if let Some((entity, new_skill_set)) = skill_set_update {
// We know this exists, because we already iterated over it with the skillset
// lock taken, so we can ignore the error.
@ -511,18 +455,10 @@ impl<'a> System<'a> for Sys {
.get_mut(*entity)
.map(|mut old_skill_set| mem::swap(&mut *old_skill_set, new_skill_set));
}
if let &mut Some((uuid, player_physics_setting)) = physics_update {
// We don't necessarily know this exists, but that's fine, because dropping
// player physics is a no op.
player_physics_settings
.settings
.insert(uuid, player_physics_setting);
}
if let Some((uuid, new_remote_controller)) = new_remote_controller.take() {
let _ = remote_controllers.insert(uuid, new_remote_controller);
}
},
);
});
// Finally, drop the deferred updates in another thread.
slow_jobs.spawn("CHUNK_DROP", move || {
drop(deferred_updates);

View File

@ -4,8 +4,7 @@ use common::{
resources::Time,
};
use common_ecs::{Job, Origin, Phase, System};
use common_net::msg::PingMsg;
use common_net::msg::ServerGeneral;
use common_net::msg::{PingMsg, ServerGeneral};
use rayon::prelude::*;
use specs::{Entities, ParJoin, Read, WriteStorage};
use tracing::{debug, info};
@ -51,7 +50,6 @@ impl<'a> System<'a> for Sys {
client.send_fallible(ServerGeneral::TimeSync(*time));
let res = super::try_recv_all(client, 4, Self::handle_ping_msg);
match res {
Err(e) => {
debug!(?entity, ?e, "network error with client, disconnecting");

View File

@ -140,7 +140,6 @@ impl SessionState {
let mut mumble_link = SharedLink::new("veloren", "veloren-voxygen");
{
let mut client = client.borrow_mut();
client.request_player_physics(global_state.settings.networking.player_physics_behavior);
client.request_lossy_terrain_compression(
global_state.settings.networking.lossy_terrain_compression,
);

View File

@ -693,14 +693,8 @@ impl SettingsChange {
adjust_entity_view_distance(entity_vd, settings, session_state)
},
Networking::ChangePlayerPhysicsBehavior {
server_authoritative,
} => {
settings.networking.player_physics_behavior = server_authoritative;
session_state
.client
.borrow_mut()
.request_player_physics(server_authoritative);
},
server_authoritative: _,
} => {},
Networking::ToggleLossyTerrainCompression(lossy_terrain_compression) => {
settings.networking.lossy_terrain_compression = lossy_terrain_compression;
session_state