From c36d6e873f813236ea023dcbd9f0d019f1fe0e53 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 21 Aug 2022 00:48:51 -0400 Subject: [PATCH 1/9] Fix issue with the region subscription system not updating on view distance changes (until the player crossed a chunk boundary and triggered the normal update). This introduces a `ViewDistance` struct that provides an abstraction around limiting the rate the view distance can be cycled up and down. This helps avoid unnecessary sending, deleting, and then resending of synced things like entities (the client will still delete its terrain locally and re-request it though). The second part of this fix is storing the last view distance in the `RegionSubscription` struct and then updating region subscriptions if this doesn't match the current view distance in the `Presence` component. --- server/src/presence.rs | 101 ++++++++++++++++++++++++++++++--- server/src/state_ext.rs | 2 +- server/src/sys/entity_sync.rs | 3 +- server/src/sys/msg/in_game.rs | 31 ++++++---- server/src/sys/msg/terrain.rs | 2 +- server/src/sys/subscription.rs | 19 +++++-- server/src/sys/terrain.rs | 4 +- server/src/sys/terrain_sync.rs | 8 ++- 8 files changed, 139 insertions(+), 31 deletions(-) diff --git a/server/src/presence.rs b/server/src/presence.rs index fea7730441..c6559d1925 100644 --- a/server/src/presence.rs +++ b/server/src/presence.rs @@ -1,12 +1,14 @@ use common_net::msg::PresenceKind; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; -use specs::{Component, DenseVecStorage, DerefFlaggedStorage, NullStorage, VecStorage}; +use specs::{Component, DerefFlaggedStorage, NullStorage}; +use std::time::{Duration, Instant}; use vek::*; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug)] pub struct Presence { - pub view_distance: u32, + // TODO: separate view distance for syncing entities? + pub view_distance: ViewDistance, pub kind: PresenceKind, pub lossy_terrain_compression: bool, } @@ -14,7 +16,7 @@ pub struct Presence { impl Presence { pub fn new(view_distance: u32, kind: PresenceKind) -> Self { Self { - view_distance, + view_distance: ViewDistance::new(view_distance, Instant::now()), kind, lossy_terrain_compression: false, } @@ -22,8 +24,7 @@ impl Presence { } impl Component for Presence { - // Presence seems <= 64 bits, so it isn't worth using DenseVecStorage. - type Storage = DerefFlaggedStorage>; + type Storage = DerefFlaggedStorage>; } // Distance from fuzzy_chunk before snapping to current chunk @@ -34,11 +35,12 @@ pub const REGION_FUZZ: u32 = 16; #[derive(Clone, Debug)] pub struct RegionSubscription { pub fuzzy_chunk: Vec2, + pub last_view_distance: u32, pub regions: HashSet>, } impl Component for RegionSubscription { - type Storage = DerefFlaggedStorage>; + type Storage = DerefFlaggedStorage>; } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] @@ -47,3 +49,88 @@ pub struct RepositionOnChunkLoad; impl Component for RepositionOnChunkLoad { type Storage = NullStorage; } + +#[derive(PartialEq, Debug, Clone, Copy)] +enum Direction { + Up, + Down, +} + +/// Distance from the [Presence] from which the world is loaded and information +/// is synced to clients. +/// +/// We limit the frequency that changes in the view distance change direction +/// (e.g. shifting from increasing the value to decreasing it). This is useful +/// since we want to avoid rapid cycles of shrinking and expanding of the view +/// distance. +#[derive(Debug)] +pub struct ViewDistance { + direction: Direction, + last_direction_change_time: Instant, + target: Option, + current: u32, +} + +impl ViewDistance { + /// Minimum time allowed between changes in direction of value adjustments. + const TIME_PER_DIR_CHANGE: Duration = Duration::from_millis(300); + + pub fn new(start_value: u32, now: Instant) -> Self { + Self { + direction: Direction::Up, + last_direction_change_time: now - Self::TIME_PER_DIR_CHANGE, + target: None, + current: start_value, + } + } + + /// Returns the current value. + pub fn current(&self) -> u32 { self.current } + + /// Applies deferred change based on the whether the time to apply it has + /// been reached. + pub fn update(&mut self, now: Instant) { + if let Some(target_val) = self.target { + if now.saturating_duration_since(self.last_direction_change_time) + > Self::TIME_PER_DIR_CHANGE + { + self.last_direction_change_time = now; + self.current = target_val; + self.target = None; + } + } + } + + /// Sets the target value. + /// + /// If this hasn't been changed recently or it is in the same direction as + /// the previous change it will be applied immediately. Otherwise, it + /// will be deferred to a later time (limiting the frequency of changes + /// in the change direction). + pub fn set_target(&mut self, new_target: u32, now: Instant) { + use core::cmp::Ordering; + let new_direction = match new_target.cmp(&self.current) { + Ordering::Equal => return, // No change needed. + Ordering::Less => Direction::Down, + Ordering::Greater => Direction::Up, + }; + + // Change is in the same direction as before so we can just apply it. + if new_direction == self.direction { + self.current = new_target; + self.target = None; + // If it has already been a while since the last direction change we can + // directly apply the request and switch the direction. + } else if now.saturating_duration_since(self.last_direction_change_time) + > Self::TIME_PER_DIR_CHANGE + { + self.direction = new_direction; + self.last_direction_change_time = now; + self.current = new_target; + self.target = None; + // Otherwise, we need to defer the request. + } else { + self.target = Some(new_target); + } + } +} diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index dd1241f5b8..d3c1bb94e7 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -519,7 +519,7 @@ impl StateExt for State { // Make sure physics components are updated self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced()); - const INITIAL_VD: u32 = 5; //will be changed after login + const INITIAL_VD: u32 = 5; // will be changed after login self.write_component_ignore_entity_dead( entity, Presence::new(INITIAL_VD, PresenceKind::Character(character_id)), diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index ceff297429..a2e5407322 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -384,7 +384,8 @@ impl<'a> System<'a> for Sys { let is_near = |o_pos: Vec3| { pos.zip_with(presence, |pos, presence| { pos.0.xy().distance_squared(o_pos.xy()) - < (presence.view_distance as f32 * TerrainChunkSize::RECT_SIZE.x as f32) + < (presence.view_distance.current() as f32 + * TerrainChunkSize::RECT_SIZE.x as f32) .powi(2) }) }; diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 474d16a902..a47a784a89 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -17,6 +17,7 @@ use common_ecs::{Job, Origin, Phase, System}; use common_net::msg::{ClientGeneral, PresenceKind, ServerGeneral}; use common_state::{BlockChange, BuildAreas}; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage}; +use std::time::Instant; use tracing::{debug, trace, warn}; use vek::*; @@ -49,6 +50,7 @@ impl Sys { _terrain_persistence: &mut TerrainPersistenceData<'_>, maybe_player: &Option<&Player>, maybe_admin: &Option<&Admin>, + time_for_vd_changes: Instant, msg: ClientGeneral, ) -> Result<(), crate::error::Error> { let presence = match maybe_presence { @@ -67,20 +69,15 @@ impl Sys { *maybe_presence = None; }, ClientGeneral::SetViewDistance(view_distance) => { - presence.view_distance = settings + let clamped_view_distance = settings .max_view_distance .map(|max| view_distance.min(max)) .unwrap_or(view_distance); + presence.view_distance.set_target(clamped_view_distance, time_for_vd_changes); - //correct client if its VD is to high - if settings - .max_view_distance - .map(|max| view_distance > max) - .unwrap_or(false) - { - client.send(ServerGeneral::SetViewDistance( - settings.max_view_distance.unwrap_or(0), - ))?; + // Correct client if its requested VD is too high. + if view_distance != clamped_view_distance { + client.send(ServerGeneral::SetViewDistance(clamped_view_distance))?; } }, ClientGeneral::ControllerInputs(inputs) => { @@ -378,6 +375,8 @@ impl<'a> System<'a> for Sys { ) { let mut server_emitter = server_event_bus.emitter(); + let time_for_vd_changes = Instant::now(); + for (entity, client, mut maybe_presence, player, maybe_admin) in ( &entities, &mut clients, @@ -387,12 +386,15 @@ impl<'a> System<'a> for Sys { ) .join() { + // If an `ExitInGame` message is received this is set to `None` allowing further + // ingame messages to be ignored. + let mut clearable_maybe_presence = maybe_presence.as_deref_mut(); let _ = super::try_recv_all(client, 2, |client, msg| { Self::handle_client_in_game_msg( &mut server_emitter, entity, client, - &mut maybe_presence.as_deref_mut(), + &mut clearable_maybe_presence, &terrain, &can_build, &is_rider, @@ -410,9 +412,16 @@ impl<'a> System<'a> for Sys { &mut terrain_persistence, &player, &maybe_admin, + time_for_vd_changes, msg, ) }); + + // Ensure deferred view distance changes are applied (if the + // requsite time has elapsed). + if let Some(mut presence) = maybe_presence { + presence.view_distance.update(time_for_vd_changes); + } } } } diff --git a/server/src/sys/msg/terrain.rs b/server/src/sys/msg/terrain.rs index af91135db6..f129ce5912 100644 --- a/server/src/sys/msg/terrain.rs +++ b/server/src/sys/msg/terrain.rs @@ -76,7 +76,7 @@ impl<'a> System<'a> for Sys { pos.0.xy().map(|e| e as f64).distance_squared( key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), - ) < ((presence.view_distance as f64 - 1.0 + ) < ((presence.view_distance.current() as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) * TerrainChunkSize::RECT_SIZE.x as f64) .powi(2) diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index a2748ac8ea..5136f44c57 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -60,7 +60,8 @@ impl<'a> System<'a> for Sys { // To update subscriptions // 1. Iterate through clients // 2. Calculate current chunk position - // 3. If chunk is the same return, otherwise continue (use fuzziness) + // 3. If chunk is different (use fuzziness) or the client view distance + // has changed continue, otherwise return // 4. Iterate through subscribed regions // 5. Check if region is still in range (use fuzziness) // 6. If not in range @@ -78,13 +79,15 @@ impl<'a> System<'a> for Sys { ) .join() { - let vd = presence.view_distance; + let vd = presence.view_distance.current(); // Calculate current chunk let chunk = (Vec2::::from(pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); - // Only update regions when moving to a new chunk - // uses a fuzzy border to prevent rapid triggering when moving along chunk - // boundaries + // Only update regions when moving to a new chunk or if view distance has + // changed. + // + // Uses a fuzzy border to prevent rapid triggering when moving along chunk + // boundaries. if chunk != subscription.fuzzy_chunk && (subscription .fuzzy_chunk @@ -96,7 +99,10 @@ impl<'a> System<'a> for Sys { e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32 }) .reduce_or() + || subscription.last_view_distance != vd { + // Update the view distance + subscription.last_view_distance = vd; // Update current chunk subscription.fuzzy_chunk = Vec2::::from(pos.0) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -216,7 +222,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32; let regions = regions_in_vd( client_pos.0, - (presence.view_distance as f32 * chunk_size) as f32 + (presence.view_distance.current() as f32 * chunk_size) as f32 + (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), ); @@ -261,6 +267,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { if let Err(e) = world.write_storage().insert(entity, RegionSubscription { fuzzy_chunk, + last_view_distance: presence.view_distance.current(), regions, }) { error!(?e, "Failed to insert region subscription component"); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 9bf2ecf288..a7243e9f39 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -269,7 +269,7 @@ impl<'a> System<'a> for Sys { .map(|e: i32| (e.unsigned_abs()).saturating_sub(2)) .magnitude_squared(); - if adjusted_dist_sqr <= presence.view_distance.pow(2) { + if adjusted_dist_sqr <= presence.view_distance.current().pow(2) { chunk_send_emitter.emit(ChunkSendEntry { entity, chunk_key: key, @@ -293,7 +293,7 @@ impl<'a> System<'a> for Sys { // For each player with a position, calculate the distance. for (presence, pos) in (&presences, &positions).join() { - if chunk_in_vd(pos.0, chunk_key, &terrain, presence.view_distance) { + if chunk_in_vd(pos.0, chunk_key, &terrain, presence.view_distance.current()) { should_drop = false; break; } diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index a12302b393..010502910e 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -33,8 +33,12 @@ impl<'a> System<'a> for Sys { // Sync changed chunks for chunk_key in &terrain_changes.modified_chunks { for (entity, presence, pos) in (&entities, &presences, &positions).join() { - if super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, presence.view_distance) - { + if super::terrain::chunk_in_vd( + pos.0, + *chunk_key, + &terrain, + presence.view_distance.current(), + ) { chunk_send_emitter.emit(ChunkSendEntry { entity, chunk_key: *chunk_key, From 895d6a2d8bf609173a1db1219d9c875215f0ac98 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 21 Aug 2022 02:16:12 -0400 Subject: [PATCH 2/9] Impove UX of server limits on the view distance * When client gets its view distance set by the server it stores that as the max view distance allowed by the server. * This is used to avoid skip sending requests if `set_view_distance` is called with larger values. * This is then also displayed in the UI via greying out the slider when it goes past the limit and drawing another slide at the limit. * The server clamping the client's view distance no longer leads to edits in voxygen's view distance settings (the view distance setting is instead preserved). --- client/src/lib.rs | 15 ++- voxygen/src/hud/mod.rs | 1 + voxygen/src/hud/settings_window/mod.rs | 34 ++++-- voxygen/src/hud/settings_window/networking.rs | 4 + voxygen/src/hud/settings_window/video.rs | 4 + voxygen/src/menu/char_selection/mod.rs | 7 +- voxygen/src/menu/main/mod.rs | 7 +- voxygen/src/session/mod.rs | 7 +- voxygen/src/session/settings_change.rs | 30 +++--- voxygen/src/ui/widgets/image_slider.rs | 100 +++++++++++------- 10 files changed, 129 insertions(+), 80 deletions(-) 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, view_distance: Option, 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 { self.view_distance } + pub fn server_view_distance_limit(&self) -> Option { self.server_view_distance_limit } + pub fn loaded_distance(&self) -> f32 { self.loaded_distance } pub fn position(&self) -> Option> { @@ -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, 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, 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, #[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, ) -> 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, 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, 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 { 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 ImageSlider { 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 ImageSlider { 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 ImageSlider 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) From 334937568e82428408ecb9b18b4e559577f950af Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 21 Aug 2022 23:21:39 -0400 Subject: [PATCH 3/9] Add an entity view distance setting that allows limiting the distance entities are synced from and displayed in. NOTE: Syncing entities work at the granularity regions which are multi-chunk squares but the display of entities in voxygen is limited in a circle with the radiues of the supplied distance. Additional details and changes: * Added `ViewDistances` struct in `common` that contains separate terrain and entity view distances (the entity view distance will be clamped by the terrain view distance in uses of this). * View distance requests from the client to the server now use this type. * When requesting the character or spectate state the client now passes its desired view distances. This is exposed as a new parameter on `Client::request_character`/`Client::request_spectate`. And the client no longer needs to send a view distance request after entering these states. This also allows us to avoid needing to initialize `Presence` with a default view distance value on the server. * Removed `DerefFlaggedStorage` from `Presence` and `RegionSubscription` since the change tracking isn't used for these components. * Add sliders in voxygen graphics and network tabs for this new setting. Show the clamped value as well as the selected value next to the slider. * Rename existing "Entities View Distance" slider (which AFAIK controls the distance at which different LOD levels apply to figures) to "Entities Detail Distance" so we can use the former name for this new slider. --- assets/voxygen/i18n/en/hud/settings.ftl | 3 +- client/src/lib.rs | 40 ++- common/net/src/msg/client.rs | 9 +- common/net/src/msg/server.rs | 4 + common/src/event.rs | 3 +- common/src/lib.rs | 2 + common/src/view_distances.rs | 23 ++ server/src/events/entity_creation.rs | 24 +- server/src/events/mod.rs | 12 +- server/src/presence.rs | 18 +- server/src/state_ext.rs | 33 ++- server/src/sys/entity_sync.rs | 2 +- server/src/sys/msg/character_screen.rs | 7 +- server/src/sys/msg/in_game.rs | 26 +- server/src/sys/msg/terrain.rs | 2 +- server/src/sys/subscription.rs | 10 +- server/src/sys/terrain.rs | 4 +- server/src/sys/terrain_sync.rs | 2 +- voxygen/src/hud/settings_window/networking.rs | 84 ++++-- voxygen/src/hud/settings_window/video.rs | 249 +++++++++++------- voxygen/src/menu/char_selection/mod.rs | 17 +- voxygen/src/scene/figure/mod.rs | 2 +- voxygen/src/scene/mod.rs | 3 +- voxygen/src/session/mod.rs | 12 +- voxygen/src/session/settings_change.rs | 57 ++-- voxygen/src/settings/graphics.rs | 6 +- 26 files changed, 435 insertions(+), 219 deletions(-) create mode 100644 common/src/view_distances.rs diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl index 97d5df9064..e9c78472c5 100644 --- a/assets/voxygen/i18n/en/hud/settings.ftl +++ b/assets/voxygen/i18n/en/hud/settings.ftl @@ -54,9 +54,10 @@ hud-settings-auto_camera = Auto camera hud-settings-bow_zoom = Zoom in when charging bow hud-settings-reset_gameplay = Reset to Defaults hud-settings-view_distance = View Distance +hud-settings-entity_view_distance = Entities View Distance hud-settings-lod_distance = LoD Distance hud-settings-sprites_view_distance = Sprites View Distance -hud-settings-figures_view_distance = Entities View Distance +hud-settings-entities_detail_distance = Entities Detail Distance hud-settings-maximum_fps = Maximum FPS hud-settings-background_fps = Background FPS hud-settings-present_mode = Present Mode diff --git a/client/src/lib.rs b/client/src/lib.rs index 5354b8f47a..24b41c8664 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -249,6 +249,7 @@ pub struct Client { state: State, server_view_distance_limit: Option, + /// Terrrain view distance view_distance: Option, lod_distance: f32, // TODO: move into voxygen @@ -805,8 +806,8 @@ impl Client { | ClientGeneral::CreateCharacter { .. } | ClientGeneral::EditCharacter { .. } | ClientGeneral::DeleteCharacter(_) - | ClientGeneral::Character(_) - | ClientGeneral::Spectate => &mut self.character_screen_stream, + | ClientGeneral::Character(_, _) + | ClientGeneral::Spectate(_) => &mut self.character_screen_stream, //Only in game ClientGeneral::ControllerInputs(_) | ClientGeneral::ControlEvent(_) @@ -881,16 +882,16 @@ impl Client { } /// Request a state transition to `ClientState::Character`. - pub fn request_character(&mut self, character_id: CharacterId) { - self.send_msg(ClientGeneral::Character(character_id)); + pub fn request_character(&mut self, character_id: CharacterId, view_distances: common::ViewDistances) { + self.send_msg(ClientGeneral::Character(character_id, view_distances)); // Assume we are in_game unless server tells us otherwise self.presence = Some(PresenceKind::Character(character_id)); } /// Request a state transition to `ClientState::Spectate`. - pub fn request_spectate(&mut self) { - self.send_msg(ClientGeneral::Spectate); + pub fn request_spectate(&mut self, view_distances: common::ViewDistances) { + self.send_msg(ClientGeneral::Spectate(view_distances)); self.presence = Some(PresenceKind::Spectator); } @@ -944,11 +945,14 @@ impl Client { self.send_msg(ClientGeneral::ExitInGame); } - pub fn set_view_distance(&mut self, view_distance: u32) { - 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_view_distances(&mut self, view_distances: common::ViewDistances) { + if self.server_view_distance_limit.map_or(true, |limit| view_distances.terrain >= limit) { + let view_distances = common::ViewDistances { + terrain: view_distances.terrain.max(1).min(65), + entity: view_distances.entity, + }; + self.view_distance = Some(view_distances.terrain); + self.send_msg(ClientGeneral::SetViewDistance(view_distances)); } } @@ -2357,18 +2361,10 @@ impl Client { ServerGeneral::CharacterEdited(character_id) => { events.push(Event::CharacterEdited(character_id)); }, - ServerGeneral::CharacterSuccess => { - debug!("client is now in ingame state on server"); - if let Some(vd) = self.view_distance { - self.set_view_distance(vd); - } - }, + ServerGeneral::CharacterSuccess => debug!("client is now in ingame state on server"), ServerGeneral::SpectatorSuccess(spawn_point) => { - if let Some(vd) = self.view_distance { - events.push(Event::StartSpectate(spawn_point)); - debug!("client is now in ingame state on server"); - self.set_view_distance(vd); - } + events.push(Event::StartSpectate(spawn_point)); + debug!("client is now in ingame state on server"); }, _ => unreachable!("Not a character_screen msg"), } diff --git a/common/net/src/msg/client.rs b/common/net/src/msg/client.rs index f373593b5c..18ad9e52d5 100644 --- a/common/net/src/msg/client.rs +++ b/common/net/src/msg/client.rs @@ -4,6 +4,7 @@ use common::{ comp, comp::{Skill, SkillGroupKind}, terrain::block::Block, + ViewDistances, }; use serde::{Deserialize, Serialize}; use vek::*; @@ -60,13 +61,13 @@ pub enum ClientGeneral { alias: String, body: comp::Body, }, - Character(CharacterId), - Spectate, + Character(CharacterId, ViewDistances), + Spectate(ViewDistances), //Only in game ControllerInputs(Box), ControlEvent(comp::ControlEvent), ControlAction(comp::ControlAction), - SetViewDistance(u32), + SetViewDistance(ViewDistances), BreakBlock(Vec3), PlaceBlock(Vec3, Block), ExitInGame, @@ -121,7 +122,7 @@ impl ClientMsg { | ClientGeneral::DeleteCharacter(_) => { c_type != ClientType::ChatOnly && presence.is_none() }, - ClientGeneral::Character(_) | ClientGeneral::Spectate => { + ClientGeneral::Character(_, _) | ClientGeneral::Spectate(_) => { c_type == ClientType::Game && presence.is_none() }, //Only in game diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 9fecc4361b..948f9a80a2 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -166,6 +166,10 @@ pub enum ServerGeneral { /// from an ingame state ExitInGameSuccess, InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent), + /// NOTE: The client can infer that entity view distance will be at most the + /// terrain view distance that we send here (and if lower it won't be + /// modified). So we just need to send the terrain VD back to the client + /// if corrections are made. SetViewDistance(u32), Outcomes(Vec), Knockback(Vec3), diff --git a/common/src/event.rs b/common/src/event.rs index d540b80b5d..4455484ace 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -107,8 +107,9 @@ pub enum ServerEvent { InitCharacterData { entity: EcsEntity, character_id: CharacterId, + requested_view_distances: crate::ViewDistances, }, - InitSpectator(EcsEntity), + InitSpectator(EcsEntity, crate::ViewDistances), UpdateCharacterData { entity: EcsEntity, components: ( diff --git a/common/src/lib.rs b/common/src/lib.rs index 88ae0bdefb..8b8e35b224 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -81,6 +81,7 @@ pub mod terrain; #[cfg(not(target_arch = "wasm32"))] pub mod typed; pub mod uid; #[cfg(not(target_arch = "wasm32"))] pub mod util; +mod view_distances; #[cfg(not(target_arch = "wasm32"))] pub mod vol; #[cfg(not(target_arch = "wasm32"))] pub mod volumes; @@ -98,3 +99,4 @@ pub use comp::inventory::loadout_builder::LoadoutBuilder; pub use explosion::{Explosion, RadiusEffect}; #[cfg(not(target_arch = "wasm32"))] pub use skillset_builder::SkillSetBuilder; +pub use view_distances::ViewDistances; diff --git a/common/src/view_distances.rs b/common/src/view_distances.rs new file mode 100644 index 0000000000..94416e5886 --- /dev/null +++ b/common/src/view_distances.rs @@ -0,0 +1,23 @@ +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub struct ViewDistances { + pub terrain: u32, + /// Server will clamp this to `terrain` if it is larger. + /// + /// NOTE: Importantly, the server still loads entities in the `terrain` view + /// distance (at least currently, please update this if you change it!), + /// but the syncing to the client is done based on the entity view + /// distance. + pub entity: u32, +} + +impl ViewDistances { + /// Clamps the terrain view distance to an optional max and clamps the + /// entity view distance to the resulting terrain view distance. + pub fn clamp(self, max: Option) -> Self { + let terrain = max.unwrap_or(u32::MAX).min(self.terrain); + Self { + terrain, + entity: self.entity.min(terrain), + } + } +} diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 4afac192c5..3e0e9f390e 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -17,6 +17,7 @@ use common::{ rtsim::RtSimEntity, uid::Uid, util::Dir, + ViewDistances, }; use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; use specs::{Builder, Entity as EcsEntity, WorldExt}; @@ -29,12 +30,29 @@ pub fn handle_initialize_character( server: &mut Server, entity: EcsEntity, character_id: CharacterId, + requested_view_distances: ViewDistances, ) { - server.state.initialize_character_data(entity, character_id); + let clamped_vds = requested_view_distances.clamp(server.settings().max_view_distance); + server + .state + .initialize_character_data(entity, character_id, clamped_vds); + // Correct client if its requested VD is too high. + if requested_view_distances.terrain != clamped_vds.terrain { + server.notify_client(entity, ServerGeneral::SetViewDistance(clamped_vds.terrain)); + } } -pub fn handle_initialize_spectator(server: &mut Server, entity: EcsEntity) { - server.state.initialize_spectator_data(entity); +pub fn handle_initialize_spectator( + server: &mut Server, + entity: EcsEntity, + requested_view_distances: ViewDistances, +) { + let clamped_vds = requested_view_distances.clamp(server.settings().max_view_distance); + server.state.initialize_spectator_data(entity, clamped_vds); + // Correct client if its requested VD is too high. + if requested_view_distances.terrain != clamped_vds.terrain { + server.notify_client(entity, ServerGeneral::SetViewDistance(clamped_vds.terrain)); + } sys::subscription::initialize_region_subscription(server.state.ecs(), entity); } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index d8fb23f2d7..9b496c77b0 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -140,8 +140,16 @@ impl Server { ServerEvent::InitCharacterData { entity, character_id, - } => handle_initialize_character(self, entity, character_id), - ServerEvent::InitSpectator(entity) => handle_initialize_spectator(self, entity), + requested_view_distances, + } => handle_initialize_character( + self, + entity, + character_id, + requested_view_distances, + ), + ServerEvent::InitSpectator(entity, requested_view_distances) => { + handle_initialize_spectator(self, entity, requested_view_distances) + }, ServerEvent::UpdateCharacterData { entity, components } => { let ( body, diff --git a/server/src/presence.rs b/server/src/presence.rs index c6559d1925..7db43d1cca 100644 --- a/server/src/presence.rs +++ b/server/src/presence.rs @@ -1,22 +1,24 @@ use common_net::msg::PresenceKind; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; -use specs::{Component, DerefFlaggedStorage, NullStorage}; +use specs::{Component, NullStorage}; use std::time::{Duration, Instant}; use vek::*; #[derive(Debug)] pub struct Presence { - // TODO: separate view distance for syncing entities? - pub view_distance: ViewDistance, + pub terrain_view_distance: ViewDistance, + pub entity_view_distance: ViewDistance, pub kind: PresenceKind, pub lossy_terrain_compression: bool, } impl Presence { - pub fn new(view_distance: u32, kind: PresenceKind) -> Self { + pub fn new(view_distances: common::ViewDistances, kind: PresenceKind) -> Self { + let now = Instant::now(); Self { - view_distance: ViewDistance::new(view_distance, Instant::now()), + terrain_view_distance: ViewDistance::new(view_distances.terrain, now), + entity_view_distance: ViewDistance::new(view_distances.entity, now), kind, lossy_terrain_compression: false, } @@ -24,7 +26,7 @@ impl Presence { } impl Component for Presence { - type Storage = DerefFlaggedStorage>; + type Storage = specs::DenseVecStorage; } // Distance from fuzzy_chunk before snapping to current chunk @@ -35,12 +37,12 @@ pub const REGION_FUZZ: u32 = 16; #[derive(Clone, Debug)] pub struct RegionSubscription { pub fuzzy_chunk: Vec2, - pub last_view_distance: u32, + pub last_entity_view_distance: u32, pub regions: HashSet>, } impl Component for RegionSubscription { - type Storage = DerefFlaggedStorage>; + type Storage = specs::DenseVecStorage; } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index d3c1bb94e7..5a271b5131 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -26,6 +26,7 @@ use common::{ resources::{Time, TimeOfDay}, slowjob::SlowJobPool, uid::{Uid, UidAllocator}, + ViewDistances, }; use common_net::{ msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral}, @@ -107,9 +108,14 @@ pub trait StateExt { index: &world::IndexOwned, ) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server - fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId); + fn initialize_character_data( + &mut self, + entity: EcsEntity, + character_id: CharacterId, + view_distances: ViewDistances, + ); /// Insert common/default components for a new spectator joining the server - fn initialize_spectator_data(&mut self, entity: EcsEntity); + fn initialize_spectator_data(&mut self, entity: EcsEntity, view_distances: ViewDistances); /// Update the components associated with the entity's current character. /// Performed after loading component data from the database fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents); @@ -488,10 +494,21 @@ impl StateExt for State { self.ecs_mut() .create_entity_synced() .with(pos) - .with(Presence::new(view_distance, PresenceKind::Spectator)) + .with(Presence::new( + ViewDistances { + terrain: view_distance, + entity: view_distance, + }, + PresenceKind::Spectator, + )) } - fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { + fn initialize_character_data( + &mut self, + entity: EcsEntity, + character_id: CharacterId, + view_distances: ViewDistances, + ) { let spawn_point = self.ecs().read_resource::().0; if let Some(player_uid) = self.read_component_copied::(entity) { @@ -519,10 +536,9 @@ impl StateExt for State { // Make sure physics components are updated self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced()); - const INITIAL_VD: u32 = 5; // will be changed after login self.write_component_ignore_entity_dead( entity, - Presence::new(INITIAL_VD, PresenceKind::Character(character_id)), + Presence::new(view_distances, PresenceKind::Character(character_id)), ); // Tell the client its request was successful. @@ -532,7 +548,7 @@ impl StateExt for State { } } - fn initialize_spectator_data(&mut self, entity: EcsEntity) { + fn initialize_spectator_data(&mut self, entity: EcsEntity, view_distances: ViewDistances) { let spawn_point = self.ecs().read_resource::().0; if self.read_component_copied::(entity).is_some() { @@ -545,10 +561,9 @@ impl StateExt for State { // Make sure physics components are updated self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced()); - const INITIAL_VD: u32 = 5; //will be changed after login self.write_component_ignore_entity_dead( entity, - Presence::new(INITIAL_VD, PresenceKind::Spectator), + Presence::new(view_distances, PresenceKind::Spectator), ); // Tell the client its request was successful. diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index a2e5407322..dc303e732f 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -384,7 +384,7 @@ impl<'a> System<'a> for Sys { let is_near = |o_pos: Vec3| { pos.zip_with(presence, |pos, presence| { pos.0.xy().distance_squared(o_pos.xy()) - < (presence.view_distance.current() as f32 + < (presence.entity_view_distance.current() as f32 * TerrainChunkSize::RECT_SIZE.x as f32) .powi(2) }) diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index d9a12f58f0..0e6ce84b28 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -64,17 +64,17 @@ impl Sys { }; match msg { // Request spectator state - ClientGeneral::Spectate => { + ClientGeneral::Spectate(requested_view_distances) => { if let Some(admin) = admins.get(entity) && admin.0 >= AdminRole::Moderator { send_join_messages()?; - server_emitter.emit(ServerEvent::InitSpectator(entity)); + server_emitter.emit(ServerEvent::InitSpectator(entity, requested_view_distances)); } else { debug!("dropped Spectate msg from unprivileged client") } }, - ClientGeneral::Character(character_id) => { + ClientGeneral::Character(character_id, requested_view_distances) => { if let Some(player) = players.get(entity) { if presences.contains(entity) { debug!("player already ingame, aborting"); @@ -117,6 +117,7 @@ impl Sys { server_emitter.emit(ServerEvent::InitCharacterData { entity, character_id, + requested_view_distances, }); } } else { diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index a47a784a89..95d92bee8b 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -53,7 +53,7 @@ impl Sys { time_for_vd_changes: Instant, msg: ClientGeneral, ) -> Result<(), crate::error::Error> { - let presence = match maybe_presence { + let presence = match maybe_presence.as_deref_mut() { Some(g) => g, None => { debug!(?entity, "client is not in_game, ignoring msg"); @@ -68,16 +68,15 @@ impl Sys { client.send(ServerGeneral::ExitInGameSuccess)?; *maybe_presence = None; }, - ClientGeneral::SetViewDistance(view_distance) => { - let clamped_view_distance = settings - .max_view_distance - .map(|max| view_distance.min(max)) - .unwrap_or(view_distance); - presence.view_distance.set_target(clamped_view_distance, time_for_vd_changes); + ClientGeneral::SetViewDistance(view_distances) => { + let clamped_vds = view_distances.clamp(settings.max_view_distance); + + presence.terrain_view_distance.set_target(clamped_vds.terrain, time_for_vd_changes); + presence.entity_view_distance.set_target(clamped_vds.entity, time_for_vd_changes); // Correct client if its requested VD is too high. - if view_distance != clamped_view_distance { - client.send(ServerGeneral::SetViewDistance(clamped_view_distance))?; + if view_distances.terrain != clamped_vds.terrain { + client.send(ServerGeneral::SetViewDistance(clamped_vds.terrain))?; } }, ClientGeneral::ControllerInputs(inputs) => { @@ -296,8 +295,8 @@ impl Sys { | ClientGeneral::CreateCharacter { .. } | ClientGeneral::EditCharacter { .. } | ClientGeneral::DeleteCharacter(_) - | ClientGeneral::Character(_) - | ClientGeneral::Spectate + | ClientGeneral::Character(_, _) + | ClientGeneral::Spectate(_) | ClientGeneral::TerrainChunkRequest { .. } | ClientGeneral::LodZoneRequest { .. } | ClientGeneral::ChatMsg(_) @@ -419,8 +418,9 @@ impl<'a> System<'a> for Sys { // Ensure deferred view distance changes are applied (if the // requsite time has elapsed). - if let Some(mut presence) = maybe_presence { - presence.view_distance.update(time_for_vd_changes); + if let Some(presence) = maybe_presence { + presence.terrain_view_distance.update(time_for_vd_changes); + presence.entity_view_distance.update(time_for_vd_changes); } } } diff --git a/server/src/sys/msg/terrain.rs b/server/src/sys/msg/terrain.rs index f129ce5912..c60ea357fb 100644 --- a/server/src/sys/msg/terrain.rs +++ b/server/src/sys/msg/terrain.rs @@ -76,7 +76,7 @@ impl<'a> System<'a> for Sys { pos.0.xy().map(|e| e as f64).distance_squared( key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), - ) < ((presence.view_distance.current() as f64 - 1.0 + ) < ((presence.terrain_view_distance.current() as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) * TerrainChunkSize::RECT_SIZE.x as f64) .powi(2) diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 5136f44c57..ea3521ddbc 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -79,7 +79,7 @@ impl<'a> System<'a> for Sys { ) .join() { - let vd = presence.view_distance.current(); + let vd = presence.entity_view_distance.current(); // Calculate current chunk let chunk = (Vec2::::from(pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -99,10 +99,10 @@ impl<'a> System<'a> for Sys { e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32 }) .reduce_or() - || subscription.last_view_distance != vd + || subscription.last_entity_view_distance != vd { // Update the view distance - subscription.last_view_distance = vd; + subscription.last_entity_view_distance = vd; // Update current chunk subscription.fuzzy_chunk = Vec2::::from(pos.0) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -222,7 +222,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32; let regions = regions_in_vd( client_pos.0, - (presence.view_distance.current() as f32 * chunk_size) as f32 + (presence.entity_view_distance.current() as f32 * chunk_size) as f32 + (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), ); @@ -267,7 +267,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { if let Err(e) = world.write_storage().insert(entity, RegionSubscription { fuzzy_chunk, - last_view_distance: presence.view_distance.current(), + last_entity_view_distance: presence.entity_view_distance.current(), regions, }) { error!(?e, "Failed to insert region subscription component"); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index a7243e9f39..369852785b 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -269,7 +269,7 @@ impl<'a> System<'a> for Sys { .map(|e: i32| (e.unsigned_abs()).saturating_sub(2)) .magnitude_squared(); - if adjusted_dist_sqr <= presence.view_distance.current().pow(2) { + if adjusted_dist_sqr <= presence.terrain_view_distance.current().pow(2) { chunk_send_emitter.emit(ChunkSendEntry { entity, chunk_key: key, @@ -293,7 +293,7 @@ impl<'a> System<'a> for Sys { // For each player with a position, calculate the distance. for (presence, pos) in (&presences, &positions).join() { - if chunk_in_vd(pos.0, chunk_key, &terrain, presence.view_distance.current()) { + if chunk_in_vd(pos.0, chunk_key, &terrain, presence.terrain_view_distance.current()) { should_drop = false; break; } diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index 010502910e..4212261a15 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -37,7 +37,7 @@ impl<'a> System<'a> for Sys { pos.0, *chunk_key, &terrain, - presence.view_distance.current(), + presence.terrain_view_distance.current(), ) { chunk_send_emitter.emit(ChunkSendEntry { entity, diff --git a/voxygen/src/hud/settings_window/networking.rs b/voxygen/src/hud/settings_window/networking.rs index 64ecd4685c..3c624ef3a2 100644 --- a/voxygen/src/hud/settings_window/networking.rs +++ b/voxygen/src/hud/settings_window/networking.rs @@ -15,9 +15,12 @@ widget_ids! { struct Ids { window, window_r, - vd_text, - vd_slider, - vd_value, + terrain_vd_text, + terrain_vd_slider, + terrain_vd_value, + entity_vd_text, + entity_vd_slider, + entity_vd_value, player_physics_behavior_text, player_physics_behavior_list, lossy_terrain_compression_button, @@ -97,35 +100,84 @@ impl<'a> Widget for Networking<'a> { .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.vd_text, ui); + .set(state.ids.terrain_vd_text, ui); + let terrain_view_distance = self.global_state.settings.graphics.terrain_view_distance; + let server_view_distance_limit = self.server_view_distance_limit.unwrap_or(u32::MAX); if let Some(new_val) = ImageSlider::discrete( - self.global_state.settings.graphics.view_distance, + terrain_view_distance, 1, 65, self.imgs.slider_indicator, self.imgs.slider, ) .w_h(104.0, 22.0) - .down_from(state.ids.vd_text, 8.0) + .down_from(state.ids.terrain_vd_text, 8.0) .track_breadth(12.0) .slider_length(10.0) - .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX)) + .soft_max(server_view_distance_limit) .pad_track((5.0, 5.0)) - .set(state.ids.vd_slider, ui) + .set(state.ids.terrain_vd_slider, ui) { - events.push(NetworkingChange::AdjustViewDistance(new_val)); + events.push(NetworkingChange::AdjustTerrainViewDistance(new_val)); } - Text::new(&format!( - "{}", - self.global_state.settings.graphics.view_distance - )) - .right_from(state.ids.vd_slider, 8.0) + Text::new(&if terrain_view_distance <= server_view_distance_limit { + format!("{terrain_view_distance}") + } else { + format!("{terrain_view_distance} ({server_view_distance_limit})") + }) + .right_from(state.ids.terrain_vd_slider, 8.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.vd_value, ui); + .set(state.ids.terrain_vd_value, ui); + + // Entity View Distance + Text::new( + &self + .localized_strings + .get_msg("hud-settings-entity_view_distance"), + ) + .down_from(state.ids.terrain_vd_slider, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.entity_vd_text, ui); + + let soft_entity_vd_max = self + .server_view_distance_limit + .unwrap_or(u32::MAX) + .min(terrain_view_distance); + let entity_view_distance = self.global_state.settings.graphics.entity_view_distance; + if let Some(new_val) = ImageSlider::discrete( + entity_view_distance, + 1, + 65, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .down_from(state.ids.entity_vd_text, 8.0) + .track_breadth(12.0) + .slider_length(10.0) + .soft_max(soft_entity_vd_max) + .pad_track((5.0, 5.0)) + .set(state.ids.entity_vd_slider, ui) + { + events.push(NetworkingChange::AdjustEntityViewDistance(new_val)); + } + + Text::new(&if entity_view_distance <= soft_entity_vd_max { + format!("{entity_view_distance}") + } else { + format!("{entity_view_distance} ({soft_entity_vd_max})") + }) + .right_from(state.ids.entity_vd_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.entity_vd_value, ui); // Player physics behavior Text::new( @@ -133,7 +185,7 @@ impl<'a> Widget for Networking<'a> { .localized_strings .get_msg("hud-settings-player_physics_behavior"), ) - .down_from(state.ids.vd_slider, 8.0) + .down_from(state.ids.entity_vd_slider, 8.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index 4e56a2dc41..7214d74707 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -36,9 +36,12 @@ widget_ids! { reset_graphics_button, fps_counter, pipeline_recreation_text, - vd_slider, - vd_text, - vd_value, + terrain_vd_slider, + terrain_vd_text, + terrain_vd_value, + entity_vd_slider, + entity_vd_text, + entity_vd_value, ld_slider, ld_text, ld_value, @@ -285,39 +288,126 @@ impl<'a> Widget for Video<'a> { .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.vd_text, ui); + .set(state.ids.terrain_vd_text, ui); + let terrain_view_distance = self.global_state.settings.graphics.terrain_view_distance; + let server_view_distance_limit = self.server_view_distance_limit.unwrap_or(u32::MAX); if let Some(new_val) = ImageSlider::discrete( - self.global_state.settings.graphics.view_distance, + terrain_view_distance, 1, 65, self.imgs.slider_indicator, self.imgs.slider, ) .w_h(104.0, 22.0) - .down_from(state.ids.vd_text, 8.0) + .down_from(state.ids.terrain_vd_text, 8.0) .track_breadth(12.0) .slider_length(10.0) - .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX)) + .soft_max(server_view_distance_limit) .pad_track((5.0, 5.0)) - .set(state.ids.vd_slider, ui) + .set(state.ids.terrain_vd_slider, ui) { - events.push(GraphicsChange::AdjustViewDistance(new_val)); + events.push(GraphicsChange::AdjustTerrainViewDistance(new_val)); } - Text::new(&format!( - "{}", - self.global_state.settings.graphics.view_distance - )) - .right_from(state.ids.vd_slider, 8.0) + Text::new(&if terrain_view_distance <= server_view_distance_limit { + format!("{terrain_view_distance}") + } else { + format!("{terrain_view_distance} ({server_view_distance_limit})") + }) + .right_from(state.ids.terrain_vd_slider, 8.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.vd_value, ui); + .set(state.ids.terrain_vd_value, ui); + + // Entity View Distance + let soft_entity_vd_max = self + .server_view_distance_limit + .unwrap_or(u32::MAX) + .min(terrain_view_distance); + let entity_view_distance = self.global_state.settings.graphics.entity_view_distance; + if let Some(new_val) = ImageSlider::discrete( + entity_view_distance, + 1, + 65, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.terrain_vd_slider, 70.0) + .track_breadth(12.0) + .slider_length(10.0) + .soft_max(soft_entity_vd_max) + .pad_track((5.0, 5.0)) + .set(state.ids.entity_vd_slider, ui) + { + events.push(GraphicsChange::AdjustEntityViewDistance(new_val)); + } + + Text::new( + &self + .localized_strings + .get_msg("hud-settings-entity_view_distance"), + ) + .up_from(state.ids.entity_vd_slider, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.entity_vd_text, ui); + + Text::new(&if entity_view_distance <= soft_entity_vd_max { + format!("{entity_view_distance}") + } else { + format!("{entity_view_distance} ({soft_entity_vd_max})") + }) + .right_from(state.ids.entity_vd_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.entity_vd_value, ui); + + // Sprites VD + if let Some(new_val) = ImageSlider::discrete( + self.global_state.settings.graphics.sprite_render_distance, + 50, + 500, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.entity_vd_slider, 70.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.sprite_dist_slider, ui) + { + events.push(GraphicsChange::AdjustSpriteRenderDistance(new_val)); + } + Text::new( + &self + .localized_strings + .get_msg("hud-settings-sprites_view_distance"), + ) + .up_from(state.ids.sprite_dist_slider, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.sprite_dist_text, ui); + + Text::new(&format!( + "{}", + self.global_state.settings.graphics.sprite_render_distance + )) + .right_from(state.ids.sprite_dist_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.sprite_dist_value, ui); // LoD Distance Text::new(&self.localized_strings.get_msg("hud-settings-lod_distance")) - .down_from(state.ids.vd_slider, 10.0) + .down_from(state.ids.terrain_vd_slider, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) @@ -350,6 +440,50 @@ impl<'a> Widget for Video<'a> { .color(TEXT_COLOR) .set(state.ids.ld_value, ui); + // Figure LOD distance + if let Some(new_val) = ImageSlider::discrete( + self.global_state + .settings + .graphics + .figure_lod_render_distance, + 50, + 500, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.ld_slider, 70.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.figure_dist_slider, ui) + { + events.push(GraphicsChange::AdjustFigureLoDRenderDistance(new_val)); + } + Text::new( + &self + .localized_strings + .get_msg("hud-settings-entities_detail_distance"), + ) + .up_from(state.ids.figure_dist_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.figure_dist_text, ui); + + Text::new(&format!( + "{}", + self.global_state + .settings + .graphics + .figure_lod_render_distance + )) + .right_from(state.ids.figure_dist_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.figure_dist_value, ui); + // Max FPS Text::new(&self.localized_strings.get_msg("hud-settings-maximum_fps")) .down_from(state.ids.ld_slider, 10.0) @@ -392,7 +526,7 @@ impl<'a> Widget for Video<'a> { .get_msg("hud-settings-background_fps"), ) .down_from(state.ids.ld_slider, 10.0) - .right_from(state.ids.max_fps_value, 30.0) + .right_from(state.ids.max_fps_value, 44.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) @@ -655,87 +789,6 @@ impl<'a> Widget for Video<'a> { .color(TEXT_COLOR) .set(state.ids.ambiance_value, ui); - // Sprites VD - if let Some(new_val) = ImageSlider::discrete( - self.global_state.settings.graphics.sprite_render_distance, - 50, - 500, - self.imgs.slider_indicator, - self.imgs.slider, - ) - .w_h(104.0, 22.0) - .right_from(state.ids.vd_slider, 50.0) - .track_breadth(12.0) - .slider_length(10.0) - .pad_track((5.0, 5.0)) - .set(state.ids.sprite_dist_slider, ui) - { - events.push(GraphicsChange::AdjustSpriteRenderDistance(new_val)); - } - Text::new( - &self - .localized_strings - .get_msg("hud-settings-sprites_view_distance"), - ) - .up_from(state.ids.sprite_dist_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.sprite_dist_text, ui); - - Text::new(&format!( - "{}", - self.global_state.settings.graphics.sprite_render_distance - )) - .right_from(state.ids.sprite_dist_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.sprite_dist_value, ui); - // Figure VD - if let Some(new_val) = ImageSlider::discrete( - self.global_state - .settings - .graphics - .figure_lod_render_distance, - 50, - 500, - self.imgs.slider_indicator, - self.imgs.slider, - ) - .w_h(104.0, 22.0) - .right_from(state.ids.sprite_dist_slider, 50.0) - .track_breadth(12.0) - .slider_length(10.0) - .pad_track((5.0, 5.0)) - .set(state.ids.figure_dist_slider, ui) - { - events.push(GraphicsChange::AdjustFigureLoDRenderDistance(new_val)); - } - Text::new( - &self - .localized_strings - .get_msg("hud-settings-figures_view_distance"), - ) - .up_from(state.ids.figure_dist_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.figure_dist_text, ui); - - Text::new(&format!( - "{}", - self.global_state - .settings - .graphics - .figure_lod_render_distance - )) - .right_from(state.ids.figure_dist_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.figure_dist_value, ui); - // AaMode Text::new( &self diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 720e365ebe..a345ad17f0 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -137,10 +137,11 @@ impl PlayState for CharSelectionState { ui::Event::Play(character_id) => { { let mut c = self.client.borrow_mut(); - c.request_character(character_id); - //Send our ViewDistance and LoD distance - c.set_view_distance(global_state.settings.graphics.view_distance); - c.set_lod_distance(global_state.settings.graphics.lod_distance); + let graphics = &global_state.settings.graphics; + c.request_character(character_id, common::ViewDistances { + terrain: graphics.terrain_view_distance, + entity: graphics.entity_view_distance, + }); } return PlayStateResult::Switch(Box::new(SessionState::new( global_state, @@ -150,9 +151,11 @@ impl PlayState for CharSelectionState { ui::Event::Spectate => { { let mut c = self.client.borrow_mut(); - c.request_spectate(); - c.set_view_distance(global_state.settings.graphics.view_distance); - c.set_lod_distance(global_state.settings.graphics.lod_distance); + let graphics = &global_state.settings.graphics; + c.request_spectate(common::ViewDistances { + terrain: graphics.terrain_view_distance, + entity: graphics.entity_view_distance, + }); } return PlayStateResult::Switch(Box::new(SessionState::new( global_state, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 4d1684dbc6..9d79d9c1c0 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -629,7 +629,7 @@ impl FigureMgr { let time = state.get_time() as f32; let tick = scene_data.tick; let ecs = state.ecs(); - let view_distance = scene_data.view_distance; + let view_distance = scene_data.entity_view_distance; let dt = state.get_delta_time(); let dt_lerp = (15.0 * dt).min(1.0); let frustum = camera.frustum(); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index a059025a47..47a50d8e40 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -120,7 +120,8 @@ pub struct SceneData<'a> { pub mutable_viewpoint: bool, pub target_entity: Option, pub loaded_distance: f32, - pub view_distance: u32, + pub terrain_view_distance: u32, // not used currently + pub entity_view_distance: u32, pub tick: u64, pub gamma: f32, pub exposure: f32, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 4cae6af2b1..237c89da82 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1697,7 +1697,11 @@ impl PlayState for SessionState { // Only highlight if interactable target_entity: self.interactable.and_then(Interactable::entity), loaded_distance: client.loaded_distance(), - view_distance: client.view_distance().unwrap_or(1), + terrain_view_distance: client.view_distance().unwrap_or(1), + entity_view_distance: client + .view_distance() + .unwrap_or(1) + .min(global_state.settings.graphics.entity_view_distance), tick: client.get_tick(), gamma: global_state.settings.graphics.gamma, exposure: global_state.settings.graphics.exposure, @@ -1783,7 +1787,11 @@ impl PlayState for SessionState { // Only highlight if interactable target_entity: self.interactable.and_then(Interactable::entity), loaded_distance: client.loaded_distance(), - view_distance: client.view_distance().unwrap_or(1), + terrain_view_distance: client.view_distance().unwrap_or(1), + entity_view_distance: client + .view_distance() + .unwrap_or(1) + .min(settings.graphics.entity_view_distance), tick: client.get_tick(), gamma: settings.graphics.gamma, exposure: settings.graphics.exposure, diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index f53d4738c9..4c82431be8 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -70,7 +70,8 @@ pub enum Gameplay { } #[derive(Clone)] pub enum Graphics { - AdjustViewDistance(u32), + AdjustTerrainViewDistance(u32), + AdjustEntityViewDistance(u32), AdjustLodDistance(u32), AdjustLodDetail(u32), AdjustSpriteRenderDistance(u32), @@ -147,7 +148,8 @@ pub enum Language { } #[derive(Clone)] pub enum Networking { - AdjustViewDistance(u32), + AdjustTerrainViewDistance(u32), + AdjustEntityViewDistance(u32), ChangePlayerPhysicsBehavior { server_authoritative: bool, }, @@ -155,6 +157,8 @@ pub enum Networking { #[cfg(feature = "discord")] ToggleDiscordIntegration(bool), + // TODO: reset option (ensure it handles the entity/terrain vd the same as graphics reset + // option) } #[derive(Clone)] @@ -354,8 +358,11 @@ impl SettingsChange { }, SettingsChange::Graphics(graphics_change) => { match graphics_change { - Graphics::AdjustViewDistance(view_distance) => { - adjust_view_distance(view_distance, global_state, session_state) + Graphics::AdjustTerrainViewDistance(terrain_vd) => { + adjust_terrain_view_distance(terrain_vd, settings, session_state) + }, + Graphics::AdjustEntityViewDistance(entity_vd) => { + adjust_entity_view_distance(entity_vd, settings, session_state) }, Graphics::AdjustLodDistance(lod_distance) => { session_state @@ -428,10 +435,7 @@ impl SettingsChange { settings.graphics = GraphicsSettings::default(); let graphics = &settings.graphics; // View distance - session_state - .client - .borrow_mut() - .set_view_distance(graphics.view_distance); + client_set_view_distance(settings, session_state); // FOV session_state.scene.camera_mut().set_fov_deg(graphics.fov); session_state @@ -599,8 +603,11 @@ impl SettingsChange { }, }, SettingsChange::Networking(networking_change) => match networking_change { - Networking::AdjustViewDistance(view_distance) => { - adjust_view_distance(view_distance, global_state, session_state) + Networking::AdjustTerrainViewDistance(terrain_vd) => { + adjust_terrain_view_distance(terrain_vd, settings, session_state) + }, + Networking::AdjustEntityViewDistance(entity_vd) => { + adjust_entity_view_distance(entity_vd, settings, session_state) }, Networking::ChangePlayerPhysicsBehavior { server_authoritative, @@ -651,15 +658,33 @@ impl SettingsChange { } } -fn adjust_view_distance( - view_distance: u32, - global_state: &mut GlobalState, +use crate::settings::Settings; + +fn adjust_terrain_view_distance( + terrain_vd: u32, + settings: &mut Settings, session_state: &mut SessionState, ) { + settings.graphics.terrain_view_distance = terrain_vd; + client_set_view_distance(settings, session_state); +} + +fn adjust_entity_view_distance( + entity_vd: u32, + settings: &mut Settings, + session_state: &mut SessionState, +) { + settings.graphics.entity_view_distance = entity_vd; + client_set_view_distance(settings, session_state); +} + +fn client_set_view_distance(settings: &Settings, session_state: &mut SessionState) { + let view_distances = common::ViewDistances { + terrain: settings.graphics.terrain_view_distance, + entity: settings.graphics.entity_view_distance, + }; session_state .client .borrow_mut() - .set_view_distance(view_distance); - - global_state.settings.graphics.view_distance = view_distance; + .set_view_distances(view_distances); } diff --git a/voxygen/src/settings/graphics.rs b/voxygen/src/settings/graphics.rs index b2a1b4858c..5feabd793f 100644 --- a/voxygen/src/settings/graphics.rs +++ b/voxygen/src/settings/graphics.rs @@ -29,7 +29,8 @@ impl fmt::Display for Fps { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct GraphicsSettings { - pub view_distance: u32, + pub terrain_view_distance: u32, + pub entity_view_distance: u32, pub lod_distance: u32, pub sprite_render_distance: u32, pub particles_enabled: bool, @@ -50,7 +51,8 @@ pub struct GraphicsSettings { impl Default for GraphicsSettings { fn default() -> Self { Self { - view_distance: 10, + terrain_view_distance: 10, + entity_view_distance: 65, lod_distance: 200, sprite_render_distance: 100, particles_enabled: true, From 37caab539c86cf352a7b88f3a24dba1f98aaa484 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 21 Aug 2022 23:50:46 -0400 Subject: [PATCH 4/9] Replace uses of "65" with `client::MAX_SELECTABLE_VD` in client and voxygen crates. --- client/src/lib.rs | 4 +++- voxygen/src/hud/settings_window/networking.rs | 4 ++-- voxygen/src/hud/settings_window/video.rs | 4 ++-- voxygen/src/settings/graphics.rs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 24b41c8664..a1497901bf 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -81,6 +81,8 @@ use tokio::runtime::Runtime; use tracing::{debug, error, trace, warn}; use vek::*; +pub const MAX_SELECTABLE_VIEW_DISTANCE: u32 = 65; + const PING_ROLLING_AVERAGE_SECS: usize = 10; #[derive(Debug)] @@ -948,7 +950,7 @@ impl Client { pub fn set_view_distances(&mut self, view_distances: common::ViewDistances) { if self.server_view_distance_limit.map_or(true, |limit| view_distances.terrain >= limit) { let view_distances = common::ViewDistances { - terrain: view_distances.terrain.max(1).min(65), + terrain: view_distances.terrain.max(1).min(MAX_SELECTABLE_VIEW_DISTANCE), entity: view_distances.entity, }; self.view_distance = Some(view_distances.terrain); diff --git a/voxygen/src/hud/settings_window/networking.rs b/voxygen/src/hud/settings_window/networking.rs index 3c624ef3a2..b892bbf122 100644 --- a/voxygen/src/hud/settings_window/networking.rs +++ b/voxygen/src/hud/settings_window/networking.rs @@ -107,7 +107,7 @@ impl<'a> Widget for Networking<'a> { if let Some(new_val) = ImageSlider::discrete( terrain_view_distance, 1, - 65, + client::MAX_SELECTABLE_VIEW_DISTANCE, self.imgs.slider_indicator, self.imgs.slider, ) @@ -153,7 +153,7 @@ impl<'a> Widget for Networking<'a> { if let Some(new_val) = ImageSlider::discrete( entity_view_distance, 1, - 65, + client::MAX_SELECTABLE_VIEW_DISTANCE, self.imgs.slider_indicator, self.imgs.slider, ) diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index 7214d74707..c10bd8f620 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -295,7 +295,7 @@ impl<'a> Widget for Video<'a> { if let Some(new_val) = ImageSlider::discrete( terrain_view_distance, 1, - 65, + client::MAX_SELECTABLE_VIEW_DISTANCE, self.imgs.slider_indicator, self.imgs.slider, ) @@ -330,7 +330,7 @@ impl<'a> Widget for Video<'a> { if let Some(new_val) = ImageSlider::discrete( entity_view_distance, 1, - 65, + client::MAX_SELECTABLE_VIEW_DISTANCE, self.imgs.slider_indicator, self.imgs.slider, ) diff --git a/voxygen/src/settings/graphics.rs b/voxygen/src/settings/graphics.rs index 5feabd793f..303bf77f49 100644 --- a/voxygen/src/settings/graphics.rs +++ b/voxygen/src/settings/graphics.rs @@ -52,7 +52,7 @@ impl Default for GraphicsSettings { fn default() -> Self { Self { terrain_view_distance: 10, - entity_view_distance: 65, + entity_view_distance: client::MAX_SELECTABLE_VIEW_DISTANCE, lod_distance: 200, sprite_render_distance: 100, particles_enabled: true, From cc1de8334d331e5c5aebf9068f191abc146786ac Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 22 Aug 2022 00:42:34 -0400 Subject: [PATCH 5/9] Clean up common/src/lib.rs to avoid repetitive wasm cfgs. Not many lines actually removed but it looks a lot better IMO. --- common/src/lib.rs | 145 +++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index 8b8e35b224..6cf1b69302 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -18,85 +18,82 @@ )] #![feature(hash_drain_filter)] -/// Re-exported crates -#[cfg(not(target_arch = "wasm32"))] -pub use uuid; +macro_rules! cfg_not_wasm { + ($($item:item)*) => { + $( + #[cfg(not(target_arch = "wasm32"))] + $item + )* + } +} + +// Re-exported crates +cfg_not_wasm! { + pub use common_assets as assets; + pub use uuid; +} + +// Modules -// modules -#[cfg(not(target_arch = "wasm32"))] -pub use common_assets as assets; -#[cfg(not(target_arch = "wasm32"))] pub mod astar; -#[cfg(not(target_arch = "wasm32"))] -mod cached_spatial_grid; -#[cfg(not(target_arch = "wasm32"))] -pub mod calendar; -#[cfg(not(target_arch = "wasm32"))] -pub mod character; -#[cfg(not(target_arch = "wasm32"))] pub mod clock; -#[cfg(not(target_arch = "wasm32"))] pub mod cmd; pub mod combat; pub mod comp; pub mod consts; -#[cfg(not(target_arch = "wasm32"))] pub mod depot; -#[cfg(not(target_arch = "wasm32"))] -pub mod effect; -#[cfg(not(target_arch = "wasm32"))] pub mod event; -#[cfg(not(target_arch = "wasm32"))] -pub mod explosion; -#[cfg(not(target_arch = "wasm32"))] -pub mod figure; -#[cfg(not(target_arch = "wasm32"))] -pub mod generation; -#[cfg(not(target_arch = "wasm32"))] pub mod grid; -#[cfg(not(target_arch = "wasm32"))] pub mod link; -#[cfg(not(target_arch = "wasm32"))] pub mod lod; -#[cfg(not(target_arch = "wasm32"))] -pub mod lottery; -#[cfg(not(target_arch = "wasm32"))] -pub mod mounting; -#[cfg(not(target_arch = "wasm32"))] pub mod npc; -#[cfg(not(target_arch = "wasm32"))] -pub mod outcome; -#[cfg(not(target_arch = "wasm32"))] pub mod path; -#[cfg(not(target_arch = "wasm32"))] pub mod ray; -#[cfg(not(target_arch = "wasm32"))] -pub mod recipe; -#[cfg(not(target_arch = "wasm32"))] -pub mod region; pub mod resources; -#[cfg(not(target_arch = "wasm32"))] pub mod rtsim; -#[cfg(not(target_arch = "wasm32"))] -pub mod skillset_builder; -#[cfg(not(target_arch = "wasm32"))] -pub mod slowjob; -#[cfg(not(target_arch = "wasm32"))] -pub mod spiral; -#[cfg(not(target_arch = "wasm32"))] -pub mod states; -#[cfg(not(target_arch = "wasm32"))] pub mod store; -#[cfg(not(target_arch = "wasm32"))] -pub mod terrain; -#[cfg(not(target_arch = "wasm32"))] pub mod time; -#[cfg(not(target_arch = "wasm32"))] pub mod trade; -#[cfg(not(target_arch = "wasm32"))] pub mod typed; pub mod uid; -#[cfg(not(target_arch = "wasm32"))] pub mod util; -mod view_distances; -#[cfg(not(target_arch = "wasm32"))] pub mod vol; -#[cfg(not(target_arch = "wasm32"))] -pub mod volumes; -#[cfg(not(target_arch = "wasm32"))] -pub mod weather; -#[cfg(not(target_arch = "wasm32"))] -pub use cached_spatial_grid::CachedSpatialGrid; -#[cfg(not(target_arch = "wasm32"))] -pub use combat::{Damage, GroupTarget, Knockback, KnockbackDir}; +// NOTE: Comment out macro to get rustfmt to re-order these as needed. +cfg_not_wasm! { + pub mod astar; + pub mod calendar; + pub mod character; + pub mod clock; + pub mod cmd; + pub mod depot; + pub mod effect; + pub mod event; + pub mod explosion; + pub mod figure; + pub mod generation; + pub mod grid; + pub mod link; + pub mod lod; + pub mod lottery; + pub mod mounting; + pub mod npc; + pub mod outcome; + pub mod path; + pub mod ray; + pub mod recipe; + pub mod region; + pub mod rtsim; + pub mod skillset_builder; + pub mod slowjob; + pub mod spiral; + pub mod states; + pub mod store; + pub mod terrain; + pub mod time; + pub mod trade; + pub mod util; + pub mod vol; + pub mod volumes; + pub mod weather; + + mod cached_spatial_grid; + mod view_distances; +} + +// We declare a macro in this module so there are issues referring to it by path +// within this crate if typed module is declared in macro expansion. +#[cfg(not(target_arch = "wasm32"))] pub mod typed; + pub use combat::{DamageKind, DamageSource}; -#[cfg(not(target_arch = "wasm32"))] -pub use comp::inventory::loadout_builder::LoadoutBuilder; -#[cfg(not(target_arch = "wasm32"))] -pub use explosion::{Explosion, RadiusEffect}; -#[cfg(not(target_arch = "wasm32"))] -pub use skillset_builder::SkillSetBuilder; -pub use view_distances::ViewDistances; + +cfg_not_wasm! { + pub use cached_spatial_grid::CachedSpatialGrid; + pub use combat::{Damage, GroupTarget, Knockback, KnockbackDir}; + pub use comp::inventory::loadout_builder::LoadoutBuilder; + pub use explosion::{Explosion, RadiusEffect}; + pub use skillset_builder::SkillSetBuilder; + pub use view_distances::ViewDistances; +} From e626519ec0f1bd1ac89bb240c926af2025b23a8d Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 22 Aug 2022 23:20:32 -0400 Subject: [PATCH 6/9] Replace "Present Mode" options with more user friendly terms: 'Fifo' -> 'Vsync capped' 'Mailbox' -> 'Vsync uncapped' 'Immediate' -> 'Vsync off' --- assets/voxygen/i18n/en/hud/settings.ftl | 6 +++--- voxygen/src/hud/settings_window/video.rs | 19 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl index e9c78472c5..cb895bebe5 100644 --- a/assets/voxygen/i18n/en/hud/settings.ftl +++ b/assets/voxygen/i18n/en/hud/settings.ftl @@ -61,9 +61,9 @@ hud-settings-entities_detail_distance = Entities Detail Distance hud-settings-maximum_fps = Maximum FPS hud-settings-background_fps = Background FPS hud-settings-present_mode = Present Mode -hud-settings-present_mode-fifo = Fifo -hud-settings-present_mode-mailbox = Mailbox -hud-settings-present_mode-immediate = Immediate +hud-settings-present_mode-vsync_capped = Vsync capped +hud-settings-present_mode-vsync_uncapped = Vsync uncapped +hud-settings-present_mode-vsync_off = Vsync off hud-settings-fov = Field of View (deg) hud-settings-gamma = Gamma hud-settings-exposure = Exposure diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c10bd8f620..6b445957a5 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -574,7 +574,7 @@ impl<'a> Widget for Video<'a> { // Present Mode Text::new(&self.localized_strings.get_msg("hud-settings-present_mode")) .down_from(state.ids.ld_slider, 10.0) - .right_from(state.ids.max_background_fps_value, 30.0) + .right_from(state.ids.max_background_fps_value, 40.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) @@ -586,16 +586,11 @@ impl<'a> Widget for Video<'a> { PresentMode::Immediate, ]; let mode_label_list = [ - &self - .localized_strings - .get_msg("hud-settings-present_mode-fifo"), - &self - .localized_strings - .get_msg("hud-settings-present_mode-mailbox"), - &self - .localized_strings - .get_msg("hud-settings-present_mode-immediate"), - ]; + "hud-settings-present_mode-vsync_capped", + "hud-settings-present_mode-vsync_uncapped", + "hud-settings-present_mode-vsync_off", + ] + .map(|k| self.localized_strings.get_msg(k)); // Get which present mode is currently active let selected = mode_list @@ -603,7 +598,7 @@ impl<'a> Widget for Video<'a> { .position(|x| *x == render_mode.present_mode); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) - .w_h(120.0, 22.0) + .w_h(150.0, 26.0) .color(MENU_BG) .label_color(TEXT_COLOR) .label_font_id(self.fonts.cyri.conrod_id) From 4126194a5c7ef7241f73292fb0ce19cf268e5c9f Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 23 Aug 2022 00:46:52 -0400 Subject: [PATCH 7/9] View distances small fixes and improvement: * Properly set view_distance field in Client when sending it to the server in request_character/request_spectator. * Removed invalid check I had included in Client::set_view_distance * ViewDistances::clamp now clamps min to 1 for both types of view distance. --- client/src/lib.rs | 36 +++++++++++++++++++++++++++--------- common/src/view_distances.rs | 6 ++++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index a1497901bf..1138e9ab4f 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -884,7 +884,12 @@ impl Client { } /// Request a state transition to `ClientState::Character`. - pub fn request_character(&mut self, character_id: CharacterId, view_distances: common::ViewDistances) { + pub fn request_character( + &mut self, + character_id: CharacterId, + view_distances: common::ViewDistances, + ) { + let view_distances = self.set_view_distances_local(view_distances); self.send_msg(ClientGeneral::Character(character_id, view_distances)); // Assume we are in_game unless server tells us otherwise @@ -893,6 +898,7 @@ impl Client { /// Request a state transition to `ClientState::Spectate`. pub fn request_spectate(&mut self, view_distances: common::ViewDistances) { + let view_distances = self.set_view_distances_local(view_distances); self.send_msg(ClientGeneral::Spectate(view_distances)); self.presence = Some(PresenceKind::Spectator); @@ -948,14 +954,26 @@ impl Client { } pub fn set_view_distances(&mut self, view_distances: common::ViewDistances) { - if self.server_view_distance_limit.map_or(true, |limit| view_distances.terrain >= limit) { - let view_distances = common::ViewDistances { - terrain: view_distances.terrain.max(1).min(MAX_SELECTABLE_VIEW_DISTANCE), - entity: view_distances.entity, - }; - self.view_distance = Some(view_distances.terrain); - self.send_msg(ClientGeneral::SetViewDistance(view_distances)); - } + let view_distances = self.set_view_distances_local(view_distances); + self.send_msg(ClientGeneral::SetViewDistance(view_distances)); + } + + /// Clamps provided view distances, locally sets the terrain view distance + /// in the client's properties and returns the clamped values for the + /// caller to send to the server. + fn set_view_distances_local( + &mut self, + view_distances: common::ViewDistances, + ) -> common::ViewDistances { + let view_distances = common::ViewDistances { + terrain: view_distances + .terrain + .max(1) + .min(MAX_SELECTABLE_VIEW_DISTANCE), + entity: view_distances.entity.max(1), + }; + self.view_distance = Some(view_distances.terrain); + view_distances } pub fn set_lod_distance(&mut self, lod_distance: u32) { diff --git a/common/src/view_distances.rs b/common/src/view_distances.rs index 94416e5886..484ab496c0 100644 --- a/common/src/view_distances.rs +++ b/common/src/view_distances.rs @@ -13,11 +13,13 @@ pub struct ViewDistances { impl ViewDistances { /// Clamps the terrain view distance to an optional max and clamps the /// entity view distance to the resulting terrain view distance. + /// + /// Also ensures both are at a minimum of 1 (unless the provided max is 0). pub fn clamp(self, max: Option) -> Self { - let terrain = max.unwrap_or(u32::MAX).min(self.terrain); + let terrain = self.terrain.max(1).min(max.unwrap_or(u32::MAX)); Self { terrain, - entity: self.entity.min(terrain), + entity: self.entity.max(1).min(terrain), } } } From 7d88baef42e9c7c12721af73418f9c4ff3488ba7 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 23 Aug 2022 01:07:56 -0400 Subject: [PATCH 8/9] Move up changelog entries that were added after the latest release --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab55163cb4..6491d9ad4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Setting for disabling flashing lights +- Spectate mode for moderators. - Currently playing music track and artist now shows in the debug menu. - Added a setting to influence the gap between music track plays. - Added a Craft All button. @@ -25,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed ### Fixed +- Fixed npc not handling interactions while fighting (especially merchants in trade) +- Fixed bug where you would still be burning after dying in lava. +- Workaround for rayon bug that caused lag spikes in slowjobs - Fixed crash due to zooming out very far - Client properly knows trade was cancelled when exiting to the character screen (and no longer tries to display the trade window when rejoining) @@ -68,8 +73,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - More varied ambient birdcalls - Cave biomes - Updated the Polish translation -- Setting for disabling flashing lights -- Spectate mode for moderators. ### Changed @@ -113,9 +116,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Combat music now loops and ends properly - Modular weapons now have a selling price - Closing a subwindow now only regrabs the cursor if no other subwindow requires it. -- Fixed npc not handling interactions while fighting (especially merchants in trade) -- Fixed bug where you would still be burning after dying in lava. -- Workaround for rayon bug that caused lag spikes in slowjobs ## [0.12.0] - 2022-02-19 From d3c90f9078ca63c821e1b0d4c6e6fd6fbb0706c0 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 23 Aug 2022 01:22:15 -0400 Subject: [PATCH 9/9] Update changelog, update client/src/bin/* with changes in Client API --- CHANGELOG.md | 15 ++++++++++++++- client/src/bin/bot/main.rs | 5 ++++- client/src/bin/swarm/main.rs | 5 ++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6491d9ad4c..bad552cbf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a setting to influence the gap between music track plays. - Added a Craft All button. - Server: Vacuum database on startup -- SeaChapel, greek/latin inspired dungeon for ocean biome coasts +- SeaChapel, greek/latin inspired dungeon for ocean biome coasts +- Entity view distance setting added (shown in graphics and network tabs). This setting controls + the distance at which entities are synced to the client and which entities are displayed in. + This is clamped to be no more than the current overall view distance setting. +- View distance settings that are lowered by the server limit (or other factors) now display an + extra ghost slider cursor when set above the limit (instead of snapping back to the limit). + Limits on the view distance by the server no longer affect the settings saved on the client. ### Changed - Use fluent for translations @@ -23,6 +29,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - /kill_npcs no longer leaves drops behind and also has bug causing it to not destroy entities fixed. - Default present mode changed to Fifo (aka 'Vsync capped'). +- Old "Entity View Distance" setting renamed to "Entity Detail Distance" (since this controls the + distance at which lower detail models are used for entities). +- Present mode options renamed for clarity: Fifo -> 'Vsync capped', Mailbox -> 'Vsync uncapped', + Immediate -> 'Vsync off'. ### Removed @@ -35,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 tries to display the trade window when rejoining) - Cancel trades for an entity when it is deleted (note this doesn't effect trades between players since their entities are not removed). +- Fixed bug where the view distance selection was not immediately applied to entity syncing when + first joining a server and when changing the view distance (previously this required moving to a + new chunk for the initial setting or subsequent change to apply). ## [0.13.0] - 2022-07-23 diff --git a/client/src/bin/bot/main.rs b/client/src/bin/bot/main.rs index d4529199f2..ce4bacff73 100644 --- a/client/src/bin/bot/main.rs +++ b/client/src/bin/bot/main.rs @@ -221,7 +221,10 @@ impl BotClient { let c = list.characters.get(0).unwrap(); if let Some(id) = c.character.id { - client.request_character(id); + client.request_character(id, common::ViewDistances { + terrain: 5, + entity: 5, + }); } } info!("ingame done"); diff --git a/client/src/bin/swarm/main.rs b/client/src/bin/swarm/main.rs index c776f8186f..90e345f6ad 100644 --- a/client/src/bin/swarm/main.rs +++ b/client/src/bin/swarm/main.rs @@ -111,7 +111,6 @@ fn run_client( let mut client = runtime .block_on(Client::new(addr, runtime_clone, &mut None)) .expect("Failed to connect to the server"); - client.set_view_distance(opt.vd); // Login // NOTE: use a no-auth server @@ -159,6 +158,10 @@ fn run_client( .character .id .expect("Why is this an option?"), + common::ViewDistances { + terrain: opt.vd, + entity: opt.vd, + }, ); // If this is the admin client then adminify the other swarm members