Merge branch 'VD-Slider' into 'master'

View distance slider

See merge request veloren/veloren!152

Former-commit-id: c285b72576f47e1e13ee5959669f51c8aa652ea0
This commit is contained in:
Joshua Barretto 2019-05-19 01:10:42 +00:00
commit 0a9d7d68d5
15 changed files with 124 additions and 47 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@
# Veloren # Veloren
voxygen/keybinds.toml voxygen/keybinds.toml
settings.toml settings.toml
voxygen/settings/
*.rar *.rar
*.log *.log
run.sh run.sh

View File

@ -16,9 +16,9 @@ fn main() {
// Create a client. // Create a client.
let mut 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()); client.send_chat("Hello!".to_string());

View File

@ -44,7 +44,7 @@ pub struct Client {
tick: u64, tick: u64,
state: State, state: State,
entity: EcsEntity, entity: EcsEntity,
view_distance: u64, view_distance: Option<u32>,
pending_chunks: HashMap<Vec2<i32>, Instant>, pending_chunks: HashMap<Vec2<i32>, Instant>,
} }
@ -52,7 +52,7 @@ pub struct Client {
impl Client { impl Client {
/// Create a new `Client`. /// Create a new `Client`.
#[allow(dead_code)] #[allow(dead_code)]
pub fn new<A: Into<SocketAddr>>(addr: A, view_distance: u64) -> Result<Self, Error> { pub fn new<A: Into<SocketAddr>>(addr: A, view_distance: Option<u32>) -> Result<Self, Error> {
let mut client_state = Some(ClientState::Connected); let mut client_state = Some(ClientState::Connected);
let mut postbox = PostBox::to(addr)?; let mut postbox = PostBox::to(addr)?;
@ -94,6 +94,12 @@ impl Client {
self.postbox.send_message(ClientMsg::Register { player }); 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 /// 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 /// computationally expensive operations that run outside of the main thread (i.e., threads that
/// block on I/O operations are exempt). /// block on I/O operations are exempt).
@ -198,16 +204,16 @@ impl Client {
.read_storage::<comp::phys::Pos>() .read_storage::<comp::phys::Pos>()
.get(self.entity) .get(self.entity)
.cloned(); .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)); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
// Remove chunks that are too far from the player. // Remove chunks that are too far from the player.
let mut chunks_to_remove = Vec::new(); let mut chunks_to_remove = Vec::new();
self.state.terrain().iter().for_each(|(key, _)| { self.state.terrain().iter().for_each(|(key, _)| {
if (Vec2::from(chunk_pos) - Vec2::from(key)) if (Vec2::from(chunk_pos) - Vec2::from(key))
.map(|e: i32| e.abs()) .map(|e: i32| e.abs() as u32)
.reduce_max() .reduce_max()
> 10 > view_distance
{ {
chunks_to_remove.push(key); chunks_to_remove.push(key);
} }
@ -218,7 +224,7 @@ impl Client {
// Request chunks from the server. // Request chunks from the server.
// TODO: This is really inefficient. // 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 i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 { for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
let key = Vec2::new(i, j); let key = Vec2::new(i, j);

View File

@ -3,11 +3,15 @@ use specs::{Component, FlaggedStorage, VecStorage};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Player { pub struct Player {
pub alias: String, pub alias: String,
pub view_distance: Option<u32>,
} }
impl Player { impl Player {
pub fn new(alias: String) -> Self { pub fn new(alias: String, view_distance: Option<u32>) -> Self {
Self { alias } Self {
alias,
view_distance,
}
} }
} }

View File

@ -12,6 +12,7 @@ pub enum ClientMsg {
body: comp::Body, body: comp::Body,
}, },
RequestState(ClientState), RequestState(ClientState),
SetViewDistance(u32),
Ping, Ping,
Pong, Pong,
Chat(String), Chat(String),

View File

@ -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) { fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String); let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias { match opt_alias {
Some(alias) => server Some(alias) => {
.state server
.write_component(entity, comp::player::Player { alias }), .state
.ecs_mut()
.write_storage::<comp::Player>()
.get_mut(entity)
.map(|player| player.alias = alias);
}
None => server None => server
.clients .clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))), .notify(entity, ServerMsg::Chat(String::from(action.help_string))),

View File

@ -198,9 +198,9 @@ impl Server {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
let dist = (Vec2::from(chunk_pos) - Vec2::from(key)) let dist = (Vec2::from(chunk_pos) - Vec2::from(key))
.map(|e: i32| e.abs()) .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( self.clients.notify(
entity, entity,
ServerMsg::TerrainChunkUpdate { ServerMsg::TerrainChunkUpdate {
@ -218,10 +218,10 @@ impl Server {
// Remove chunks that are too far from players. // Remove chunks that are too far from players.
let mut chunks_to_remove = Vec::new(); let mut chunks_to_remove = Vec::new();
self.state.terrain().iter().for_each(|(key, _)| { 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 each player with a position, calculate the distance.
for (_, pos) in ( for (player, pos) in (
&self.state.ecs().read_storage::<comp::Player>(), &self.state.ecs().read_storage::<comp::Player>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(), &self.state.ecs().read_storage::<comp::phys::Pos>(),
) )
@ -229,12 +229,15 @@ impl Server {
{ {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
let dist = Vec2::from(chunk_pos - key) let dist = Vec2::from(chunk_pos - key)
.map(|e: i32| e.abs()) .map(|e: i32| e.abs() as u32)
.reduce_max(); .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); chunks_to_remove.push(key);
} }
}); });
@ -340,6 +343,16 @@ impl Server {
// Use RequestState instead (No need to send `player` again). // Use RequestState instead (No need to send `player` again).
_ => client.error_state(RequestStateError::Impossible), _ => client.error_state(RequestStateError::Impossible),
}, },
ClientMsg::SetViewDistance(view_distance) => match client.client_state {
ClientState::Character { .. } => {
state
.ecs_mut()
.write_storage::<comp::Player>()
.get_mut(entity)
.map(|player| player.view_distance = Some(view_distance));
}
_ => {}
},
ClientMsg::Character { name, body } => match client.client_state { ClientMsg::Character { name, body } => match client.client_state {
// Become Registered first. // Become Registered first.
ClientState::Connected => { ClientState::Connected => {

View File

@ -128,7 +128,6 @@ image_ids! {
// Spell Book Window // Spell Book Window
spellbook_icon: "voxygen/element/icons/spellbook.png", spellbook_icon: "voxygen/element/icons/spellbook.png",
// Bag // Bag
bag: "voxygen/element/buttons/bag/closed.png", bag: "voxygen/element/buttons/bag/closed.png",
bag_hover: "voxygen/element/buttons/bag/closed_hover.png", bag_hover: "voxygen/element/buttons/bag/closed_hover.png",

View File

@ -90,6 +90,7 @@ font_ids! {
pub enum Event { pub enum Event {
SendMessage(String), SendMessage(String),
AdjustViewDistance(u32),
Logout, Logout,
Quit, Quit,
} }
@ -207,6 +208,8 @@ pub struct Hud {
to_focus: Option<Option<widget::Id>>, to_focus: Option<Option<widget::Id>>,
settings: Settings, settings: Settings,
force_ungrab: bool, force_ungrab: bool,
// TODO: move to settings
current_vd: u32,
} }
impl Hud { impl Hud {
@ -243,6 +246,7 @@ impl Hud {
to_focus: None, to_focus: None,
settings, settings,
force_ungrab: false, force_ungrab: false,
current_vd: 5,
} }
} }
@ -372,18 +376,21 @@ impl Hud {
// Settings // Settings
if let Windows::Settings = self.show.open_windows { 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) .set(self.ids.settings_window, ui_widgets)
{ {
Some(settings_window::Event::ToggleHelp) => self.show.toggle_help(), match event {
Some(settings_window::Event::ToggleInventoryTestButton) => { settings_window::Event::ToggleHelp => self.show.toggle_help(),
self.show.inventory_test_button = !self.show.inventory_test_button 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 => {}
} }
} }

View File

@ -1,11 +1,19 @@
use super::{img_ids::Imgs, Fonts, TEXT_COLOR}; use super::{img_ids::Imgs, Fonts, TEXT_COLOR};
use crate::{hud::Show, ui::ToggleButton}; use crate::{hud::Show, ui::ToggleButton};
use crate::{
render::Renderer,
ui::{
self,
img_ids::{ImageGraphic, VoxelGraphic},
ImageSlider, ScaleMode, Ui,
},
window::Window,
};
use conrod_core::{ use conrod_core::{
color, color,
widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget::{self, Button, Image, Rectangle, Scrollbar, Text},
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
}; };
widget_ids! { widget_ids! {
struct Ids { struct Ids {
@ -34,6 +42,8 @@ widget_ids! {
sound, sound,
test, test,
video, video,
vd_slider,
vd_slider_text,
} }
} }
@ -52,16 +62,19 @@ pub struct SettingsWindow<'a> {
imgs: &'a Imgs, imgs: &'a Imgs,
fonts: &'a Fonts, fonts: &'a Fonts,
current_vd: u32,
#[conrod(common_builder)] #[conrod(common_builder)]
common: widget::CommonBuilder, common: widget::CommonBuilder,
} }
impl<'a> SettingsWindow<'a> { 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 { Self {
show, show,
imgs, imgs,
fonts, fonts,
current_vd,
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
} }
} }
@ -78,12 +91,13 @@ pub enum Event {
ToggleInventoryTestButton, ToggleInventoryTestButton,
ToggleDebug, ToggleDebug,
Close, Close,
AdjustViewDistance(u32),
} }
impl<'a> Widget for SettingsWindow<'a> { impl<'a> Widget for SettingsWindow<'a> {
type State = State; type State = State;
type Style = (); type Style = ();
type Event = Option<Event>; type Event = Vec<Event>;
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State { State {
@ -99,6 +113,8 @@ impl<'a> Widget for SettingsWindow<'a> {
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event { fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args; let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
// Frame Alignment // Frame Alignment
Rectangle::fill_with([824.0, 488.0], color::TRANSPARENT) Rectangle::fill_with([824.0, 488.0], color::TRANSPARENT)
.middle_of(ui.window) .middle_of(ui.window)
@ -132,7 +148,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.settings_close, ui) .set(state.ids.settings_close, ui)
.was_clicked() .was_clicked()
{ {
return Some(Event::Close); events.push(Event::Close);
} }
// Title // Title
@ -180,7 +196,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.button_help, ui); .set(state.ids.button_help, ui);
if self.show.help != show_help { if self.show.help != show_help {
return Some(Event::ToggleHelp); events.push(Event::ToggleHelp);
} }
Text::new("Show Help") Text::new("Show Help")
@ -204,7 +220,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.inventory_test_button, ui); .set(state.ids.inventory_test_button, ui);
if self.show.inventory_test_button != inventory_test_button { if self.show.inventory_test_button != inventory_test_button {
return Some(Event::ToggleInventoryTestButton); events.push(Event::ToggleInventoryTestButton);
} }
Text::new("Show Inventory Test Button") Text::new("Show Inventory Test Button")
@ -225,7 +241,7 @@ impl<'a> Widget for SettingsWindow<'a> {
.set(state.ids.debug_button, ui); .set(state.ids.debug_button, ui);
if self.show.debug != show_debug { if self.show.debug != show_debug {
return Some(Event::ToggleDebug); events.push(Event::ToggleDebug);
} }
Text::new("Show Debug Window") Text::new("Show Debug Window")
@ -450,7 +466,32 @@ impl<'a> Widget for SettingsWindow<'a> {
{ {
state.update(|s| s.settings_tab = SettingsTab::Video); 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 // 5 Sound
if Button::image(if let SettingsTab::Sound = state.settings_tab { if Button::image(if let SettingsTab::Sound = state.settings_tab {
self.imgs.settings_button_pressed self.imgs.settings_button_pressed
@ -479,6 +520,6 @@ impl<'a> Widget for SettingsWindow<'a> {
state.update(|s| s.settings_tab = SettingsTab::Sound); state.update(|s| s.settings_tab = SettingsTab::Sound);
} }
None events
} }
} }

View File

@ -24,13 +24,8 @@ pub struct ClientInit {
rx: Receiver<Result<Client, Error>>, rx: Receiver<Result<Client, Error>>,
} }
impl ClientInit { impl ClientInit {
pub fn new( pub fn new(connection_args: (String, u16, bool), player: comp::Player, wait: bool) -> Self {
connection_args: (String, u16, bool),
client_args: (comp::Player, u64),
wait: bool,
) -> Self {
let (server_address, default_port, prefer_ipv6) = connection_args; let (server_address, default_port, prefer_ipv6) = connection_args;
let (player, view_distance) = client_args;
let (tx, rx) = channel(); let (tx, rx) = channel();
@ -53,7 +48,7 @@ impl ClientInit {
let mut last_err = None; let mut last_err = None;
for socket_addr in first_addrs.into_iter().chain(second_addrs) { 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) => { Ok(mut client) => {
client.register(player); client.register(player);
let _ = tx.send(Ok(client)); let _ = tx.send(Ok(client));

View File

@ -106,7 +106,7 @@ impl PlayState for MainMenuState {
// Don't try to connect if there is already a connection in progress. // Don't try to connect if there is already a connection in progress.
client_init = client_init.or(Some(ClientInit::new( client_init = client_init.or(Some(ClientInit::new(
(server_address, DEFAULT_PORT, false), (server_address, DEFAULT_PORT, false),
(comp::Player::new(username.clone()), 300), comp::Player::new(username.clone(), Some(10)),
false, false,
))); )));
} }

View File

@ -30,7 +30,7 @@ impl PlayState for StartSingleplayerState {
let client_init = ClientInit::new( let client_init = ClientInit::new(
(server_address.clone(), self.sock.port(), false), (server_address.clone(), self.sock.port(), false),
(comp::Player::new(username.clone()), 300), comp::Player::new(username.clone(), Some(10)),
true, true,
); );

View File

@ -190,6 +190,9 @@ impl PlayState for SessionState {
HudEvent::Quit => { HudEvent::Quit => {
return PlayStateResult::Shutdown; return PlayStateResult::Shutdown;
} }
HudEvent::AdjustViewDistance(view_distance) => {
self.client.borrow_mut().set_view_distance(view_distance)
}
} }
} }

View File

@ -287,6 +287,8 @@ pub enum Event {
KeyUp(Key), KeyUp(Key),
/// Event that the ui uses. /// Event that the ui uses.
Ui(ui::Event), Ui(ui::Event),
// The view distance has been changed
ViewDistanceChanged(u32),
/// Game settings have changed. /// Game settings have changed.
SettingsChanged, SettingsChanged,
} }