Merge branch 'imbris/dedup-chunk-gen-attempt' into 'master'

More robustly avoid duplicate chunk generation

See merge request veloren/veloren!2912
This commit is contained in:
Joshua Barretto 2021-10-08 22:00:50 +00:00
commit a7a9e00a3a
7 changed files with 66 additions and 37 deletions

View File

@ -148,7 +148,6 @@ pub enum ServerEvent {
CreateWaypoint(Vec3<f32>),
ClientDisconnect(EcsEntity, DisconnectReason),
ClientDisconnectWithoutPersistence(EcsEntity),
ChunkRequest(EcsEntity, Vec2<i32>),
Command(EcsEntity, String, Vec<String>),
/// Send a chat message to the player from an npc or other player
Chat(comp::UnresolvedChatMsg),

View File

@ -65,14 +65,17 @@ impl ChunkGenerator {
}
pub fn recv_new_chunk(&mut self) -> Option<ChunkGenResult> {
if let Ok((key, res)) = self.chunk_rx.try_recv() {
self.pending_chunks.remove(&key);
self.metrics.chunks_served.inc();
// TODO: do anything else if res is an Err?
Some((key, res))
} else {
None
// Make sure chunk wasn't cancelled and if it was check to see if there are more
// chunks to receive
while let Ok((key, res)) = self.chunk_rx.try_recv() {
if self.pending_chunks.remove(&key).is_some() {
self.metrics.chunks_served.inc();
// TODO: do anything else if res is an Err?
return Some((key, res));
}
}
None
}
pub fn pending_chunks(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {

View File

@ -51,7 +51,6 @@ impl Server {
span!(_guard, "handle_events", "Server::handle_events");
let mut frontend_events = Vec::new();
let mut requested_chunks = Vec::new();
let mut commands = Vec::new();
let mut chat_messages = Vec::new();
@ -191,10 +190,6 @@ impl Server {
true,
))
},
ServerEvent::ChunkRequest(entity, key) => {
requested_chunks.push((entity, key));
},
ServerEvent::Command(entity, name, args) => {
commands.push((entity, name, args));
},
@ -245,11 +240,6 @@ impl Server {
}
}
// Generate requested chunks
for (entity, key) in requested_chunks {
self.generate_chunk(entity, key);
}
for (entity, name, args) in commands {
self.process_command(entity, name, args);
}

View File

@ -180,6 +180,11 @@ impl BattleModeBuffer {
}
}
pub struct ChunkRequest {
entity: EcsEntity,
key: Vec2<i32>,
}
pub struct Server {
state: State,
world: Arc<World>,
@ -234,6 +239,7 @@ impl Server {
path: data_dir.to_owned(),
});
state.ecs_mut().insert(EventBus::<ServerEvent>::default());
state.ecs_mut().insert(Vec::<ChunkRequest>::new());
state.ecs_mut().insert(LoginProvider::new(
settings.auth_server_address.clone(),
Arc::clone(&runtime),
@ -1071,7 +1077,7 @@ impl Server {
pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
let ecs = self.state.ecs();
let slow_jobs = ecs.write_resource::<SlowJobPool>();
let slow_jobs = ecs.read_resource::<SlowJobPool>();
ecs.write_resource::<ChunkGenerator>().generate_chunk(
Some(entity),
key,

View File

@ -27,7 +27,7 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch::<melee::Sys>(dispatch_builder, &[&projectile::Sys::sys_name()]);
//Note: server should not depend on interpolation system
dispatch::<agent::Sys>(dispatch_builder, &[]);
dispatch::<terrain::Sys>(dispatch_builder, &[]);
dispatch::<terrain::Sys>(dispatch_builder, &[&msg::terrain::Sys::sys_name()]);
dispatch::<waypoint::Sys>(dispatch_builder, &[]);
dispatch::<invite_timeout::Sys>(dispatch_builder, &[]);
dispatch::<persistence::Sys>(dispatch_builder, &[]);

View File

@ -1,7 +1,6 @@
use crate::{client::Client, metrics::NetworkRequestMetrics, presence::Presence};
use crate::{client::Client, metrics::NetworkRequestMetrics, presence::Presence, ChunkRequest};
use common::{
comp::Pos,
event::{EventBus, ServerEvent},
spiral::Spiral2d,
terrain::{TerrainChunkSize, TerrainGrid},
vol::RectVolSize,
@ -9,7 +8,7 @@ use common::{
use common_ecs::{Job, Origin, ParMode, Phase, System};
use common_net::msg::{ClientGeneral, SerializedTerrainChunk, ServerGeneral};
use rayon::iter::ParallelIterator;
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage};
use specs::{Entities, Join, ParJoin, ReadExpect, ReadStorage, Write};
use tracing::{debug, trace};
/// This system will handle new messages from clients
@ -19,9 +18,9 @@ impl<'a> System<'a> for Sys {
#[allow(clippy::type_complexity)]
type SystemData = (
Entities<'a>,
Read<'a, EventBus<ServerEvent>>,
ReadExpect<'a, TerrainGrid>,
ReadExpect<'a, NetworkRequestMetrics>,
Write<'a, Vec<ChunkRequest>>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Presence>,
ReadStorage<'a, Client>,
@ -35,21 +34,19 @@ impl<'a> System<'a> for Sys {
job: &mut Job<Self>,
(
entities,
server_event_bus,
terrain,
network_metrics,
mut chunk_requests,
positions,
presences,
clients,
): Self::SystemData,
) {
let mut server_emitter = server_event_bus.emitter();
job.cpu_stats.measure(ParMode::Rayon);
let mut events = (&entities, &clients, (&presences).maybe())
let mut new_chunk_requests = (&entities, &clients, (&presences).maybe())
.par_join()
.map(|(entity, client, maybe_presence)| {
let mut events = Vec::new();
let mut chunk_requests = Vec::new();
let _ = super::try_recv_all(client, 5, |client, msg| {
let presence = match maybe_presence {
Some(g) => g,
@ -93,7 +90,7 @@ impl<'a> System<'a> for Sys {
},
None => {
network_metrics.chunks_generation_triggered.inc();
events.push(ServerEvent::ChunkRequest(entity, key));
chunk_requests.push(ChunkRequest { entity, key });
},
}
} else {
@ -117,19 +114,21 @@ impl<'a> System<'a> for Sys {
for rpos in Spiral2d::new().take((crate::MIN_VD as usize + 1).pow(2)) {
let key = player_chunk + rpos;
if terrain.get_key(key).is_none() {
events.push(ServerEvent::ChunkRequest(entity, key));
// TODO: @zesterer do we want to be sending these chunk to the client
// even if they aren't requested? If we don't we could replace the
// entity here with Option<Entity> and pass in None.
chunk_requests.push(ChunkRequest { entity, key });
}
}
}
events
chunk_requests
})
.flatten()
.collect::<Vec<_>>();
job.cpu_stats.measure(ParMode::Single);
for event in events.drain(..) {
server_emitter.emit(event);
}
chunk_requests.append(&mut new_chunk_requests);
}
}

View File

@ -1,5 +1,10 @@
#[cfg(feature = "persistent_world")]
use crate::TerrainPersistence;
#[cfg(not(feature = "worldgen"))]
use test_world::{IndexOwned, World};
#[cfg(feature = "worldgen")]
use world::{IndexOwned, World};
use crate::{
chunk_generator::ChunkGenerator,
client::Client,
@ -7,14 +12,15 @@ use crate::{
presence::{Presence, RepositionOnChunkLoad},
rtsim::RtSim,
settings::Settings,
SpawnPoint, Tick,
ChunkRequest, SpawnPoint, Tick,
};
use common::{
comp::{self, agent, bird_medium, BehaviorCapability, ForceUpdate, Pos, Waypoint},
event::{EventBus, ServerEvent},
generation::EntityInfo,
lottery::LootSpec,
resources::Time,
resources::{Time, TimeOfDay},
slowjob::SlowJobPool,
terrain::TerrainGrid,
LoadoutBuilder, SkillSetBuilder,
};
@ -104,10 +110,15 @@ impl<'a> System<'a> for Sys {
Read<'a, Tick>,
Read<'a, SpawnPoint>,
Read<'a, Settings>,
Read<'a, TimeOfDay>,
ReadExpect<'a, SlowJobPool>,
ReadExpect<'a, IndexOwned>,
ReadExpect<'a, Arc<World>>,
ReadExpect<'a, NetworkRequestMetrics>,
WriteExpect<'a, ChunkGenerator>,
WriteExpect<'a, TerrainGrid>,
Write<'a, TerrainChanges>,
Write<'a, Vec<ChunkRequest>>,
WriteExpect<'a, RtSim>,
TerrainPersistenceData<'a>,
WriteStorage<'a, Pos>,
@ -131,10 +142,15 @@ impl<'a> System<'a> for Sys {
tick,
spawn_point,
server_settings,
time_of_day,
slow_jobs,
index,
world,
network_metrics,
mut chunk_generator,
mut terrain,
mut terrain_changes,
mut chunk_requests,
mut rtsim,
mut _terrain_persistence,
mut positions,
@ -149,6 +165,22 @@ impl<'a> System<'a> for Sys {
) {
let mut server_emitter = server_event_bus.emitter();
// Generate requested chunks
//
// Submit requests for chunks right before receiving finished chunks so that we
// don't create duplicate work for chunks that just finished but are not
// yet added to the terrain.
chunk_requests.drain(..).for_each(|request| {
chunk_generator.generate_chunk(
Some(request.entity),
request.key,
&slow_jobs,
Arc::clone(&world),
index.clone(),
*time_of_day,
)
});
// 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();