diff --git a/.gitignore b/.gitignore index 23ff1bf194..dcd92a42c3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ # Veloren voxygen/keybinds.toml settings.toml +voxygen/settings/ *.rar *.log run.sh diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index 7fa1022b3a..b8b0ef39f5 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -16,9 +16,9 @@ fn main() { // Create a client. let mut client = - Client::new(([127, 0, 0, 1], 59003), 300).expect("Failed to create client instance"); + Client::new(([127, 0, 0, 1], 59003), None).expect("Failed to create client instance"); - client.register(comp::Player::new("test".to_string())); + client.register(comp::Player::new("test".to_string(), None)); client.send_chat("Hello!".to_string()); diff --git a/client/src/lib.rs b/client/src/lib.rs index d1e4d1508a..b2ea500e61 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -44,7 +44,7 @@ pub struct Client { tick: u64, state: State, entity: EcsEntity, - view_distance: u64, + view_distance: Option, pending_chunks: HashMap, Instant>, } @@ -52,7 +52,7 @@ pub struct Client { impl Client { /// Create a new `Client`. #[allow(dead_code)] - pub fn new>(addr: A, view_distance: u64) -> Result { + pub fn new>(addr: A, view_distance: Option) -> Result { let mut client_state = Some(ClientState::Connected); let mut postbox = PostBox::to(addr)?; @@ -94,6 +94,12 @@ impl Client { self.postbox.send_message(ClientMsg::Register { player }); } + pub fn set_view_distance(&mut self, view_distance: u32) { + self.view_distance = Some(view_distance.max(5).min(25)); + self.postbox + .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail + } + /// Get a reference to the client's worker thread pool. This pool should be used for any /// computationally expensive operations that run outside of the main thread (i.e., threads that /// block on I/O operations are exempt). @@ -198,16 +204,16 @@ impl Client { .read_storage::() .get(self.entity) .cloned(); - if let Some(pos) = pos { + if let (Some(pos), Some(view_distance)) = (pos, self.view_distance) { let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); // Remove chunks that are too far from the player. let mut chunks_to_remove = Vec::new(); self.state.terrain().iter().for_each(|(key, _)| { if (Vec2::from(chunk_pos) - Vec2::from(key)) - .map(|e: i32| e.abs()) + .map(|e: i32| e.abs() as u32) .reduce_max() - > 10 + > view_distance { chunks_to_remove.push(key); } @@ -218,7 +224,7 @@ impl Client { // Request chunks from the server. // TODO: This is really inefficient. - 'outer: for dist in 0..10 { + '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 { let key = Vec2::new(i, j); diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index 6f3bc54ef0..2c895083e0 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -3,11 +3,15 @@ use specs::{Component, FlaggedStorage, VecStorage}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { pub alias: String, + pub view_distance: Option, } impl Player { - pub fn new(alias: String) -> Self { - Self { alias } + pub fn new(alias: String, view_distance: Option) -> Self { + Self { + alias, + view_distance, + } } } diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 80756d9d94..2c3d2bbc3f 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -12,6 +12,7 @@ pub enum ClientMsg { body: comp::Body, }, RequestState(ClientState), + SetViewDistance(u32), Ping, Pong, Chat(String), diff --git a/server/src/cmd.rs b/server/src/cmd.rs index c0b3df6776..b31ed2ee28 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -135,9 +135,14 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { let opt_alias = scan_fmt!(&args, action.arg_fmt, String); match opt_alias { - Some(alias) => server - .state - .write_component(entity, comp::player::Player { alias }), + Some(alias) => { + server + .state + .ecs_mut() + .write_storage::() + .get_mut(entity) + .map(|player| player.alias = alias); + } None => server .clients .notify(entity, ServerMsg::Chat(String::from(action.help_string))), diff --git a/server/src/lib.rs b/server/src/lib.rs index 8a818e6b2f..e1e94d1e86 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -198,9 +198,9 @@ impl Server { 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(); + .reduce_max() as u32; - if dist < 10 { + if player.view_distance.map(|vd| dist < vd).unwrap_or(false) { self.clients.notify( entity, ServerMsg::TerrainChunkUpdate { @@ -218,10 +218,10 @@ impl Server { // Remove chunks that are too far from players. let mut chunks_to_remove = Vec::new(); self.state.terrain().iter().for_each(|(key, _)| { - let mut min_dist = i32::MAX; + let mut should_drop = true; // For each player with a position, calculate the distance. - for (_, pos) in ( + for (player, pos) in ( &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), ) @@ -229,12 +229,15 @@ impl Server { { let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let dist = Vec2::from(chunk_pos - key) - .map(|e: i32| e.abs()) + .map(|e: i32| e.abs() as u32) .reduce_max(); - min_dist = min_dist.min(dist); + + if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) { + should_drop = false; + } } - if min_dist > 10 { + if should_drop { chunks_to_remove.push(key); } }); @@ -340,6 +343,16 @@ impl Server { // Use RequestState instead (No need to send `player` again). _ => client.error_state(RequestStateError::Impossible), }, + ClientMsg::SetViewDistance(view_distance) => match client.client_state { + ClientState::Character { .. } => { + state + .ecs_mut() + .write_storage::() + .get_mut(entity) + .map(|player| player.view_distance = Some(view_distance)); + } + _ => {} + }, ClientMsg::Character { name, body } => match client.client_state { // Become Registered first. ClientState::Connected => { diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index e52d7096d3..d514b4e1af 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -128,7 +128,6 @@ image_ids! { // Spell Book Window spellbook_icon: "voxygen/element/icons/spellbook.png", - // Bag bag: "voxygen/element/buttons/bag/closed.png", bag_hover: "voxygen/element/buttons/bag/closed_hover.png", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 32411f7891..97ddf8071d 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -90,6 +90,7 @@ font_ids! { pub enum Event { SendMessage(String), + AdjustViewDistance(u32), Logout, Quit, } @@ -207,6 +208,8 @@ pub struct Hud { to_focus: Option>, settings: Settings, force_ungrab: bool, + // TODO: move to settings + current_vd: u32, } impl Hud { @@ -243,6 +246,7 @@ impl Hud { to_focus: None, settings, force_ungrab: false, + current_vd: 5, } } @@ -372,18 +376,21 @@ impl Hud { // Settings if let Windows::Settings = self.show.open_windows { - match SettingsWindow::new(&self.show, &self.imgs, &self.fonts) + for event in SettingsWindow::new(&self.show, &self.imgs, &self.fonts, self.current_vd) .set(self.ids.settings_window, ui_widgets) { - Some(settings_window::Event::ToggleHelp) => self.show.toggle_help(), - Some(settings_window::Event::ToggleInventoryTestButton) => { - self.show.inventory_test_button = !self.show.inventory_test_button + match event { + settings_window::Event::ToggleHelp => self.show.toggle_help(), + settings_window::Event::ToggleInventoryTestButton => { + self.show.inventory_test_button = !self.show.inventory_test_button + } + settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug, + settings_window::Event::Close => self.show.open_windows = Windows::None, + settings_window::Event::AdjustViewDistance(view_distance) => { + self.current_vd = view_distance; + events.push(Event::AdjustViewDistance(view_distance)); + } } - Some(settings_window::Event::ToggleDebug) => self.show.debug = !self.show.debug, - Some(settings_window::Event::Close) => { - self.show.open_windows = Windows::None; - } - None => {} } } diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index d3b507b819..3f340ebe4b 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -1,11 +1,19 @@ use super::{img_ids::Imgs, Fonts, TEXT_COLOR}; use crate::{hud::Show, ui::ToggleButton}; +use crate::{ + render::Renderer, + ui::{ + self, + img_ids::{ImageGraphic, VoxelGraphic}, + ImageSlider, ScaleMode, Ui, + }, + window::Window, +}; use conrod_core::{ color, widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; - widget_ids! { struct Ids { @@ -34,6 +42,8 @@ widget_ids! { sound, test, video, + vd_slider, + vd_slider_text, } } @@ -52,16 +62,19 @@ pub struct SettingsWindow<'a> { imgs: &'a Imgs, fonts: &'a Fonts, + current_vd: u32, + #[conrod(common_builder)] common: widget::CommonBuilder, } impl<'a> SettingsWindow<'a> { - pub fn new(show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts) -> Self { + pub fn new(show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts, current_vd: u32) -> Self { Self { show, imgs, fonts, + current_vd, common: widget::CommonBuilder::default(), } } @@ -78,12 +91,13 @@ pub enum Event { ToggleInventoryTestButton, ToggleDebug, Close, + AdjustViewDistance(u32), } impl<'a> Widget for SettingsWindow<'a> { type State = State; type Style = (); - type Event = Option; + type Event = Vec; fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { State { @@ -99,6 +113,8 @@ impl<'a> Widget for SettingsWindow<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; + let mut events = Vec::new(); + // Frame Alignment Rectangle::fill_with([824.0, 488.0], color::TRANSPARENT) .middle_of(ui.window) @@ -132,7 +148,7 @@ impl<'a> Widget for SettingsWindow<'a> { .set(state.ids.settings_close, ui) .was_clicked() { - return Some(Event::Close); + events.push(Event::Close); } // Title @@ -180,7 +196,7 @@ impl<'a> Widget for SettingsWindow<'a> { .set(state.ids.button_help, ui); if self.show.help != show_help { - return Some(Event::ToggleHelp); + events.push(Event::ToggleHelp); } Text::new("Show Help") @@ -204,7 +220,7 @@ impl<'a> Widget for SettingsWindow<'a> { .set(state.ids.inventory_test_button, ui); if self.show.inventory_test_button != inventory_test_button { - return Some(Event::ToggleInventoryTestButton); + events.push(Event::ToggleInventoryTestButton); } Text::new("Show Inventory Test Button") @@ -225,7 +241,7 @@ impl<'a> Widget for SettingsWindow<'a> { .set(state.ids.debug_button, ui); if self.show.debug != show_debug { - return Some(Event::ToggleDebug); + events.push(Event::ToggleDebug); } Text::new("Show Debug Window") @@ -450,7 +466,32 @@ impl<'a> Widget for SettingsWindow<'a> { { state.update(|s| s.settings_tab = SettingsTab::Video); } + // Contents + if let SettingsTab::Video = state.settings_tab { + Text::new("Viewdistance") + .top_left_with_margins_on(state.ids.settings_content, 10.0, 10.0) + .font_size(14) + .font_id(self.fonts.opensans) + .color(TEXT_COLOR) + .set(state.ids.vd_slider_text, ui); + if let Some(new_val) = ImageSlider::discrete( + self.current_vd, + 1, + 25, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .down_from(state.ids.vd_slider_text, 10.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.vd_slider, ui) + { + events.push(Event::AdjustViewDistance(new_val as u32)); + } + } // 5 Sound if Button::image(if let SettingsTab::Sound = state.settings_tab { self.imgs.settings_button_pressed @@ -479,6 +520,6 @@ impl<'a> Widget for SettingsWindow<'a> { state.update(|s| s.settings_tab = SettingsTab::Sound); } - None + events } } diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 933db4b8a4..ceef63c874 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -24,13 +24,8 @@ pub struct ClientInit { rx: Receiver>, } impl ClientInit { - pub fn new( - connection_args: (String, u16, bool), - client_args: (comp::Player, u64), - wait: bool, - ) -> Self { + pub fn new(connection_args: (String, u16, bool), player: comp::Player, wait: bool) -> Self { let (server_address, default_port, prefer_ipv6) = connection_args; - let (player, view_distance) = client_args; let (tx, rx) = channel(); @@ -53,7 +48,7 @@ impl ClientInit { let mut last_err = None; for socket_addr in first_addrs.into_iter().chain(second_addrs) { - match Client::new(socket_addr, view_distance) { + match Client::new(socket_addr, player.view_distance) { Ok(mut client) => { client.register(player); let _ = tx.send(Ok(client)); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 2cdd432e7b..f59e06f4a3 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -106,7 +106,7 @@ 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()), 300), + comp::Player::new(username.clone(), Some(10)), false, ))); } diff --git a/voxygen/src/menu/main/start_singleplayer.rs b/voxygen/src/menu/main/start_singleplayer.rs index 87d3220bfd..addd4be521 100644 --- a/voxygen/src/menu/main/start_singleplayer.rs +++ b/voxygen/src/menu/main/start_singleplayer.rs @@ -30,7 +30,7 @@ impl PlayState for StartSingleplayerState { let client_init = ClientInit::new( (server_address.clone(), self.sock.port(), false), - (comp::Player::new(username.clone()), 300), + comp::Player::new(username.clone(), Some(10)), true, ); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 8b958d5a21..6f7993be2b 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -190,6 +190,9 @@ impl PlayState for SessionState { HudEvent::Quit => { return PlayStateResult::Shutdown; } + HudEvent::AdjustViewDistance(view_distance) => { + self.client.borrow_mut().set_view_distance(view_distance) + } } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 135751109b..90e7d1aeb4 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -287,6 +287,8 @@ pub enum Event { KeyUp(Key), /// Event that the ui uses. Ui(ui::Event), + // The view distance has been changed + ViewDistanceChanged(u32), /// Game settings have changed. SettingsChanged, }