Merge branch 'ProfessionalHobbyist/configurable_day_night_length' into 'master'

Add setting to control length of day/night cycle for servers. ("Fix" for https://gitlab.com/veloren/veloren/-/issues/1798)

See merge request veloren/veloren!3836
This commit is contained in:
Isse 2023-03-23 16:33:39 +00:00
commit 1298fc792b
12 changed files with 90 additions and 7 deletions

View File

@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
with a keybind to enable/disable the setting, and an Auto/Toggle behavior setting. Auto behavior with a keybind to enable/disable the setting, and an Auto/Toggle behavior setting. Auto behavior
will only lock the camera zoom while movement and combat inputs are also being pressed. will only lock the camera zoom while movement and combat inputs are also being pressed.
- Custom spots can be added without recompilation (only ron and vox files) - Custom spots can be added without recompilation (only ron and vox files)
- Setting in userdata/server/server_config/settings.ron that controls the length of each day/night cycle.
### Changed ### Changed
- Bats move slower and use a simple proportional controller to maintain altitude - Bats move slower and use a simple proportional controller to maintain altitude

View File

@ -40,6 +40,7 @@ use common::{
outcome::Outcome, outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook}, recipe::{ComponentRecipeBook, RecipeBook},
resources::{GameMode, PlayerEntity, Time, TimeOfDay}, resources::{GameMode, PlayerEntity, Time, TimeOfDay},
shared_server_config::ServerConstants,
spiral::Spiral2d, spiral::Spiral2d,
terrain::{ terrain::{
block::Block, map::MapConfig, neighbors, site::DungeonKindMeta, BiomeKind, block::Block, map::MapConfig, neighbors, site::DungeonKindMeta, BiomeKind,
@ -264,6 +265,8 @@ pub struct Client {
pending_chunks: HashMap<Vec2<i32>, Instant>, pending_chunks: HashMap<Vec2<i32>, Instant>,
target_time_of_day: Option<TimeOfDay>, target_time_of_day: Option<TimeOfDay>,
connected_server_constants: ServerConstants,
} }
/// Holds data related to the current players characters, as well as some /// Holds data related to the current players characters, as well as some
@ -355,6 +358,7 @@ impl Client {
component_recipe_book, component_recipe_book,
material_stats, material_stats,
ability_map, ability_map,
server_constants,
} = loop { } = loop {
tokio::select! { tokio::select! {
// Spawn in a blocking thread (leaving the network thread free). This is mostly // Spawn in a blocking thread (leaving the network thread free). This is mostly
@ -744,6 +748,8 @@ impl Client {
pending_chunks: HashMap::new(), pending_chunks: HashMap::new(),
target_time_of_day: None, target_time_of_day: None,
connected_server_constants: server_constants,
}) })
} }
@ -1802,6 +1808,7 @@ impl Client {
}, },
true, true,
None, None,
&self.connected_server_constants,
); );
// TODO: avoid emitting these in the first place // TODO: avoid emitting these in the first place
let _ = self let _ = self

View File

@ -12,6 +12,7 @@ use common::{
outcome::Outcome, outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook}, recipe::{ComponentRecipeBook, RecipeBook},
resources::{Time, TimeOfDay}, resources::{Time, TimeOfDay},
shared_server_config::ServerConstants,
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
trade::{PendingTrade, SitePrices, TradeId, TradeResult}, trade::{PendingTrade, SitePrices, TradeId, TradeResult},
uid::Uid, uid::Uid,
@ -66,6 +67,7 @@ pub enum ServerInit {
component_recipe_book: ComponentRecipeBook, component_recipe_book: ComponentRecipeBook,
material_stats: MaterialStatManifest, material_stats: MaterialStatManifest,
ability_map: comp::item::tool::AbilityMap, ability_map: comp::item::tool::AbilityMap,
server_constants: ServerConstants,
}, },
} }

View File

@ -31,6 +31,7 @@ pub mod combat;
pub mod comp; pub mod comp;
pub mod consts; pub mod consts;
pub mod resources; pub mod resources;
pub mod shared_server_config;
pub mod uid; pub mod uid;
// NOTE: Comment out macro to get rustfmt to re-order these as needed. // NOTE: Comment out macro to get rustfmt to re-order these as needed.

View File

@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};
/// Per-server constant data (configs) that stays the same for the server's
/// life.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ServerConstants {
pub day_cycle_coefficient: f64,
}
impl Default for ServerConstants {
fn default() -> Self {
ServerConstants {
// == 30.0 via server settings (the default)
day_cycle_coefficient: 24.0 * 2.0,
}
}
}

View File

