mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
fix(player list): Show players not in range on the player list
fix(overflow): Stops including block updates that fail (since chunks don't exist on the client) in `TerrainUpdates` (which would trigger meshing of those nonexistent chunks). Furthermore, removes remeshing of chunks with block updates if those chunks don't have all their neighbours (since those wouldn't be meshed in the first place).
This commit is contained in:
parent
722024ed7f
commit
ec3e075020
@ -10,8 +10,8 @@ pub use specs::{join::Join, saveload::Marker, Entity as EcsEntity, ReadStorage,
|
||||
use common::{
|
||||
comp::{self, ControlEvent, Controller, ControllerInputs, InventoryManip},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, RequestStateError,
|
||||
ServerError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG,
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||
RequestStateError, ServerError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
net::PostBox,
|
||||
state::State,
|
||||
@ -52,6 +52,7 @@ pub struct Client {
|
||||
thread_pool: ThreadPool,
|
||||
pub server_info: ServerInfo,
|
||||
pub world_map: Arc<DynamicImage>,
|
||||
pub player_list: HashMap<u64, String>,
|
||||
|
||||
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||
|
||||
@ -71,7 +72,6 @@ pub struct Client {
|
||||
|
||||
impl Client {
|
||||
/// Create a new `Client`.
|
||||
#[allow(dead_code)]
|
||||
pub fn new<A: Into<SocketAddr>>(addr: A, view_distance: Option<u32>) -> Result<Self, Error> {
|
||||
let client_state = ClientState::Connected;
|
||||
let mut postbox = PostBox::to(addr)?;
|
||||
@ -137,6 +137,7 @@ impl Client {
|
||||
thread_pool,
|
||||
server_info,
|
||||
world_map,
|
||||
player_list: HashMap::new(),
|
||||
|
||||
postbox,
|
||||
|
||||
@ -154,7 +155,6 @@ impl Client {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn with_thread_pool(mut self, thread_pool: ThreadPool) -> Self {
|
||||
self.thread_pool = thread_pool;
|
||||
self
|
||||
@ -282,7 +282,6 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Send a chat message to the server.
|
||||
#[allow(dead_code)]
|
||||
pub fn send_chat(&mut self, msg: String) {
|
||||
match validate_chat_msg(&msg) {
|
||||
Ok(()) => self.postbox.send_message(ClientMsg::chat(msg)),
|
||||
@ -294,7 +293,6 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Remove all cached terrain
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_terrain(&mut self) {
|
||||
self.state.clear_terrain();
|
||||
self.pending_chunks.clear();
|
||||
@ -316,7 +314,6 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||
#[allow(dead_code)]
|
||||
pub fn tick(&mut self, inputs: ControllerInputs, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||
// This tick function is the centre of the Veloren universe. Most client-side things are
|
||||
// managed from here, and as such it's important that it stays organised. Please consult
|
||||
@ -389,6 +386,10 @@ impl Client {
|
||||
// Remove chunks that are too far from the player.
|
||||
let mut chunks_to_remove = Vec::new();
|
||||
self.state.terrain().iter().for_each(|(key, _)| {
|
||||
// Subtract 2 from the offset before computing squared magnitude
|
||||
// 1 for the chunks needed bordering other chunks for meshing
|
||||
// 1 as a buffer so that if the player moves back in that direction the chunks
|
||||
// don't need to be reloaded
|
||||
if (chunk_pos - key)
|
||||
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
|
||||
.magnitude_squared()
|
||||
@ -492,7 +493,6 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Clean up the client after a tick.
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup(&mut self) {
|
||||
// Cleanup the local state
|
||||
self.state.cleanup();
|
||||
@ -531,6 +531,27 @@ impl Client {
|
||||
},
|
||||
ServerMsg::Shutdown => return Err(Error::ServerShutdown),
|
||||
ServerMsg::InitialSync { .. } => return Err(Error::ServerWentMad),
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(list)) => {
|
||||
self.player_list = list
|
||||
}
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Add(uid, name)) => {
|
||||
if let Some(old_name) = self.player_list.insert(uid, name.clone()) {
|
||||
warn!("Received msg to insert {} with uid {} into the player list but there was already an entry for {} with the same uid that was overwritten!", name, uid, old_name);
|
||||
}
|
||||
}
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(uid)) => {
|
||||
if self.player_list.remove(&uid).is_none() {
|
||||
warn!("Received msg to remove uid {} from the player list by they weren't in the list!", uid);
|
||||
}
|
||||
}
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(uid, new_name)) => {
|
||||
if let Some(name) = self.player_list.get_mut(&uid) {
|
||||
*name = new_name;
|
||||
} else {
|
||||
warn!("Received msg to alias player with uid {} to {} but this uid is not in the player list", uid, new_name);
|
||||
}
|
||||
}
|
||||
|
||||
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
|
||||
ServerMsg::Pong => {
|
||||
self.last_server_pong = Instant::now();
|
||||
@ -602,9 +623,11 @@ impl Client {
|
||||
}
|
||||
self.pending_chunks.remove(&key);
|
||||
}
|
||||
ServerMsg::TerrainBlockUpdates(mut blocks) => blocks
|
||||
.drain()
|
||||
.for_each(|(pos, block)| self.state.set_block(pos, block)),
|
||||
ServerMsg::TerrainBlockUpdates(mut blocks) => {
|
||||
blocks.drain().for_each(|(pos, block)| {
|
||||
self.state.set_block(pos, block);
|
||||
});
|
||||
}
|
||||
ServerMsg::StateAnswer(Ok(state)) => {
|
||||
self.client_state = state;
|
||||
}
|
||||
@ -636,24 +659,20 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Get the player's entity.
|
||||
#[allow(dead_code)]
|
||||
pub fn entity(&self) -> EcsEntity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
/// Get the client state
|
||||
#[allow(dead_code)]
|
||||
pub fn get_client_state(&self) -> ClientState {
|
||||
self.client_state
|
||||
}
|
||||
|
||||
/// Get the current tick number.
|
||||
#[allow(dead_code)]
|
||||
pub fn get_tick(&self) -> u64 {
|
||||
self.tick
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_ping_ms(&self) -> f64 {
|
||||
self.last_ping_delta * 1000.0
|
||||
}
|
||||
@ -661,19 +680,16 @@ impl Client {
|
||||
/// 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).
|
||||
#[allow(dead_code)]
|
||||
pub fn thread_pool(&self) -> &ThreadPool {
|
||||
&self.thread_pool
|
||||
}
|
||||
|
||||
/// Get a reference to the client's game state.
|
||||
#[allow(dead_code)]
|
||||
pub fn state(&self) -> &State {
|
||||
&self.state
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the client's game state.
|
||||
#[allow(dead_code)]
|
||||
pub fn state_mut(&mut self) -> &mut State {
|
||||
&mut self.state
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ pub enum ClientMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
ChatMsg {
|
||||
chat_type: ChatType,
|
||||
chat_type: ChatType, // This is unused afaik, TODO: remove
|
||||
message: String,
|
||||
},
|
||||
PlayerPhysics {
|
||||
|
@ -5,7 +5,7 @@ pub mod server;
|
||||
// Reexports
|
||||
pub use self::client::ClientMsg;
|
||||
pub use self::ecs_packet::EcsCompPacket;
|
||||
pub use self::server::{RequestStateError, ServerError, ServerInfo, ServerMsg};
|
||||
pub use self::server::{PlayerListUpdate, RequestStateError, ServerError, ServerInfo, ServerMsg};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientState {
|
||||
|
@ -23,6 +23,14 @@ pub struct ServerInfo {
|
||||
pub git_date: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum PlayerListUpdate {
|
||||
Init(HashMap<u64, String>),
|
||||
Add(u64, String),
|
||||
Remove(u64),
|
||||
Alias(u64, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerMsg {
|
||||
InitialSync {
|
||||
@ -31,6 +39,7 @@ pub enum ServerMsg {
|
||||
time_of_day: state::TimeOfDay,
|
||||
// world_map: Vec2<usize>, /*, Vec<u32>)*/
|
||||
},
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
StateAnswer(Result<ClientState, (RequestStateError, ClientState)>),
|
||||
ForceState(ClientState),
|
||||
Ping,
|
||||
|
@ -326,17 +326,14 @@ impl State {
|
||||
|
||||
// Apply terrain changes
|
||||
let mut terrain = self.ecs.write_resource::<TerrainGrid>();
|
||||
self.ecs
|
||||
.read_resource::<BlockChange>()
|
||||
.blocks
|
||||
.iter()
|
||||
.for_each(|(pos, block)| {
|
||||
let _ = terrain.set(*pos, *block);
|
||||
});
|
||||
self.ecs.write_resource::<TerrainChanges>().modified_blocks = std::mem::replace(
|
||||
let mut modified_blocks = std::mem::replace(
|
||||
&mut self.ecs.write_resource::<BlockChange>().blocks,
|
||||
Default::default(),
|
||||
);
|
||||
// Apply block modifications
|
||||
// Only include in `TerrainChanges` if successful
|
||||
modified_blocks.retain(|pos, block| terrain.set(*pos, *block).is_ok());
|
||||
self.ecs.write_resource::<TerrainChanges>().modified_blocks = modified_blocks;
|
||||
|
||||
// Process local events
|
||||
let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
|
||||
|
@ -167,6 +167,8 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
|
||||
pub struct CachedVolGrid2d<'a, V: RectRasterableVol> {
|
||||
vol_grid_2d: &'a VolGrid2d<V>,
|
||||
// This can't be invalidated by mutations of the chunks hashmap since we hold an immutable
|
||||
// reference to the `VolGrid2d`
|
||||
cache: Option<(Vec2<i32>, Arc<V>)>,
|
||||
}
|
||||
impl<'a, V: RectRasterableVol> CachedVolGrid2d<'a, V> {
|
||||
@ -178,9 +180,9 @@ impl<'a, V: RectRasterableVol> CachedVolGrid2d<'a, V> {
|
||||
}
|
||||
}
|
||||
impl<'a, V: RectRasterableVol + ReadVol> CachedVolGrid2d<'a, V> {
|
||||
// Note: this may be invalidated by mutations of the chunks hashmap
|
||||
#[inline(always)]
|
||||
pub fn get(&mut self, pos: Vec3<i32>) -> Result<&V::Vox, VolGrid2dError<V>> {
|
||||
// Calculate chunk key from block pos
|
||||
let ck = VolGrid2d::<V>::chunk_key(pos);
|
||||
let chunk = if self
|
||||
.cache
|
||||
@ -188,13 +190,16 @@ impl<'a, V: RectRasterableVol + ReadVol> CachedVolGrid2d<'a, V> {
|
||||
.map(|(key, _)| *key == ck)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// If the chunk with that key is in the cache use that
|
||||
&self.cache.as_ref().unwrap().1
|
||||
} else {
|
||||
// Otherwise retrieve from the hashmap
|
||||
let chunk = self
|
||||
.vol_grid_2d
|
||||
.chunks
|
||||
.get(&ck)
|
||||
.ok_or(VolGrid2dError::NoSuchChunk)?;
|
||||
// Store most recently looked up chunk in the cache
|
||||
self.cache = Some((ck, chunk.clone()));
|
||||
chunk
|
||||
};
|
||||
|
@ -8,11 +8,11 @@ use common::{
|
||||
assets, comp,
|
||||
event::{EventBus, ServerEvent},
|
||||
hierarchical::ChunkPath,
|
||||
msg::ServerMsg,
|
||||
msg::{PlayerListUpdate, ServerMsg},
|
||||
npc::{get_npc_name, NpcKind},
|
||||
pathfinding::WorldPath,
|
||||
state::TimeOfDay,
|
||||
sync::WorldSyncExt,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
@ -407,6 +407,19 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C
|
||||
.write_storage::<comp::Player>()
|
||||
.get_mut(entity)
|
||||
.map(|player| player.alias = alias);
|
||||
|
||||
// Update name on client player lists
|
||||
let ecs = server.state.ecs();
|
||||
if let (Some(uid), Some(player)) = (
|
||||
ecs.read_storage::<Uid>().get(entity),
|
||||
ecs.read_storage::<comp::Player>().get(entity),
|
||||
) {
|
||||
let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(
|
||||
(*uid).into(),
|
||||
player.alias.clone(),
|
||||
));
|
||||
server.state.notify_registered_clients(msg);
|
||||
}
|
||||
} else {
|
||||
server.notify_client(entity, ServerMsg::private(String::from(action.help_string)));
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use common::{
|
||||
assets, comp,
|
||||
effect::Effect,
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{ClientMsg, ClientState, ServerError, ServerInfo, ServerMsg},
|
||||
msg::{ClientMsg, ClientState, PlayerListUpdate, ServerError, ServerInfo, ServerMsg},
|
||||
net::PostOffice,
|
||||
state::{BlockChange, State, TimeOfDay},
|
||||
sync::{Uid, WorldSyncExt},
|
||||
@ -762,6 +762,17 @@ impl Server {
|
||||
}
|
||||
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
// Tell other clients to remove from player list
|
||||
if let (Some(uid), Some(_)) = (
|
||||
state.read_storage::<Uid>().get(entity),
|
||||
state.read_storage::<comp::Player>().get(entity),
|
||||
) {
|
||||
state.notify_registered_clients(ServerMsg::PlayerListUpdate(
|
||||
PlayerListUpdate::Remove((*uid).into()),
|
||||
))
|
||||
}
|
||||
|
||||
// Delete client entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete disconnected client: {:?}", err);
|
||||
}
|
||||
|
@ -3,12 +3,16 @@ use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
|
||||
use common::{
|
||||
comp::{Admin, Body, CanBuild, Controller, ForceUpdate, Ori, Player, Pos, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{validate_chat_msg, ChatMsgValidationError, MAX_BYTES_CHAT_MSG},
|
||||
msg::{ClientMsg, ClientState, RequestStateError, ServerMsg},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||
RequestStateError, ServerMsg, MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
state::{BlockChange, Time},
|
||||
sync::Uid,
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::Vox,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use specs::{
|
||||
Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage,
|
||||
};
|
||||
@ -22,6 +26,7 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, Time>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
Write<'a, SysTimer<Self>>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, CanBuild>,
|
||||
ReadStorage<'a, Admin>,
|
||||
@ -44,6 +49,7 @@ impl<'a> System<'a> for Sys {
|
||||
time,
|
||||
terrain,
|
||||
mut timer,
|
||||
uids,
|
||||
bodies,
|
||||
can_build,
|
||||
admins,
|
||||
@ -64,6 +70,14 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let mut new_chat_msgs = Vec::new();
|
||||
|
||||
// Player list to send new players.
|
||||
let player_list = (&uids, &players)
|
||||
.join()
|
||||
.map(|(uid, player)| ((*uid).into(), player.alias.clone()))
|
||||
.collect::<HashMap<_, _>>();
|
||||
// List of new players to update player lists of all clients.
|
||||
let mut new_players = Vec::new();
|
||||
|
||||
for (entity, client) in (&entities, &mut clients).join() {
|
||||
let mut disconnect = false;
|
||||
let new_msgs = client.postbox.new_messages();
|
||||
@ -127,10 +141,18 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
match client.client_state {
|
||||
ClientState::Connected => {
|
||||
// Add Player component to this client
|
||||
let _ = players.insert(entity, player);
|
||||
|
||||
// Tell the client its request was successful.
|
||||
client.allow_state(ClientState::Registered);
|
||||
|
||||
// Send initial player list
|
||||
client.notify(ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(
|
||||
player_list.clone(),
|
||||
)));
|
||||
// Add to list to notify all clients of the new player
|
||||
new_players.push(entity);
|
||||
}
|
||||
// Use RequestState instead (No need to send `player` again).
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
@ -281,6 +303,20 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new players.
|
||||
// Tell all clients to add them to the player list.
|
||||
for entity in new_players {
|
||||
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
||||
let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Add(
|
||||
(*uid).into(),
|
||||
player.alias.clone(),
|
||||
));
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(msg.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new chat messages.
|
||||
for (entity, msg) in new_chat_msgs {
|
||||
match msg {
|
||||
|
@ -71,6 +71,9 @@ impl<'a> System<'a> for Sys {
|
||||
})
|
||||
{
|
||||
let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32));
|
||||
// Subtract 2 from the offset before computing squared magnitude
|
||||
// 1 since chunks need neighbors to be meshed
|
||||
// 1 to act as a buffer if the player moves in that direction
|
||||
let adjusted_dist_sqr = (Vec2::from(chunk_pos) - Vec2::from(key))
|
||||
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
|
||||
.magnitude_squared();
|
||||
|
@ -1,13 +1,11 @@
|
||||
use super::{img_ids::Imgs, Fonts, Show, TEXT_COLOR, TEXT_COLOR_3};
|
||||
|
||||
use common::comp;
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
||||
widget_ids, /*, Color*/
|
||||
Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use specs::{Join, WorldExt};
|
||||
|
||||
use client::{self, Client};
|
||||
|
||||
@ -178,25 +176,12 @@ impl<'a> Widget for Social<'a> {
|
||||
|
||||
// Players list
|
||||
// TODO: this list changes infrequently enough that it should not have to be recreated every frame
|
||||
let ecs = self.client.state().ecs();
|
||||
let players = ecs.read_storage::<comp::Player>();
|
||||
let mut count = 0;
|
||||
for player in players.join() {
|
||||
if ids.player_names.len() <= count {
|
||||
ids.update(|ids| {
|
||||
ids.player_names
|
||||
.resize(count + 1, &mut ui.widget_id_generator())
|
||||
})
|
||||
}
|
||||
|
||||
Text::new(&player.alias)
|
||||
.down_from(ids.online_title, count as f64 * (15.0 + 3.0))
|
||||
.font_size(15)
|
||||
.font_id(self.fonts.cyri)
|
||||
.color(TEXT_COLOR)
|
||||
.set(ids.player_names[count], ui);
|
||||
|
||||
count += 1;
|
||||
let count = self.client.player_list.len();
|
||||
if ids.player_names.len() < count {
|
||||
ids.update(|ids| {
|
||||
ids.player_names
|
||||
.resize(count, &mut ui.widget_id_generator())
|
||||
})
|
||||
}
|
||||
Text::new(&format!("{} player(s) online\n", count))
|
||||
.top_left_with_margins_on(ids.content_align, -2.0, 7.0)
|
||||
@ -204,6 +189,14 @@ impl<'a> Widget for Social<'a> {
|
||||
.font_id(self.fonts.cyri)
|
||||
.color(TEXT_COLOR)
|
||||
.set(ids.online_title, ui);
|
||||
for (i, (_, player_alias)) in self.client.player_list.iter().enumerate() {
|
||||
Text::new(player_alias)
|
||||
.down(3.0)
|
||||
.font_size(15)
|
||||
.font_id(self.fonts.cyri)
|
||||
.color(TEXT_COLOR)
|
||||
.set(ids.player_names[i], ui);
|
||||
}
|
||||
}
|
||||
|
||||
// Friends Tab
|
||||
|
@ -17,7 +17,7 @@ use common::{
|
||||
use crossbeam::channel;
|
||||
use dot_vox::DotVoxData;
|
||||
use frustum_query::frustum::Frustum;
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::{f32, fmt::Debug, i32, marker::PhantomData, ops::Mul, time::Duration};
|
||||
use vek::*;
|
||||
|
||||
@ -836,31 +836,48 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
.map(|(p, _)| *p)
|
||||
{
|
||||
let chunk_pos = client.state().terrain().pos_key(pos);
|
||||
let new_mesh_state = ChunkMeshState {
|
||||
pos: chunk_pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: None,
|
||||
};
|
||||
// Only mesh if this chunk has all its neighbors
|
||||
// If it does have all its neighbors either it should have already been meshed or is in
|
||||
// mesh_todo
|
||||
match self.mesh_todo.entry(chunk_pos) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.insert(new_mesh_state);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
if self.chunks.contains_key(&chunk_pos) {
|
||||
entry.insert(new_mesh_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.mesh_todo.insert(
|
||||
chunk_pos,
|
||||
ChunkMeshState {
|
||||
pos: chunk_pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: None,
|
||||
},
|
||||
);
|
||||
|
||||
// Handle chunks on chunk borders
|
||||
// Handle block changes on chunk borders
|
||||
for x in -1..2 {
|
||||
for y in -1..2 {
|
||||
let neighbour_pos = pos + Vec3::new(x, y, 0);
|
||||
let neighbour_chunk_pos = client.state().terrain().pos_key(neighbour_pos);
|
||||
|
||||
if neighbour_chunk_pos != chunk_pos {
|
||||
self.mesh_todo.insert(
|
||||
neighbour_chunk_pos,
|
||||
ChunkMeshState {
|
||||
pos: neighbour_chunk_pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: None,
|
||||
},
|
||||
);
|
||||
let new_mesh_state = ChunkMeshState {
|
||||
pos: neighbour_chunk_pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: None,
|
||||
};
|
||||
// Only mesh if this chunk has all its neighbors
|
||||
match self.mesh_todo.entry(neighbour_chunk_pos) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.insert(new_mesh_state);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
if self.chunks.contains_key(&neighbour_chunk_pos) {
|
||||
entry.insert(new_mesh_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remesh all neighbours because we have complex lighting now
|
||||
|
Loading…
Reference in New Issue
Block a user