From e17477979fb043b7cdb972972f551cc2be404c4e Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 29 Mar 2021 02:45:56 -0400 Subject: [PATCH] Remove extra chunk cloning and parallelize serialization in the server terrain sys --- client/src/lib.rs | 2 +- common/net/src/msg/server.rs | 4 +-- common/state/src/state.rs | 4 +-- server/src/sys/msg/terrain.rs | 5 +-- server/src/sys/terrain.rs | 58 ++++++++++++++++++++++------------ server/src/sys/terrain_sync.rs | 7 ++-- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 347326ad81..39ce8d113e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -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); }, diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 9eabad35ca..239d3e10d0 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -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, - chunk: Result, ()>, + chunk: Result, ()>, }, TerrainBlockUpdates(HashMap, Block>), // Always possible diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 2261cb2a4c..9031c57b55 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -383,11 +383,11 @@ impl State { } /// Insert the provided chunk into this state's terrain. - pub fn insert_chunk(&mut self, key: Vec2, chunk: TerrainChunk) { + pub fn insert_chunk(&mut self, key: Vec2, chunk: Arc) { if self .ecs .write_resource::() - .insert(key, Arc::new(chunk)) + .insert(key, chunk) .is_some() { self.ecs diff --git a/server/src/sys/msg/terrain.rs b/server/src/sys/msg/terrain.rs index cf00b83229..1dd5f82ecb 100644 --- a/server/src/sys/msg/terrain.rs +++ b/server/src/sys/msg/terrain.rs @@ -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 => { diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 8c4b9d5aa3..1d3f224828 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -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 diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index 5bd5925277..77f7cd1a86 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -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));