veloren_voxygen/settings/
mod.rs

1use directories_next::UserDirs;
2use serde::{Deserialize, Serialize};
3use std::{
4    fs,
5    path::{Path, PathBuf},
6};
7use tracing::warn;
8
9pub mod audio;
10pub mod chat;
11pub mod control;
12pub mod gamepad;
13pub mod gameplay;
14pub mod graphics;
15pub mod interface;
16pub mod language;
17pub mod networking;
18
19pub use audio::{AudioOutput, AudioSettings};
20pub use chat::ChatSettings;
21pub use control::ControlSettings;
22pub use gamepad::GamepadSettings;
23pub use gameplay::GameplaySettings;
24pub use graphics::{Fps, GraphicsSettings, get_fps};
25pub use interface::InterfaceSettings;
26pub use language::LanguageSettings;
27pub use networking::NetworkingSettings;
28
29/// `Settings` contains everything that can be configured in the settings.ron
30/// file.
31#[derive(Clone, Debug, Serialize, Deserialize)]
32#[serde(default)]
33pub struct Settings {
34    pub chat: ChatSettings,
35    pub controls: ControlSettings,
36    pub interface: InterfaceSettings,
37    pub gameplay: GameplaySettings,
38    pub networking: NetworkingSettings,
39    pub graphics: GraphicsSettings,
40    pub audio: AudioSettings,
41    pub show_disclaimer: bool,
42    pub send_logon_commands: bool,
43    // TODO: Remove at a later date, for dev testing
44    pub logon_commands: Vec<String>,
45    pub language: LanguageSettings,
46    pub screenshots_path: PathBuf,
47    pub controller: GamepadSettings,
48}
49
50impl Default for Settings {
51    fn default() -> Self {
52        let user_dirs = UserDirs::new().expect("System's $HOME directory path not found!");
53
54        // Chooses a path to store the screenshots by the following order:
55        //  - The VOXYGEN_SCREENSHOT environment variable
56        //  - The user's picture directory
57        //  - The executable's directory
58        // This only selects if there isn't already an entry in the settings file
59        let screenshots_path = std::env::var_os("VOXYGEN_SCREENSHOT")
60            .map(PathBuf::from)
61            .or_else(|| user_dirs.picture_dir().map(|dir| dir.join("veloren")))
62            .or_else(|| {
63                std::env::current_exe()
64                    .ok()
65                    .and_then(|dir| dir.parent().map(PathBuf::from))
66            })
67            .expect("Couldn't choose a place to store the screenshots");
68
69        Settings {
70            chat: ChatSettings::default(),
71            controls: ControlSettings::default(),
72            interface: InterfaceSettings::default(),
73            gameplay: GameplaySettings::default(),
74            networking: NetworkingSettings::default(),
75            graphics: GraphicsSettings::default(),
76            audio: AudioSettings::default(),
77            show_disclaimer: true,
78            send_logon_commands: false,
79            logon_commands: Vec::new(),
80            language: LanguageSettings::default(),
81            screenshots_path,
82            controller: GamepadSettings::default(),
83        }
84    }
85}
86
87impl Settings {
88    pub fn load(config_dir: &Path) -> Self {
89        let path = Self::get_path(config_dir);
90
91        if let Ok(file) = fs::File::open(&path) {
92            match ron::de::from_reader::<_, Self>(file) {
93                Ok(s) => return s,
94                Err(e) => {
95                    warn!(?e, "Failed to parse setting file! Fallback to default.");
96                    // Rename the corrupted settings file
97                    let mut new_path = path.to_owned();
98                    new_path.pop();
99                    new_path.push("settings.invalid.ron");
100                    if let Err(e) = fs::rename(&path, &new_path) {
101                        warn!(?e, ?path, ?new_path, "Failed to rename settings file.");
102                    }
103                },
104            }
105        }
106        // This is reached if either:
107        // - The file can't be opened (presumably it doesn't exist)
108        // - Or there was an error parsing the file
109        let default_settings = Self::default();
110        default_settings.save_to_file_warn(config_dir);
111        default_settings
112    }
113
114    pub fn save_to_file_warn(&self, config_dir: &Path) {
115        if let Err(e) = self.save_to_file(config_dir) {
116            warn!(?e, "Failed to save settings");
117        }
118    }
119
120    pub fn save_to_file(&self, config_dir: &Path) -> std::io::Result<()> {
121        let path = Self::get_path(config_dir);
122        if let Some(dir) = path.parent() {
123            fs::create_dir_all(dir)?;
124        }
125
126        let ron = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap();
127        fs::write(path, ron.as_bytes())
128    }
129
130    fn get_path(config_dir: &Path) -> PathBuf { config_dir.join("settings.ron") }
131
132    pub fn display_warnings(&self) {
133        if !self.graphics.render_mode.experimental_shaders.is_empty() {
134            warn!(
135                "One or more experimental shaders are enabled, all rendering guarantees are off. \
136                 Experimental shaders may be unmaintained, mutually-incompatible, entirely \
137                 broken, or may cause your GPU to explode. You have been warned!"
138            );
139        }
140    }
141}