mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/subscription-vd-fix' into 'master'
Fix issue with the region subscription system not updating on view distance changes and other semi related changes See merge request veloren/veloren!3557
This commit is contained in:
commit
bc51d4d2a7
25
CHANGELOG.md
25
CHANGELOG.md
@ -8,11 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Setting for disabling flashing lights
|
||||||
|
- Spectate mode for moderators.
|
||||||
- Currently playing music track and artist now shows in the debug menu.
|
- 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 setting to influence the gap between music track plays.
|
||||||
- Added a Craft All button.
|
- Added a Craft All button.
|
||||||
- Server: Vacuum database on startup
|
- 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
|
### Changed
|
||||||
- Use fluent for translations
|
- Use fluent for translations
|
||||||
@ -21,15 +29,25 @@ 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
|
- /kill_npcs no longer leaves drops behind and also has bug causing it to not destroy entities
|
||||||
fixed.
|
fixed.
|
||||||
- Default present mode changed to Fifo (aka 'Vsync capped').
|
- 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
|
### Removed
|
||||||
|
|
||||||
### Fixed
|
### 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
|
- Fixed crash due to zooming out very far
|
||||||
- Client properly knows trade was cancelled when exiting to the character screen (and no longer
|
- Client properly knows trade was cancelled when exiting to the character screen (and no longer
|
||||||
tries to display the trade window when rejoining)
|
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
|
- Cancel trades for an entity when it is deleted (note this doesn't effect trades between players
|
||||||
since their entities are not removed).
|
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
|
## [0.13.0] - 2022-07-23
|
||||||
|
|
||||||
@ -68,8 +86,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- More varied ambient birdcalls
|
- More varied ambient birdcalls
|
||||||
- Cave biomes
|
- Cave biomes
|
||||||
- Updated the Polish translation
|
- Updated the Polish translation
|
||||||
- Setting for disabling flashing lights
|
|
||||||
- Spectate mode for moderators.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -113,9 +129,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Combat music now loops and ends properly
|
- Combat music now loops and ends properly
|
||||||
- Modular weapons now have a selling price
|
- Modular weapons now have a selling price
|
||||||
- Closing a subwindow now only regrabs the cursor if no other subwindow requires it.
|
- 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
|
## [0.12.0] - 2022-02-19
|
||||||
|
|
||||||
|
@ -54,15 +54,16 @@ hud-settings-auto_camera = Auto camera
|
|||||||
hud-settings-bow_zoom = Zoom in when charging bow
|
hud-settings-bow_zoom = Zoom in when charging bow
|
||||||
hud-settings-reset_gameplay = Reset to Defaults
|
hud-settings-reset_gameplay = Reset to Defaults
|
||||||
hud-settings-view_distance = View Distance
|
hud-settings-view_distance = View Distance
|
||||||
|
hud-settings-entity_view_distance = Entities View Distance
|
||||||
hud-settings-lod_distance = LoD Distance
|
hud-settings-lod_distance = LoD Distance
|
||||||
hud-settings-sprites_view_distance = Sprites View 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-maximum_fps = Maximum FPS
|
||||||
hud-settings-background_fps = Background FPS
|
hud-settings-background_fps = Background FPS
|
||||||
hud-settings-present_mode = Present Mode
|
hud-settings-present_mode = Present Mode
|
||||||
hud-settings-present_mode-fifo = Fifo
|
hud-settings-present_mode-vsync_capped = Vsync capped
|
||||||
hud-settings-present_mode-mailbox = Mailbox
|
hud-settings-present_mode-vsync_uncapped = Vsync uncapped
|
||||||
hud-settings-present_mode-immediate = Immediate
|
hud-settings-present_mode-vsync_off = Vsync off
|
||||||
hud-settings-fov = Field of View (deg)
|
hud-settings-fov = Field of View (deg)
|
||||||
hud-settings-gamma = Gamma
|
hud-settings-gamma = Gamma
|
||||||
hud-settings-exposure = Exposure
|
hud-settings-exposure = Exposure
|
||||||
|
@ -221,7 +221,10 @@ impl BotClient {
|
|||||||
|
|
||||||
let c = list.characters.get(0).unwrap();
|
let c = list.characters.get(0).unwrap();
|
||||||
if let Some(id) = c.character.id {
|
if let Some(id) = c.character.id {
|
||||||
client.request_character(id);
|
client.request_character(id, common::ViewDistances {
|
||||||
|
terrain: 5,
|
||||||
|
entity: 5,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("ingame done");
|
info!("ingame done");
|
||||||
|
@ -111,7 +111,6 @@ fn run_client(
|
|||||||
let mut client = runtime
|
let mut client = runtime
|
||||||
.block_on(Client::new(addr, runtime_clone, &mut None))
|
.block_on(Client::new(addr, runtime_clone, &mut None))
|
||||||
.expect("Failed to connect to the server");
|
.expect("Failed to connect to the server");
|
||||||
client.set_view_distance(opt.vd);
|
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
// NOTE: use a no-auth server
|
// NOTE: use a no-auth server
|
||||||
@ -159,6 +158,10 @@ fn run_client(
|
|||||||
.character
|
.character
|
||||||
.id
|
.id
|
||||||
.expect("Why is this an option?"),
|
.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
|
// If this is the admin client then adminify the other swarm members
|
||||||
|
@ -81,6 +81,8 @@ use tokio::runtime::Runtime;
|
|||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
pub const MAX_SELECTABLE_VIEW_DISTANCE: u32 = 65;
|
||||||
|
|
||||||
const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -248,6 +250,8 @@ pub struct Client {
|
|||||||
tick: u64,
|
tick: u64,
|
||||||
state: State,
|
state: State,
|
||||||
|
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
|
/// Terrrain view distance
|
||||||
view_distance: Option<u32>,
|
view_distance: Option<u32>,
|
||||||
lod_distance: f32,
|
lod_distance: f32,
|
||||||
// TODO: move into voxygen
|
// TODO: move into voxygen
|
||||||
@ -707,6 +711,7 @@ impl Client {
|
|||||||
|
|
||||||
tick: 0,
|
tick: 0,
|
||||||
state,
|
state,
|
||||||
|
server_view_distance_limit: None,
|
||||||
view_distance: None,
|
view_distance: None,
|
||||||
lod_distance: 4.0,
|
lod_distance: 4.0,
|
||||||
loaded_distance: 0.0,
|
loaded_distance: 0.0,
|
||||||
@ -803,8 +808,8 @@ impl Client {
|
|||||||
| ClientGeneral::CreateCharacter { .. }
|
| ClientGeneral::CreateCharacter { .. }
|
||||||
| ClientGeneral::EditCharacter { .. }
|
| ClientGeneral::EditCharacter { .. }
|
||||||
| ClientGeneral::DeleteCharacter(_)
|
| ClientGeneral::DeleteCharacter(_)
|
||||||
| ClientGeneral::Character(_)
|
| ClientGeneral::Character(_, _)
|
||||||
| ClientGeneral::Spectate => &mut self.character_screen_stream,
|
| ClientGeneral::Spectate(_) => &mut self.character_screen_stream,
|
||||||
//Only in game
|
//Only in game
|
||||||
ClientGeneral::ControllerInputs(_)
|
ClientGeneral::ControllerInputs(_)
|
||||||
| ClientGeneral::ControlEvent(_)
|
| ClientGeneral::ControlEvent(_)
|
||||||
@ -879,16 +884,22 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request a state transition to `ClientState::Character`.
|
/// Request a state transition to `ClientState::Character`.
|
||||||
pub fn request_character(&mut self, character_id: CharacterId) {
|
pub fn request_character(
|
||||||
self.send_msg(ClientGeneral::Character(character_id));
|
&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
|
// Assume we are in_game unless server tells us otherwise
|
||||||
self.presence = Some(PresenceKind::Character(character_id));
|
self.presence = Some(PresenceKind::Character(character_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request a state transition to `ClientState::Spectate`.
|
/// Request a state transition to `ClientState::Spectate`.
|
||||||
pub fn request_spectate(&mut self) {
|
pub fn request_spectate(&mut self, view_distances: common::ViewDistances) {
|
||||||
self.send_msg(ClientGeneral::Spectate);
|
let view_distances = self.set_view_distances_local(view_distances);
|
||||||
|
self.send_msg(ClientGeneral::Spectate(view_distances));
|
||||||
|
|
||||||
self.presence = Some(PresenceKind::Spectator);
|
self.presence = Some(PresenceKind::Spectator);
|
||||||
}
|
}
|
||||||
@ -942,10 +953,27 @@ impl Client {
|
|||||||
self.send_msg(ClientGeneral::ExitInGame);
|
self.send_msg(ClientGeneral::ExitInGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_view_distance(&mut self, view_distance: u32) {
|
pub fn set_view_distances(&mut self, view_distances: common::ViewDistances) {
|
||||||
let view_distance = view_distance.max(1).min(65);
|
let view_distances = self.set_view_distances_local(view_distances);
|
||||||
self.view_distance = Some(view_distance);
|
self.send_msg(ClientGeneral::SetViewDistance(view_distances));
|
||||||
self.send_msg(ClientGeneral::SetViewDistance(view_distance));
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
pub fn set_lod_distance(&mut self, lod_distance: u32) {
|
||||||
@ -1486,6 +1514,8 @@ impl Client {
|
|||||||
|
|
||||||
pub fn view_distance(&self) -> Option<u32> { self.view_distance }
|
pub fn view_distance(&self) -> Option<u32> { self.view_distance }
|
||||||
|
|
||||||
|
pub fn server_view_distance_limit(&self) -> Option<u32> { self.server_view_distance_limit }
|
||||||
|
|
||||||
pub fn loaded_distance(&self) -> f32 { self.loaded_distance }
|
pub fn loaded_distance(&self) -> f32 { self.loaded_distance }
|
||||||
|
|
||||||
pub fn position(&self) -> Option<Vec3<f32>> {
|
pub fn position(&self) -> Option<Vec3<f32>> {
|
||||||
@ -2254,6 +2284,9 @@ impl Client {
|
|||||||
ServerGeneral::SetViewDistance(vd) => {
|
ServerGeneral::SetViewDistance(vd) => {
|
||||||
self.view_distance = Some(vd);
|
self.view_distance = Some(vd);
|
||||||
frontend_events.push(Event::SetViewDistance(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) => {
|
ServerGeneral::Outcomes(outcomes) => {
|
||||||
frontend_events.extend(outcomes.into_iter().map(Event::Outcome))
|
frontend_events.extend(outcomes.into_iter().map(Event::Outcome))
|
||||||
@ -2348,18 +2381,10 @@ impl Client {
|
|||||||
ServerGeneral::CharacterEdited(character_id) => {
|
ServerGeneral::CharacterEdited(character_id) => {
|
||||||
events.push(Event::CharacterEdited(character_id));
|
events.push(Event::CharacterEdited(character_id));
|
||||||
},
|
},
|
||||||
ServerGeneral::CharacterSuccess => {
|
ServerGeneral::CharacterSuccess => debug!("client is now in ingame state on server"),
|
||||||
debug!("client is now in ingame state on server");
|
|
||||||
if let Some(vd) = self.view_distance {
|
|
||||||
self.set_view_distance(vd);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ServerGeneral::SpectatorSuccess(spawn_point) => {
|
ServerGeneral::SpectatorSuccess(spawn_point) => {
|
||||||
if let Some(vd) = self.view_distance {
|
events.push(Event::StartSpectate(spawn_point));
|
||||||
events.push(Event::StartSpectate(spawn_point));
|
debug!("client is now in ingame state on server");
|
||||||
debug!("client is now in ingame state on server");
|
|
||||||
self.set_view_distance(vd);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => unreachable!("Not a character_screen msg"),
|
_ => unreachable!("Not a character_screen msg"),
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
comp::{Skill, SkillGroupKind},
|
comp::{Skill, SkillGroupKind},
|
||||||
terrain::block::Block,
|
terrain::block::Block,
|
||||||
|
ViewDistances,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -60,13 +61,13 @@ pub enum ClientGeneral {
|
|||||||
alias: String,
|
alias: String,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
},
|
},
|
||||||
Character(CharacterId),
|
Character(CharacterId, ViewDistances),
|
||||||
Spectate,
|
Spectate(ViewDistances),
|
||||||
//Only in game
|
//Only in game
|
||||||
ControllerInputs(Box<comp::ControllerInputs>),
|
ControllerInputs(Box<comp::ControllerInputs>),
|
||||||
ControlEvent(comp::ControlEvent),
|
ControlEvent(comp::ControlEvent),
|
||||||
ControlAction(comp::ControlAction),
|
ControlAction(comp::ControlAction),
|
||||||
SetViewDistance(u32),
|
SetViewDistance(ViewDistances),
|
||||||
BreakBlock(Vec3<i32>),
|
BreakBlock(Vec3<i32>),
|
||||||
PlaceBlock(Vec3<i32>, Block),
|
PlaceBlock(Vec3<i32>, Block),
|
||||||
ExitInGame,
|
ExitInGame,
|
||||||
@ -121,7 +122,7 @@ impl ClientMsg {
|
|||||||
| ClientGeneral::DeleteCharacter(_) => {
|
| ClientGeneral::DeleteCharacter(_) => {
|
||||||
c_type != ClientType::ChatOnly && presence.is_none()
|
c_type != ClientType::ChatOnly && presence.is_none()
|
||||||
},
|
},
|
||||||
ClientGeneral::Character(_) | ClientGeneral::Spectate => {
|
ClientGeneral::Character(_, _) | ClientGeneral::Spectate(_) => {
|
||||||
c_type == ClientType::Game && presence.is_none()
|
c_type == ClientType::Game && presence.is_none()
|
||||||
},
|
},
|
||||||
//Only in game
|
//Only in game
|
||||||
|
@ -166,6 +166,10 @@ pub enum ServerGeneral {
|
|||||||
/// from an ingame state
|
/// from an ingame state
|
||||||
ExitInGameSuccess,
|
ExitInGameSuccess,
|
||||||
InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent),
|
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),
|
SetViewDistance(u32),
|
||||||
Outcomes(Vec<Outcome>),
|
Outcomes(Vec<Outcome>),
|
||||||
Knockback(Vec3<f32>),
|
Knockback(Vec3<f32>),
|
||||||
|
@ -107,8 +107,9 @@ pub enum ServerEvent {
|
|||||||
InitCharacterData {
|
InitCharacterData {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
character_id: CharacterId,
|
character_id: CharacterId,
|
||||||
|
requested_view_distances: crate::ViewDistances,
|
||||||
},
|
},
|
||||||
InitSpectator(EcsEntity),
|
InitSpectator(EcsEntity, crate::ViewDistances),
|
||||||
UpdateCharacterData {
|
UpdateCharacterData {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
components: (
|
components: (
|
||||||
|
@ -18,83 +18,82 @@
|
|||||||
)]
|
)]
|
||||||
#![feature(hash_drain_filter)]
|
#![feature(hash_drain_filter)]
|
||||||
|
|
||||||
/// Re-exported crates
|
macro_rules! cfg_not_wasm {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
($($item:item)*) => {
|
||||||
pub use uuid;
|
$(
|
||||||
|
#[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 combat;
|
||||||
pub mod comp;
|
pub mod comp;
|
||||||
pub mod consts;
|
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;
|
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;
|
pub mod uid;
|
||||||
#[cfg(not(target_arch = "wasm32"))] pub mod util;
|
|
||||||
#[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"))]
|
// NOTE: Comment out macro to get rustfmt to re-order these as needed.
|
||||||
pub use cached_spatial_grid::CachedSpatialGrid;
|
cfg_not_wasm! {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
pub mod astar;
|
||||||
pub use combat::{Damage, GroupTarget, Knockback, KnockbackDir};
|
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};
|
pub use combat::{DamageKind, DamageSource};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub use comp::inventory::loadout_builder::LoadoutBuilder;
|
cfg_not_wasm! {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
pub use cached_spatial_grid::CachedSpatialGrid;
|
||||||
pub use explosion::{Explosion, RadiusEffect};
|
pub use combat::{Damage, GroupTarget, Knockback, KnockbackDir};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
pub use comp::inventory::loadout_builder::LoadoutBuilder;
|
||||||
pub use skillset_builder::SkillSetBuilder;
|
pub use explosion::{Explosion, RadiusEffect};
|
||||||
|
pub use skillset_builder::SkillSetBuilder;
|
||||||
|
pub use view_distances::ViewDistances;
|
||||||
|
}
|
||||||
|
25
common/src/view_distances.rs
Normal file
25
common/src/view_distances.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#[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.
|
||||||
|
///
|
||||||
|
/// Also ensures both are at a minimum of 1 (unless the provided max is 0).
|
||||||
|
pub fn clamp(self, max: Option<u32>) -> Self {
|
||||||
|
let terrain = self.terrain.max(1).min(max.unwrap_or(u32::MAX));
|
||||||
|
Self {
|
||||||
|
terrain,
|
||||||
|
entity: self.entity.max(1).min(terrain),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ use common::{
|
|||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
|
ViewDistances,
|
||||||
};
|
};
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||||
@ -29,12 +30,29 @@ pub fn handle_initialize_character(
|
|||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
character_id: CharacterId,
|
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) {
|
pub fn handle_initialize_spectator(
|
||||||
server.state.initialize_spectator_data(entity);
|
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);
|
sys::subscription::initialize_region_subscription(server.state.ecs(), entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +140,16 @@ impl Server {
|
|||||||
ServerEvent::InitCharacterData {
|
ServerEvent::InitCharacterData {
|
||||||
entity,
|
entity,
|
||||||
character_id,
|
character_id,
|
||||||
} => handle_initialize_character(self, entity, character_id),
|
requested_view_distances,
|
||||||
ServerEvent::InitSpectator(entity) => handle_initialize_spectator(self, entity),
|
} => 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 } => {
|
ServerEvent::UpdateCharacterData { entity, components } => {
|
||||||
let (
|
let (
|
||||||
body,
|
body,
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
use common_net::msg::PresenceKind;
|
use common_net::msg::PresenceKind;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DenseVecStorage, DerefFlaggedStorage, NullStorage, VecStorage};
|
use specs::{Component, NullStorage};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Debug)]
|
||||||
pub struct Presence {
|
pub struct Presence {
|
||||||
pub view_distance: u32,
|
pub terrain_view_distance: ViewDistance,
|
||||||
|
pub entity_view_distance: ViewDistance,
|
||||||
pub kind: PresenceKind,
|
pub kind: PresenceKind,
|
||||||
pub lossy_terrain_compression: bool,
|
pub lossy_terrain_compression: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Presence {
|
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 {
|
Self {
|
||||||
view_distance,
|
terrain_view_distance: ViewDistance::new(view_distances.terrain, now),
|
||||||
|
entity_view_distance: ViewDistance::new(view_distances.entity, now),
|
||||||
kind,
|
kind,
|
||||||
lossy_terrain_compression: false,
|
lossy_terrain_compression: false,
|
||||||
}
|
}
|
||||||
@ -22,8 +26,7 @@ impl Presence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Presence {
|
impl Component for Presence {
|
||||||
// Presence seems <= 64 bits, so it isn't worth using DenseVecStorage.
|
type Storage = specs::DenseVecStorage<Self>;
|
||||||
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distance from fuzzy_chunk before snapping to current chunk
|
// Distance from fuzzy_chunk before snapping to current chunk
|
||||||
@ -34,11 +37,12 @@ pub const REGION_FUZZ: u32 = 16;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RegionSubscription {
|
pub struct RegionSubscription {
|
||||||
pub fuzzy_chunk: Vec2<i32>,
|
pub fuzzy_chunk: Vec2<i32>,
|
||||||
|
pub last_entity_view_distance: u32,
|
||||||
pub regions: HashSet<Vec2<i32>>,
|
pub regions: HashSet<Vec2<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for RegionSubscription {
|
impl Component for RegionSubscription {
|
||||||
type Storage = DerefFlaggedStorage<Self, DenseVecStorage<Self>>;
|
type Storage = specs::DenseVecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
@ -47,3 +51,88 @@ pub struct RepositionOnChunkLoad;
|
|||||||
impl Component for RepositionOnChunkLoad {
|
impl Component for RepositionOnChunkLoad {
|
||||||
type Storage = NullStorage<Self>;
|
type Storage = NullStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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<u32>,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ use common::{
|
|||||||
resources::{Time, TimeOfDay},
|
resources::{Time, TimeOfDay},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
|
ViewDistances,
|
||||||
};
|
};
|
||||||
use common_net::{
|
use common_net::{
|
||||||
msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral},
|
msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral},
|
||||||
@ -107,9 +108,14 @@ pub trait StateExt {
|
|||||||
index: &world::IndexOwned,
|
index: &world::IndexOwned,
|
||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
/// Insert common/default components for a new character joining the server
|
/// 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
|
/// 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.
|
/// Update the components associated with the entity's current character.
|
||||||
/// Performed after loading component data from the database
|
/// Performed after loading component data from the database
|
||||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
||||||
@ -488,10 +494,21 @@ impl StateExt for State {
|
|||||||
self.ecs_mut()
|
self.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.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::<SpawnPoint>().0;
|
let spawn_point = self.ecs().read_resource::<SpawnPoint>().0;
|
||||||
|
|
||||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||||
@ -519,10 +536,9 @@ impl StateExt for State {
|
|||||||
// Make sure physics components are updated
|
// Make sure physics components are updated
|
||||||
self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced());
|
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(
|
self.write_component_ignore_entity_dead(
|
||||||
entity,
|
entity,
|
||||||
Presence::new(INITIAL_VD, PresenceKind::Character(character_id)),
|
Presence::new(view_distances, PresenceKind::Character(character_id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tell the client its request was successful.
|
// 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::<SpawnPoint>().0;
|
let spawn_point = self.ecs().read_resource::<SpawnPoint>().0;
|
||||||
|
|
||||||
if self.read_component_copied::<Uid>(entity).is_some() {
|
if self.read_component_copied::<Uid>(entity).is_some() {
|
||||||
@ -545,10 +561,9 @@ impl StateExt for State {
|
|||||||
// Make sure physics components are updated
|
// Make sure physics components are updated
|
||||||
self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced());
|
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(
|
self.write_component_ignore_entity_dead(
|
||||||
entity,
|
entity,
|
||||||
Presence::new(INITIAL_VD, PresenceKind::Spectator),
|
Presence::new(view_distances, PresenceKind::Spectator),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tell the client its request was successful.
|
// Tell the client its request was successful.
|
||||||
|
@ -384,7 +384,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
let is_near = |o_pos: Vec3<f32>| {
|
let is_near = |o_pos: Vec3<f32>| {
|
||||||
pos.zip_with(presence, |pos, presence| {
|
pos.zip_with(presence, |pos, presence| {
|
||||||
pos.0.xy().distance_squared(o_pos.xy())
|
pos.0.xy().distance_squared(o_pos.xy())
|
||||||
< (presence.view_distance as f32 * TerrainChunkSize::RECT_SIZE.x as f32)
|
< (presence.entity_view_distance.current() as f32
|
||||||
|
* TerrainChunkSize::RECT_SIZE.x as f32)
|
||||||
.powi(2)
|
.powi(2)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -64,17 +64,17 @@ impl Sys {
|
|||||||
};
|
};
|
||||||
match msg {
|
match msg {
|
||||||
// Request spectator state
|
// Request spectator state
|
||||||
ClientGeneral::Spectate => {
|
ClientGeneral::Spectate(requested_view_distances) => {
|
||||||
if let Some(admin) = admins.get(entity) && admin.0 >= AdminRole::Moderator {
|
if let Some(admin) = admins.get(entity) && admin.0 >= AdminRole::Moderator {
|
||||||
send_join_messages()?;
|
send_join_messages()?;
|
||||||
|
|
||||||
server_emitter.emit(ServerEvent::InitSpectator(entity));
|
server_emitter.emit(ServerEvent::InitSpectator(entity, requested_view_distances));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
debug!("dropped Spectate msg from unprivileged client")
|
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 let Some(player) = players.get(entity) {
|
||||||
if presences.contains(entity) {
|
if presences.contains(entity) {
|
||||||
debug!("player already ingame, aborting");
|
debug!("player already ingame, aborting");
|
||||||
@ -117,6 +117,7 @@ impl Sys {
|
|||||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
server_emitter.emit(ServerEvent::InitCharacterData {
|
||||||
entity,
|
entity,
|
||||||
character_id,
|
character_id,
|
||||||
|
requested_view_distances,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,6 +17,7 @@ use common_ecs::{Job, Origin, Phase, System};
|
|||||||
use common_net::msg::{ClientGeneral, PresenceKind, ServerGeneral};
|
use common_net::msg::{ClientGeneral, PresenceKind, ServerGeneral};
|
||||||
use common_state::{BlockChange, BuildAreas};
|
use common_state::{BlockChange, BuildAreas};
|
||||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage};
|
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage};
|
||||||
|
use std::time::Instant;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -49,9 +50,10 @@ impl Sys {
|
|||||||
_terrain_persistence: &mut TerrainPersistenceData<'_>,
|
_terrain_persistence: &mut TerrainPersistenceData<'_>,
|
||||||
maybe_player: &Option<&Player>,
|
maybe_player: &Option<&Player>,
|
||||||
maybe_admin: &Option<&Admin>,
|
maybe_admin: &Option<&Admin>,
|
||||||
|
time_for_vd_changes: Instant,
|
||||||
msg: ClientGeneral,
|
msg: ClientGeneral,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
let presence = match maybe_presence {
|
let presence = match maybe_presence.as_deref_mut() {
|
||||||
Some(g) => g,
|
Some(g) => g,
|
||||||
None => {
|
None => {
|
||||||
debug!(?entity, "client is not in_game, ignoring msg");
|
debug!(?entity, "client is not in_game, ignoring msg");
|
||||||
@ -66,21 +68,15 @@ impl Sys {
|
|||||||
client.send(ServerGeneral::ExitInGameSuccess)?;
|
client.send(ServerGeneral::ExitInGameSuccess)?;
|
||||||
*maybe_presence = None;
|
*maybe_presence = None;
|
||||||
},
|
},
|
||||||
ClientGeneral::SetViewDistance(view_distance) => {
|
ClientGeneral::SetViewDistance(view_distances) => {
|
||||||
presence.view_distance = settings
|
let clamped_vds = view_distances.clamp(settings.max_view_distance);
|
||||||
.max_view_distance
|
|
||||||
.map(|max| view_distance.min(max))
|
|
||||||
.unwrap_or(view_distance);
|
|
||||||
|
|
||||||
//correct client if its VD is to high
|
presence.terrain_view_distance.set_target(clamped_vds.terrain, time_for_vd_changes);
|
||||||
if settings
|
presence.entity_view_distance.set_target(clamped_vds.entity, time_for_vd_changes);
|
||||||
.max_view_distance
|
|
||||||
.map(|max| view_distance > max)
|
// Correct client if its requested VD is too high.
|
||||||
.unwrap_or(false)
|
if view_distances.terrain != clamped_vds.terrain {
|
||||||
{
|
client.send(ServerGeneral::SetViewDistance(clamped_vds.terrain))?;
|
||||||
client.send(ServerGeneral::SetViewDistance(
|
|
||||||
settings.max_view_distance.unwrap_or(0),
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientGeneral::ControllerInputs(inputs) => {
|
ClientGeneral::ControllerInputs(inputs) => {
|
||||||
@ -299,8 +295,8 @@ impl Sys {
|
|||||||
| ClientGeneral::CreateCharacter { .. }
|
| ClientGeneral::CreateCharacter { .. }
|
||||||
| ClientGeneral::EditCharacter { .. }
|
| ClientGeneral::EditCharacter { .. }
|
||||||
| ClientGeneral::DeleteCharacter(_)
|
| ClientGeneral::DeleteCharacter(_)
|
||||||
| ClientGeneral::Character(_)
|
| ClientGeneral::Character(_, _)
|
||||||
| ClientGeneral::Spectate
|
| ClientGeneral::Spectate(_)
|
||||||
| ClientGeneral::TerrainChunkRequest { .. }
|
| ClientGeneral::TerrainChunkRequest { .. }
|
||||||
| ClientGeneral::LodZoneRequest { .. }
|
| ClientGeneral::LodZoneRequest { .. }
|
||||||
| ClientGeneral::ChatMsg(_)
|
| ClientGeneral::ChatMsg(_)
|
||||||
@ -378,6 +374,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
) {
|
) {
|
||||||
let mut server_emitter = server_event_bus.emitter();
|
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 (
|
for (entity, client, mut maybe_presence, player, maybe_admin) in (
|
||||||
&entities,
|
&entities,
|
||||||
&mut clients,
|
&mut clients,
|
||||||
@ -387,12 +385,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.join()
|
.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| {
|
let _ = super::try_recv_all(client, 2, |client, msg| {
|
||||||
Self::handle_client_in_game_msg(
|
Self::handle_client_in_game_msg(
|
||||||
&mut server_emitter,
|
&mut server_emitter,
|
||||||
entity,
|
entity,
|
||||||
client,
|
client,
|
||||||
&mut maybe_presence.as_deref_mut(),
|
&mut clearable_maybe_presence,
|
||||||
&terrain,
|
&terrain,
|
||||||
&can_build,
|
&can_build,
|
||||||
&is_rider,
|
&is_rider,
|
||||||
@ -410,9 +411,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
&mut terrain_persistence,
|
&mut terrain_persistence,
|
||||||
&player,
|
&player,
|
||||||
&maybe_admin,
|
&maybe_admin,
|
||||||
|
time_for_vd_changes,
|
||||||
msg,
|
msg,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ensure deferred view distance changes are applied (if the
|
||||||
|
// requsite time has elapsed).
|
||||||
|
if let Some(presence) = maybe_presence {
|
||||||
|
presence.terrain_view_distance.update(time_for_vd_changes);
|
||||||
|
presence.entity_view_distance.update(time_for_vd_changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
pos.0.xy().map(|e| e as f64).distance_squared(
|
pos.0.xy().map(|e| e as f64).distance_squared(
|
||||||
key.map(|e| e as f64 + 0.5)
|
key.map(|e| e as f64 + 0.5)
|
||||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
||||||
) < ((presence.view_distance as f64 - 1.0
|
) < ((presence.terrain_view_distance.current() as f64 - 1.0
|
||||||
+ 2.5 * 2.0_f64.sqrt())
|
+ 2.5 * 2.0_f64.sqrt())
|
||||||
* TerrainChunkSize::RECT_SIZE.x as f64)
|
* TerrainChunkSize::RECT_SIZE.x as f64)
|
||||||
.powi(2)
|
.powi(2)
|
||||||
|
@ -60,7 +60,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// To update subscriptions
|
// To update subscriptions
|
||||||
// 1. Iterate through clients
|
// 1. Iterate through clients
|
||||||
// 2. Calculate current chunk position
|
// 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
|
// 4. Iterate through subscribed regions
|
||||||
// 5. Check if region is still in range (use fuzziness)
|
// 5. Check if region is still in range (use fuzziness)
|
||||||
// 6. If not in range
|
// 6. If not in range
|
||||||
@ -78,13 +79,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
let vd = presence.view_distance;
|
let vd = presence.entity_view_distance.current();
|
||||||
// Calculate current chunk
|
// Calculate current chunk
|
||||||
let chunk = (Vec2::<f32>::from(pos.0))
|
let chunk = (Vec2::<f32>::from(pos.0))
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||||
// Only update regions when moving to a new chunk
|
// Only update regions when moving to a new chunk or if view distance has
|
||||||
// uses a fuzzy border to prevent rapid triggering when moving along chunk
|
// changed.
|
||||||
// boundaries
|
//
|
||||||
|
// Uses a fuzzy border to prevent rapid triggering when moving along chunk
|
||||||
|
// boundaries.
|
||||||
if chunk != subscription.fuzzy_chunk
|
if chunk != subscription.fuzzy_chunk
|
||||||
&& (subscription
|
&& (subscription
|
||||||
.fuzzy_chunk
|
.fuzzy_chunk
|
||||||
@ -96,7 +99,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32
|
e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32
|
||||||
})
|
})
|
||||||
.reduce_or()
|
.reduce_or()
|
||||||
|
|| subscription.last_entity_view_distance != vd
|
||||||
{
|
{
|
||||||
|
// Update the view distance
|
||||||
|
subscription.last_entity_view_distance = vd;
|
||||||
// Update current chunk
|
// Update current chunk
|
||||||
subscription.fuzzy_chunk = Vec2::<f32>::from(pos.0)
|
subscription.fuzzy_chunk = Vec2::<f32>::from(pos.0)
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
.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 chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
||||||
let regions = regions_in_vd(
|
let regions = regions_in_vd(
|
||||||
client_pos.0,
|
client_pos.0,
|
||||||
(presence.view_distance 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(),
|
+ (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 {
|
if let Err(e) = world.write_storage().insert(entity, RegionSubscription {
|
||||||
fuzzy_chunk,
|
fuzzy_chunk,
|
||||||
|
last_entity_view_distance: presence.entity_view_distance.current(),
|
||||||
regions,
|
regions,
|
||||||
}) {
|
}) {
|
||||||
error!(?e, "Failed to insert region subscription component");
|
error!(?e, "Failed to insert region subscription component");
|
||||||
|
@ -269,7 +269,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map(|e: i32| (e.unsigned_abs()).saturating_sub(2))
|
.map(|e: i32| (e.unsigned_abs()).saturating_sub(2))
|
||||||
.magnitude_squared();
|
.magnitude_squared();
|
||||||
|
|
||||||
if adjusted_dist_sqr <= presence.view_distance.pow(2) {
|
if adjusted_dist_sqr <= presence.terrain_view_distance.current().pow(2) {
|
||||||
chunk_send_emitter.emit(ChunkSendEntry {
|
chunk_send_emitter.emit(ChunkSendEntry {
|
||||||
entity,
|
entity,
|
||||||
chunk_key: key,
|
chunk_key: key,
|
||||||
@ -293,7 +293,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// For each player with a position, calculate the distance.
|
// For each player with a position, calculate the distance.
|
||||||
for (presence, pos) in (&presences, &positions).join() {
|
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.terrain_view_distance.current()) {
|
||||||
should_drop = false;
|
should_drop = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Sync changed chunks
|
// Sync changed chunks
|
||||||
for chunk_key in &terrain_changes.modified_chunks {
|
for chunk_key in &terrain_changes.modified_chunks {
|
||||||
for (entity, presence, pos) in (&entities, &presences, &positions).join() {
|
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.terrain_view_distance.current(),
|
||||||
|
) {
|
||||||
chunk_send_emitter.emit(ChunkSendEntry {
|
chunk_send_emitter.emit(ChunkSendEntry {
|
||||||
entity,
|
entity,
|
||||||
chunk_key: *chunk_key,
|
chunk_key: *chunk_key,
|
||||||
|
@ -3063,6 +3063,7 @@ impl Hud {
|
|||||||
&self.imgs,
|
&self.imgs,
|
||||||
&self.fonts,
|
&self.fonts,
|
||||||
i18n,
|
i18n,
|
||||||
|
client.server_view_distance_limit(),
|
||||||
fps as f32,
|
fps as f32,
|
||||||
)
|
)
|
||||||
.set(self.ids.settings_window, ui_widgets)
|
.set(self.ids.settings_window, ui_widgets)
|
||||||
|
@ -93,6 +93,7 @@ pub struct SettingsWindow<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
fps: f32,
|
fps: f32,
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
@ -105,6 +106,7 @@ impl<'a> SettingsWindow<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
fps: f32,
|
fps: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -113,6 +115,7 @@ impl<'a> SettingsWindow<'a> {
|
|||||||
imgs,
|
imgs,
|
||||||
fonts,
|
fonts,
|
||||||
localized_strings,
|
localized_strings,
|
||||||
|
server_view_distance_limit,
|
||||||
fps,
|
fps,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
}
|
}
|
||||||
@ -299,11 +302,17 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
SettingsTab::Video => {
|
SettingsTab::Video => {
|
||||||
for change in
|
for change in video::Video::new(
|
||||||
video::Video::new(global_state, imgs, fonts, localized_strings, self.fps)
|
global_state,
|
||||||
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
|
imgs,
|
||||||
.wh_of(state.ids.settings_content_align)
|
fonts,
|
||||||
.set(state.ids.video, ui)
|
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()));
|
events.push(Event::SettingsChange(change.into()));
|
||||||
}
|
}
|
||||||
@ -327,11 +336,16 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
SettingsTab::Networking => {
|
SettingsTab::Networking => {
|
||||||
for change in
|
for change in networking::Networking::new(
|
||||||
networking::Networking::new(global_state, imgs, fonts, localized_strings)
|
global_state,
|
||||||
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
|
imgs,
|
||||||
.wh_of(state.ids.settings_content_align)
|
fonts,
|
||||||
.set(state.ids.networking, ui)
|
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()));
|
events.push(Event::SettingsChange(change.into()));
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,12 @@ widget_ids! {
|
|||||||
struct Ids {
|
struct Ids {
|
||||||
window,
|
window,
|
||||||
window_r,
|
window_r,
|
||||||
vd_text,
|
terrain_vd_text,
|
||||||
vd_slider,
|
terrain_vd_slider,
|
||||||
vd_value,
|
terrain_vd_value,
|
||||||
|
entity_vd_text,
|
||||||
|
entity_vd_slider,
|
||||||
|
entity_vd_value,
|
||||||
player_physics_behavior_text,
|
player_physics_behavior_text,
|
||||||
player_physics_behavior_list,
|
player_physics_behavior_list,
|
||||||
lossy_terrain_compression_button,
|
lossy_terrain_compression_button,
|
||||||
@ -34,6 +37,7 @@ pub struct Networking<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
}
|
}
|
||||||
@ -43,12 +47,14 @@ impl<'a> Networking<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
global_state,
|
global_state,
|
||||||
imgs,
|
imgs,
|
||||||
fonts,
|
fonts,
|
||||||
localized_strings,
|
localized_strings,
|
||||||
|
server_view_distance_limit,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,34 +100,84 @@ impl<'a> Widget for Networking<'a> {
|
|||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.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(
|
if let Some(new_val) = ImageSlider::discrete(
|
||||||
self.global_state.settings.graphics.view_distance,
|
terrain_view_distance,
|
||||||
1,
|
1,
|
||||||
65,
|
client::MAX_SELECTABLE_VIEW_DISTANCE,
|
||||||
self.imgs.slider_indicator,
|
self.imgs.slider_indicator,
|
||||||
self.imgs.slider,
|
self.imgs.slider,
|
||||||
)
|
)
|
||||||
.w_h(104.0, 22.0)
|
.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)
|
.track_breadth(12.0)
|
||||||
.slider_length(10.0)
|
.slider_length(10.0)
|
||||||
|
.soft_max(server_view_distance_limit)
|
||||||
.pad_track((5.0, 5.0))
|
.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!(
|
Text::new(&if terrain_view_distance <= server_view_distance_limit {
|
||||||
"{}",
|
format!("{terrain_view_distance}")
|
||||||
self.global_state.settings.graphics.view_distance
|
} else {
|
||||||
))
|
format!("{terrain_view_distance} ({server_view_distance_limit})")
|
||||||
.right_from(state.ids.vd_slider, 8.0)
|
})
|
||||||
|
.right_from(state.ids.terrain_vd_slider, 8.0)
|
||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.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,
|
||||||
|
client::MAX_SELECTABLE_VIEW_DISTANCE,
|
||||||
|
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
|
// Player physics behavior
|
||||||
Text::new(
|
Text::new(
|
||||||
@ -129,7 +185,7 @@ impl<'a> Widget for Networking<'a> {
|
|||||||
.localized_strings
|
.localized_strings
|
||||||
.get_msg("hud-settings-player_physics_behavior"),
|
.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_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
|
@ -36,9 +36,12 @@ widget_ids! {
|
|||||||
reset_graphics_button,
|
reset_graphics_button,
|
||||||
fps_counter,
|
fps_counter,
|
||||||
pipeline_recreation_text,
|
pipeline_recreation_text,
|
||||||
vd_slider,
|
terrain_vd_slider,
|
||||||
vd_text,
|
terrain_vd_text,
|
||||||
vd_value,
|
terrain_vd_value,
|
||||||
|
entity_vd_slider,
|
||||||
|
entity_vd_text,
|
||||||
|
entity_vd_value,
|
||||||
ld_slider,
|
ld_slider,
|
||||||
ld_text,
|
ld_text,
|
||||||
ld_value,
|
ld_value,
|
||||||
@ -131,6 +134,7 @@ pub struct Video<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
fps: f32,
|
fps: f32,
|
||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
@ -141,6 +145,7 @@ impl<'a> Video<'a> {
|
|||||||
imgs: &'a Imgs,
|
imgs: &'a Imgs,
|
||||||
fonts: &'a Fonts,
|
fonts: &'a Fonts,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
|
server_view_distance_limit: Option<u32>,
|
||||||
fps: f32,
|
fps: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -148,6 +153,7 @@ impl<'a> Video<'a> {
|
|||||||
imgs,
|
imgs,
|
||||||
fonts,
|
fonts,
|
||||||
localized_strings,
|
localized_strings,
|
||||||
|
server_view_distance_limit,
|
||||||
fps,
|
fps,
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
}
|
}
|
||||||
@ -282,38 +288,126 @@ impl<'a> Widget for Video<'a> {
|
|||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.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(
|
if let Some(new_val) = ImageSlider::discrete(
|
||||||
self.global_state.settings.graphics.view_distance,
|
terrain_view_distance,
|
||||||
1,
|
1,
|
||||||
65,
|
client::MAX_SELECTABLE_VIEW_DISTANCE,
|
||||||
self.imgs.slider_indicator,
|
self.imgs.slider_indicator,
|
||||||
self.imgs.slider,
|
self.imgs.slider,
|
||||||
)
|
)
|
||||||
.w_h(104.0, 22.0)
|
.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)
|
.track_breadth(12.0)
|
||||||
.slider_length(10.0)
|
.slider_length(10.0)
|
||||||
|
.soft_max(server_view_distance_limit)
|
||||||
.pad_track((5.0, 5.0))
|
.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!(
|
Text::new(&if terrain_view_distance <= server_view_distance_limit {
|
||||||
"{}",
|
format!("{terrain_view_distance}")
|
||||||
self.global_state.settings.graphics.view_distance
|
} else {
|
||||||
))
|
format!("{terrain_view_distance} ({server_view_distance_limit})")
|
||||||
.right_from(state.ids.vd_slider, 8.0)
|
})
|
||||||
|
.right_from(state.ids.terrain_vd_slider, 8.0)
|
||||||
.font_size(self.fonts.cyri.scale(14))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.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,
|
||||||
|
client::MAX_SELECTABLE_VIEW_DISTANCE,
|
||||||
|
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
|
// LoD Distance
|
||||||
Text::new(&self.localized_strings.get_msg("hud-settings-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_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -346,6 +440,50 @@ impl<'a> Widget for Video<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.ld_value, ui);
|
.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
|
// Max FPS
|
||||||
Text::new(&self.localized_strings.get_msg("hud-settings-maximum_fps"))
|
Text::new(&self.localized_strings.get_msg("hud-settings-maximum_fps"))
|
||||||
.down_from(state.ids.ld_slider, 10.0)
|
.down_from(state.ids.ld_slider, 10.0)
|
||||||
@ -388,7 +526,7 @@ impl<'a> Widget for Video<'a> {
|
|||||||
.get_msg("hud-settings-background_fps"),
|
.get_msg("hud-settings-background_fps"),
|
||||||
)
|
)
|
||||||
.down_from(state.ids.ld_slider, 10.0)
|
.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_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -436,7 +574,7 @@ impl<'a> Widget for Video<'a> {
|
|||||||
// Present Mode
|
// Present Mode
|
||||||
Text::new(&self.localized_strings.get_msg("hud-settings-present_mode"))
|
Text::new(&self.localized_strings.get_msg("hud-settings-present_mode"))
|
||||||
.down_from(state.ids.ld_slider, 10.0)
|
.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_size(self.fonts.cyri.scale(14))
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
@ -448,16 +586,11 @@ impl<'a> Widget for Video<'a> {
|
|||||||
PresentMode::Immediate,
|
PresentMode::Immediate,
|
||||||
];
|
];
|
||||||
let mode_label_list = [
|
let mode_label_list = [
|
||||||
&self
|
"hud-settings-present_mode-vsync_capped",
|
||||||
.localized_strings
|
"hud-settings-present_mode-vsync_uncapped",
|
||||||
.get_msg("hud-settings-present_mode-fifo"),
|
"hud-settings-present_mode-vsync_off",
|
||||||
&self
|
]
|
||||||
.localized_strings
|
.map(|k| self.localized_strings.get_msg(k));
|
||||||
.get_msg("hud-settings-present_mode-mailbox"),
|
|
||||||
&self
|
|
||||||
.localized_strings
|
|
||||||
.get_msg("hud-settings-present_mode-immediate"),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get which present mode is currently active
|
// Get which present mode is currently active
|
||||||
let selected = mode_list
|
let selected = mode_list
|
||||||
@ -465,7 +598,7 @@ impl<'a> Widget for Video<'a> {
|
|||||||
.position(|x| *x == render_mode.present_mode);
|
.position(|x| *x == render_mode.present_mode);
|
||||||
|
|
||||||
if let Some(clicked) = DropDownList::new(&mode_label_list, selected)
|
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)
|
.color(MENU_BG)
|
||||||
.label_color(TEXT_COLOR)
|
.label_color(TEXT_COLOR)
|
||||||
.label_font_id(self.fonts.cyri.conrod_id)
|
.label_font_id(self.fonts.cyri.conrod_id)
|
||||||
@ -651,87 +784,6 @@ impl<'a> Widget for Video<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.ambiance_value, ui);
|
.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
|
// AaMode
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&self
|
||||||
|
@ -137,10 +137,11 @@ impl PlayState for CharSelectionState {
|
|||||||
ui::Event::Play(character_id) => {
|
ui::Event::Play(character_id) => {
|
||||||
{
|
{
|
||||||
let mut c = self.client.borrow_mut();
|
let mut c = self.client.borrow_mut();
|
||||||
c.request_character(character_id);
|
let graphics = &global_state.settings.graphics;
|
||||||
//Send our ViewDistance and LoD distance
|
c.request_character(character_id, common::ViewDistances {
|
||||||
c.set_view_distance(global_state.settings.graphics.view_distance);
|
terrain: graphics.terrain_view_distance,
|
||||||
c.set_lod_distance(global_state.settings.graphics.lod_distance);
|
entity: graphics.entity_view_distance,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||||
global_state,
|
global_state,
|
||||||
@ -150,9 +151,11 @@ impl PlayState for CharSelectionState {
|
|||||||
ui::Event::Spectate => {
|
ui::Event::Spectate => {
|
||||||
{
|
{
|
||||||
let mut c = self.client.borrow_mut();
|
let mut c = self.client.borrow_mut();
|
||||||
c.request_spectate();
|
let graphics = &global_state.settings.graphics;
|
||||||
c.set_view_distance(global_state.settings.graphics.view_distance);
|
c.request_spectate(common::ViewDistances {
|
||||||
c.set_lod_distance(global_state.settings.graphics.lod_distance);
|
terrain: graphics.terrain_view_distance,
|
||||||
|
entity: graphics.entity_view_distance,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||||
global_state,
|
global_state,
|
||||||
@ -215,12 +218,7 @@ impl PlayState for CharSelectionState {
|
|||||||
Ok(events) => {
|
Ok(events) => {
|
||||||
for event in events {
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
client::Event::SetViewDistance(vd) => {
|
client::Event::SetViewDistance(_vd) => {},
|
||||||
global_state.settings.graphics.view_distance = vd;
|
|
||||||
global_state
|
|
||||||
.settings
|
|
||||||
.save_to_file_warn(&global_state.config_dir);
|
|
||||||
},
|
|
||||||
client::Event::Disconnect => {
|
client::Event::Disconnect => {
|
||||||
global_state.info_message = Some(
|
global_state.info_message = Some(
|
||||||
localized_strings
|
localized_strings
|
||||||
|
@ -231,12 +231,7 @@ impl PlayState for MainMenuState {
|
|||||||
Ok(events) => {
|
Ok(events) => {
|
||||||
for event in events {
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
client::Event::SetViewDistance(vd) => {
|
client::Event::SetViewDistance(_vd) => {},
|
||||||
global_state.settings.graphics.view_distance = vd;
|
|
||||||
global_state
|
|
||||||
.settings
|
|
||||||
.save_to_file_warn(&global_state.config_dir);
|
|
||||||
},
|
|
||||||
client::Event::Disconnect => {
|
client::Event::Disconnect => {
|
||||||
global_state.info_message = Some(
|
global_state.info_message = Some(
|
||||||
localized_strings
|
localized_strings
|
||||||
|
@ -629,7 +629,7 @@ impl FigureMgr {
|
|||||||
let time = state.get_time() as f32;
|
let time = state.get_time() as f32;
|
||||||
let tick = scene_data.tick;
|
let tick = scene_data.tick;
|
||||||
let ecs = state.ecs();
|
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 = state.get_delta_time();
|
||||||
let dt_lerp = (15.0 * dt).min(1.0);
|
let dt_lerp = (15.0 * dt).min(1.0);
|
||||||
let frustum = camera.frustum();
|
let frustum = camera.frustum();
|
||||||
|
@ -120,7 +120,8 @@ pub struct SceneData<'a> {
|
|||||||
pub mutable_viewpoint: bool,
|
pub mutable_viewpoint: bool,
|
||||||
pub target_entity: Option<specs::Entity>,
|
pub target_entity: Option<specs::Entity>,
|
||||||
pub loaded_distance: f32,
|
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 tick: u64,
|
||||||
pub gamma: f32,
|
pub gamma: f32,
|
||||||
pub exposure: f32,
|
pub exposure: f32,
|
||||||
|
@ -351,12 +351,7 @@ impl SessionState {
|
|||||||
client::Event::Notification(n) => {
|
client::Event::Notification(n) => {
|
||||||
self.hud.new_notification(n);
|
self.hud.new_notification(n);
|
||||||
},
|
},
|
||||||
client::Event::SetViewDistance(vd) => {
|
client::Event::SetViewDistance(_vd) => {},
|
||||||
global_state.settings.graphics.view_distance = vd;
|
|
||||||
global_state
|
|
||||||
.settings
|
|
||||||
.save_to_file_warn(&global_state.config_dir);
|
|
||||||
},
|
|
||||||
client::Event::Outcome(outcome) => outcomes.push(outcome),
|
client::Event::Outcome(outcome) => outcomes.push(outcome),
|
||||||
client::Event::CharacterCreated(_) => {},
|
client::Event::CharacterCreated(_) => {},
|
||||||
client::Event::CharacterEdited(_) => {},
|
client::Event::CharacterEdited(_) => {},
|
||||||
@ -1702,7 +1697,11 @@ impl PlayState for SessionState {
|
|||||||
// Only highlight if interactable
|
// Only highlight if interactable
|
||||||
target_entity: self.interactable.and_then(Interactable::entity),
|
target_entity: self.interactable.and_then(Interactable::entity),
|
||||||
loaded_distance: client.loaded_distance(),
|
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(),
|
tick: client.get_tick(),
|
||||||
gamma: global_state.settings.graphics.gamma,
|
gamma: global_state.settings.graphics.gamma,
|
||||||
exposure: global_state.settings.graphics.exposure,
|
exposure: global_state.settings.graphics.exposure,
|
||||||
@ -1788,7 +1787,11 @@ impl PlayState for SessionState {
|
|||||||
// Only highlight if interactable
|
// Only highlight if interactable
|
||||||
target_entity: self.interactable.and_then(Interactable::entity),
|
target_entity: self.interactable.and_then(Interactable::entity),
|
||||||
loaded_distance: client.loaded_distance(),
|
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(),
|
tick: client.get_tick(),
|
||||||
gamma: settings.graphics.gamma,
|
gamma: settings.graphics.gamma,
|
||||||
exposure: settings.graphics.exposure,
|
exposure: settings.graphics.exposure,
|
||||||
|
@ -70,7 +70,8 @@ pub enum Gameplay {
|
|||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Graphics {
|
pub enum Graphics {
|
||||||
AdjustViewDistance(u32),
|
AdjustTerrainViewDistance(u32),
|
||||||
|
AdjustEntityViewDistance(u32),
|
||||||
AdjustLodDistance(u32),
|
AdjustLodDistance(u32),
|
||||||
AdjustLodDetail(u32),
|
AdjustLodDetail(u32),
|
||||||
AdjustSpriteRenderDistance(u32),
|
AdjustSpriteRenderDistance(u32),
|
||||||
@ -147,7 +148,8 @@ pub enum Language {
|
|||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Networking {
|
pub enum Networking {
|
||||||
AdjustViewDistance(u32),
|
AdjustTerrainViewDistance(u32),
|
||||||
|
AdjustEntityViewDistance(u32),
|
||||||
ChangePlayerPhysicsBehavior {
|
ChangePlayerPhysicsBehavior {
|
||||||
server_authoritative: bool,
|
server_authoritative: bool,
|
||||||
},
|
},
|
||||||
@ -155,6 +157,8 @@ pub enum Networking {
|
|||||||
|
|
||||||
#[cfg(feature = "discord")]
|
#[cfg(feature = "discord")]
|
||||||
ToggleDiscordIntegration(bool),
|
ToggleDiscordIntegration(bool),
|
||||||
|
// TODO: reset option (ensure it handles the entity/terrain vd the same as graphics reset
|
||||||
|
// option)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -354,13 +358,11 @@ impl SettingsChange {
|
|||||||
},
|
},
|
||||||
SettingsChange::Graphics(graphics_change) => {
|
SettingsChange::Graphics(graphics_change) => {
|
||||||
match graphics_change {
|
match graphics_change {
|
||||||
Graphics::AdjustViewDistance(view_distance) => {
|
Graphics::AdjustTerrainViewDistance(terrain_vd) => {
|
||||||
session_state
|
adjust_terrain_view_distance(terrain_vd, settings, session_state)
|
||||||
.client
|
},
|
||||||
.borrow_mut()
|
Graphics::AdjustEntityViewDistance(entity_vd) => {
|
||||||
.set_view_distance(view_distance);
|
adjust_entity_view_distance(entity_vd, settings, session_state)
|
||||||
|
|
||||||
settings.graphics.view_distance = view_distance;
|
|
||||||
},
|
},
|
||||||
Graphics::AdjustLodDistance(lod_distance) => {
|
Graphics::AdjustLodDistance(lod_distance) => {
|
||||||
session_state
|
session_state
|
||||||
@ -433,10 +435,7 @@ impl SettingsChange {
|
|||||||
settings.graphics = GraphicsSettings::default();
|
settings.graphics = GraphicsSettings::default();
|
||||||
let graphics = &settings.graphics;
|
let graphics = &settings.graphics;
|
||||||
// View distance
|
// View distance
|
||||||
session_state
|
client_set_view_distance(settings, session_state);
|
||||||
.client
|
|
||||||
.borrow_mut()
|
|
||||||
.set_view_distance(graphics.view_distance);
|
|
||||||
// FOV
|
// FOV
|
||||||
session_state.scene.camera_mut().set_fov_deg(graphics.fov);
|
session_state.scene.camera_mut().set_fov_deg(graphics.fov);
|
||||||
session_state
|
session_state
|
||||||
@ -604,12 +603,11 @@ impl SettingsChange {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
SettingsChange::Networking(networking_change) => match networking_change {
|
SettingsChange::Networking(networking_change) => match networking_change {
|
||||||
Networking::AdjustViewDistance(view_distance) => {
|
Networking::AdjustTerrainViewDistance(terrain_vd) => {
|
||||||
session_state
|
adjust_terrain_view_distance(terrain_vd, settings, session_state)
|
||||||
.client
|
},
|
||||||
.borrow_mut()
|
Networking::AdjustEntityViewDistance(entity_vd) => {
|
||||||
.set_view_distance(view_distance);
|
adjust_entity_view_distance(entity_vd, settings, session_state)
|
||||||
settings.graphics.view_distance = view_distance;
|
|
||||||
},
|
},
|
||||||
Networking::ChangePlayerPhysicsBehavior {
|
Networking::ChangePlayerPhysicsBehavior {
|
||||||
server_authoritative,
|
server_authoritative,
|
||||||
@ -654,6 +652,39 @@ impl SettingsChange {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
settings.save_to_file_warn(&global_state.config_dir);
|
global_state
|
||||||
|
.settings
|
||||||
|
.save_to_file_warn(&global_state.config_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_distances(view_distances);
|
||||||
|
}
|
||||||
|
@ -29,7 +29,8 @@ impl fmt::Display for Fps {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct GraphicsSettings {
|
pub struct GraphicsSettings {
|
||||||
pub view_distance: u32,
|
pub terrain_view_distance: u32,
|
||||||
|
pub entity_view_distance: u32,
|
||||||
pub lod_distance: u32,
|
pub lod_distance: u32,
|
||||||
pub sprite_render_distance: u32,
|
pub sprite_render_distance: u32,
|
||||||
pub particles_enabled: bool,
|
pub particles_enabled: bool,
|
||||||
@ -50,7 +51,8 @@ pub struct GraphicsSettings {
|
|||||||
impl Default for GraphicsSettings {
|
impl Default for GraphicsSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_distance: 10,
|
terrain_view_distance: 10,
|
||||||
|
entity_view_distance: client::MAX_SELECTABLE_VIEW_DISTANCE,
|
||||||
lod_distance: 200,
|
lod_distance: 200,
|
||||||
sprite_render_distance: 100,
|
sprite_render_distance: 100,
|
||||||
particles_enabled: true,
|
particles_enabled: true,
|
||||||
|
@ -29,6 +29,10 @@ pub struct ImageSlider<T, K> {
|
|||||||
value: T,
|
value: T,
|
||||||
min: T,
|
min: T,
|
||||||
max: 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.
|
/// The amount in which the slider's display should be skewed.
|
||||||
///
|
///
|
||||||
/// Higher skew amounts (above 1.0) will weigh lower values.
|
/// Higher skew amounts (above 1.0) will weigh lower values.
|
||||||
@ -65,6 +69,7 @@ widget_ids! {
|
|||||||
struct Ids {
|
struct Ids {
|
||||||
track,
|
track,
|
||||||
slider,
|
slider,
|
||||||
|
soft_max_slider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +81,7 @@ pub struct State {
|
|||||||
impl<T, K> ImageSlider<T, K> {
|
impl<T, K> ImageSlider<T, K> {
|
||||||
builder_methods! {
|
builder_methods! {
|
||||||
pub skew { skew = f32 }
|
pub skew { skew = f32 }
|
||||||
|
pub soft_max { soft_max = T }
|
||||||
pub pad_track { track.padding = (f32, f32) }
|
pub pad_track { track.padding = (f32, f32) }
|
||||||
pub hover_image { slider.hover_image_id = Some(image::Id) }
|
pub hover_image { slider.hover_image_id = Some(image::Id) }
|
||||||
pub press_image { slider.press_image_id = Some(image::Id) }
|
pub press_image { slider.press_image_id = Some(image::Id) }
|
||||||
@ -85,18 +91,16 @@ impl<T, K> ImageSlider<T, K> {
|
|||||||
pub slider_color { slider.color = Some(Color) }
|
pub slider_color { slider.color = Some(Color) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(
|
fn new(value: T, min: T, max: T, slider_image_id: image::Id, track_image_id: image::Id) -> Self
|
||||||
value: T,
|
where
|
||||||
min: T,
|
T: Copy,
|
||||||
max: T,
|
{
|
||||||
slider_image_id: image::Id,
|
|
||||||
track_image_id: image::Id,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
value,
|
value,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
|
soft_max: max,
|
||||||
skew: 1.0,
|
skew: 1.0,
|
||||||
track: Track {
|
track: Track {
|
||||||
image_id: track_image_id,
|
image_id: track_image_id,
|
||||||
@ -133,7 +137,7 @@ where
|
|||||||
|
|
||||||
impl<T> ImageSlider<T, Discrete>
|
impl<T> ImageSlider<T, Discrete>
|
||||||
where
|
where
|
||||||
T: Integer,
|
T: Integer + Copy,
|
||||||
{
|
{
|
||||||
pub fn discrete(
|
pub fn discrete(
|
||||||
value: T,
|
value: T,
|
||||||
@ -266,45 +270,67 @@ where
|
|||||||
.unwrap_or(slider.image_id);
|
.unwrap_or(slider.image_id);
|
||||||
|
|
||||||
// A rectangle for positioning and sizing the slider.
|
// A rectangle for positioning and sizing the slider.
|
||||||
let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0);
|
let slider_rect = |slider_value| {
|
||||||
let unskewed_perc = value_perc.powf(1.0 / skew as f64);
|
let value_perc = utils::map_range(slider_value, min, max, 0.0, 1.0);
|
||||||
let slider_rect = if is_horizontal {
|
let unskewed_perc = value_perc.powf(1.0 / skew as f64);
|
||||||
let pos = utils::map_range(
|
if is_horizontal {
|
||||||
unskewed_perc,
|
let pos = utils::map_range(
|
||||||
0.0,
|
unskewed_perc,
|
||||||
1.0,
|
0.0,
|
||||||
rect.x.start + start_pad,
|
1.0,
|
||||||
rect.x.end - end_pad,
|
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 {
|
let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64);
|
||||||
x: Range::from_pos_and_len(pos, w),
|
Rect {
|
||||||
..rect
|
x: Range::from_pos_and_len(pos, w),
|
||||||
}
|
..rect
|
||||||
} else {
|
}
|
||||||
let pos = utils::map_range(
|
} else {
|
||||||
unskewed_perc,
|
let pos = utils::map_range(
|
||||||
0.0,
|
unskewed_perc,
|
||||||
1.0,
|
0.0,
|
||||||
rect.y.start + start_pad,
|
1.0,
|
||||||
rect.y.end - end_pad,
|
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 {
|
let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64);
|
||||||
y: Range::from_pos_and_len(pos, h),
|
Rect {
|
||||||
..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)
|
Image::new(slider_image)
|
||||||
.x_y(x, y)
|
.x_y(x, y)
|
||||||
.w_h(w, h)
|
.w_h(w, h)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.graphics_for(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);
|
.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 the value has just changed, return the new value.
|
||||||
if value != new_value {
|
if value != new_value {
|
||||||
Some(new_value)
|
Some(new_value)
|
||||||
|
Loading…
Reference in New Issue
Block a user