From 045f41dae7fe2f554a460d113842ac8d735c079b Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 26 May 2019 16:13:42 -0400 Subject: [PATCH 1/7] Use vd setting when creating client Former-commit-id: b9a32c6b2f1a61a407991507c63ba7d708723410 --- client/src/lib.rs | 8 ++++---- server/src/lib.rs | 3 ++- voxygen/src/menu/main/mod.rs | 5 ++++- voxygen/src/menu/main/start_singleplayer.rs | 5 ++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index d9038df8da..d68faaf8b1 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -105,7 +105,7 @@ impl Client { } pub fn set_view_distance(&mut self, view_distance: u32) { - self.view_distance = Some(view_distance.max(5).min(25)); + self.view_distance = Some(view_distance.max(1).min(25)); self.postbox .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail } @@ -230,9 +230,9 @@ impl Client { // Request chunks from the server. // TODO: This is really inefficient. - 'outer: for dist in 0..view_distance as i32 { - for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 { - for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 { + 'outer: for dist in 0..=view_distance as i32 { + for i in chunk_pos.x - dist..=chunk_pos.x + dist { + for j in chunk_pos.y - dist..=chunk_pos.y + dist { let key = Vec2::new(i, j); if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains_key(&key) diff --git a/server/src/lib.rs b/server/src/lib.rs index dd1ca47691..4dc494d446 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -295,7 +295,7 @@ impl Server { .map(|e: i32| e.abs()) .reduce_max() as u32; - if player.view_distance.map(|vd| dist < vd).unwrap_or(false) { + if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) { self.clients.notify( entity, ServerMsg::TerrainChunkUpdate { @@ -329,6 +329,7 @@ impl Server { if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) { should_drop = false; + break; } } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index f59e06f4a3..7d2957dce4 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -106,7 +106,10 @@ impl PlayState for MainMenuState { // Don't try to connect if there is already a connection in progress. client_init = client_init.or(Some(ClientInit::new( (server_address, DEFAULT_PORT, false), - comp::Player::new(username.clone(), Some(10)), + comp::Player::new( + username.clone(), + Some(global_state.settings.graphics.view_distance), + ), false, ))); } diff --git a/voxygen/src/menu/main/start_singleplayer.rs b/voxygen/src/menu/main/start_singleplayer.rs index c3e3446294..a44efca663 100644 --- a/voxygen/src/menu/main/start_singleplayer.rs +++ b/voxygen/src/menu/main/start_singleplayer.rs @@ -30,7 +30,10 @@ impl PlayState for StartSingleplayerState { let client_init = ClientInit::new( (server_address.clone(), self.sock.port(), true), - comp::Player::new(username.clone(), Some(10)), + comp::Player::new( + username.clone(), + Some(global_state.settings.graphics.view_distance), + ), true, ); From 38416ef37c0692ec1e022752bf91c734079943d8 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 26 May 2019 16:42:45 -0400 Subject: [PATCH 2/7] Show disclaimer once Former-commit-id: f4937dba2caca207b02deb18ad209a8435e65b61 --- voxygen/src/main.rs | 4 ++++ voxygen/src/menu/main/mod.rs | 3 +++ voxygen/src/menu/main/ui.rs | 10 ++++++---- voxygen/src/settings.rs | 2 ++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index f81f17c803..1be0b4426c 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -81,6 +81,10 @@ pub trait PlayState { fn main() { // Set up the global state. let mut settings = Settings::load(); + // Save settings to add new fields or create the file if it is not already there + // TODO: Handle this result. + settings.save_to_file(); + let window = Window::new(&settings).expect("Failed to create window!"); // Initialize logging. diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 7d2957dce4..e738697894 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -117,6 +117,9 @@ impl PlayState for MainMenuState { return PlayStateResult::Push(Box::new(StartSingleplayerState::new())); } MainMenuEvent::Quit => return PlayStateResult::Shutdown, + MainMenuEvent::DisclaimerClosed => { + global_state.settings.show_disclaimer = false + } } } diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 2bd084f4b8..44e395f972 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -90,6 +90,7 @@ pub enum Event { }, StartSingleplayer, Quit, + DisclaimerClosed, } pub struct MainMenuUi { @@ -129,11 +130,11 @@ impl MainMenuUi { login_error: None, connecting: None, show_servers: false, - show_disclaimer: true, + show_disclaimer: global_state.settings.show_disclaimer, } } - fn update_layout(&mut self, global_state: &GlobalState) -> Vec { + fn update_layout(&mut self, global_state: &mut GlobalState) -> Vec { let mut events = Vec::new(); let ref mut ui_widgets = self.ui.set_widgets(); let version = env!("CARGO_PKG_VERSION"); @@ -209,8 +210,9 @@ impl MainMenuUi { .set(self.ids.disc_button, ui_widgets) .was_clicked() { - self.show_disclaimer = false - }; + self.show_disclaimer = false; + events.push(Event::DisclaimerClosed); + } } else { // TODO: Don't use macros for this? // Input fields diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index d92d5bd626..ba8565138a 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -13,6 +13,7 @@ pub struct Settings { pub log: Log, pub graphics: GraphicsSettings, pub audio: AudioSettings, + pub show_disclaimer: bool, } /// `ControlSettings` contains keybindings. @@ -117,6 +118,7 @@ impl Default for Settings { sfx_volume: 0.5, audio_device: None, }, + show_disclaimer: true, } } } From 59967c603c5cc82d5e899d9447b5996f578f28f7 Mon Sep 17 00:00:00 2001 From: timokoesters Date: Mon, 27 May 2019 14:16:25 +0200 Subject: [PATCH 3/7] Save settings when the game closes Former-commit-id: 0190f45ddbd87ec6ae2e4d4d959672d22e560907 --- voxygen/src/main.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 1be0b4426c..ac451374e4 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -81,12 +81,15 @@ pub trait PlayState { fn main() { // Set up the global state. let mut settings = Settings::load(); - // Save settings to add new fields or create the file if it is not already there - // TODO: Handle this result. - settings.save_to_file(); - let window = Window::new(&settings).expect("Failed to create window!"); + let mut global_state = GlobalState { + audio: AudioFrontend::new(&settings.audio), + window, + settings, + }; + + // Initialize logging. let term_log_level = std::env::var_os("VOXYGEN_LOG") .and_then(|env| env.to_str().map(|s| s.to_owned())) @@ -97,13 +100,13 @@ fn main() { WriteLogger::new( log::LevelFilter::Info, Config::default(), - File::create(&settings.log.file).unwrap(), + File::create(&global_state.settings.log.file).unwrap(), ), ]) .unwrap(); // Set up panic handler to relay swish panic messages to the user - let settings_clone = settings.clone(); + let settings_clone = global_state.settings.clone(); let default_hook = panic::take_hook(); panic::set_hook(Box::new(move |panic_info| { let panic_info_payload = panic_info.payload(); @@ -164,16 +167,10 @@ fn main() { default_hook(panic_info); })); - if settings.audio.audio_device == None { - settings.audio.audio_device = Some(AudioFrontend::get_default_device()); + if global_state.settings.audio.audio_device == None { + global_state.settings.audio.audio_device = Some(AudioFrontend::get_default_device()); } - let mut global_state = GlobalState { - audio: AudioFrontend::new(&settings.audio), - window, - settings, - }; - // Set up the initial play state. let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; states @@ -231,4 +228,7 @@ fn main() { } } } + // Save settings to add new fields or create the file if it is not already there + // TODO: Handle this result. + global_state.settings.save_to_file(); } From e3e8afd99b93cdb7ee97e3a69c8df1e607013aac Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 27 May 2019 13:01:00 -0400 Subject: [PATCH 4/7] Stop drawing entities outside the vd Former-commit-id: 34878fb66ec22ac77a7400724b80ad7f8321f4cf --- client/src/lib.rs | 4 +++ server/src/lib.rs | 7 ++-- voxygen/src/hud/mod.rs | 44 ++++++++++++++++++------ voxygen/src/scene/figure.rs | 68 +++++++++++++++++++++++++++++++++---- voxygen/src/scene/mod.rs | 3 +- 5 files changed, 105 insertions(+), 21 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index d68faaf8b1..dd43d40628 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -110,6 +110,10 @@ impl Client { .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail } + pub fn view_distance(&self) -> Option { + self.view_distance + } + /// Send a chat message to the server. #[allow(dead_code)] pub fn send_chat(&mut self, msg: String) { diff --git a/server/src/lib.rs b/server/src/lib.rs index 4dc494d446..1eb3af791a 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -283,19 +283,22 @@ impl Server { // Also, send the chunk data to anybody that is close by. if let Ok((key, chunk)) = self.chunk_rx.try_recv() { // Send the chunk to all nearby players. - for (entity, player, pos) in ( + for (entity, view_distance, pos) in ( &self.state.ecs().entities(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), ) .join() + .filter_map(|(entity, player, pos)| { + player.view_distance.map(|vd| (entity, vd, pos)) + }) { let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let dist = (Vec2::from(chunk_pos) - Vec2::from(key)) .map(|e: i32| e.abs()) .reduce_max() as u32; - if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) { + if dist <= view_distance { self.clients.notify( entity, ServerMsg::TerrainChunkUpdate { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index f27c128a9c..5bc2e971ac 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -31,8 +31,7 @@ use crate::{ GlobalState, }; use client::Client; -use common::comp; -use common::comp::phys::Pos; +use common::{comp, terrain::TerrainChunkSize, vol::VolSize}; use conrod_core::{ color, graph, widget::{self, Button, Image, Rectangle, Text}, @@ -105,7 +104,7 @@ font_ids! { pub struct DebugInfo { pub tps: f64, pub ping_ms: f64, - pub coordinates: Option, + pub coordinates: Option, } pub enum Event { @@ -305,6 +304,14 @@ impl Hud { let player = ecs.read_storage::(); let entities = ecs.entities(); let me = client.entity(); + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); let mut name_id_walker = self.ids.name_tags.walk(); let mut health_id_walker = self.ids.health_bars.walk(); let mut health_back_id_walker = self.ids.health_bar_backs.walk(); @@ -313,6 +320,14 @@ impl Hud { for (pos, name) in (&entities, &pos, &actor, &stats, player.maybe()) .join() .filter(|(entity, _, _, stats, _)| *entity != me && !stats.is_dead) + // Don't process nametags outside the vd (visibility further limited by ui backend) + .filter(|(_, pos, _, _, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) .map(|(entity, pos, actor, _, player)| match actor { comp::Actor::Character { name: char_name, .. @@ -342,14 +357,21 @@ impl Hud { } // Render Health Bars - for (entity, pos, stats) in - (&entities, &pos, &stats) - .join() - .filter(|(entity, _, stats)| { - *entity != me - && !stats.is_dead - && stats.hp.get_current() != stats.hp.get_maximum() - }) + for (entity, pos, stats) in (&entities, &pos, &stats) + .join() + .filter(|(entity, _, stats)| { + *entity != me + && !stats.is_dead + && stats.hp.get_current() != stats.hp.get_maximum() + }) + // Don't process health bars outside the vd (visibility further limited by ui backend) + .filter(|(_, pos, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) { let back_id = health_back_id_walker.next( &mut self.ids.health_bar_backs, diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 96f7462e52..aea1e91490 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -26,6 +26,8 @@ use common::{ figure::Segment, msg, msg::ClientState, + terrain::TerrainChunkSize, + vol::VolSize, }; use dot_vox::DotVoxData; use specs::{Component, Entity as EcsEntity, Join, VecStorage}; @@ -461,6 +463,15 @@ impl FigureMgr { pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { let time = client.state().get_time(); let ecs = client.state().ecs(); + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + for (entity, pos, vel, dir, actor, animation_info, stats) in ( &ecs.entities(), &ecs.read_storage::(), @@ -472,6 +483,32 @@ impl FigureMgr { ) .join() { + // Don't process figures outside the vd + let vd_percent = (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (100 * d.abs() as u32) / (view_distance * sz) + }) + .reduce_max(); + // Keep from re-adding/removing entities on the border of the vd + if vd_percent > 120 { + match actor { + comp::Actor::Character { body, .. } => match body { + Body::Humanoid(_) => { + self.character_states.remove(&entity); + } + Body::Quadruped(_) => { + self.quadruped_states.remove(&entity); + } + Body::QuadrupedMedium(_) => { + self.QuadrupedMedium_states.remove(&entity); + } + }, + } + continue; + } else if vd_percent > 100 { + continue; + } + // Change in health as color! let col = stats .and_then(|stats| stats.hp.last_change) @@ -610,17 +647,36 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - for (entity, actor, stat) in ( + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + + for (entity, _, _, _, actor, _, _) in ( &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), &ecs.read_storage::(), - &ecs.read_storage::(), // Just to make sure the entity is alive + &ecs.read_storage::(), + ecs.read_storage::().maybe(), ) .join() + // Don't render figures outside the vd + .filter(|(_, pos, _, _, _, _, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) + // Don't render dead entities + .filter(|(e, _, _, _, a, _, stats)| stats.map_or(true, |s| !s.is_dead)) { - if stat.is_dead { - continue; - } - match actor { comp::Actor::Character { body, .. } => { if let Some((locals, bone_consts)) = match body { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 8fcb143ada..0f8d33f4ce 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -116,8 +116,7 @@ impl Scene { .ecs() .read_storage::() .get(client.entity()) - .map(|pos| pos.0) - .unwrap_or(Vec3::zero()); + .map_or(Vec3::zero(), |pos| pos.0); // Alter camera position to match player. self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 2.1); From 942096c754a456facb92ecea3e74e0bedb6e35e7 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 27 May 2019 14:07:35 -0400 Subject: [PATCH 5/7] Only send physics updates for entities within vd Former-commit-id: d3307641cd31418bf8b9831296a96d58926184e0 --- server/src/client.rs | 26 ++++++++++++++++++++++++++ server/src/lib.rs | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/server/src/client.rs b/server/src/client.rs index 61cdf9815a..560579ffec 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -83,6 +83,16 @@ impl Clients { } } + pub fn notify_ingame_if bool>(&mut self, msg: ServerMsg, mut f: F) { + for (entity, client) in self.clients.iter_mut().filter(|(e, _)| f(**e)) { + if client.client_state == ClientState::Spectator + || client.client_state == ClientState::Character + { + client.notify(msg.clone()); + } + } + } + pub fn notify_registered_except(&mut self, except_entity: EcsEntity, msg: ServerMsg) { for (entity, client) in self.clients.iter_mut() { if client.client_state != ClientState::Connected && *entity != except_entity { @@ -101,4 +111,20 @@ impl Clients { } } } + + pub fn notify_ingame_if_except bool>( + &mut self, + except_entity: EcsEntity, + msg: ServerMsg, + mut f: F, + ) { + for (entity, client) in self.clients.iter_mut().filter(|(e, _)| f(**e)) { + if (client.client_state == ClientState::Spectator + || client.client_state == ClientState::Character) + && *entity != except_entity + { + client.notify(msg.clone()); + } + } + } } diff --git a/server/src/lib.rs b/server/src/lib.rs index 1eb3af791a..c6f8d96d88 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -17,7 +17,8 @@ use common::{ msg::{ClientMsg, ClientState, RequestStateError, ServerMsg}, net::PostOffice, state::{State, Uid}, - terrain::TerrainChunk, + terrain::{TerrainChunk, TerrainChunkSize}, + vol::VolSize, }; use specs::{ join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder, @@ -690,9 +691,34 @@ impl Server { dir, }; + let state = &self.state; + let mut clients = &mut self.clients; + + let in_vd = |entity| { + // Get client position. + let client_pos = match state.ecs().read_storage::().get(entity) { + Some(pos) => pos.0, + None => return false, + }; + // Get client view distance + let client_vd = match state.ecs().read_storage::().get(entity) { + Some(comp::Player { + view_distance: Some(vd), + .. + }) => *vd, + _ => return false, + }; + + (pos.0 - client_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < client_vd * sz as u32 + }) + .reduce_and() + }; + match force_update { - Some(_) => self.clients.notify_registered(msg), - None => self.clients.notify_registered_except(entity, msg), + Some(_) => clients.notify_ingame_if(msg, in_vd), + None => clients.notify_ingame_if_except(entity, msg, in_vd), } } @@ -714,8 +740,8 @@ impl Server { animation_info: animation_info.clone(), }; match force_update { - Some(_) => self.clients.notify_registered(msg), - None => self.clients.notify_registered_except(entity, msg), + Some(_) => self.clients.notify_ingame(msg), + None => self.clients.notify_ingame_except(entity, msg), } } } From c41fc026a898e2e9c947ef343f5d239a790d08e8 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 27 May 2019 14:11:22 -0400 Subject: [PATCH 6/7] fmt Former-commit-id: 288b2b5d5872aebd255e464c921521c0b8b864fa --- voxygen/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index ac451374e4..692425a801 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -89,7 +89,6 @@ fn main() { settings, }; - // Initialize logging. let term_log_level = std::env::var_os("VOXYGEN_LOG") .and_then(|env| env.to_str().map(|s| s.to_owned())) From 061ab9f45e47a76620436c8b552f64278a9df851 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 27 May 2019 15:20:02 -0400 Subject: [PATCH 7/7] limit ingame element size Former-commit-id: b60e297096ad354cae77482fe84aca88bb9a8f3e --- voxygen/src/ui/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 271204cd39..0fc7568e33 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -573,7 +573,15 @@ impl Ui { / 10.0) .min(1.6) .max(0.2); - Placement::InWorld(parameters.num, Some(parameters.res)) + + // Scale down ingame elements that are close to the camera + let res = if scale_factor > 3.2 { + parameters.res * scale_factor as f32 / 3.2 + } else { + parameters.res + }; + + Placement::InWorld(parameters.num, Some(res)) } else { Placement::InWorld(parameters.num, None) };