diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 6e8d1e4..710db45 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -86,6 +86,25 @@ fn main() { let mut schedule = Schedule::default(); schedule.add_systems(stream_chunks); + // Loading phase: dispatch the initial region and wait for the worker pool to finish before granting control. + info!("Streaming initial region"); + loop { + schedule.run(&mut world); + + let server_world = world.resource::(); + let resident = server_world.loaded_count(); + let in_flight = server_world.in_flight_count(); + // Loading progress is simply the resident fraction of all known chunks. + let total = resident + in_flight; + debug!(resident, in_flight, total, "loading progress"); + + // The region is ready once at least one chunk has been generated and none remain in flight. + if server_world.streaming_idle() && resident > 0 { + break; + } + } + info!("Initial region ready; granting player control"); + // Drive several ticks, marching the dummy one chunk along +X between each. The manual movement stands in for network-driven player input and exists only to exercise load/unload as the anchor moves. for step in 0..5 { info!(step, "tick"); diff --git a/crates/server/src/world_server.rs b/crates/server/src/world_server.rs index bd278b7..b6eac45 100644 --- a/crates/server/src/world_server.rs +++ b/crates/server/src/world_server.rs @@ -104,6 +104,18 @@ impl ServerWorld { self.chunks.len() } + /// Number of chunks dispatched to the worker pool but not yet returned. + #[must_use] + pub fn in_flight_count(&self) -> usize { + self.in_flight.len() + } + + /// Returns `true` when the worker pool has no outstanding generation work, i.e. every dispatched chunk has been returned. A loading gate can poll this to decide when an initial region has finished streaming. + #[must_use] + pub fn streaming_idle(&self) -> bool { + self.in_flight.is_empty() + } + /// Reconciles resident chunks against a desired set without blocking the caller: finished chunks are drained from the worker pool, resident chunks absent from `desired` are evicted, and still-missing chunks are dispatched to the pool. pub fn reconcile(&mut self, desired: &HashSet) -> StreamStats { // Drain: absorb every chunk the workers have finished since the last pass.