Synvael/crates/server/src/world_server.rs

74 lines
2.5 KiB
Rust

// SPDX-License-Identifier: AGPL-3.0-only
//! Authoritative chunk storage and generation logic for the server.
use rayon::prelude::*;
use shared::{
generator::VoxelGenerator,
world::{Chunk, ChunkPos, EntityPos},
};
use std::collections::HashMap;
/// The server's authoritative representation of the world.
pub struct ServerWorld {
generator: VoxelGenerator,
chunks: HashMap<ChunkPos, Chunk>,
}
impl ServerWorld {
/// Initializes a new authoritative server world with the provided generator.
#[must_use]
pub fn new(generator: VoxelGenerator) -> Self {
Self {
generator,
chunks: HashMap::new(),
}
}
/// Retrieves a reference to the chunk at the given position, generating it if necessary.
pub fn get_chunk(&mut self, pos: ChunkPos) -> &Chunk {
self.chunks
.entry(pos)
.or_insert_with(|| self.generator.generate_chunk(pos))
}
/// Generates and caches all chunks in a cylindrical region around the given position.
pub fn load_around(&mut self, center: EntityPos, radius: i32) {
// The chunk anchor is the exact streaming key; no lossy world-to-chunk conversion is needed.
let center = center.chunk;
// Phase 1: collect the positions inside the cylinder that are not yet resident
let mut pending = Vec::new();
for x in center.x - radius..=center.x + radius {
for z in center.z - radius..=center.z + radius {
for y in center.y - radius / 2..=center.y + radius / 2 {
let dx = x - center.x;
let dz = z - center.z;
if (dx * dx + dz * dz) <= radius * radius {
let pos = ChunkPos::new(x, y, z);
if !self.chunks.contains_key(&pos) {
pending.push(pos);
}
}
}
}
}
// Phase 2: generate the missing chunks in parallel
let generator = &self.generator;
let generated: Vec<(ChunkPos, Chunk)> = pending
.into_par_iter()
.map(|pos| (pos, generator.generate_chunk(pos)))
.collect();
// Phase 3: move the finished chunks into the map
let count = generated.len();
for (pos, chunk) in generated {
self.chunks.insert(pos, chunk);
}
tracing::info!(chunks = count, center = ?center, radius, "generated chunk cylinder");
}
}