Submit chunk requests to the generator within the terrain system right before receiving new chunks, so that duplicate work is not done for chunks that just finished but were not yet added to the terrain.

This commit is contained in:
Imbris 2021-10-08 14:31:04 -04:00
parent 8575661d28
commit 5394ab16bb
6 changed files with 56 additions and 30 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

@ -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();