mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
commit
e5eafdfdc0
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user