@ -16,6 +16,7 @@ use common::{
DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, PlayerPhysicsSettings, Time, DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, PlayerPhysicsSettings, Time,
TimeOfDay, TimeOfDay,
}, },
shared_server_config::ServerConstants,
slowjob::SlowJobPool, slowjob::SlowJobPool,
terrain::{Block, MapSizeLg, TerrainChunk, TerrainGrid}, terrain::{Block, MapSizeLg, TerrainChunk, TerrainGrid},
time::DayPeriod, time::DayPeriod,
@ -39,9 +40,6 @@ use std::{sync::Arc, time::Instant};
use timer_queue::TimerQueue; use timer_queue::TimerQueue;
use vek::*; use vek::*;
/// How much faster should an in-game day be compared to a real day?
// TODO: Don't hard-code this.
const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
/// At what point should we stop speeding up physics to compensate for lag? If /// At what point should we stop speeding up physics to compensate for lag? If
/// we speed physics up too fast, we'd skip important physics events like /// we speed physics up too fast, we'd skip important physics events like
/// collisions. This constant determines the upper limit. If delta time exceeds /// collisions. This constant determines the upper limit. If delta time exceeds
@ -598,6 +596,7 @@ impl State {
add_systems: impl Fn(&mut DispatcherBuilder), add_systems: impl Fn(&mut DispatcherBuilder),
update_terrain_and_regions: bool, update_terrain_and_regions: bool,
mut metrics: Option<&mut StateTickMetrics>, mut metrics: Option<&mut StateTickMetrics>,
server_constants: &ServerConstants,
) { ) {
span!(_guard, "tick", "State::tick"); span!(_guard, "tick", "State::tick");
@ -611,7 +610,8 @@ impl State {
} }
// Change the time accordingly. // Change the time accordingly.
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR; self.ecs.write_resource::<TimeOfDay>().0 +=
dt.as_secs_f64() * server_constants.day_cycle_coefficient;
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64(); self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
// Update delta time. // Update delta time.

View File

@ -6,6 +6,7 @@ mod tests {
Controller, Energy, Ori, PhysicsState, Poise, Pos, Skill, Stats, Vel, Controller, Energy, Ori, PhysicsState, Poise, Pos, Skill, Stats, Vel,
}, },
resources::{DeltaTime, GameMode, Time}, resources::{DeltaTime, GameMode, Time},
shared_server_config::ServerConstants,
terrain::{MapSizeLg, TerrainChunk}, terrain::{MapSizeLg, TerrainChunk},
uid::Uid, uid::Uid,
util::Dir, util::Dir,
@ -81,6 +82,8 @@ mod tests {
}, },
false, false,
None, None,
// Dummy ServerConstants
&ServerConstants::default(),
); );
} }

View File

@ -1,6 +1,6 @@
use crate::utils; use crate::utils;
use approx::assert_relative_eq; use approx::assert_relative_eq;
use common::{comp::Controller, resources::Time}; use common::{comp::Controller, resources::Time, shared_server_config::ServerConstants};
use specs::WorldExt; use specs::WorldExt;
use std::error::Error; use std::error::Error;
use utils::{DT, DT_F64, EPSILON}; use utils::{DT, DT_F64, EPSILON};
@ -18,6 +18,7 @@ fn simple_run() {
}, },
false, false,
None, None,
&ServerConstants::default(),
); );
} }

View File

@ -7,6 +7,7 @@ use common::{
Vel, Vel,
}, },
resources::{DeltaTime, GameMode, Time}, resources::{DeltaTime, GameMode, Time},
shared_server_config::ServerConstants,
skillset_builder::SkillSetBuilder, skillset_builder::SkillSetBuilder,
terrain::{ terrain::{
Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainGrid, Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainGrid,
@ -64,6 +65,7 @@ pub fn tick(state: &mut State, dt: Duration) {
}, },
false, false,
None, None,
&ServerConstants::default(),
); );
} }

View File

@ -79,6 +79,7 @@ use common::{
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
resources::{BattleMode, GameMode, Time, TimeOfDay}, resources::{BattleMode, GameMode, Time, TimeOfDay},
rtsim::RtSimEntity, rtsim::RtSimEntity,
shared_server_config::ServerConstants,
slowjob::SlowJobPool, slowjob::SlowJobPool,
terrain::{TerrainChunk, TerrainChunkSize}, terrain::{TerrainChunk, TerrainChunkSize},
vol::RectRasterableVol, vol::RectRasterableVol,
@ -205,6 +206,8 @@ pub struct Server {
metrics_shutdown: Arc<Notify>, metrics_shutdown: Arc<Notify>,
database_settings: Arc<RwLock<DatabaseSettings>>, database_settings: Arc<RwLock<DatabaseSettings>>,
disconnect_all_clients_requested: bool, disconnect_all_clients_requested: bool,
server_constants: ServerConstants,
} }
impl Server { impl Server {
@ -561,6 +564,10 @@ impl Server {
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]
rtsim::init(&mut state); rtsim::init(&mut state);
let server_constants = ServerConstants {
day_cycle_coefficient: 1440.0 / settings.day_length,
};
let this = Self { let this = Self {
state, state,
world, world,
@ -571,6 +578,8 @@ impl Server {
metrics_shutdown, metrics_shutdown,
database_settings, database_settings,
disconnect_all_clients_requested: false, disconnect_all_clients_requested: false,
server_constants,
}; };
debug!(?settings, "created veloren server with"); debug!(?settings, "created veloren server with");
@ -701,6 +710,7 @@ impl Server {
}, },
false, false,
Some(&mut state_tick_metrics), Some(&mut state_tick_metrics),
&self.server_constants,
); );
let before_handle_events = Instant::now(); let before_handle_events = Instant::now();

