Merge branch 'zesterer/block_diffs' into 'master'

Send block diffs instead of entire chunks on block change

Closes #184

See merge request veloren/veloren!347
This commit is contained in:
Joshua Barretto 2019-07-20 16:08:37 +00:00
commit e5eafdfdc0
5 changed files with 115 additions and 47 deletions

View File

@ -401,6 +401,9 @@ impl Client {
self.state.insert_chunk(key, *chunk); self.state.insert_chunk(key, *chunk);
self.pending_chunks.remove(&key); self.pending_chunks.remove(&key);
} }
ServerMsg::TerrainBlockUpdates(mut blocks) => blocks
.drain()
.for_each(|(pos, block)| self.state.set_block(pos, block)),
ServerMsg::StateAnswer(Ok(state)) => { ServerMsg::StateAnswer(Ok(state)) => {
self.client_state = state; self.client_state = state;
} }

View File

@ -1,5 +1,9 @@
use super::{ClientState, EcsCompPacket, EcsResPacket}; use super::{ClientState, EcsCompPacket, EcsResPacket};
use crate::{comp, terrain::TerrainChunk}; use crate::{
comp,
terrain::{Block, TerrainChunk},
};
use fxhash::FxHashMap;
use vek::*; use vek::*;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -41,6 +45,7 @@ pub enum ServerMsg {
key: Vec2<i32>, key: Vec2<i32>,
chunk: Box<TerrainChunk>, chunk: Box<TerrainChunk>,
}, },
TerrainBlockUpdates(FxHashMap<Vec3<i32>, Block>),
Error(ServerError), Error(ServerError),
Disconnect, Disconnect,
Shutdown, Shutdown,

View File

