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.pending_chunks.remove(&key);
}
ServerMsg::TerrainBlockUpdates(mut blocks) => blocks
.drain()
.for_each(|(pos, block)| self.state.set_block(pos, block)),
ServerMsg::StateAnswer(Ok(state)) => {
self.client_state = state;
}

View File

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

View File

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

View File

@ -17,7 +17,7 @@ use common::{
comp,
msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg},
net::PostOffice,
state::{State, TerrainChange, TimeOfDay, Uid},
state::{State, TimeOfDay, Uid},
terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap},
vol::VolSize,
vol::Vox,
@ -300,7 +300,7 @@ impl Server {
self.sync_clients();
// 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();
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.
@ -387,6 +400,7 @@ impl Server {
let mut new_chat_msgs = Vec::new();
let mut disconnected_clients = Vec::new();
let mut requested_chunks = Vec::new();
let mut modified_blocks = Vec::new();
self.clients.remove_if(|entity, client| {
let mut disconnect = false;
@ -515,9 +529,7 @@ impl Server {
.get(entity)
.is_some()
{
let mut terrain_change =
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, Block::empty());
modified_blocks.push((pos, Block::empty()));
}
}
ClientMsg::PlaceBlock(pos, block) => {
@ -527,9 +539,7 @@ impl Server {
.get(entity)
.is_some()
{
let mut terrain_change =
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, block);
modified_blocks.push((pos, block));
}
}
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
@ -616,6 +626,10 @@ impl Server {
self.generate_chunk(key);
}
for (pos, block) in modified_blocks {
self.state.set_block(pos, block);
}
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.
for (modified, pos) in client
.state()
.chunk_changes()
.terrain_changes()
.modified_chunks
.iter()
.map(|c| (true, c))
.chain(
client
.state()
.chunk_changes()
.terrain_changes()
.new_chunks
.iter()
.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.
for pos in &client.state().chunk_changes().removed_chunks {
for pos in &client.state().terrain_changes().removed_chunks {
self.chunks.remove(pos);
self.mesh_todo.remove(pos);
}