From a90d073c2250d7da1c88e55f8a36aa649d8ad7f2 Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Wed, 5 Jun 2019 11:57:48 -0400
Subject: [PATCH 01/10] Adds mouse sensitivity sliders that update settings
 file.

---
 voxygen/src/hud/mod.rs             |  8 ++++
 voxygen/src/hud/settings_window.rs | 75 +++++++++++++++++++++++++++---
 voxygen/src/session.rs             | 14 +++++-
 voxygen/src/settings.rs            | 14 ++++--
 voxygen/src/window.rs              |  6 +--
 5 files changed, 103 insertions(+), 14 deletions(-)

diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index 71e76b92ba..e349a8c78c 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -108,6 +108,8 @@ pub struct DebugInfo {
 
 pub enum Event {
     SendMessage(String),
+    AdjustMousePan(u32),
+    AdjustMouseZoom(u32),
     AdjustViewDistance(u32),
     AdjustVolume(f32),
     ChangeAudioDevice(String),
@@ -567,6 +569,12 @@ impl Hud {
                     settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug,
                     settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab),
                     settings_window::Event::Close => self.show.settings(false),
+                    settings_window::Event::AdjustMousePan(sensitivity) => {
+                        events.push(Event::AdjustMousePan(sensitivity));
+                    }
+                    settings_window::Event::AdjustMouseZoom(sensitivity) => {
+                        events.push(Event::AdjustMouseZoom(sensitivity));
+                    }
                     settings_window::Event::AdjustViewDistance(view_distance) => {
                         events.push(Event::AdjustViewDistance(view_distance));
                     }
diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index 9c0a02c7c3..a0533f0938 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -31,6 +31,10 @@ widget_ids! {
         interface,
         inventory_test_button,
         inventory_test_button_label,
+        mouse_pan_slider,
+        mouse_pan_text,
+        mouse_zoom_slider,
+        mouse_zoom_text,
         settings_bg,
         sound,
         test,
@@ -92,6 +96,8 @@ pub enum Event {
     ToggleDebug,
     ChangeTab(SettingsTab),
     Close,
+    AdjustMousePan(u32),
+    AdjustMouseZoom(u32),
     AdjustViewDistance(u32),
     AdjustVolume(f32),
     ChangeAudioDevice(String),
@@ -160,7 +166,7 @@ impl<'a> Widget for SettingsWindow<'a> {
             .color(TEXT_COLOR)
             .set(state.ids.settings_title, ui);
 
-        // Interface
+        // 1) Interface Tab -------------------------------
         if Button::image(if let SettingsTab::Interface = self.show.settings_tab {
             self.imgs.settings_button_pressed
         } else {
@@ -187,6 +193,7 @@ impl<'a> Widget for SettingsWindow<'a> {
             events.push(Event::ChangeTab(SettingsTab::Interface));
         }
 
+        // Contents
         if let SettingsTab::Interface = self.show.settings_tab {
             // Help
             let show_help =
@@ -255,7 +262,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                 .set(state.ids.debug_button_label, ui);
         }
 
-        // 2 Gameplay
+        // 2) Gameplay Tab --------------------------------
         if Button::image(if let SettingsTab::Gameplay = self.show.settings_tab {
             self.imgs.settings_button_pressed
         } else {
@@ -282,7 +289,58 @@ impl<'a> Widget for SettingsWindow<'a> {
             events.push(Event::ChangeTab(SettingsTab::Gameplay));
         }
 
-        // 3 Controls
+        // Contents
+        if let SettingsTab::Gameplay = self.show.settings_tab {
+            Text::new("Pan Sensitivity")
+                .top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.mouse_pan_text, ui);
+
+            if let Some(new_val) = ImageSlider::discrete(
+                (self.global_state.settings.gameplay.pan_sensitivity * 100.0) as u32,
+                1,
+                200,
+                self.imgs.slider_indicator,
+                self.imgs.slider,
+            )
+            .w_h(208.0, 22.0)
+            .down_from(state.ids.mouse_pan_text, 10.0)
+            .track_breadth(30.0)
+            .slider_length(10.0)
+            .pad_track((5.0, 5.0))
+            .set(state.ids.mouse_pan_slider, ui)
+            {
+                events.push(Event::AdjustMousePan(new_val));
+            }
+
+            Text::new("Zoom Sensitivity")
+                .down_from(state.ids.mouse_pan_slider, 10.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.mouse_zoom_text, ui);
+
+            if let Some(new_val) = ImageSlider::discrete(
+                (self.global_state.settings.gameplay.zoom_sensitivity * 100.0) as u32,
+                1,
+                200,
+                self.imgs.slider_indicator,
+                self.imgs.slider,
+            )
+            .w_h(208.0, 22.0)
+            .down_from(state.ids.mouse_zoom_text, 10.0)
+            .track_breadth(30.0)
+            .slider_length(10.0)
+            .pad_track((5.0, 5.0))
+            .set(state.ids.mouse_zoom_slider, ui)
+            {
+                events.push(Event::AdjustMouseZoom(new_val));
+            }
+        }
+
+        // 3) Controls Tab --------------------------------
         if Button::image(if let SettingsTab::Controls = self.show.settings_tab {
             self.imgs.settings_button_pressed
         } else {
@@ -308,6 +366,8 @@ impl<'a> Widget for SettingsWindow<'a> {
         {
             events.push(Event::ChangeTab(SettingsTab::Controls));
         }
+
+        // Contents
         if let SettingsTab::Controls = self.show.settings_tab {
             Text::new(
                 "Free Cursor\n\
@@ -376,7 +436,6 @@ impl<'a> Widget for SettingsWindow<'a> {
             /tp [Name] - Teleports you to another player    \n\
             /jump <dx> <dy> <dz> - Offset your position \n\
             /goto <x> <y> <z> - Teleport to a position  \n\
-            /tp <name> - Teleport to another player \n\
             /kill - Kill yourself   \n\
             /pig - Spawn pig NPC    \n\
             /wolf - Spawn wolf NPC  \n\
@@ -462,7 +521,8 @@ impl<'a> Widget for SettingsWindow<'a> {
             .font_size(18)
             .set(state.ids.controls_controls, ui);
         }
-        // 4 Video
+
+        // 4) Video Tab -----------------------------------
         if Button::image(if let SettingsTab::Video = self.show.settings_tab {
             self.imgs.settings_button_pressed
         } else {
@@ -489,6 +549,7 @@ impl<'a> Widget for SettingsWindow<'a> {
         {
             events.push(Event::ChangeTab(SettingsTab::Video));
         }
+
         // Contents
         if let SettingsTab::Video = self.show.settings_tab {
             Text::new("View Distance")
@@ -515,7 +576,8 @@ impl<'a> Widget for SettingsWindow<'a> {
                 events.push(Event::AdjustViewDistance(new_val));
             }
         }
-        // 5 Sound
+
+        // 5) Sound Tab -----------------------------------
         if Button::image(if let SettingsTab::Sound = self.show.settings_tab {
             self.imgs.settings_button_pressed
         } else {
@@ -542,6 +604,7 @@ impl<'a> Widget for SettingsWindow<'a> {
         {
             events.push(Event::ChangeTab(SettingsTab::Sound));
         }
+
         // Contents
         if let SettingsTab::Sound = self.show.settings_tab {
             Text::new("Volume")
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index 913e421e24..28aa3d8f2c 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -149,10 +149,10 @@ impl PlayState for SessionState {
                 return PlayStateResult::Pop;
             }
 
-            // Maintain global state
+            // Maintain global state.
             global_state.maintain();
 
-            // extract HUD events ensuring the client borrow gets dropped
+            // Extract HUD events ensuring the client borrow gets dropped.
             let hud_events = self.hud.maintain(
                 &self.client.borrow(),
                 global_state,
@@ -170,6 +170,7 @@ impl PlayState for SessionState {
                 },
                 &self.scene.camera(),
             );
+
             // Maintain the UI.
             for event in hud_events {
                 match event {
@@ -184,6 +185,15 @@ impl PlayState for SessionState {
                     HudEvent::Quit => {
                         return PlayStateResult::Shutdown;
                     }
+                    HudEvent::AdjustMousePan(sensitivity) => {
+                        global_state.settings.gameplay.pan_sensitivity = sensitivity as f32 / 100.0;
+                        global_state.settings.save_to_file();
+                    }
+                    HudEvent::AdjustMouseZoom(sensitivity) => {
+                        global_state.settings.gameplay.zoom_sensitivity =
+                            sensitivity as f32 / 100.0;
+                        global_state.settings.save_to_file();
+                    }
                     HudEvent::AdjustViewDistance(view_distance) => {
                         self.client.borrow_mut().set_view_distance(view_distance);
 
diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index b9d89db96e..488413ef02 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -4,11 +4,12 @@ use glutin::{MouseButton, VirtualKeyCode};
 use serde_derive::{Deserialize, Serialize};
 use std::{fs, io::prelude::*, path::PathBuf};
 
-/// `Settings` contains everything that can be configured in the Settings.toml file.
+/// `Settings` contains everything that can be configured in the settings.ron file.
 #[derive(Clone, Debug, Serialize, Deserialize)]
 #[serde(default)]
 pub struct Settings {
     pub controls: ControlSettings,
+    pub gameplay: GameplaySettings,
     pub networking: NetworkingSettings,
     pub log: Log,
     pub graphics: GraphicsSettings,
@@ -41,9 +42,14 @@ pub struct ControlSettings {
     pub fullscreen: KeyMouse,
     pub screenshot: KeyMouse,
     pub toggle_ingame_ui: KeyMouse,
+    pub attack: KeyMouse,
+}
+
+/// `GameplaySettings` contains sensitivity and gameplay options.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct GameplaySettings {
     pub pan_sensitivity: f32,
     pub zoom_sensitivity: f32,
-    pub attack: KeyMouse,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -100,9 +106,11 @@ impl Default for Settings {
                 fullscreen: KeyMouse::Key(VirtualKeyCode::F11),
                 screenshot: KeyMouse::Key(VirtualKeyCode::F4),
                 toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
+                attack: KeyMouse::Mouse(MouseButton::Left),
+            },
+            gameplay: GameplaySettings {
                 pan_sensitivity: 1.0,
                 zoom_sensitivity: 1.0,
-                attack: KeyMouse::Mouse(MouseButton::Left),
             },
             networking: NetworkingSettings {
                 username: "Username".to_string(),
diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs
index 967dc2a0af..de1f02d94f 100644
--- a/voxygen/src/window.rs
+++ b/voxygen/src/window.rs
@@ -54,7 +54,7 @@ pub enum Event {
     InputUpdate(GameInput, bool),
     /// Event that the ui uses.
     Ui(ui::Event),
-    // The view distance has been changed
+    /// The view distance has changed.
     ViewDistanceChanged(u32),
     /// Game settings have changed.
     SettingsChanged,
@@ -140,8 +140,8 @@ impl Window {
             renderer: Renderer::new(device, factory, win_color_view, win_depth_view)?,
             window,
             cursor_grabbed: false,
-            pan_sensitivity: settings.controls.pan_sensitivity,
-            zoom_sensitivity: settings.controls.zoom_sensitivity,
+            pan_sensitivity: settings.gameplay.pan_sensitivity,
+            zoom_sensitivity: settings.gameplay.zoom_sensitivity,
             fullscreen: false,
             needs_refresh_resize: false,
             key_map,

From 41f3cd58039102ca61831445c99838ed5b60a5c6 Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Thu, 6 Jun 2019 13:42:13 -0400
Subject: [PATCH 02/10] Changes setting type to u32, enforces live in-game
 updates to setting.

---
 voxygen/src/hud/settings_window.rs | 39 ++++++++++++++++++++++--------
 voxygen/src/session.rs             |  7 +++---
 voxygen/src/settings.rs            |  8 +++---
 voxygen/src/window.rs              | 10 ++++----
 4 files changed, 42 insertions(+), 22 deletions(-)

diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index a0533f0938..0acea547e6 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -32,9 +32,11 @@ widget_ids! {
         inventory_test_button,
         inventory_test_button_label,
         mouse_pan_slider,
-        mouse_pan_text,
+        mouse_pan_label,
+        mouse_pan_value,
         mouse_zoom_slider,
-        mouse_zoom_text,
+        mouse_zoom_label,
+        mouse_zoom_value,
         settings_bg,
         sound,
         test,
@@ -291,22 +293,25 @@ impl<'a> Widget for SettingsWindow<'a> {
 
         // Contents
         if let SettingsTab::Gameplay = self.show.settings_tab {
+            let display_pan = self.global_state.settings.gameplay.pan_sensitivity;
+            let display_zoom = self.global_state.settings.gameplay.zoom_sensitivity;
+
             Text::new("Pan Sensitivity")
                 .top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
                 .font_size(14)
                 .font_id(self.fonts.opensans)
                 .color(TEXT_COLOR)
-                .set(state.ids.mouse_pan_text, ui);
+                .set(state.ids.mouse_pan_label, ui);
 
             if let Some(new_val) = ImageSlider::discrete(
-                (self.global_state.settings.gameplay.pan_sensitivity * 100.0) as u32,
+                display_pan,
                 1,
                 200,
                 self.imgs.slider_indicator,
                 self.imgs.slider,
             )
-            .w_h(208.0, 22.0)
-            .down_from(state.ids.mouse_pan_text, 10.0)
+            .w_h(550.0, 22.0)
+            .down_from(state.ids.mouse_pan_label, 10.0)
             .track_breadth(30.0)
             .slider_length(10.0)
             .pad_track((5.0, 5.0))
@@ -315,22 +320,29 @@ impl<'a> Widget for SettingsWindow<'a> {
                 events.push(Event::AdjustMousePan(new_val));
             }
 
+            Text::new(&format!("{}", display_pan))
+                .right_from(state.ids.mouse_pan_slider, 8.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.mouse_pan_value, ui);
+
             Text::new("Zoom Sensitivity")
                 .down_from(state.ids.mouse_pan_slider, 10.0)
                 .font_size(14)
                 .font_id(self.fonts.opensans)
                 .color(TEXT_COLOR)
-                .set(state.ids.mouse_zoom_text, ui);
+                .set(state.ids.mouse_zoom_label, ui);
 
             if let Some(new_val) = ImageSlider::discrete(
-                (self.global_state.settings.gameplay.zoom_sensitivity * 100.0) as u32,
+                display_zoom,
                 1,
                 200,
                 self.imgs.slider_indicator,
                 self.imgs.slider,
             )
-            .w_h(208.0, 22.0)
-            .down_from(state.ids.mouse_zoom_text, 10.0)
+            .w_h(550.0, 22.0)
+            .down_from(state.ids.mouse_zoom_label, 10.0)
             .track_breadth(30.0)
             .slider_length(10.0)
             .pad_track((5.0, 5.0))
@@ -338,6 +350,13 @@ impl<'a> Widget for SettingsWindow<'a> {
             {
                 events.push(Event::AdjustMouseZoom(new_val));
             }
+
+            Text::new(&format!("{}", display_zoom))
+                .right_from(state.ids.mouse_zoom_slider, 8.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.mouse_zoom_value, ui);
         }
 
         // 3) Controls Tab --------------------------------
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index 28aa3d8f2c..253737924e 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -186,12 +186,13 @@ impl PlayState for SessionState {
                         return PlayStateResult::Shutdown;
                     }
                     HudEvent::AdjustMousePan(sensitivity) => {
-                        global_state.settings.gameplay.pan_sensitivity = sensitivity as f32 / 100.0;
+                        global_state.window.pan_sensitivity = sensitivity;
+                        global_state.settings.gameplay.pan_sensitivity = sensitivity;
                         global_state.settings.save_to_file();
                     }
                     HudEvent::AdjustMouseZoom(sensitivity) => {
-                        global_state.settings.gameplay.zoom_sensitivity =
-                            sensitivity as f32 / 100.0;
+                        global_state.window.zoom_sensitivity = sensitivity;
+                        global_state.settings.gameplay.zoom_sensitivity = sensitivity;
                         global_state.settings.save_to_file();
                     }
                     HudEvent::AdjustViewDistance(view_distance) => {
diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index 488413ef02..63e1cccfa6 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -48,8 +48,8 @@ pub struct ControlSettings {
 /// `GameplaySettings` contains sensitivity and gameplay options.
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct GameplaySettings {
-    pub pan_sensitivity: f32,
-    pub zoom_sensitivity: f32,
+    pub pan_sensitivity: u32,
+    pub zoom_sensitivity: u32,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -109,8 +109,8 @@ impl Default for Settings {
                 attack: KeyMouse::Mouse(MouseButton::Left),
             },
             gameplay: GameplaySettings {
-                pan_sensitivity: 1.0,
-                zoom_sensitivity: 1.0,
+                pan_sensitivity: 100,
+                zoom_sensitivity: 100,
             },
             networking: NetworkingSettings {
                 username: "Username".to_string(),
diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs
index de1f02d94f..fd158a4708 100644
--- a/voxygen/src/window.rs
+++ b/voxygen/src/window.rs
@@ -71,8 +71,8 @@ pub struct Window {
     renderer: Renderer,
     window: glutin::GlWindow,
     cursor_grabbed: bool,
-    pub pan_sensitivity: f32,
-    pub zoom_sensitivity: f32,
+    pub pan_sensitivity: u32,
+    pub zoom_sensitivity: u32,
     fullscreen: bool,
     needs_refresh_resize: bool,
     key_map: HashMap<KeyMouse, GameInput>,
@@ -230,14 +230,14 @@ impl Window {
                     glutin::DeviceEvent::MouseMotion {
                         delta: (dx, dy), ..
                     } if cursor_grabbed && *focused => events.push(Event::CursorPan(Vec2::new(
-                        dx as f32 * pan_sensitivity,
-                        dy as f32 * pan_sensitivity,
+                        dx as f32 * (pan_sensitivity as f32 / 100.0),
+                        dy as f32 * (pan_sensitivity as f32 / 100.0),
                     ))),
                     glutin::DeviceEvent::MouseWheel {
                         delta: glutin::MouseScrollDelta::LineDelta(_x, y),
                         ..
                     } if cursor_grabbed && *focused => {
-                        events.push(Event::Zoom(y as f32 * zoom_sensitivity))
+                        events.push(Event::Zoom(y * (zoom_sensitivity as f32 / 100.0)))
                     }
                     _ => {}
                 },

From 8a19a6f2a365fdc90b5f6fb8c3fe0a2823c4a21d Mon Sep 17 00:00:00 2001
From: Monty Marz <m.marzouq@gmx.de>
Date: Thu, 6 Jun 2019 17:54:11 +0000
Subject: [PATCH 03/10] Configurable max fps

---
 voxygen/src/hud/mod.rs             |  4 ++++
 voxygen/src/hud/settings_window.rs | 29 ++++++++++++++++++++++++++++-
 voxygen/src/session.rs             |  2 +-
 3 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index e349a8c78c..a035d17316 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -113,6 +113,7 @@ pub enum Event {
     AdjustViewDistance(u32),
     AdjustVolume(f32),
     ChangeAudioDevice(String),
+    MaximumFPS(u32),
     CharacterSelection,
     Logout,
     Quit,
@@ -581,6 +582,9 @@ impl Hud {
                     settings_window::Event::AdjustVolume(volume) => {
                         events.push(Event::AdjustVolume(volume));
                     }
+                    settings_window::Event::MaximumFPS(max_fps) => {
+                        events.push(Event::MaximumFPS(max_fps));
+                    }
                     settings_window::Event::ChangeAudioDevice(name) => {
                         events.push(Event::ChangeAudioDevice(name));
                     }
diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index 0acea547e6..d6e661e06f 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -43,6 +43,9 @@ widget_ids! {
         video,
         vd_slider,
         vd_slider_text,
+        max_fps_slider,
+        max_fps_text,
+        max_fps_value,
         audio_volume_slider,
         audio_volume_text,
         audio_device_list,
@@ -103,6 +106,7 @@ pub enum Event {
     AdjustViewDistance(u32),
     AdjustVolume(f32),
     ChangeAudioDevice(String),
+    MaximumFPS(u32),
 }
 
 impl<'a> Widget for SettingsWindow<'a> {
@@ -586,7 +590,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                 self.imgs.slider,
             )
             .w_h(104.0, 22.0)
-            .down_from(state.ids.vd_slider_text, 10.0)
+            .down_from(state.ids.vd_slider_text, 8.0)
             .track_breadth(12.0)
             .slider_length(10.0)
             .pad_track((5.0, 5.0))
@@ -594,6 +598,29 @@ impl<'a> Widget for SettingsWindow<'a> {
             {
                 events.push(Event::AdjustViewDistance(new_val));
             }
+            Text::new("Maximum FPS")
+                .top_left_with_margins_on(state.ids.settings_content, 60.0, 10.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.max_fps_text, ui);
+
+            if let Some(new_val) = ImageSlider::discrete(
+                self.global_state.settings.graphics.view_distance,
+                50,
+                150,
+                self.imgs.slider_indicator,
+                self.imgs.slider,
+            )
+            .w_h(104.0, 22.0)
+            .down_from(state.ids.max_fps_text, 8.0)
+            .track_breadth(12.0)
+            .slider_length(10.0)
+            .pad_track((5.0, 5.0))
+            .set(state.ids.max_fps_slider, ui)
+            {
+                events.push(Event::MaximumFPS(new_val));
+            }
         }
 
         // 5) Sound Tab -----------------------------------
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index 253737924e..ea1d01d3d3 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -13,7 +13,7 @@ use log::{error, warn};
 use std::{cell::RefCell, rc::Rc, time::Duration};
 use vek::*;
 
-const FPS: u64 = 60;
+const FPS: u64 = 1000;
 
 pub struct SessionState {
     scene: Scene,

From 527f33a7784d93969f3a722bb0eacef8b9802b8c Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Thu, 6 Jun 2019 15:11:39 -0400
Subject: [PATCH 04/10] Adds an FPS setting and slider.

---
 chat-cli/src/main.rs                   |  4 +--
 voxygen/src/hud/mod.rs                 |  4 +--
 voxygen/src/hud/settings_window.rs     | 47 +++++++++++++++++++++-----
 voxygen/src/menu/char_selection/mod.rs |  6 ++--
 voxygen/src/menu/main/mod.rs           |  6 ++--
 voxygen/src/session.rs                 | 20 ++++++++---
 voxygen/src/settings.rs                |  9 +++--
 7 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs
index f68a5b47e9..bd0fb66bc2 100644
--- a/chat-cli/src/main.rs
+++ b/chat-cli/src/main.rs
@@ -3,7 +3,7 @@ use common::{clock::Clock, comp};
 use log::{error, info};
 use std::time::Duration;
 
-const FPS: u64 = 60;
+const TICK_RATE: u64 = 10; // Low value is okay, just reading messages.
 
 fn main() {
     // Initialize logging.
@@ -48,6 +48,6 @@ fn main() {
         client.cleanup();
 
         // Wait for the next tick.
-        clock.tick(Duration::from_millis(1000 / FPS));
+        clock.tick(Duration::from_millis(1000 / TICK_RATE));
     }
 }
diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index a035d17316..ccafa92385 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -113,7 +113,7 @@ pub enum Event {
     AdjustViewDistance(u32),
     AdjustVolume(f32),
     ChangeAudioDevice(String),
-    MaximumFPS(u32),
+    ChangeMaxFPS(u32),
     CharacterSelection,
     Logout,
     Quit,
@@ -583,7 +583,7 @@ impl Hud {
                         events.push(Event::AdjustVolume(volume));
                     }
                     settings_window::Event::MaximumFPS(max_fps) => {
-                        events.push(Event::MaximumFPS(max_fps));
+                        events.push(Event::ChangeMaxFPS(max_fps));
                     }
                     settings_window::Event::ChangeAudioDevice(name) => {
                         events.push(Event::ChangeAudioDevice(name));
diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index d6e661e06f..c4fcf0b1b6 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -8,6 +8,9 @@ use conrod_core::{
     widget::{self, Button, DropDownList, Image, Rectangle, Scrollbar, Text},
     widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
 };
+
+const FPS_CHOICES: [u32; 11] = [1, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500];
+
 widget_ids! {
     struct Ids {
         settings_content,
@@ -42,7 +45,8 @@ widget_ids! {
         test,
         video,
         vd_slider,
-        vd_slider_text,
+        vd_text,
+        vd_value,
         max_fps_slider,
         max_fps_text,
         max_fps_value,
@@ -300,6 +304,7 @@ impl<'a> Widget for SettingsWindow<'a> {
             let display_pan = self.global_state.settings.gameplay.pan_sensitivity;
             let display_zoom = self.global_state.settings.gameplay.zoom_sensitivity;
 
+            // Mouse Pan Sensitivity
             Text::new("Pan Sensitivity")
                 .top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
                 .font_size(14)
@@ -331,6 +336,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                 .color(TEXT_COLOR)
                 .set(state.ids.mouse_pan_value, ui);
 
+            // Mouse Zoom Sensitivity
             Text::new("Zoom Sensitivity")
                 .down_from(state.ids.mouse_pan_slider, 10.0)
                 .font_size(14)
@@ -575,12 +581,13 @@ impl<'a> Widget for SettingsWindow<'a> {
 
         // Contents
         if let SettingsTab::Video = self.show.settings_tab {
+            // View Distance
             Text::new("View Distance")
                 .top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0)
                 .font_size(14)
                 .font_id(self.fonts.opensans)
                 .color(TEXT_COLOR)
-                .set(state.ids.vd_slider_text, ui);
+                .set(state.ids.vd_text, ui);
 
             if let Some(new_val) = ImageSlider::discrete(
                 self.global_state.settings.graphics.view_distance,
@@ -590,7 +597,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                 self.imgs.slider,
             )
             .w_h(104.0, 22.0)
-            .down_from(state.ids.vd_slider_text, 8.0)
+            .down_from(state.ids.vd_text, 8.0)
             .track_breadth(12.0)
             .slider_length(10.0)
             .pad_track((5.0, 5.0))
@@ -598,17 +605,32 @@ impl<'a> Widget for SettingsWindow<'a> {
             {
                 events.push(Event::AdjustViewDistance(new_val));
             }
+
+            Text::new(&format!(
+                "{}",
+                self.global_state.settings.graphics.view_distance
+            ))
+            .right_from(state.ids.vd_slider, 8.0)
+            .font_size(14)
+            .font_id(self.fonts.opensans)
+            .color(TEXT_COLOR)
+            .set(state.ids.vd_value, ui);
+
+            // Max FPS
             Text::new("Maximum FPS")
-                .top_left_with_margins_on(state.ids.settings_content, 60.0, 10.0)
+                .down_from(state.ids.vd_slider, 10.0)
                 .font_size(14)
                 .font_id(self.fonts.opensans)
                 .color(TEXT_COLOR)
                 .set(state.ids.max_fps_text, ui);
 
-            if let Some(new_val) = ImageSlider::discrete(
-                self.global_state.settings.graphics.view_distance,
-                50,
-                150,
+            if let Some(which) = ImageSlider::discrete(
+                FPS_CHOICES
+                    .iter()
+                    .position(|&x| x == self.global_state.settings.graphics.max_fps)
+                    .unwrap_or(0),
+                1,
+                10,
                 self.imgs.slider_indicator,
                 self.imgs.slider,
             )
@@ -619,8 +641,15 @@ impl<'a> Widget for SettingsWindow<'a> {
             .pad_track((5.0, 5.0))
             .set(state.ids.max_fps_slider, ui)
             {
-                events.push(Event::MaximumFPS(new_val));
+                events.push(Event::MaximumFPS(FPS_CHOICES[which]));
             }
+
+            Text::new(&format!("{}", self.global_state.settings.graphics.max_fps))
+                .right_from(state.ids.max_fps_slider, 8.0)
+                .font_size(14)
+                .font_id(self.fonts.opensans)
+                .color(TEXT_COLOR)
+                .set(state.ids.max_fps_value, ui);
         }
 
         // 5) Sound Tab -----------------------------------
diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs
index 2192ce711d..e8e34a3247 100644
--- a/voxygen/src/menu/char_selection/mod.rs
+++ b/voxygen/src/menu/char_selection/mod.rs
@@ -14,8 +14,6 @@ use std::{cell::RefCell, rc::Rc, time::Duration};
 use ui::CharSelectionUi;
 use vek::*;
 
-const FPS: u64 = 60;
-
 pub struct CharSelectionState {
     char_selection_ui: CharSelectionUi,
     client: Rc<RefCell<Client>>,
@@ -125,7 +123,9 @@ impl PlayState for CharSelectionState {
                 .expect("Failed to swap window buffers");
 
             // Wait for the next tick.
-            clock.tick(Duration::from_millis(1000 / FPS));
+            clock.tick(Duration::from_millis(
+                1000 / (global_state.settings.graphics.max_fps as u64),
+            ));
 
             current_client_state = self.client.borrow().get_client_state();
         }
diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs
index 0fe4faf702..59c3cfb38c 100644
--- a/voxygen/src/menu/main/mod.rs
+++ b/voxygen/src/menu/main/mod.rs
@@ -12,8 +12,6 @@ use std::time::Duration;
 use ui::{Event as MainMenuEvent, MainMenuUi};
 use vek::*;
 
-const FPS: u64 = 60;
-
 pub struct MainMenuState {
     main_menu_ui: MainMenuUi,
 }
@@ -133,7 +131,9 @@ impl PlayState for MainMenuState {
                 .expect("Failed to swap window buffers!");
 
             // Wait for the next tick
-            clock.tick(Duration::from_millis(1000 / FPS));
+            clock.tick(Duration::from_millis(
+                1000 / (global_state.settings.graphics.max_fps as u64),
+            ));
         }
     }
 
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index ea1d01d3d3..6e20d3e725 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -13,8 +13,6 @@ use log::{error, warn};
 use std::{cell::RefCell, rc::Rc, time::Duration};
 use vek::*;
 
-const FPS: u64 = 1000;
-
 pub struct SessionState {
     scene: Scene,
     client: Rc<RefCell<Client>>,
@@ -188,12 +186,16 @@ impl PlayState for SessionState {
                     HudEvent::AdjustMousePan(sensitivity) => {
                         global_state.window.pan_sensitivity = sensitivity;
                         global_state.settings.gameplay.pan_sensitivity = sensitivity;
-                        global_state.settings.save_to_file();
+                        if let Err(err) = global_state.settings.save_to_file() {
+                            warn!("Failed to save settings: {:?}", err);
+                        }
                     }
                     HudEvent::AdjustMouseZoom(sensitivity) => {
                         global_state.window.zoom_sensitivity = sensitivity;
                         global_state.settings.gameplay.zoom_sensitivity = sensitivity;
-                        global_state.settings.save_to_file();
+                        if let Err(err) = global_state.settings.save_to_file() {
+                            warn!("Failed to save settings: {:?}", err);
+                        }
                     }
                     HudEvent::AdjustViewDistance(view_distance) => {
                         self.client.borrow_mut().set_view_distance(view_distance);
@@ -219,6 +221,12 @@ impl PlayState for SessionState {
                             warn!("Failed to save settings!\n{:?}", err);
                         }
                     }
+                    HudEvent::ChangeMaxFPS(fps) => {
+                        global_state.settings.graphics.max_fps = fps;
+                        if let Err(err) = global_state.settings.save_to_file() {
+                            warn!("Failed to save settings!\n{:?}", err);
+                        }
+                    }
                 }
             }
 
@@ -236,7 +244,9 @@ impl PlayState for SessionState {
                 .expect("Failed to swap window buffers!");
 
             // Wait for the next tick.
-            clock.tick(Duration::from_millis(1000 / FPS));
+            clock.tick(Duration::from_millis(
+                1000 / global_state.settings.graphics.max_fps as u64,
+            ));
 
             // Clean things up after the tick.
             self.cleanup();
diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index 63e1cccfa6..635cbf0d39 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -64,12 +64,14 @@ pub struct Log {
     pub file: PathBuf,
 }
 
+/// `GraphicsSettings` contains settings related to framerate and in-game visuals.
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct GraphicsSettings {
     pub view_distance: u32,
+    pub max_fps: u32,
 }
 
-/// AudioSettings controls the volume of different audio subsystems and which
+/// `AudioSettings` controls the volume of different audio subsystems and which
 /// device is used.
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct AudioSettings {
@@ -120,7 +122,10 @@ impl Default for Settings {
             log: Log {
                 file: "voxygen.log".into(),
             },
-            graphics: GraphicsSettings { view_distance: 5 },
+            graphics: GraphicsSettings {
+                view_distance: 5,
+                max_fps: 60,
+            },
             audio: AudioSettings {
                 music_volume: 0.5,
                 sfx_volume: 0.5,

From 24f7323d04d1236bdec8ac4ba567be7c688a870e Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Thu, 6 Jun 2019 15:23:51 -0400
Subject: [PATCH 05/10] Allows selection of an unstable 1 FPS option.

---
 voxygen/src/hud/settings_window.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index c4fcf0b1b6..f725a95b15 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -629,7 +629,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                     .iter()
                     .position(|&x| x == self.global_state.settings.graphics.max_fps)
                     .unwrap_or(0),
-                1,
+                0,
                 10,
                 self.imgs.slider_indicator,
                 self.imgs.slider,

From e18947eed10bd7ce421f1e114fc0f666ddfa2cde Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Thu, 6 Jun 2019 15:26:01 -0400
Subject: [PATCH 06/10] Changes the fallback slider value to a safe one.

---
 voxygen/src/hud/settings_window.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index f725a95b15..8413f19482 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -628,7 +628,7 @@ impl<'a> Widget for SettingsWindow<'a> {
                 FPS_CHOICES
                     .iter()
                     .position(|&x| x == self.global_state.settings.graphics.max_fps)
-                    .unwrap_or(0),
+                    .unwrap_or(5),
                 0,
                 10,
                 self.imgs.slider_indicator,

From 026ac3297257a9ee3eaf2c1d965f575a0906a066 Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Thu, 6 Jun 2019 15:32:47 -0400
Subject: [PATCH 07/10] Amends the suggested 1 FPS option to 15 to prevent
 accidental session crashes.

---
 voxygen/src/hud/settings_window.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs
index 8413f19482..1f7083e04f 100644
--- a/voxygen/src/hud/settings_window.rs
+++ b/voxygen/src/hud/settings_window.rs
@@ -9,7 +9,7 @@ use conrod_core::{
     widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
 };
 
-const FPS_CHOICES: [u32; 11] = [1, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500];
+const FPS_CHOICES: [u32; 11] = [15, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500];
 
 widget_ids! {
     struct Ids {

From b180f89104f18c68212bf3aebae4666cb706ba5e Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Sat, 8 Jun 2019 19:35:23 -0400
Subject: [PATCH 08/10] Enhances deserialization so settings which are missing
 are added with default values.

---
 voxygen/src/settings.rs | 177 +++++++++++++++++++++++++---------------
 1 file changed, 110 insertions(+), 67 deletions(-)

diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index 635cbf0d39..0e8d73cc18 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -4,21 +4,9 @@ use glutin::{MouseButton, VirtualKeyCode};
 use serde_derive::{Deserialize, Serialize};
 use std::{fs, io::prelude::*, path::PathBuf};
 
-/// `Settings` contains everything that can be configured in the settings.ron file.
-#[derive(Clone, Debug, Serialize, Deserialize)]
-#[serde(default)]
-pub struct Settings {
-    pub controls: ControlSettings,
-    pub gameplay: GameplaySettings,
-    pub networking: NetworkingSettings,
-    pub log: Log,
-    pub graphics: GraphicsSettings,
-    pub audio: AudioSettings,
-    pub show_disclaimer: bool,
-}
-
 /// `ControlSettings` contains keybindings.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct ControlSettings {
     pub toggle_cursor: KeyMouse,
     pub escape: KeyMouse,
@@ -45,35 +33,108 @@ pub struct ControlSettings {
     pub attack: KeyMouse,
 }
 
+impl Default for ControlSettings {
+    fn default() -> Self {
+        Self {
+            toggle_cursor: KeyMouse::Key(VirtualKeyCode::Tab),
+            escape: KeyMouse::Key(VirtualKeyCode::Escape),
+            enter: KeyMouse::Key(VirtualKeyCode::Return),
+            move_forward: KeyMouse::Key(VirtualKeyCode::W),
+            move_left: KeyMouse::Key(VirtualKeyCode::A),
+            move_back: KeyMouse::Key(VirtualKeyCode::S),
+            move_right: KeyMouse::Key(VirtualKeyCode::D),
+            jump: KeyMouse::Key(VirtualKeyCode::Space),
+            glide: KeyMouse::Key(VirtualKeyCode::LShift),
+            map: KeyMouse::Key(VirtualKeyCode::M),
+            bag: KeyMouse::Key(VirtualKeyCode::B),
+            quest_log: KeyMouse::Key(VirtualKeyCode::L),
+            character_window: KeyMouse::Key(VirtualKeyCode::C),
+            social: KeyMouse::Key(VirtualKeyCode::O),
+            spellbook: KeyMouse::Key(VirtualKeyCode::P),
+            settings: KeyMouse::Key(VirtualKeyCode::N),
+            help: KeyMouse::Key(VirtualKeyCode::F1),
+            toggle_interface: KeyMouse::Key(VirtualKeyCode::F2),
+            toggle_debug: KeyMouse::Key(VirtualKeyCode::F3),
+            fullscreen: KeyMouse::Key(VirtualKeyCode::F11),
+            screenshot: KeyMouse::Key(VirtualKeyCode::F4),
+            toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
+            attack: KeyMouse::Mouse(MouseButton::Left),
+        }
+    }
+}
+
 /// `GameplaySettings` contains sensitivity and gameplay options.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct GameplaySettings {
     pub pan_sensitivity: u32,
     pub zoom_sensitivity: u32,
 }
 
+impl Default for GameplaySettings {
+    fn default() -> Self {
+        Self {
+            pan_sensitivity: 100,
+            zoom_sensitivity: 100,
+        }
+    }
+}
+
+/// `NetworkingSettings` stores server and networking settings.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct NetworkingSettings {
     pub username: String,
     pub servers: Vec<String>,
     pub default_server: usize,
 }
 
+impl Default for NetworkingSettings {
+    fn default() -> Self {
+        Self {
+            username: "Username".to_string(),
+            servers: vec!["server.veloren.net".to_string()],
+            default_server: 0,
+        }
+    }
+}
+
+/// `Log` stores the name to the log file.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct Log {
     pub file: PathBuf,
 }
 
+impl Default for Log {
+    fn default() -> Self {
+        Self {
+            file: "voxygen.log".into(),
+        }
+    }
+}
+
 /// `GraphicsSettings` contains settings related to framerate and in-game visuals.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct GraphicsSettings {
     pub view_distance: u32,
     pub max_fps: u32,
 }
 
+impl Default for GraphicsSettings {
+    fn default() -> Self {
+        Self {
+            view_distance: 5,
+            max_fps: 60,
+        }
+    }
+}
+
 /// `AudioSettings` controls the volume of different audio subsystems and which
 /// device is used.
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 pub struct AudioSettings {
     pub music_volume: f32,
     pub sfx_volume: f32,
@@ -82,55 +143,38 @@ pub struct AudioSettings {
     pub audio_device: Option<String>,
 }
 
+impl Default for AudioSettings {
+    fn default() -> Self {
+        Self {
+            music_volume: 0.5,
+            sfx_volume: 0.5,
+            audio_device: None,
+        }
+    }
+}
+
+/// `Settings` contains everything that can be configured in the settings.ron file.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
+pub struct Settings {
+    pub controls: ControlSettings,
+    pub gameplay: GameplaySettings,
+    pub networking: NetworkingSettings,
+    pub log: Log,
+    pub graphics: GraphicsSettings,
+    pub audio: AudioSettings,
+    pub show_disclaimer: bool,
+}
+
 impl Default for Settings {
     fn default() -> Self {
         Settings {
-            controls: ControlSettings {
-                toggle_cursor: KeyMouse::Key(VirtualKeyCode::Tab),
-                escape: KeyMouse::Key(VirtualKeyCode::Escape),
-                enter: KeyMouse::Key(VirtualKeyCode::Return),
-                move_forward: KeyMouse::Key(VirtualKeyCode::W),
-                move_left: KeyMouse::Key(VirtualKeyCode::A),
-                move_back: KeyMouse::Key(VirtualKeyCode::S),
-                move_right: KeyMouse::Key(VirtualKeyCode::D),
-                jump: KeyMouse::Key(VirtualKeyCode::Space),
-                glide: KeyMouse::Key(VirtualKeyCode::LShift),
-                map: KeyMouse::Key(VirtualKeyCode::M),
-                bag: KeyMouse::Key(VirtualKeyCode::B),
-                quest_log: KeyMouse::Key(VirtualKeyCode::L),
-                character_window: KeyMouse::Key(VirtualKeyCode::C),
-                social: KeyMouse::Key(VirtualKeyCode::O),
-                spellbook: KeyMouse::Key(VirtualKeyCode::P),
-                settings: KeyMouse::Key(VirtualKeyCode::N),
-                help: KeyMouse::Key(VirtualKeyCode::F1),
-                toggle_interface: KeyMouse::Key(VirtualKeyCode::F2),
-                toggle_debug: KeyMouse::Key(VirtualKeyCode::F3),
-                fullscreen: KeyMouse::Key(VirtualKeyCode::F11),
-                screenshot: KeyMouse::Key(VirtualKeyCode::F4),
-                toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
-                attack: KeyMouse::Mouse(MouseButton::Left),
-            },
-            gameplay: GameplaySettings {
-                pan_sensitivity: 100,
-                zoom_sensitivity: 100,
-            },
-            networking: NetworkingSettings {
-                username: "Username".to_string(),
-                servers: vec!["server.veloren.net".to_string()],
-                default_server: 0,
-            },
-            log: Log {
-                file: "voxygen.log".into(),
-            },
-            graphics: GraphicsSettings {
-                view_distance: 5,
-                max_fps: 60,
-            },
-            audio: AudioSettings {
-                music_volume: 0.5,
-                sfx_volume: 0.5,
-                audio_device: None,
-            },
+            controls: ControlSettings::default(),
+            gameplay: GameplaySettings::default(),
+            networking: NetworkingSettings::default(),
+            log: Log::default(),
+            graphics: GraphicsSettings::default(),
+            audio: AudioSettings::default(),
             show_disclaimer: true,
         }
     }
@@ -150,23 +194,22 @@ impl Settings {
 
     pub fn save_to_file(&self) -> std::io::Result<()> {
         let path = Settings::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(())
     }
 
     fn get_settings_path() -> PathBuf {
-        let proj_dirs =
-            ProjectDirs::from("net", "veloren", "voxygen").expect("No home directory defined!");
-        let path = proj_dirs.config_dir();
-        path.join("settings");
-        let path = path.with_extension("ron");
-        path
+        let proj_dirs = ProjectDirs::from("net", "veloren", "voxygen")
+            .expect("System's $HOME directory path not found!");
+        proj_dirs
+            .config_dir()
+            .join("settings")
+            .with_extension("ron")
     }
 }

From ea207b911a21fbcd4fe475274535bec727355993 Mon Sep 17 00:00:00 2001
From: Cody <Mason.Cody.M@gmail.com>
Date: Sat, 8 Jun 2019 19:49:48 -0400
Subject: [PATCH 09/10] Amends a variable name.

---
 chat-cli/src/main.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs
index bd0fb66bc2..fdf8607bf9 100644
--- a/chat-cli/src/main.rs
+++ b/chat-cli/src/main.rs
@@ -3,7 +3,7 @@ use common::{clock::Clock, comp};
 use log::{error, info};
 use std::time::Duration;
 
-const TICK_RATE: u64 = 10; // Low value is okay, just reading messages.
+const TPS: u64 = 10; // Low value is okay, just reading messages.
 
 fn main() {
     // Initialize logging.
@@ -48,6 +48,6 @@ fn main() {
         client.cleanup();
 
         // Wait for the next tick.
-        clock.tick(Duration::from_millis(1000 / TICK_RATE));
+        clock.tick(Duration::from_millis(1000 / TPS));
     }
 }

From 8dcf030bd8338265b6c7642873a62d3a25579ce6 Mon Sep 17 00:00:00 2001
From: Imbris <hank.aa7@gmail.com>
Date: Sun, 9 Jun 2019 14:14:02 -0400
Subject: [PATCH 10/10] Use Instant and running compensation in clock

---
 common/src/clock.rs | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/common/src/clock.rs b/common/src/clock.rs
index f6aa8b268e..4fa250523b 100644
--- a/common/src/clock.rs
+++ b/common/src/clock.rs
@@ -1,23 +1,25 @@
 use std::{
     thread,
-    time::{Duration, SystemTime},
+    time::{Duration, Instant},
 };
 
 const CLOCK_SMOOTHING: f64 = 0.9;
 
 pub struct Clock {
-    last_sys_time: SystemTime,
+    last_sys_time: Instant,
     last_delta: Option<Duration>,
     running_tps_average: f64,
+    compensation: f64,
 }
 
 impl Clock {
     #[allow(dead_code)]
     pub fn new() -> Self {
         Self {
-            last_sys_time: SystemTime::now(),
+            last_sys_time: Instant::now(),
             last_delta: None,
             running_tps_average: 0.0,
+            compensation: 1.0,
         }
     }
 
@@ -38,27 +40,25 @@ impl Clock {
 
     #[allow(dead_code)]
     pub fn tick(&mut self, tgt: Duration) {
-        let delta = SystemTime::now()
-            .duration_since(self.last_sys_time)
-            .expect("Time went backwards!");
+        let delta = Instant::now().duration_since(self.last_sys_time);
 
         // Attempt to sleep to fill the gap.
         if let Some(sleep_dur) = tgt.checked_sub(delta) {
-            let adjustment = if self.running_tps_average == 0.0 {
-                1.0
-            } else {
-                tgt.as_secs_f64() / self.running_tps_average
-            };
-            thread::sleep(Duration::from_secs_f64(
-                sleep_dur.as_secs_f64() * adjustment,
-            ));
+            if self.running_tps_average != 0.0 {
+                self.compensation =
+                    (self.compensation + (tgt.as_secs_f64() / self.running_tps_average) - 1.0)
+                        .max(0.0)
+            }
+
+            let sleep_secs = sleep_dur.as_secs_f64() * self.compensation;
+            if sleep_secs > 0.0 {
+                thread::sleep(Duration::from_secs_f64(sleep_secs));
+            }
         }
 
-        let delta = SystemTime::now()
-            .duration_since(self.last_sys_time)
-            .expect("Time went backwards!");
+        let delta = Instant::now().duration_since(self.last_sys_time);
 
-        self.last_sys_time = SystemTime::now();
+        self.last_sys_time = Instant::now();
         self.last_delta = Some(delta);
         self.running_tps_average = if self.running_tps_average == 0.0 {
             delta.as_secs_f64()