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.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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user