diff --git a/Cargo.lock b/Cargo.lock index 95b1746eff..d797ede87e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4713,6 +4713,8 @@ dependencies = [ "clap", "crossterm", "lazy_static", + "ron", + "serde", "signal-hook", "tracing", "tracing-subscriber", @@ -4761,7 +4763,6 @@ dependencies = [ "rodio", "ron", "serde", - "serde_derive", "specs", "specs-idvs", "tracing", diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index 236c23f79f..b35d06d537 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -20,6 +20,8 @@ lazy_static = "1" signal-hook = "0.1.16" tracing = { version = "0.1", default-features = false } tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec"] } +ron = {version = "0.6", default-features = false} +serde = {version = "1.0", features = [ "rc", "derive" ]} # Tracy tracing-tracy = { version = "0.2.0", optional = true } diff --git a/server-cli/src/main.rs b/server-cli/src/main.rs index 6cf6cf3641..7c54e03843 100644 --- a/server-cli/src/main.rs +++ b/server-cli/src/main.rs @@ -3,6 +3,7 @@ #![feature(bool_to_option)] mod logging; +mod settings; mod shutdown_coordinator; mod tui_runner; mod tuilog; @@ -58,6 +59,9 @@ fn main() -> io::Result<()> { logging::init(basic); + // Load settings + let settings = settings::Settings::load(); + // Panic hook to ensure that console mode is set back correctly if in non-basic // mode let hook = std::panic::take_hook(); @@ -80,18 +84,18 @@ fn main() -> io::Result<()> { path }); - // Load settings - let mut settings = ServerSettings::load(server_data_dir.as_ref()); + // Load server settings + let mut server_settings = ServerSettings::load(server_data_dir.as_ref()); if no_auth { - settings.auth_server_address = None; + server_settings.auth_server_address = None; } - let server_port = &settings.gameserver_address.port(); - let metrics_port = &settings.metrics_address.port(); + let server_port = &server_settings.gameserver_address.port(); + let metrics_port = &server_settings.metrics_address.port(); // Create server let mut server = - Server::new(settings, server_data_dir).expect("Failed to create server instance!"); + Server::new(server_settings, server_data_dir).expect("Failed to create server instance!"); info!( ?server_port, @@ -103,7 +107,7 @@ fn main() -> io::Result<()> { loop { // Terminate the server if instructed to do so by the shutdown coordinator - if shutdown_coordinator.check(&mut server) { + if shutdown_coordinator.check(&mut server, &settings) { break; } diff --git a/server-cli/src/settings.rs b/server-cli/src/settings.rs new file mode 100644 index 0000000000..3cca9ec466 --- /dev/null +++ b/server-cli/src/settings.rs @@ -0,0 +1,75 @@ +use serde::{Deserialize, Serialize}; +use std::{fs, path::PathBuf}; +use tracing::warn; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(default)] +pub struct Settings { + pub update_shutdown_grace_period_secs: u32, + pub update_shutdown_message: String, +} + +impl Default for Settings { + fn default() -> Self { + Self { + update_shutdown_grace_period_secs: 120, + update_shutdown_message: "The server is restarting for an update".to_owned(), + } + } +} + +impl Settings { + pub fn load() -> Self { + let path = Self::get_settings_path(); + + if let Ok(file) = fs::File::open(&path) { + match ron::de::from_reader(file) { + Ok(s) => return s, + Err(e) => { + warn!(?e, "Failed to parse setting file! Fallback to default."); + // Rename the corrupted settings file + let mut new_path = path.to_owned(); + new_path.pop(); + new_path.push("settings.invalid.ron"); + if let Err(e) = std::fs::rename(&path, &new_path) { + warn!(?e, ?path, ?new_path, "Failed to rename settings file."); + } + }, + } + } + // This is reached if either: + // - The file can't be opened (presumably it doesn't exist) + // - Or there was an error parsing the file + let default_settings = Self::default(); + default_settings.save_to_file_warn(); + default_settings + } + + pub fn save_to_file_warn(&self) { + if let Err(e) = self.save_to_file() { + warn!(?e, "Failed to save settings"); + } + } + + fn save_to_file(&self) -> std::io::Result<()> { + let path = Self::get_settings_path(); + if let Some(dir) = path.parent() { + fs::create_dir_all(dir)?; + } + + let ron = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap(); + fs::write(path, ron.as_bytes()) + } + + pub fn get_settings_path() -> PathBuf { + let mut path = data_dir(); + path.push("settings.ron"); + path + } +} + +pub fn data_dir() -> PathBuf { + let mut path = common::userdata_dir_workspace!(); + path.push("server-cli"); + path +} diff --git a/server-cli/src/shutdown_coordinator.rs b/server-cli/src/shutdown_coordinator.rs index 1716ea1f04..59705c2003 100644 --- a/server-cli/src/shutdown_coordinator.rs +++ b/server-cli/src/shutdown_coordinator.rs @@ -1,3 +1,4 @@ +use crate::settings::Settings; use common::comp::chat::ChatType; use server::Server; use std::{ @@ -78,9 +79,9 @@ impl ShutdownCoordinator { /// shutdown. If the grace period for an initiated shutdown has expired, /// returns `true` which triggers the loop in `main.rs` to break and /// exit the server process. - pub fn check(&mut self, server: &mut Server) -> bool { + pub fn check(&mut self, server: &mut Server, settings: &Settings) -> bool { // Check whether SIGUSR1 has been set - self.check_sigusr1_signal(server); + self.check_sigusr1_signal(server, settings); // If a shutdown is in progress, check whether it's time to send another warning // message or shut down if the grace period has expired. @@ -112,12 +113,12 @@ impl ShutdownCoordinator { /// Veloren server to send SIGUSR1 instead of SIGTERM which allows us to /// react specifically to shutdowns that are for an update. /// NOTE: SIGUSR1 is not supported on Windows - fn check_sigusr1_signal(&mut self, server: &mut Server) { + fn check_sigusr1_signal(&mut self, server: &mut Server, settings: &Settings) { if self.sigusr1_signal.load(Ordering::Relaxed) && self.shutdown_initiated_at.is_none() { info!("Received SIGUSR1 signal, initiating graceful shutdown"); let grace_period = - Duration::from_secs(server.settings().update_shutdown_grace_period_secs); - let shutdown_message = server.settings().update_shutdown_message.to_owned(); + Duration::from_secs(u64::from(settings.update_shutdown_grace_period_secs)); + let shutdown_message = settings.update_shutdown_message.to_owned(); self.initiate_shutdown(server, grace_period, shutdown_message); // Reset the SIGUSR1 signal indicator in case shutdown is aborted and we need to diff --git a/server/src/settings.rs b/server/src/settings.rs index bea63f1f31..c03bfbd72c 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -45,8 +45,6 @@ pub struct ServerSettings { pub banned_words_files: Vec, pub max_player_group_size: u32, pub client_timeout: Duration, - pub update_shutdown_grace_period_secs: u64, - pub update_shutdown_message: String, } impl Default for ServerSettings { @@ -66,8 +64,6 @@ impl Default for ServerSettings { banned_words_files: Vec::new(), max_player_group_size: 6, client_timeout: Duration::from_secs(40), - update_shutdown_grace_period_secs: 120, - update_shutdown_message: "The server is restarting for an update".to_owned(), } } } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index ceb2da01c2..49c2136acf 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -67,8 +67,7 @@ num = "0.2" rand = "0.7" rodio = {version = "0.11", default-features = false, features = ["wav", "vorbis"]} ron = {version = "0.6", default-features = false} -serde = {version = "1.0", features = [ "rc" ]} -serde_derive = "1.0" +serde = {version = "1.0", features = [ "rc", "derive" ]} treeculler = "0.1.0" uvth = "3.1.1" # vec_map = { version = "0.8.2" } diff --git a/voxygen/src/controller.rs b/voxygen/src/controller.rs index 44b286c2e2..86f3020a3f 100644 --- a/voxygen/src/controller.rs +++ b/voxygen/src/controller.rs @@ -4,7 +4,7 @@ use crate::window::{GameInput, MenuInput}; use gilrs::{ev::Code as GilCode, Axis as GilAxis, Button as GilButton}; use hashbrown::HashMap; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; /// Contains all controller related settings and keymaps #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/voxygen/src/hud/hotbar.rs b/voxygen/src/hud/hotbar.rs index ab02df0c70..321b6451d2 100644 --- a/voxygen/src/hud/hotbar.rs +++ b/voxygen/src/hud/hotbar.rs @@ -1,4 +1,4 @@ -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, PartialEq)] pub enum Slot { diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 12de1f50cf..b86fa53eb8 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -12,7 +12,7 @@ use conrod_core::image::Id; use dot_vox::DotVoxData; use hashbrown::HashMap; use image::DynamicImage; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::{fs::File, io::BufReader, sync::Arc}; use tracing::{error, warn}; use vek::*; diff --git a/voxygen/src/i18n.rs b/voxygen/src/i18n.rs index ca554f4d5b..479fef3121 100644 --- a/voxygen/src/i18n.rs +++ b/voxygen/src/i18n.rs @@ -1,7 +1,7 @@ use common::assets::{self, Asset}; use deunicode::deunicode; use ron::de::from_reader; -use serde_derive::*; +use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, fs::File, diff --git a/voxygen/src/meta.rs b/voxygen/src/meta.rs index 30ef1a3c7b..4794e6725d 100644 --- a/voxygen/src/meta.rs +++ b/voxygen/src/meta.rs @@ -1,6 +1,6 @@ use common::comp; use directories::ProjectDirs; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::{fs, io::Write, path::PathBuf}; use tracing::warn; diff --git a/voxygen/src/profile.rs b/voxygen/src/profile.rs index 34990f1de2..dbb7267b11 100644 --- a/voxygen/src/profile.rs +++ b/voxygen/src/profile.rs @@ -1,7 +1,7 @@ use crate::{hud, settings}; use common::character::CharacterId; use hashbrown::HashMap; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::{fs, io::Write, path::PathBuf}; use tracing::warn; diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 1c747e78d0..a7c36497e5 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -62,7 +62,7 @@ pub trait Pipeline { type Vertex: Clone + gfx::traits::Pod + gfx::pso::buffer::Structure; } -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; /// Anti-aliasing modes #[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum AaMode { diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index bc87ab9312..32c3c997d4 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -20,7 +20,7 @@ use common::{ }; use dot_vox::DotVoxData; use hashbrown::HashMap; -use serde_derive::Deserialize; +use serde::Deserialize; use std::sync::Arc; use tracing::{error, warn}; use vek::*; diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 5acdca42df..be4eed60db 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -7,8 +7,8 @@ use crate::{ }; use directories_next::UserDirs; use hashbrown::{HashMap, HashSet}; -use serde_derive::{Deserialize, Serialize}; -use std::{fs, io::prelude::*, path::PathBuf}; +use serde::{Deserialize, Serialize}; +use std::{fs, path::PathBuf}; use tracing::warn; use winit::event::{MouseButton, VirtualKeyCode}; @@ -291,7 +291,7 @@ impl Default for GamepadSettings { pub mod con_settings { use crate::controller::*; use gilrs::{Axis as GilAxis, Button as GilButton}; - use serde_derive::{Deserialize, Serialize}; + use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] @@ -755,7 +755,7 @@ impl Default for Settings { impl Settings { pub fn load() -> Self { - let path = Settings::get_settings_path(); + let path = Self::get_settings_path(); if let Ok(file) = fs::File::open(&path) { match ron::de::from_reader(file) { @@ -787,15 +787,13 @@ impl Settings { } pub fn save_to_file(&self) -> std::io::Result<()> { - let path = Settings::get_settings_path(); + let path = Self::get_settings_path(); if let Some(dir) = path.parent() { fs::create_dir_all(dir)?; } - let mut config_file = fs::File::create(path)?; - let s: &str = &ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap(); - config_file.write_all(s.as_bytes()).unwrap(); - Ok(()) + let ron = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap(); + fs::write(path, ron.as_bytes()) } pub fn get_settings_path() -> PathBuf { diff --git a/voxygen/src/ui/scale.rs b/voxygen/src/ui/scale.rs index 01eb21afba..2845585b77 100644 --- a/voxygen/src/ui/scale.rs +++ b/voxygen/src/ui/scale.rs @@ -1,5 +1,5 @@ use crate::{render::Renderer, window::Window}; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use vek::*; /// Type of scaling to use. diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index d0215f2934..f9007e43af 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -10,7 +10,7 @@ use gilrs::{EventType, Gilrs}; use hashbrown::HashMap; use itertools::Itertools; use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt}; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::fmt; use tracing::{error, info, warn}; use vek::*;