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 9e0fbd392d
commit e17477979f
6 changed files with 49 additions and 31 deletions

View File

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

View File

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

View File

@ -383,11 +383,11 @@ impl State {
}
/// 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
.ecs
.write_resource::<TerrainGrid>()
.insert(key, Arc::new(chunk))
.insert(key, chunk)
.is_some()
{
self.ecs

View File

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

View File

@ -69,6 +69,7 @@ impl<'a> System<'a> for Sys {
// Fetch any generated `TerrainChunk`s and insert them into the terrain.
// 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() {
let (chunk, supplement) = match res {
Ok((chunk, supplement)) => (chunk, supplement),
@ -85,31 +86,16 @@ impl<'a> System<'a> for Sys {
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) {
if lazy_msg.is_none() {
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
key,
chunk: Ok(Box::new(chunk.clone())),
}));
}
lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg));
}
}
// Arcify the chunk
let chunk = Arc::new(chunk);
// Add to list of chunks to send to nearby players.
new_chunks.push((key, Arc::clone(&chunk)));
// TODO: code duplication for chunk insertion between here and state.rs
// 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);
} else {
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.
let mut chunks_to_remove = Vec::new();
terrain

View File

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