View File

@ -22,6 +22,7 @@ use core::time::Duration;
use portpicker::pick_unused_port; use portpicker::pick_unused_port;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt::Display,
fs, fs,
net::{Ipv4Addr, Ipv6Addr, SocketAddr}, net::{Ipv4Addr, Ipv6Addr, SocketAddr},
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -166,6 +167,8 @@ pub struct Settings {
pub world_seed: u32, pub world_seed: u32,
pub server_name: String, pub server_name: String,
pub start_time: f64, pub start_time: f64,
/// Length of a day in minutes.
pub day_length: f64,
/// When set to None, loads the default map file (if available); otherwise, /// When set to None, loads the default map file (if available); otherwise,
/// uses the value of the file options to decide how to proceed. /// uses the value of the file options to decide how to proceed.
pub map_file: Option<FileOpts>, pub map_file: Option<FileOpts>,
@ -203,6 +206,7 @@ impl Default for Settings {
world_seed: DEFAULT_WORLD_SEED, world_seed: DEFAULT_WORLD_SEED,
server_name: "Veloren Server".into(), server_name: "Veloren Server".into(),
max_players: 100, max_players: 100,
day_length: 30.0,
start_time: 9.0 * 3600.0, start_time: 9.0 * 3600.0,
map_file: None, map_file: None,
max_view_distance: Some(65), max_view_distance: Some(65),
@ -223,7 +227,7 @@ impl Settings {
pub fn load(path: &Path) -> Self { pub fn load(path: &Path) -> Self {
let path = Self::get_settings_path(path); let path = Self::get_settings_path(path);
if let Ok(file) = fs::File::open(&path) { let mut settings = if let Ok(file) = fs::File::open(&path) {
match ron::de::from_reader(file) { match ron::de::from_reader(file) {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
@ -249,7 +253,10 @@ impl Settings {
error!(?e, "Failed to create default settings file!"); error!(?e, "Failed to create default settings file!");
} }
default_settings default_settings
} };
settings.validate();
settings
} }
fn save_to_file(&self, path: &Path) -> std::io::Result<()> { fn save_to_file(&self, path: &Path) -> std::io::Result<()> {
@ -302,6 +309,35 @@ impl Settings {
path.push(SETTINGS_FILENAME); path.push(SETTINGS_FILENAME);
path path
} }
fn validate(&mut self) {
const INVALID_SETTING_MSG: &str =
"Invalid value for setting in userdata/server/server_config/settings.ron.";
let default_values = Settings::default();
if self.day_length <= 0.0 {
warn!(
"{} Setting: day_length, Value: {}. Set day_length to it's default value of {}. \
Help: day_length must be a positive floating point value above 0.",
INVALID_SETTING_MSG, self.day_length, default_values.day_length
);
self.day_length = default_values.day_length;
}
}
}
pub enum InvalidSettingsError {
InvalidDayDuration,
}
impl Display for InvalidSettingsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InvalidSettingsError::InvalidDayDuration => {
f.write_str("Invalid settings error: Day length was invalid (zero or negative).")
},
}
}
} }
pub fn with_config_dir(path: &Path) -> PathBuf { pub fn with_config_dir(path: &Path) -> PathBuf {

View File

@ -10,6 +10,7 @@ use common::{
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
recipe::{default_component_recipe_book, default_recipe_book}, recipe::{default_component_recipe_book, default_recipe_book},
resources::TimeOfDay, resources::TimeOfDay,
shared_server_config::ServerConstants,
uid::{Uid, UidAllocator}, uid::{Uid, UidAllocator},
}; };
use common_base::prof_span; use common_base::prof_span;
@ -349,6 +350,9 @@ impl<'a> System<'a> for Sys {
component_recipe_book: default_component_recipe_book().cloned(), component_recipe_book: default_component_recipe_book().cloned(),
material_stats: (*read_data.material_stats).clone(), material_stats: (*read_data.material_stats).clone(),
ability_map: (*read_data.ability_map).clone(), ability_map: (*read_data.ability_map).clone(),
server_constants: ServerConstants {
day_cycle_coefficient: 1440.0 / read_data.settings.day_length
},
})?; })?;
debug!("Done initial sync with client."); debug!("Done initial sync with client.");