diff --git a/client/src/lib.rs b/client/src/lib.rs index 51cc93fd67..5354b8f47a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -248,6 +248,7 @@ pub struct Client { tick: u64, state: State, + server_view_distance_limit: Option<u32>, view_distance: Option<u32>, lod_distance: f32, // TODO: move into voxygen @@ -707,6 +708,7 @@ impl Client { tick: 0, state, + server_view_distance_limit: None, view_distance: None, lod_distance: 4.0, loaded_distance: 0.0, @@ -943,9 +945,11 @@ impl Client { } pub fn set_view_distance(&mut self, view_distance: u32) { - let view_distance = view_distance.max(1).min(65); - self.view_distance = Some(view_distance); - self.send_msg(ClientGeneral::SetViewDistance(view_distance)); + if self.server_view_distance_limit.map_or(true, |limit| view_distance >= limit) { + let view_distance = view_distance.max(1).min(65); + self.view_distance = Some(view_distance); + self.send_msg(ClientGeneral::SetViewDistance(view_distance)); + } } pub fn set_lod_distance(&mut self, lod_distance: u32) { @@ -1486,6 +1490,8 @@ impl Client { pub fn view_distance(&self) -> Option<u32> { self.view_distance } + pub fn server_view_distance_limit(&self) -> Option<u32> { self.server_view_distance_limit } + pub fn loaded_distance(&self) -> f32 { self.loaded_distance } pub fn position(&self) -> Option<Vec3<f32>> { @@ -2254,6 +2260,9 @@ impl Client { ServerGeneral::SetViewDistance(vd) => { self.view_distance = Some(vd); frontend_events.push(Event::SetViewDistance(vd)); + // If the server is correcting client vd selection we assume this is the max + // allowed view distance. + self.server_view_distance_limit = Some(vd); }, ServerGeneral::Outcomes(outcomes) => { frontend_events.extend(outcomes.into_iter().map(Event::Outcome)) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 8acb48ce9c..5d950626d1 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3063,6 +3063,7 @@ impl Hud { &self.imgs, &self.fonts, i18n, + client.server_view_distance_limit(), fps as f32, ) .set(self.ids.settings_window, ui_widgets) diff --git a/voxygen/src/hud/settings_window/mod.rs b/voxygen/src/hud/settings_window/mod.rs index 6b045ef933..6e55347e52 100644 --- a/voxygen/src/hud/settings_window/mod.rs +++ b/voxygen/src/hud/settings_window/mod.rs @@ -93,6 +93,7 @@ pub struct SettingsWindow<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, fps: f32, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -105,6 +106,7 @@ impl<'a> SettingsWindow<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, fps: f32, ) -> Self { Self { @@ -113,6 +115,7 @@ impl<'a> SettingsWindow<'a> { imgs, fonts, localized_strings, + server_view_distance_limit, fps, common: widget::CommonBuilder::default(), } @@ -299,11 +302,17 @@ impl<'a> Widget for SettingsWindow<'a> { } }, SettingsTab::Video => { - for change in - video::Video::new(global_state, imgs, fonts, localized_strings, self.fps) - .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0) - .wh_of(state.ids.settings_content_align) - .set(state.ids.video, ui) + for change in video::Video::new( + global_state, + imgs, + fonts, + localized_strings, + self.server_view_distance_limit, + self.fps, + ) + .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0) + .wh_of(state.ids.settings_content_align) + .set(state.ids.video, ui) { events.push(Event::SettingsChange(change.into())); } @@ -327,11 +336,16 @@ impl<'a> Widget for SettingsWindow<'a> { } }, SettingsTab::Networking => { - for change in - networking::Networking::new(global_state, imgs, fonts, localized_strings) - .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0) - .wh_of(state.ids.settings_content_align) - .set(state.ids.networking, ui) + for change in networking::Networking::new( + global_state, + imgs, + fonts, + localized_strings, + self.server_view_distance_limit, + ) + .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0) + .wh_of(state.ids.settings_content_align) + .set(state.ids.networking, ui) { events.push(Event::SettingsChange(change.into())); } diff --git a/voxygen/src/hud/settings_window/networking.rs b/voxygen/src/hud/settings_window/networking.rs index 8a7904e55e..64ecd4685c 100644 --- a/voxygen/src/hud/settings_window/networking.rs +++ b/voxygen/src/hud/settings_window/networking.rs @@ -34,6 +34,7 @@ pub struct Networking<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, #[conrod(common_builder)] common: widget::CommonBuilder, } @@ -43,12 +44,14 @@ impl<'a> Networking<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, ) -> Self { Self { global_state, imgs, fonts, localized_strings, + server_view_distance_limit, common: widget::CommonBuilder::default(), } } @@ -107,6 +110,7 @@ impl<'a> Widget for Networking<'a> { .down_from(state.ids.vd_text, 8.0) .track_breadth(12.0) .slider_length(10.0) + .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX)) .pad_track((5.0, 5.0)) .set(state.ids.vd_slider, ui) { diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index 2d4d9cd1a8..4e56a2dc41 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -131,6 +131,7 @@ pub struct Video<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, fps: f32, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -141,6 +142,7 @@ impl<'a> Video<'a> { imgs: &'a Imgs, fonts: &'a Fonts, localized_strings: &'a Localization, + server_view_distance_limit: Option<u32>, fps: f32, ) -> Self { Self { @@ -148,6 +150,7 @@ impl<'a> Video<'a> { imgs, fonts, localized_strings, + server_view_distance_limit, fps, common: widget::CommonBuilder::default(), } @@ -295,6 +298,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.vd_text, 8.0) .track_breadth(12.0) .slider_length(10.0) + .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX)) .pad_track((5.0, 5.0)) .set(state.ids.vd_slider, ui) { diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 3fbbf7e212..720e365ebe 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -215,12 +215,7 @@ impl PlayState for CharSelectionState { Ok(events) => { for event in events { match event { - client::Event::SetViewDistance(vd) => { - global_state.settings.graphics.view_distance = vd; - global_state - .settings - .save_to_file_warn(&global_state.config_dir); - }, + client::Event::SetViewDistance(_vd) => {}, client::Event::Disconnect => { global_state.info_message = Some( localized_strings diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 88246035b1..7b7d3cfa1e 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -231,12 +231,7 @@ impl PlayState for MainMenuState { Ok(events) => { for event in events { match event { - client::Event::SetViewDistance(vd) => { - global_state.settings.graphics.view_distance = vd; - global_state - .settings - .save_to_file_warn(&global_state.config_dir); - }, + client::Event::SetViewDistance(_vd) => {}, client::Event::Disconnect => { global_state.info_message = Some( localized_strings diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index f3ab46bdff..4cae6af2b1 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -351,12 +351,7 @@ impl SessionState { client::Event::Notification(n) => { self.hud.new_notification(n); }, - client::Event::SetViewDistance(vd) => { - global_state.settings.graphics.view_distance = vd; - global_state - .settings - .save_to_file_warn(&global_state.config_dir); - }, + client::Event::SetViewDistance(_vd) => {}, client::Event::Outcome(outcome) => outcomes.push(outcome), client::Event::CharacterCreated(_) => {}, client::Event::CharacterEdited(_) => {}, diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index 4870820688..f53d4738c9 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -355,12 +355,7 @@ impl SettingsChange { SettingsChange::Graphics(graphics_change) => { match graphics_change { Graphics::AdjustViewDistance(view_distance) => { - session_state - .client - .borrow_mut() - .set_view_distance(view_distance); - - settings.graphics.view_distance = view_distance; + adjust_view_distance(view_distance, global_state, session_state) }, Graphics::AdjustLodDistance(lod_distance) => { session_state @@ -605,11 +600,7 @@ impl SettingsChange { }, SettingsChange::Networking(networking_change) => match networking_change { Networking::AdjustViewDistance(view_distance) => { - session_state - .client - .borrow_mut() - .set_view_distance(view_distance); - settings.graphics.view_distance = view_distance; + adjust_view_distance(view_distance, global_state, session_state) }, Networking::ChangePlayerPhysicsBehavior { server_authoritative, @@ -654,6 +645,21 @@ impl SettingsChange { }, }, } - settings.save_to_file_warn(&global_state.config_dir); + global_state + .settings + .save_to_file_warn(&global_state.config_dir); } } + +fn adjust_view_distance( + view_distance: u32, + global_state: &mut GlobalState, + session_state: &mut SessionState, +) { + session_state + .client + .borrow_mut() + .set_view_distance(view_distance); + + global_state.settings.graphics.view_distance = view_distance; +} diff --git a/voxygen/src/ui/widgets/image_slider.rs b/voxygen/src/ui/widgets/image_slider.rs index dbff4e518e..cc273b8f15 100644 --- a/voxygen/src/ui/widgets/image_slider.rs +++ b/voxygen/src/ui/widgets/image_slider.rs @@ -29,6 +29,10 @@ pub struct ImageSlider<T, K> { value: T, min: T, max: T, + // If `value > soft_max` we will display the slider at `soft_max` along with a faded ghost + // slider at `value`. The slider displayed at `soft_max` is purely a visual indicator and has + // no effect on the values produced by this slider. + soft_max: T, /// The amount in which the slider's display should be skewed. /// /// Higher skew amounts (above 1.0) will weigh lower values. @@ -65,6 +69,7 @@ widget_ids! { struct Ids { track, slider, + soft_max_slider, } } @@ -76,6 +81,7 @@ pub struct State { impl<T, K> ImageSlider<T, K> { builder_methods! { pub skew { skew = f32 } + pub soft_max { soft_max = T } pub pad_track { track.padding = (f32, f32) } pub hover_image { slider.hover_image_id = Some(image::Id) } pub press_image { slider.press_image_id = Some(image::Id) } @@ -85,18 +91,16 @@ impl<T, K> ImageSlider<T, K> { pub slider_color { slider.color = Some(Color) } } - fn new( - value: T, - min: T, - max: T, - slider_image_id: image::Id, - track_image_id: image::Id, - ) -> Self { + fn new(value: T, min: T, max: T, slider_image_id: image::Id, track_image_id: image::Id) -> Self + where + T: Copy, + { Self { common: widget::CommonBuilder::default(), value, min, max, + soft_max: max, skew: 1.0, track: Track { image_id: track_image_id, @@ -133,7 +137,7 @@ where impl<T> ImageSlider<T, Discrete> where - T: Integer, + T: Integer + Copy, { pub fn discrete( value: T, @@ -266,45 +270,67 @@ where .unwrap_or(slider.image_id); // A rectangle for positioning and sizing the slider. - let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0); - let unskewed_perc = value_perc.powf(1.0 / skew as f64); - let slider_rect = if is_horizontal { - let pos = utils::map_range( - unskewed_perc, - 0.0, - 1.0, - rect.x.start + start_pad, - rect.x.end - end_pad, - ); - let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64); - Rect { - x: Range::from_pos_and_len(pos, w), - ..rect - } - } else { - let pos = utils::map_range( - unskewed_perc, - 0.0, - 1.0, - rect.y.start + start_pad, - rect.y.end - end_pad, - ); - let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64); - Rect { - y: Range::from_pos_and_len(pos, h), - ..rect + let slider_rect = |slider_value| { + let value_perc = utils::map_range(slider_value, min, max, 0.0, 1.0); + let unskewed_perc = value_perc.powf(1.0 / skew as f64); + if is_horizontal { + let pos = utils::map_range( + unskewed_perc, + 0.0, + 1.0, + rect.x.start + start_pad, + rect.x.end - end_pad, + ); + let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64); + Rect { + x: Range::from_pos_and_len(pos, w), + ..rect + } + } else { + let pos = utils::map_range( + unskewed_perc, + 0.0, + 1.0, + rect.y.start + start_pad, + rect.y.end - end_pad, + ); + let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64); + Rect { + y: Range::from_pos_and_len(pos, h), + ..rect + } } }; - let (x, y, w, h) = slider_rect.x_y_w_h(); + // Whether soft max slider needs to be displayed and main slider faded to look + // like a ghost. + let over_soft_max = new_value > self.soft_max; + + let (x, y, w, h) = slider_rect(new_value).x_y_w_h(); + let fade = if over_soft_max { 0.5 } else { 1.0 }; Image::new(slider_image) .x_y(x, y) .w_h(w, h) .parent(id) .graphics_for(id) - .color(slider.color) + .color(Some( + slider + .color + .map_or(Color::Rgba(1.0, 1.0, 1.0, fade), |c: Color| c.alpha(fade)), + )) .set(state.ids.slider, ui); + if over_soft_max { + let (x, y, w, h) = slider_rect(self.soft_max).x_y_w_h(); + Image::new(slider_image) + .x_y(x, y) + .w_h(w, h) + .parent(id) + .graphics_for(id) + .color(slider.color) + .set(state.ids.soft_max_slider, ui); + } + // If the value has just changed, return the new value. if value != new_value { Some(new_value)