@ -8,6 +8,7 @@ use crate::{
terrain::{Block, TerrainChunk, TerrainMap}, terrain::{Block, TerrainChunk, TerrainMap},
vol::WriteVol, vol::WriteVol,
}; };
use fxhash::{FxHashMap, FxHashSet};
use rayon::{ThreadPool, ThreadPoolBuilder}; use rayon::{ThreadPool, ThreadPoolBuilder};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use specs::{ use specs::{
@ -16,11 +17,7 @@ use specs::{
Component, DispatcherBuilder, Entity as EcsEntity, Component, DispatcherBuilder, Entity as EcsEntity,
}; };
use sphynx; use sphynx;
use std::{ use std::{sync::Arc, time::Duration};
collections::{HashMap, HashSet},
sync::Arc,
time::Duration,
};
use vek::*; use vek::*;
/// How much faster should an in-game day be compared to a real day? /// How much faster should an in-game day be compared to a real day?
@ -45,19 +42,19 @@ pub struct DeltaTime(pub f32);
/// lag. Ideally, we'd avoid such a situation. /// lag. Ideally, we'd avoid such a situation.
const MAX_DELTA_TIME: f32 = 1.0; const MAX_DELTA_TIME: f32 = 1.0;
pub struct TerrainChange { pub struct BlockChange {
blocks: HashMap<Vec3<i32>, Block>, blocks: FxHashMap<Vec3<i32>, Block>,
} }
impl Default for TerrainChange { impl Default for BlockChange {
fn default() -> Self { fn default() -> Self {
Self { Self {
blocks: HashMap::new(), blocks: FxHashMap::default(),
} }
} }
} }
impl TerrainChange { impl BlockChange {
pub fn set(&mut self, pos: Vec3<i32>, block: Block) { pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
self.blocks.insert(pos, block); self.blocks.insert(pos, block);
} }
@ -67,22 +64,25 @@ impl TerrainChange {
} }
} }
pub struct ChunkChanges { pub struct TerrainChanges {
pub new_chunks: HashSet<Vec2<i32>>, pub new_chunks: FxHashSet<Vec2<i32>>,
pub modified_chunks: HashSet<Vec2<i32>>, pub modified_chunks: FxHashSet<Vec2<i32>>,
pub removed_chunks: HashSet<Vec2<i32>>, pub removed_chunks: FxHashSet<Vec2<i32>>,
pub modified_blocks: FxHashMap<Vec3<i32>, Block>,
} }
impl Default for ChunkChanges { impl Default for TerrainChanges {
fn default() -> Self { fn default() -> Self {
Self { Self {
new_chunks: HashSet::new(), new_chunks: FxHashSet::default(),
modified_chunks: HashSet::new(), modified_chunks: FxHashSet::default(),
removed_chunks: HashSet::new(), removed_chunks: FxHashSet::default(),
modified_blocks: FxHashMap::default(),
} }
} }
} }
impl ChunkChanges {
impl TerrainChanges {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.new_chunks.clear(); self.new_chunks.clear();
self.modified_chunks.clear(); self.modified_chunks.clear();
@ -162,8 +162,8 @@ impl State {
ecs.add_resource(Time(0.0)); ecs.add_resource(Time(0.0));
ecs.add_resource(DeltaTime(0.0)); ecs.add_resource(DeltaTime(0.0));
ecs.add_resource(TerrainMap::new().unwrap()); ecs.add_resource(TerrainMap::new().unwrap());
ecs.add_resource(TerrainChange::default()); ecs.add_resource(BlockChange::default());
ecs.add_resource(ChunkChanges::default()); ecs.add_resource(TerrainChanges::default());
} }
/// Register a component with the state's ECS. /// Register a component with the state's ECS.
@ -200,9 +200,9 @@ impl State {
&mut self.ecs &mut self.ecs
} }
/// Get a reference to the `Changes` structure of the state. This contains /// Get a reference to the `TerrainChanges` structure of the state. This contains
/// information about state that has changed since the last game tick. /// information about terrain state that has changed since the last game tick.
pub fn chunk_changes(&self) -> Fetch<ChunkChanges> { pub fn terrain_changes(&self) -> Fetch<TerrainChanges> {
self.ecs.read_resource() self.ecs.read_resource()
} }
@ -235,6 +235,11 @@ impl State {
self.ecs.write_resource() self.ecs.write_resource()
} }
/// Get a writable reference to this state's terrain.
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
self.ecs.write_resource::<BlockChange>().set(pos, block);
}
/// Removes every chunk of the terrain. /// Removes every chunk of the terrain.
pub fn clear_terrain(&mut self) { pub fn clear_terrain(&mut self) {
let keys = self let keys = self
@ -257,12 +262,12 @@ impl State {
.is_some() .is_some()
{ {
self.ecs self.ecs
.write_resource::<ChunkChanges>() .write_resource::<TerrainChanges>()
.modified_chunks .modified_chunks
.insert(key); .insert(key);
} else { } else {
self.ecs self.ecs
.write_resource::<ChunkChanges>() .write_resource::<TerrainChanges>()
.new_chunks .new_chunks
.insert(key); .insert(key);
} }
@ -277,7 +282,7 @@ impl State {
.is_some() .is_some()
{ {
self.ecs self.ecs
.write_resource::<ChunkChanges>() .write_resource::<TerrainChanges>()
.removed_chunks .removed_chunks
.insert(key); .insert(key);
} }
@ -304,24 +309,25 @@ impl State {
// Apply terrain changes // Apply terrain changes
let mut terrain = self.ecs.write_resource::<TerrainMap>(); let mut terrain = self.ecs.write_resource::<TerrainMap>();
let mut chunk_changes = self.ecs.write_resource::<ChunkChanges>();
self.ecs self.ecs
.write_resource::<TerrainChange>() .read_resource::<BlockChange>()
.blocks .blocks
.drain() .iter()
.for_each(|(pos, block)| { .for_each(|(pos, block)| {
if terrain.set(pos, block).is_ok() { if terrain.set(*pos, *block).is_err() {
chunk_changes.modified_chunks.insert(terrain.pos_key(pos));
} else {
warn!("Tried to modify block outside of terrain at {:?}", pos); warn!("Tried to modify block outside of terrain at {:?}", pos);
} }
}); });
std::mem::swap(
&mut self.ecs.write_resource::<BlockChange>().blocks,
&mut self.ecs.write_resource::<TerrainChanges>().modified_blocks,
)
} }
/// Clean up the state after a tick. /// Clean up the state after a tick.
pub fn cleanup(&mut self) { pub fn cleanup(&mut self) {
// Clean up data structures from the last tick. // Clean up data structures from the last tick.
self.ecs.write_resource::<TerrainChange>().clear(); self.ecs.write_resource::<TerrainChanges>().clear();
self.ecs.write_resource::<ChunkChanges>().clear(); self.ecs.write_resource::<BlockChange>().clear();
} }
} }

View File

@ -17,7 +17,7 @@ use common::{
comp, comp,
msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg}, msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg},
net::PostOffice, net::PostOffice,
state::{State, TerrainChange, TimeOfDay, Uid}, state::{State, TimeOfDay, Uid},
terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap}, terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap},
vol::VolSize, vol::VolSize,
vol::Vox, vol::Vox,
@ -300,7 +300,7 @@ impl Server {
self.sync_clients(); self.sync_clients();
// Sync changed chunks // Sync changed chunks
'chunk: for chunk_key in &self.state.chunk_changes().modified_chunks { 'chunk: for chunk_key in &self.state.terrain_changes().modified_chunks {
let terrain = self.state.terrain(); let terrain = self.state.terrain();
for (entity, player, pos) in ( for (entity, player, pos) in (
@ -328,6 +328,19 @@ impl Server {
} }
} }
} }
// Sync changed blocks
let msg =
ServerMsg::TerrainBlockUpdates(self.state.terrain_changes().modified_blocks.clone());
for (entity, player) in (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Player>(),
)
.join()
{
if player.view_distance.is_some() {
self.clients.notify(entity, msg.clone());
}
}
// 7) Finish the tick, pass control back to the frontend. // 7) Finish the tick, pass control back to the frontend.
@ -387,6 +400,7 @@ impl Server {
let mut new_chat_msgs = Vec::new(); let mut new_chat_msgs = Vec::new();
let mut disconnected_clients = Vec::new(); let mut disconnected_clients = Vec::new();
let mut requested_chunks = Vec::new(); let mut requested_chunks = Vec::new();
let mut modified_blocks = Vec::new();
self.clients.remove_if(|entity, client| { self.clients.remove_if(|entity, client| {
let mut disconnect = false; let mut disconnect = false;
@ -515,9 +529,7 @@ impl Server {
.get(entity) .get(entity)
.is_some() .is_some()
{ {
let mut terrain_change = modified_blocks.push((pos, Block::empty()));
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, Block::empty());
} }
} }
ClientMsg::PlaceBlock(pos, block) => { ClientMsg::PlaceBlock(pos, block) => {
@ -527,9 +539,7 @@ impl Server {
.get(entity) .get(entity)
.is_some() .is_some()
{ {
let mut terrain_change = modified_blocks.push((pos, block));
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, block);
} }
} }
ClientMsg::TerrainChunkRequest { key } => match client.client_state { ClientMsg::TerrainChunkRequest { key } => match client.client_state {
@ -616,6 +626,10 @@ impl Server {
self.generate_chunk(key); self.generate_chunk(key);
} }
for (pos, block) in modified_blocks {
self.state.set_block(pos, block);
}
Ok(frontend_events) Ok(frontend_events)
} }

View File

@ -90,14 +90,14 @@ impl Terrain {
// Add any recently created or changed chunks to the list of chunks to be meshed. // Add any recently created or changed chunks to the list of chunks to be meshed.
for (modified, pos) in client for (modified, pos) in client
.state() .state()
.chunk_changes() .terrain_changes()
.modified_chunks .modified_chunks
.iter() .iter()
.map(|c| (true, c)) .map(|c| (true, c))
.chain( .chain(
client client
.state() .state()
.chunk_changes() .terrain_changes()
.new_chunks .new_chunks
.iter() .iter()
.map(|c| (false, c)), .map(|c| (false, c)),
@ -137,8 +137,48 @@ impl Terrain {
} }
} }
} }
// Add the chunks belonging to recently changed blocks to the list of chunks to be meshed
for pos in client
.state()
.terrain_changes()
.modified_blocks
.iter()
.map(|(p, _)| *p)
{
let chunk_pos = client.state().terrain().pos_key(pos);
self.mesh_todo.insert(
chunk_pos,
ChunkMeshState {
pos: chunk_pos,
started_tick: current_tick,
active_worker: None,
},
);
// Handle chunks 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,
},
);
}
}
}
}
// Remove any models for chunks that have been recently removed. // Remove any models for chunks that have been recently removed.
for pos in &client.state().chunk_changes().removed_chunks { for pos in &client.state().terrain_changes().removed_chunks {
self.chunks.remove(pos); self.chunks.remove(pos);
self.mesh_todo.remove(pos); self.mesh_todo.remove(pos);
} }