mirror of
https://gitlab.com/veloren/veloren.git
synced 2025-07-25 21:02:31 +00:00
Merge branch 'presist_server_physics' into 'master'
Persist server authoritative physics See merge request veloren/veloren!4211
This commit is contained in:
@ -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
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
76
server/src/settings/server_physics.rs
Normal file
76
server/src/settings/server_physics.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
Reference in New Issue
Block a user