Remove extra chunk cloning and parallelize serialization in the server terrain sys

This commit is contained in:
Imbris 2021-03-29 02:45:56 -04:00
parent b8ab6e2ea0
commit 91f092bdaf
6 changed files with 49 additions and 31 deletions

View File

@ -1924,7 +1924,7 @@ impl Client {
match msg { match msg {
ServerGeneral::TerrainChunkUpdate { key, chunk } => { ServerGeneral::TerrainChunkUpdate { key, chunk } => {
if let Ok(chunk) = chunk { if let Ok(chunk) = chunk {
self.state.insert_chunk(key, *chunk); self.state.insert_chunk(key, chunk);
} }
self.pending_chunks.remove(&key); self.pending_chunks.remove(&key);
}, },

View File

@ -12,7 +12,7 @@ use common::{
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::{sync::Arc, time::Duration};
use vek::*; use vek::*;
///This struct contains all messages the server might send (on different ///This struct contains all messages the server might send (on different
@ -106,7 +106,7 @@ pub enum ServerGeneral {
// Ingame related AND terrain stream // Ingame related AND terrain stream
TerrainChunkUpdate { TerrainChunkUpdate {
key: Vec2<i32>, key: Vec2<i32>,
chunk: Result<Box<TerrainChunk>, ()>, chunk: Result<Arc<TerrainChunk>, ()>,
}, },
TerrainBlockUpdates(HashMap<Vec3<i32>, Block>), TerrainBlockUpdates(HashMap<Vec3<i32>, Block>),
// Always possible // Always possible

View File

@ -383,11 +383,11 @@ impl State {
} }
/// Insert the provided chunk into this state's terrain. /// Insert the provided chunk into this state's terrain.
pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: TerrainChunk) { pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: Arc<TerrainChunk>) {
if self if self
.ecs .ecs
.write_resource::<TerrainGrid>() .write_resource::<TerrainGrid>()
.insert(key, Arc::new(chunk)) .insert(key, chunk)
.is_some() .is_some()
{ {
self.ecs self.ecs

View File

@ -9,6 +9,7 @@ use common_ecs::{Job, Origin, ParMode, Phase, System};
use common_net::msg::{ClientGeneral, ServerGeneral}; use common_net::msg::{ClientGeneral, ServerGeneral};
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage}; use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage};
use std::sync::Arc;
use tracing::{debug, trace}; use tracing::{debug, trace};
/// This system will handle new messages from clients /// This system will handle new messages from clients
@ -74,12 +75,12 @@ impl<'a> System<'a> for Sys {
true true
}; };
if in_vd { if in_vd {
match terrain.get_key(key) { match terrain.get_key_arc(key) {
Some(chunk) => { Some(chunk) => {
network_metrics.chunks_served_from_memory.inc(); network_metrics.chunks_served_from_memory.inc();
client.send(ServerGeneral::TerrainChunkUpdate { client.send(ServerGeneral::TerrainChunkUpdate {
key, key,
chunk: Ok(Box::new(chunk.clone())), chunk: Ok(Arc::clone(chunk)),
})? })?
}, },
None => { None => {

View File

@ -69,6 +69,7 @@ impl<'a> System<'a> for Sys {
// Fetch any generated `TerrainChunk`s and insert them into the terrain. // Fetch any generated `TerrainChunk`s and insert them into the terrain.
// Also, send the chunk data to anybody that is close by. // Also, send the chunk data to anybody that is close by.
let mut new_chunks = Vec::new();
'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() { 'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() {
let (chunk, supplement) = match res { let (chunk, supplement) = match res {
Ok((chunk, supplement)) => (chunk, supplement), Ok((chunk, supplement)) => (chunk, supplement),
@ -85,31 +86,16 @@ impl<'a> System<'a> for Sys {
continue 'insert_terrain_chunks; continue 'insert_terrain_chunks;
}, },
}; };
// Send the chunk to all nearby players.
let mut lazy_msg = None;
for (presence, pos, client) in (&presences, &positions, &clients).join() {
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 = (chunk_pos - key)
.map(|e: i32| (e.abs() as u32).saturating_sub(2))
.magnitude_squared();
if adjusted_dist_sqr <= presence.view_distance.pow(2) { // Arcify the chunk
if lazy_msg.is_none() { let chunk = Arc::new(chunk);
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
key, // Add to list of chunks to send to nearby players.
chunk: Ok(Box::new(chunk.clone())), new_chunks.push((key, Arc::clone(&chunk)));
}));
}
lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg));
}
}
// TODO: code duplication for chunk insertion between here and state.rs // TODO: code duplication for chunk insertion between here and state.rs
// Insert the chunk into terrain changes // Insert the chunk into terrain changes
if terrain.insert(key, Arc::new(chunk)).is_some() { if terrain.insert(key, chunk).is_some() {
terrain_changes.modified_chunks.insert(key); terrain_changes.modified_chunks.insert(key);
} else { } else {
terrain_changes.new_chunks.insert(key); terrain_changes.new_chunks.insert(key);
@ -233,6 +219,36 @@ impl<'a> System<'a> for Sys {
} }
} }
// Send the chunk to all nearby players.
use rayon::iter::{IntoParallelIterator, ParallelIterator};
new_chunks.into_par_iter().for_each(|(key, chunk)| {
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
key,
chunk: Ok(chunk),
});
let mut lazy_msg = None;
(&presences, &positions, &clients)
.join()
.for_each(|(presence, pos, client)| {
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 = (chunk_pos - key)
.map(|e: i32| (e.abs() as u32).saturating_sub(2))
.magnitude_squared();
if adjusted_dist_sqr <= presence.view_distance.pow(2) {
if let Some(msg) = msg.take() {
lazy_msg = Some(client.prepare(msg));
};
lazy_msg.as_ref().map(|msg| client.send_prepared(msg));
}
});
});
// Remove chunks that are too far from players. // Remove chunks that are too far from players.
let mut chunks_to_remove = Vec::new(); let mut chunks_to_remove = Vec::new();
terrain terrain

View File

@ -4,6 +4,7 @@ use common_ecs::{Job, Origin, Phase, System};
use common_net::msg::ServerGeneral; use common_net::msg::ServerGeneral;
use common_state::TerrainChanges; use common_state::TerrainChanges;
use specs::{Join, Read, ReadExpect, ReadStorage}; use specs::{Join, Read, ReadExpect, ReadStorage};
use std::sync::Arc;
/// This systems sends new chunks to clients as well as changes to existing /// This systems sends new chunks to clients as well as changes to existing
/// chunks /// chunks
@ -37,10 +38,10 @@ impl<'a> System<'a> for Sys {
if lazy_msg.is_none() { if lazy_msg.is_none() {
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate { lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
key: *chunk_key, key: *chunk_key,
chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { chunk: Ok(match terrain.get_key_arc(*chunk_key) {
Some(chunk) => chunk.clone(), Some(chunk) => Arc::clone(chunk),
None => break 'chunk, None => break 'chunk,
})), }),
})); }));
} }
lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg));