Merge branch 'presist_server_physics' into 'master'

Persist server authoritative physics

See merge request veloren/veloren!4211
This commit is contained in:
crab
2024-11-12 22:58:41 +00:00
7 changed files with 131 additions and 18 deletions

View File

@ -76,6 +76,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ambient noise now gets filtered when underwater.
- Renamed Site Savannah Pit to Savannah Town
- Replaced building Savannah Pit with Savannah Town Airship Dock
- Server authoritative physics forces now persist across server restarts.
### Removed

View File

@ -829,6 +829,7 @@ impl ServerChatCommand {
vec![
PlayerName(Required),
Boolean("enabled", "true".to_string(), Optional),
Message(Optional),
],
Content::localized("command-server_physics-desc"),
Some(Moderator),

View File

@ -93,13 +93,10 @@ 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 server_authoritative(&self) -> bool { self.client_optin }
pub fn client_authoritative(&self) -> bool { !self.server_authoritative() }
}

View File

@ -8,8 +8,9 @@ use crate::{
location::Locations,
login_provider::LoginProvider,
settings::{
banlist::NormalizedIpAddr, server_description::ServerDescription, BanInfo, BanOperation,
BanOperationError, EditableSetting, SettingError, WhitelistInfo, WhitelistRecord,
banlist::NormalizedIpAddr, server_description::ServerDescription,
server_physics::ServerPhysicsForceRecord, BanInfo, BanOperation, BanOperationError,
EditableSetting, SettingError, WhitelistInfo, WhitelistRecord,
},
sys::terrain::SpawnEntityData,
wiring::{self, OutputFormula},
@ -52,7 +53,7 @@ use common::{
npc::{self, get_npc_name},
outcome::Outcome,
parse_cmd_args,
resources::{BattleMode, PlayerPhysicsSettings, ProgramTime, Secs, Time, TimeOfDay, TimeScale},
resources::{BattleMode, ProgramTime, Secs, Time, TimeOfDay, TimeScale},
rtsim::{Actor, Role},
spiral::Spiral2d,
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind},
@ -5132,14 +5133,39 @@ fn handle_server_physics(
args: Vec<String>,
action: &ServerChatCommand,
) -> CmdResult<()> {
if let (Some(username), enabled_opt) = parse_cmd_args!(args, String, bool) {
if let (Some(username), enabled_opt, reason) = parse_cmd_args!(args, String, bool, String) {
let uuid = find_username(server, &username)?;
let server_force = enabled_opt.unwrap_or(true);
let data_dir = server.data_dir();
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;
let result = server
.editable_settings_mut()
.server_physics_force_list
.edit(data_dir.as_ref(), |list| {
if server_force {
let Some(by) = server
.state()
.ecs()
.read_storage::<comp::Player>()
.get(client)
.map(|player| (player.uuid(), player.alias.clone()))
else {
return Some(Some(Content::localized("command-you-dont-exist")));
};
list.insert(uuid, ServerPhysicsForceRecord {
by: Some(by),
reason,
});
Some(None)
} else {
list.remove(&uuid);
Some(None)
}
});
if let Some((Some(error), _)) = result {
return Err(error);
}
server.notify_client(
client,
@ -5147,7 +5173,7 @@ fn handle_server_physics(
ChatType::CommandInfo,
Content::Plain(format!(
"Updated physics settings for {} ({}): {:?}",
username, uuid, entry
username, uuid, server_force
)),
),
);

View File

@ -2,6 +2,7 @@ pub mod admin;
pub mod banlist;
mod editable;
pub mod server_description;
pub mod server_physics;
pub mod whitelist;
pub use editable::{EditableSetting, Error as SettingError};
@ -35,12 +36,15 @@ use world::sim::{FileOpts, DEFAULT_WORLD_SEED};
use self::server_description::ServerDescription;
use self::server_physics::ServerPhysicsForceList;
const CONFIG_DIR: &str = "server_config";
const SETTINGS_FILENAME: &str = "settings.ron";
const WHITELIST_FILENAME: &str = "whitelist.ron";
const BANLIST_FILENAME: &str = "banlist.ron";
const SERVER_DESCRIPTION_FILENAME: &str = "description.ron";
const ADMINS_FILENAME: &str = "admins.ron";
const SERVER_PHYSICS_FORCE_FILENAME: &str = "server_physics_force.ron";
pub const SINGLEPLAYER_SERVER_NAME: &str = "Singleplayer";
@ -384,6 +388,7 @@ pub struct EditableSettings {
pub banlist: Banlist,
pub server_description: ServerDescriptions,
pub admins: Admins,
pub server_physics_force_list: ServerPhysicsForceList,
}
impl EditableSettings {
@ -393,6 +398,7 @@ impl EditableSettings {
banlist: Banlist::load(data_dir),
server_description: ServerDescriptions::load(data_dir),
admins: Admins::load(data_dir),
server_physics_force_list: ServerPhysicsForceList::load(data_dir),
}
}

View File

@ -0,0 +1,76 @@
//! List of players which are not allowed to use client side physics, to punish
//! abuse
use super::{editable::Version, EditableSetting, SERVER_PHYSICS_FORCE_FILENAME as FILENAME};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;
pub use v0::*;
#[derive(Deserialize, Serialize)]
pub enum ServerPhysicsForceListRaw {
V0(ServerPhysicsForceList),
}
impl TryFrom<ServerPhysicsForceListRaw> for (Version, ServerPhysicsForceList) {
type Error = <ServerPhysicsForceList as EditableSetting>::Error;
fn try_from(value: ServerPhysicsForceListRaw) -> Result<Self, Self::Error> {
use ServerPhysicsForceListRaw::*;
Ok(match value {
V0(mut value) => (value.validate()?, value),
})
}
}
impl From<ServerPhysicsForceList> for ServerPhysicsForceListRaw {
fn from(value: ServerPhysicsForceList) -> Self { Self::V0(value) }
}
impl EditableSetting for ServerPhysicsForceList {
type Error = Infallible;
type Legacy = ServerPhysicsForceList;
type Setting = ServerPhysicsForceListRaw;
const FILENAME: &'static str = FILENAME;
}
type Latest = ServerPhysicsForceList;
mod v0 {
use super::Latest;
use authc::Uuid;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use crate::settings::{editable::Version, EditableSetting};
#[derive(Clone, Deserialize, Serialize, Debug)]
pub struct ServerPhysicsForceRecord {
/// Moderator/Admin who forced the player to server authoritative
/// physics, none if applied via the server (currently not possible)
pub by: Option<(Uuid, String)>,
pub reason: Option<String>,
}
#[derive(Clone, Deserialize, Serialize, Default)]
pub struct ServerPhysicsForceList(HashMap<Uuid, ServerPhysicsForceRecord>);
impl Deref for ServerPhysicsForceList {
type Target = HashMap<Uuid, ServerPhysicsForceRecord>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for ServerPhysicsForceList {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl ServerPhysicsForceList {
pub(super) fn validate(&mut self) -> Result<Version, <Latest as EditableSetting>::Error> {
Ok(Version::Latest)
}
}
}

View File

@ -1,6 +1,6 @@
#[cfg(feature = "persistent_world")]
use crate::TerrainPersistence;
use crate::{client::Client, Settings};
use crate::{client::Client, EditableSettings, Settings};
use common::{
comp::{
Admin, AdminRole, Body, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori,
@ -72,6 +72,7 @@ impl Sys {
settings: &Read<'_, Settings>,
build_areas: &Read<'_, AreasContainer<BuildArea>>,
player_physics_setting: Option<&mut PlayerPhysicsSetting>,
server_physics_forced: bool,
maybe_admin: &Option<&Admin>,
time_for_vd_changes: Instant,
msg: ClientGeneral,
@ -146,6 +147,7 @@ impl Sys {
&& healths.get(entity).map_or(true, |h| !h.is_dead)
&& is_rider.get(entity).is_none()
&& is_volume_rider.get(entity).is_none()
&& !server_physics_forced
&& player_physics_setting
.as_ref()
.map_or(true, |s| s.client_authoritative())
@ -280,8 +282,11 @@ impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Events<'a>,
ReadExpect<'a, TerrainGrid>,
ReadExpect<'a, SlowJobPool>,
(
ReadExpect<'a, TerrainGrid>,
ReadExpect<'a, SlowJobPool>,
ReadExpect<'a, EditableSettings>,
),
ReadStorage<'a, CanBuild>,
WriteStorage<'a, ForceUpdate>,
ReadStorage<'a, Is<Rider>>,
@ -315,8 +320,7 @@ impl<'a> System<'a> for Sys {
(
entities,
events,
terrain,
slow_jobs,
(terrain, slow_jobs, editable_settings),
can_build,
mut force_updates,
is_rider,
@ -390,6 +394,7 @@ impl<'a> System<'a> for Sys {
.unwrap_or_default()
});
let mut new_player_physics_setting = old_player_physics_setting;
let is_server_physics_forced = maybe_player.map_or(true, |p| editable_settings.server_physics_force_list.contains_key(&p.uuid()));
// 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();
@ -414,6 +419,7 @@ impl<'a> System<'a> for Sys {
&settings,
&build_areas,
new_player_physics_setting.as_mut(),
is_server_physics_forced,
&maybe_admin,
time_for_vd_changes,
msg,