diff --git a/server/src/lib.rs b/server/src/lib.rs index 96d9fa3224..3ce446fed5 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -41,8 +41,7 @@ use std::{ }; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; -use world::World; - +use world::{sim::WORLD_SIZE, World}; const CLIENT_TIMEOUT: f64 = 20.0; // Seconds pub enum Event { @@ -84,9 +83,6 @@ impl Server { /// Create a new `Server` pub fn new(settings: ServerSettings) -> Result { let mut state = State::default(); - state - .ecs_mut() - .add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 600.0))); state .ecs_mut() .add_resource(EventBus::::default()); @@ -110,12 +106,61 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); + let world = World::generate(settings.world_seed); + + let spawn_point = { + // NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops, + // but are needed to be explicit about casting (and to make the compiler stop complaining) + + // spawn in the chunk, that is in the middle of the world + let spawn_chunk: Vec2 = WORLD_SIZE.map(|e| e as i32) / 2; + // calculate the absolute position of the chunk in the world + // (we could add TerrainChunkSize::RECT_SIZE / 2 here, to spawn in the midde of the chunk) + let spawn_location = spawn_chunk * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + + // get a z cache for the collumn in which we want to spawn + let mut block_sampler = world.sample_blocks(); + let z_cache = block_sampler + .get_z_cache(spawn_location) + .expect(&format!("no z_cache found for chunk: {}", spawn_chunk)); + + // get the minimum and maximum z values at which there could be soild blocks + let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler); + // round range outwards, so no potential air block is missed + let min_z = min_z.floor() as i32; + let max_z = max_z.ceil() as i32; + + // loop over all blocks from min_z to max_z + 1 + // until the first air block is found + // (up to max_z + 1, because max_z could still be a soild block) + // if no air block is found default to max_z + 1 + let z = (min_z..=max_z + 1) + .find(|z| { + block_sampler + .get_with_z_cache( + Vec3::new(spawn_location.x, spawn_location.y, *z), + Some(&z_cache), + false, + ) + .map(|b| b.is_air()) + .unwrap_or(false) + }) + .unwrap_or(max_z + 1); + + // build the actual spawn point and + // add 0.5, so that the player spawns in the middle of the block + Vec3::new(spawn_location.x, spawn_location.y, z).map(|e| (e as f32)) + 0.5 + }; + + // set the spawn point we calculated above + state.ecs_mut().add_resource(SpawnPoint(spawn_point)); + // Set starting time for the server. state.ecs_mut().write_resource::().0 = settings.start_time; let this = Self { state, - world: Arc::new(World::generate(settings.world_seed)), + world: Arc::new(world), postoffice: PostOffice::bind(settings.gameserver_address)?,