diff --git a/common/src/event.rs b/common/src/event.rs index c9eded34a1..4c7d1bc896 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -148,7 +148,6 @@ pub enum ServerEvent { CreateWaypoint(Vec3), ClientDisconnect(EcsEntity, DisconnectReason), ClientDisconnectWithoutPersistence(EcsEntity), - ChunkRequest(EcsEntity, Vec2), Command(EcsEntity, String, Vec), /// Send a chat message to the player from an npc or other player Chat(comp::UnresolvedChatMsg), diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 201d9a1258..35911547e4 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -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); } diff --git a/server/src/lib.rs b/server/src/lib.rs index f73147b480..be13805968 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -180,6 +180,11 @@ impl BattleModeBuffer { } } +pub struct ChunkRequest { + entity: EcsEntity, + key: Vec2, +} + pub struct Server { state: State, world: Arc, @@ -234,6 +239,7 @@ impl Server { path: data_dir.to_owned(), }); state.ecs_mut().insert(EventBus::::default()); + state.ecs_mut().insert(Vec::::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) { let ecs = self.state.ecs(); - let slow_jobs = ecs.write_resource::(); + let slow_jobs = ecs.read_resource::(); ecs.write_resource::().generate_chunk( Some(entity), key, diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 64ea2d60b8..d0f76023c6 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -27,7 +27,7 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch::(dispatch_builder, &[&projectile::Sys::sys_name()]); //Note: server should not depend on interpolation system dispatch::(dispatch_builder, &[]); - dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[&msg::terrain::Sys::sys_name()]); dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[]); diff --git a/server/src/sys/msg/terrain.rs b/server/src/sys/msg/terrain.rs index a68cd5f85b..bf6c81b715 100644 --- a/server/src/sys/msg/terrain.rs +++ b/server/src/sys/msg/terrain.rs @@ -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>, ReadExpect<'a, TerrainGrid>, ReadExpect<'a, NetworkRequestMetrics>, + Write<'a, Vec>, ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, @@ -35,21 +34,19 @@ impl<'a> System<'a> for Sys { job: &mut Job, ( 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 and pass in None. + chunk_requests.push(ChunkRequest { entity, key }); } } } - events + chunk_requests }) .flatten() .collect::>(); job.cpu_stats.measure(ParMode::Single); - for event in events.drain(..) { - server_emitter.emit(event); - } + + chunk_requests.append(&mut new_chunk_requests); } } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 932f98a27d..91a7741d42 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -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>, ReadExpect<'a, NetworkRequestMetrics>, WriteExpect<'a, ChunkGenerator>, WriteExpect<'a, TerrainGrid>, Write<'a, TerrainChanges>, + Write<'a, Vec>, 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();