Merge branch 'imbris/voxygen-settings-improvements' into 'master'

Cleanup things in voxygen settings, making the directory externally specified, removing unused and planned to be removed things

Closes #853

See merge request veloren/veloren!2645
This commit is contained in:
Imbris 2021-07-19 15:06:52 +00:00
commit b8b27cb5f7
12 changed files with 111 additions and 240 deletions

View File

@ -3436,12 +3436,16 @@ impl Hud {
.clamped(1.25, max_zoom / 64.0);
global_state.settings.interface.map_zoom = new_zoom_lvl;
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
} else if global_state.settings.interface.minimap_show {
let new_zoom_lvl = global_state.settings.interface.minimap_zoom * factor;
global_state.settings.interface.minimap_zoom = new_zoom_lvl;
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
}
show.map && global_state.settings.interface.minimap_show

View File

@ -50,9 +50,12 @@ use crate::{
use common::clock::Clock;
use common_base::span;
use i18n::LocalizationHandle;
use std::path::PathBuf;
/// A type used to store state that is shared between all play states.
pub struct GlobalState {
pub userdata_dir: PathBuf,
pub config_dir: PathBuf,
pub settings: Settings,
pub profile: Profile,
pub window: Window,

View File

@ -17,33 +17,54 @@ use veloren_voxygen::{
#[cfg(feature = "hot-reloading")]
use common::assets;
use common::clock::Clock;
use std::panic;
use std::{panic, path::PathBuf};
use tracing::{error, info, warn};
#[cfg(feature = "egui-ui")]
use veloren_voxygen::ui::egui::EguiState;
#[allow(clippy::manual_unwrap_or)]
fn main() {
let userdata_dir = common_base::userdata_dir_workspace!();
// Determine where Voxygen's logs should go
// Choose a path to store the logs by the following order:
// - The VOXYGEN_LOGS environment variable
// - The <userdata>/voxygen/logs
let logs_dir = std::env::var_os("VOXYGEN_LOGS")
.map(PathBuf::from)
.unwrap_or_else(|| userdata_dir.join("voxygen").join("logs"));
// Init logging and hold the guards.
const LOG_FILENAME: &str = "voxygen.log";
let _guards = common_frontend::init_stdout(Some((&logs_dir, LOG_FILENAME)));
info!("Using userdata dir at: {}", userdata_dir.display());
// Determine Voxygen's config directory either by env var or placed in veloren's
// userdata folder
let config_dir = std::env::var_os("VOXYGEN_CONFIG")
.map(PathBuf::from)
.and_then(|path| {
if path.exists() {
Some(path)
} else {
warn!(?path, "VOXYGEN_CONFIG points to invalid path.");
None
}
})
.unwrap_or_else(|| userdata_dir.join("voxygen"));
info!("Using config dir at: {}", config_dir.display());
// Load the settings
// Note: This won't log anything due to it being called before
// `logging::init`. The issue is we need to read a setting to decide
// whether we create a log file or not.
let mut settings = Settings::load();
let mut settings = Settings::load(&config_dir);
// Save settings to add new fields or create the file if it is not already there
if let Err(err) = settings.save_to_file() {
if let Err(err) = settings.save_to_file(&config_dir) {
panic!("Failed to save settings: {:?}", err);
}
// Init logging and hold the guards.
const LOG_FILENAME: &str = "voxygen.log";
let _guards = common_frontend::init_stdout(Some((&settings.log.logs_path, LOG_FILENAME)));
if let Some(path) = veloren_voxygen::settings::voxygen_data_dir().parent() {
info!("Using userdata dir at: {}", path.display());
} else {
error!("Can't log userdata dir, voxygen data dir has no parent!");
}
// Set up panic handler to relay swish panic messages to the user
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
@ -92,9 +113,7 @@ fn main() {
Panic Payload: {:?}\n\
PanicInfo: {}\n\
Game version: {} [{}]",
Settings::load()
.log
.logs_path
logs_dir
.join("voxygen-<date>.log")
.display(),
reason,
@ -171,7 +190,7 @@ fn main() {
audio.set_sfx_volume(settings.audio.sfx_volume);
// Load the profile.
let profile = Profile::load();
let profile = Profile::load(&config_dir);
let mut i18n =
LocalizationHandle::load(&settings.language.selected_language).unwrap_or_else(|error| {
@ -198,6 +217,8 @@ fn main() {
let egui_state = EguiState::new(&window);
let global_state = GlobalState {
userdata_dir,
config_dir,
audio,
profile,
window,

View File

@ -139,7 +139,9 @@ impl PlayState for CharSelectionState {
global_state
.profile
.set_selected_character(server_name, selected);
global_state.profile.save_to_file_warn();
global_state
.profile
.save_to_file_warn(&global_state.config_dir);
},
}
}
@ -185,7 +187,9 @@ impl PlayState for CharSelectionState {
match event {
client::Event::SetViewDistance(vd) => {
global_state.settings.graphics.view_distance = vd;
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
},
client::Event::Disconnect => {
global_state.info_message = Some(

View File

@ -175,7 +175,9 @@ impl PlayState for MainMenuState {
match event {
client::Event::SetViewDistance(vd) => {
global_state.settings.graphics.view_distance = vd;
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
},
client::Event::Disconnect => {
global_state.info_message = Some(
@ -238,7 +240,9 @@ impl PlayState for MainMenuState {
if !net_settings.servers.contains(&server_address) {
net_settings.servers.push(server_address.clone());
}
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
let connection_args = if use_quic {
ConnectionArgs::Quic {
@ -302,7 +306,9 @@ impl PlayState for MainMenuState {
.networking
.trusted_auth_servers
.insert(auth_server.clone());
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
}
self.init
.client()

View File

@ -1,100 +0,0 @@
use common::comp;
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::{fs, io::Write, path::PathBuf};
use tracing::warn;
const VALID_VERSION: u32 = 0; // Change this if you broke charsaves
#[derive(Clone, Debug, Serialize, Deserialize)]
#[repr(C)]
pub struct CharacterData {
pub name: String,
pub body: comp::Body,
pub tool: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
//#[serde(default)]
#[repr(C)]
pub struct Meta {
pub characters: Vec<CharacterData>,
pub selected_character: usize,
pub version: u32,
}
impl Meta {
pub fn delete_character(&mut self, index: usize) {
self.characters.remove(index);
if index < self.selected_character {
self.selected_character -= 1;
}
}
pub fn add_character(&mut self, data: CharacterData) -> usize {
self.characters.push(data);
// return new character's index
self.characters.len() - 1
}
pub fn load() -> Self {
let path = Self::get_meta_path();
if let Ok(file) = fs::File::open(&path) {
match ron::de::from_reader::<_, Meta>(file) {
Ok(s) => {
if s.version == VALID_VERSION {
return s;
}
},
Err(e) => {
warn!(?e, ?file, "Failed to parse meta file! Fallback to default");
// Rename the corrupted settings file
let mut new_path = path.to_owned();
new_path.pop();
new_path.push("meta.invalid.ron");
if let Err(e) = std::fs::rename(path.clone(), new_path.clone()) {
warn!(?e, ?path, ?new_path, "Failed to rename meta 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 = Self::default();
default.save_to_file_warn();
default
}
pub fn save_to_file_warn(&self) {
if let Err(err) = self.save_to_file() {
warn!(?e, "Failed to save settings");
}
}
pub fn save_to_file(&self) -> std::io::Result<()> {
let path = Self::get_meta_path();
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
let mut meta_file = fs::File::create(path)?;
let s: &str = &ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap();
meta_file.write_all(s.as_bytes()).unwrap();
Ok(())
}
pub fn get_meta_path() -> PathBuf {
if let Some(path) = std::env::var_os("VOXYGEN_CONFIG") {
let meta = PathBuf::from(path).join("meta.ron");
if meta.exists() || meta.parent().map(|x| x.exists()).unwrap_or(false) {
return meta;
}
warn!(?path, "VOXYGEN_CONFIG points to invalid path.");
}
let proj_dirs = ProjectDirs::from("net", "veloren", "voxygen")
.expect("System's $HOME directory path not found!");
proj_dirs.config_dir().join("meta").with_extension("ron")
}
}

View File

@ -1,8 +1,12 @@
use crate::{hud, settings};
use crate::hud;
use common::{character::CharacterId, comp::slot::InvSlotId};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use std::{fs, io::Write, path::PathBuf};
use std::{
fs,
io::Write,
path::{Path, PathBuf},
};
use tracing::warn;
/// Represents a character in the profile.
@ -76,8 +80,8 @@ impl Default for Profile {
impl Profile {
/// Load the profile.ron file from the standard path or create it.
pub fn load() -> Self {
let path = Profile::get_path();
pub fn load(config_dir: &Path) -> Self {
let path = Profile::get_path(config_dir);
if let Ok(file) = fs::File::open(&path) {
match ron::de::from_reader(file) {
@ -100,13 +104,13 @@ impl Profile {
// - The file can't be opened (presumably it doesn't exist)
// - Or there was an error parsing the file
let default_profile = Self::default();
default_profile.save_to_file_warn();
default_profile.save_to_file_warn(config_dir);
default_profile
}
/// Save the current profile to disk, warn on failure.
pub fn save_to_file_warn(&self) {
if let Err(e) = self.save_to_file() {
pub fn save_to_file_warn(&self, config_dir: &Path) {
if let Err(e) = self.save_to_file(config_dir) {
warn!(?e, "Failed to save profile");
}
}
@ -194,8 +198,8 @@ impl Profile {
}
/// Save the current profile to disk.
fn save_to_file(&self) -> std::io::Result<()> {
let path = Profile::get_path();
fn save_to_file(&self, config_dir: &Path) -> std::io::Result<()> {
let path = Profile::get_path(config_dir);
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
@ -206,19 +210,7 @@ impl Profile {
Ok(())
}
fn get_path() -> PathBuf {
if let Some(path) = std::env::var_os("VOXYGEN_CONFIG") {
let profile = PathBuf::from(path.clone()).join("profile.ron");
if profile.exists() || profile.parent().map(|x| x.exists()).unwrap_or(false) {
return profile;
}
warn!(?path, "VOXYGEN_CONFIG points to invalid path.");
}
let mut path = settings::voxygen_data_dir();
path.push("profile.ron");
path
}
fn get_path(config_dir: &Path) -> PathBuf { config_dir.join("profile.ron") }
}
#[cfg(test)]

View File

@ -83,8 +83,12 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
},
winit::event::Event::LoopDestroyed => {
// Save any unsaved changes to settings and profile
global_state.settings.save_to_file_warn();
global_state.profile.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
global_state
.profile
.save_to_file_warn(&global_state.config_dir);
},
_ => {},
}
@ -100,7 +104,7 @@ fn handle_main_events_cleared(
// Screenshot / Fullscreen toggle
global_state
.window
.resolve_deduplicated_events(&mut global_state.settings);
.resolve_deduplicated_events(&mut global_state.settings, &global_state.config_dir);
// Run tick here
// What's going on here?

View File

@ -259,7 +259,9 @@ impl SessionState {
},
client::Event::SetViewDistance(vd) => {
global_state.settings.graphics.view_distance = vd;
global_state.settings.save_to_file_warn();
global_state
.settings
.save_to_file_warn(&global_state.config_dir);
},
client::Event::Outcome(outcome) => outcomes.push(outcome),
client::Event::CharacterCreated(_) => {},
@ -1305,7 +1307,9 @@ impl PlayState for SessionState {
state.slots,
);
global_state.profile.save_to_file_warn();
global_state
.profile
.save_to_file_warn(&global_state.config_dir);
info!("Event! -> ChangedHotbarState")
},

View File

@ -202,7 +202,6 @@ impl SettingsChange {
global_state.audio.set_sfx_volume(audio.sfx_volume);
},
}
settings.save_to_file_warn();
},
SettingsChange::Chat(chat_change) => {
let chat_tabs = &mut settings.chat.chat_tabs;
@ -242,7 +241,6 @@ impl SettingsChange {
settings.chat = ChatSettings::default();
},
}
settings.save_to_file_warn();
},
SettingsChange::Control(control_change) => match control_change {
Control::ChangeBinding(game_input) => {
@ -250,7 +248,6 @@ impl SettingsChange {
},
Control::ResetKeyBindings => {
settings.controls = ControlSettings::default();
settings.save_to_file_warn();
},
},
SettingsChange::Gamepad(gamepad_change) => match gamepad_change {},
@ -323,7 +320,6 @@ impl SettingsChange {
window.mouse_y_inversion = settings.gameplay.mouse_y_inversion;
},
}
settings.save_to_file_warn();
},
SettingsChange::Graphics(graphics_change) => {
match graphics_change {
@ -423,7 +419,6 @@ impl SettingsChange {
global_state.window.set_size(graphics.window_size.into());
},
}
settings.save_to_file_warn();
},
SettingsChange::Interface(interface_change) => {
match interface_change {
@ -534,7 +529,6 @@ impl SettingsChange {
.set_scaling_mode(settings.interface.ui_scale);
},
}
settings.save_to_file_warn();
},
SettingsChange::Language(language_change) => match language_change {
Language::ChangeLanguage(new_language) => {
@ -556,5 +550,6 @@ impl SettingsChange {
},
SettingsChange::Networking(networking_change) => match networking_change {},
}
settings.save_to_file_warn(&global_state.config_dir);
}
}

View File

@ -1,6 +1,9 @@
use directories_next::UserDirs;
use serde::{Deserialize, Serialize};
use std::{fs, path::PathBuf};
use std::{
fs,
path::{Path, PathBuf},
};
use tracing::warn;
pub mod audio;
@ -23,40 +26,6 @@ pub use interface::InterfaceSettings;
pub use language::LanguageSettings;
pub use networking::NetworkingSettings;
/// `Log` stores whether we should create a log file
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct Log {
// Whether to create a log file or not.
// Default is to create one.
pub log_to_file: bool,
// The path on which the logs will be stored
pub logs_path: PathBuf,
}
impl Default for Log {
fn default() -> Self {
// Chooses a path to store the logs by the following order:
// - The VOXYGEN_LOGS environment variable
// - The ProjectsDirs data local directory
// This function is only called if there isn't already an entry in the settings
// file. However, the VOXYGEN_LOGS environment variable always overrides
// the log file path if set.
let logs_path = std::env::var_os("VOXYGEN_LOGS")
.map(PathBuf::from)
.unwrap_or_else(|| {
let mut path = voxygen_data_dir();
path.push("logs");
path
});
Self {
log_to_file: true,
logs_path,
}
}
}
/// `Settings` contains everything that can be configured in the settings.ron
/// file.
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -67,7 +36,6 @@ pub struct Settings {
pub interface: InterfaceSettings,
pub gameplay: GameplaySettings,
pub networking: NetworkingSettings,
pub log: Log,
pub graphics: GraphicsSettings,
pub audio: AudioSettings,
pub show_disclaimer: bool,
@ -104,7 +72,6 @@ impl Default for Settings {
interface: InterfaceSettings::default(),
gameplay: GameplaySettings::default(),
networking: NetworkingSettings::default(),
log: Log::default(),
graphics: GraphicsSettings::default(),
audio: AudioSettings::default(),
show_disclaimer: true,
@ -118,25 +85,12 @@ impl Default for Settings {
}
impl Settings {
pub fn load() -> Self {
let path = Self::get_settings_path();
pub fn load(config_dir: &Path) -> Self {
let path = Self::get_path(config_dir);
if let Ok(file) = fs::File::open(&path) {
match ron::de::from_reader::<_, Self>(file) {
Ok(mut s) => {
// Override the logs path if it is explicitly set using the VOXYGEN_LOGS
// environment variable. This is needed to support package managers that enforce
// strict application confinement (e.g. snap). In fact, the veloren snap package
// relies on this environment variable to be respected in
// order to communicate a path where the snap package is
// allowed to write to.
if let Some(logs_path_override) =
std::env::var_os("VOXYGEN_LOGS").map(PathBuf::from)
{
s.log.logs_path = logs_path_override;
}
return s;
},
Ok(s) => return s,
Err(e) => {
warn!(?e, "Failed to parse setting file! Fallback to default.");
// Rename the corrupted settings file
@ -153,18 +107,18 @@ impl Settings {
// - 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.save_to_file_warn(config_dir);
default_settings
}
pub fn save_to_file_warn(&self) {
if let Err(e) = self.save_to_file() {
pub fn save_to_file_warn(&self, config_dir: &Path) {
if let Err(e) = self.save_to_file(config_dir) {
warn!(?e, "Failed to save settings");
}
}
pub fn save_to_file(&self) -> std::io::Result<()> {
let path = Self::get_settings_path();
pub fn save_to_file(&self, config_dir: &Path) -> std::io::Result<()> {
let path = Self::get_path(config_dir);
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
@ -173,25 +127,5 @@ impl Settings {
fs::write(path, ron.as_bytes())
}
pub fn get_settings_path() -> PathBuf {
if let Some(path) = std::env::var_os("VOXYGEN_CONFIG") {
let settings = PathBuf::from(&path).join("settings.ron");
if settings.exists() || settings.parent().map(|x| x.exists()).unwrap_or(false) {
return settings;
}
warn!(?path, "VOXYGEN_CONFIG points to invalid path.");
}
let mut path = voxygen_data_dir();
path.push("settings.ron");
path
}
}
pub fn voxygen_data_dir() -> PathBuf {
// Note: since voxygen is technically a lib we made need to lift this up to
// run.rs
let mut path = common_base::userdata_dir_workspace!();
path.push("voxygen");
path
fn get_path(config_dir: &Path) -> PathBuf { config_dir.join("settings.ron") }
}

View File

@ -703,7 +703,11 @@ impl Window {
pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
pub fn resolve_deduplicated_events(&mut self, settings: &mut Settings) {
pub fn resolve_deduplicated_events(
&mut self,
settings: &mut Settings,
config_dir: &std::path::Path,
) {
// Handle screenshots and toggling fullscreen
if self.take_screenshot {
self.take_screenshot = false;
@ -711,7 +715,7 @@ impl Window {
}
if self.toggle_fullscreen {
self.toggle_fullscreen = false;
self.toggle_fullscreen(settings);
self.toggle_fullscreen(settings, config_dir);
}
}
@ -1170,7 +1174,7 @@ impl Window {
}
}
pub fn toggle_fullscreen(&mut self, settings: &mut Settings) {
pub fn toggle_fullscreen(&mut self, settings: &mut Settings, config_dir: &std::path::Path) {
let fullscreen = FullScreenSettings {
enabled: !self.is_fullscreen(),
..settings.graphics.fullscreen
@ -1178,7 +1182,7 @@ impl Window {
self.set_fullscreen_mode(fullscreen);
settings.graphics.fullscreen = fullscreen;
settings.save_to_file_warn();
settings.save_to_file_warn(config_dir);
}
pub fn is_fullscreen(&self) -> bool { self.fullscreen.enabled }