mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'vd_and_disclaimer' into 'master'
View Distance Tweaks and Showing Disclaimer Once Closes #126 See merge request veloren/veloren!185 Former-commit-id: a808bbdea6d9cf84cbadb5c592f9db6dc6b0140d
This commit is contained in:
commit
09327914b5
@ -105,11 +105,15 @@ 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
|
||||
}
|
||||
|
||||
pub fn view_distance(&self) -> Option<u32> {
|
||||
self.view_distance
|
||||
}
|
||||
|
||||
/// Send a chat message to the server.
|
||||
#[allow(dead_code)]
|
||||
pub fn send_chat(&mut self, msg: String) {
|
||||
@ -230,9 +234,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)
|
||||
|
@ -83,6 +83,16 @@ impl Clients {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_ingame_if<F: FnMut(EcsEntity) -> 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<F: FnMut(EcsEntity) -> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
@ -283,19 +284,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::<comp::Player>(),
|
||||
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
||||
)
|
||||
.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 {
|
||||
@ -329,6 +333,7 @@ impl Server {
|
||||
|
||||
if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) {
|
||||
should_drop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,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::<comp::phys::Pos>().get(entity) {
|
||||
Some(pos) => pos.0,
|
||||
None => return false,
|
||||
};
|
||||
// Get client view distance
|
||||
let client_vd = match state.ecs().read_storage::<comp::Player>().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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Pos>,
|
||||
pub coordinates: Option<comp::phys::Pos>,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
@ -305,6 +304,14 @@ impl Hud {
|
||||
let player = ecs.read_storage::<comp::Player>();
|
||||
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::<comp::phys::Pos>()
|
||||
.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,
|
||||
|
@ -83,6 +83,12 @@ fn main() {
|
||||
let mut settings = Settings::load();
|
||||
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()))
|
||||
@ -93,13 +99,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();
|
||||
@ -160,16 +166,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<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
|
||||
states
|
||||
@ -227,4 +227,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();
|
||||
}
|
||||
|
@ -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,
|
||||
)));
|
||||
}
|
||||
@ -114,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
|
@ -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<Event> {
|
||||
fn update_layout(&mut self, global_state: &mut GlobalState) -> Vec<Event> {
|
||||
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
|
||||
|
@ -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::<comp::phys::Pos>()
|
||||
.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::<comp::phys::Pos>(),
|
||||
@ -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::<comp::phys::Pos>()
|
||||
.get(client.entity())
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
for (entity, _, _, _, actor, _, _) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
&ecs.read_storage::<comp::phys::Vel>(),
|
||||
&ecs.read_storage::<comp::phys::Dir>(),
|
||||
&ecs.read_storage::<comp::Actor>(),
|
||||
&ecs.read_storage::<comp::Stats>(), // Just to make sure the entity is alive
|
||||
&ecs.read_storage::<comp::AnimationInfo>(),
|
||||
ecs.read_storage::<comp::Stats>().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 {
|
||||
|
@ -116,8 +116,7 @@ impl Scene {
|
||||
.ecs()
|
||||
.read_storage::<comp::phys::Pos>()
|
||||
.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);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user