diff --git a/Cargo.lock b/Cargo.lock index 968f43a472..415d7012ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2329,6 +2329,7 @@ dependencies = [ "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2435,6 +2436,14 @@ dependencies = [ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_xorshift" version = "0.1.1" @@ -3858,6 +3867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" "checksum raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9db80d08d3ed847ce4fb3def46de0af4bfb6155bd09bd6eaf28b5ac72541c1f1" diff --git a/common/src/astar.rs b/common/src/astar.rs index 22718cc857..f399712919 100644 --- a/common/src/astar.rs +++ b/common/src/astar.rs @@ -80,6 +80,7 @@ where let mut visited = HashSet::new(); visited.insert(initial.clone()); + let mut iters = 0; while let Some(PathEntry { node: current, .. }) = potential_nodes.pop() { if current == target { return Some(reconstruct_path(&came_from, ¤t)); @@ -105,6 +106,12 @@ where } } } + + iters += 1; + if iters >= 10000 { + println!("Ran out of turns!"); + break; + } } None diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index f63abb5e8a..ff6d05485e 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,4 +1,4 @@ -use crate::pathfinding::WorldPath; +use crate::{path::Chaser, pathfinding::WorldPath}; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IDVStorage; use vek::*; @@ -8,7 +8,7 @@ pub enum Agent { Wanderer(Vec2), Pet { target: EcsEntity, - offset: Vec2, + chaser: Chaser, }, Enemy { bearing: Vec2, @@ -26,6 +26,13 @@ impl Agent { target: None, } } + + pub fn pet(target: EcsEntity) -> Self { + Agent::Pet { + target, + chaser: Chaser::default(), + } + } } impl Component for Agent { diff --git a/common/src/generation.rs b/common/src/generation.rs new file mode 100644 index 0000000000..d411f9c76c --- /dev/null +++ b/common/src/generation.rs @@ -0,0 +1,11 @@ +use vek::*; + +pub struct NpcInfo { + pub pos: Vec3, + pub boss: bool, +} + +#[derive(Default)] +pub struct ChunkSupplement { + pub npcs: Vec, +} diff --git a/common/src/hierarchical.rs b/common/src/hierarchical.rs index 0bfe94aec7..32dcb3e747 100644 --- a/common/src/hierarchical.rs +++ b/common/src/hierarchical.rs @@ -45,7 +45,7 @@ impl ChunkPath { pub fn chunk_get_neighbors( _vol: &VolGrid2d, pos: &Vec2, - ) -> impl IntoIterator> { + ) -> impl Iterator> { let directions = vec![ Vec2::new(1, 0), // Right chunk Vec2::new(-1, 0), // Left chunk @@ -53,7 +53,14 @@ impl ChunkPath { Vec2::new(0, -1), // Bottom chunk ]; - let neighbors: Vec> = directions.into_iter().map(|dir| dir + pos).collect(); + let mut neighbors = Vec::new(); + for x in -2..3 { + for y in -2..3 { + neighbors.push(pos + Vec2::new(x, y)); + } + } + + //let neighbors: Vec> = directions.into_iter().map(|dir| dir + pos).collect(); neighbors.into_iter() } @@ -61,7 +68,7 @@ impl ChunkPath { &mut self, vol: &VolGrid2d, pos: Vec3, - ) -> impl IntoIterator> { + ) -> impl Iterator> { let directions = vec![ Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 1), // Forward upward @@ -102,7 +109,7 @@ impl ChunkPath { .any(|new_pos| new_pos.cmpeq(&vol.pos_key(pos)).iter().all(|e| *e)); } _ => { - println!("No chunk path"); + //println!("No chunk path"); } } return is_walkable_position && is_within_chunk; @@ -110,11 +117,11 @@ impl ChunkPath { pub fn get_worldpath( &mut self, vol: &VolGrid2d, - ) -> WorldPath { + ) -> Result { let wp = WorldPath::new(vol, self.from, self.dest, |vol, pos| { - self.worldpath_get_neighbors(vol, *pos) + self.worldpath_get_neighbors(vol, pos) }); - println!("Fetching world path from hierarchical path: {:?}", wp); + //println!("Fetching world path from hierarchical path: {:?}", wp); wp } } diff --git a/common/src/lib.rs b/common/src/lib.rs index 9826d8b5a7..b6cfb55ea3 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -14,9 +14,11 @@ pub mod comp; pub mod effect; pub mod event; pub mod figure; +pub mod generation; pub mod hierarchical; pub mod msg; pub mod npc; +pub mod path; pub mod pathfinding; pub mod ray; pub mod region; diff --git a/common/src/path.rs b/common/src/path.rs new file mode 100644 index 0000000000..c7241f0376 --- /dev/null +++ b/common/src/path.rs @@ -0,0 +1,127 @@ +use crate::{ + pathfinding::WorldPath, + terrain::Block, + vol::{BaseVol, ReadVol}, +}; +use std::iter::FromIterator; +use vek::*; + +// Path + +#[derive(Default, Clone, Debug)] +pub struct Path { + nodes: Vec>, +} + +impl FromIterator> for Path { + fn from_iter>>(iter: I) -> Self { + Self { + nodes: iter.into_iter().collect(), + } + } +} + +impl Path { + pub fn len(&self) -> usize { + self.nodes.len() + } + + pub fn start(&self) -> Option> { + self.nodes.first().copied() + } + + pub fn end(&self) -> Option> { + self.nodes.last().copied() + } +} + +// Route: A path that can be progressed along + +#[derive(Default, Clone, Debug)] +pub struct Route { + path: Path, + next_idx: usize, +} + +impl From for Route { + fn from(path: Path) -> Self { + Self { path, next_idx: 0 } + } +} + +impl Route { + pub fn path(&self) -> &Path { + &self.path + } + + pub fn next(&self) -> Option> { + self.path.nodes.get(self.next_idx).copied() + } + + pub fn is_finished(&self) -> bool { + self.next().is_none() + } + + pub fn traverse(&mut self, vol: &V, pos: Vec3) -> Option> + where + V: BaseVol + ReadVol, + { + let next = self.next()?; + if vol.get(next).map(|b| b.is_solid()).unwrap_or(false) { + None + } else { + let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + if next_tgt.distance_squared(pos) < 1.0f32.powf(2.0) { + self.next_idx += 1; + } + Some(next_tgt - pos) + } + } +} + +// Chaser: A self-contained system that attempts to chase a moving target + +#[derive(Default, Clone, Debug)] +pub struct Chaser { + route: Route, +} + +impl Chaser { + pub fn chase(&mut self, vol: &V, tgt: Vec3, pos: Vec3) -> Option> + where + V: BaseVol + ReadVol, + { + let pos_to_tgt = pos.distance(tgt); + + if pos_to_tgt < 4.0 { + return None; + } + + let bearing = if let Some(end) = self.route.path().end() { + let end_to_tgt = end.map(|e| e as f32).distance(tgt); + if end_to_tgt > pos_to_tgt * 0.3 + 5.0 { + None + } else { + self.route.traverse(vol, pos) + } + } else { + None + }; + + // TODO: What happens when we get stuck? + if let Some(bearing) = bearing { + Some(bearing) + } else { + let path: Path = WorldPath::find(vol, pos, tgt) + .ok() + .and_then(|wp| wp.path.map(|nodes| nodes.into_iter().rev())) + .into_iter() + .flatten() + .collect(); + + self.route = path.into(); + + Some(tgt - pos) + } + } +} diff --git a/common/src/pathfinding.rs b/common/src/pathfinding.rs index f970498141..ca8e8f1d45 100644 --- a/common/src/pathfinding.rs +++ b/common/src/pathfinding.rs @@ -13,30 +13,38 @@ pub struct WorldPath { } impl WorldPath { - pub fn new( - vol: &V, + pub fn find<'a, V: ReadVol>(vol: &'a V, from: Vec3, dest: Vec3) -> Result { + Self::new(vol, from, dest, Self::get_neighbors) + } + + pub fn new<'a, V: ReadVol, I>( + vol: &'a V, from: Vec3, dest: Vec3, - get_neighbors: impl FnMut(&V, &Vec3) -> I, - ) -> Self + get_neighbors: impl FnMut(&'a V, Vec3) -> I, + ) -> Result where - I: IntoIterator>, + I: Iterator> + 'a, { let ifrom: Vec3 = Vec3::from(from.map(|e| e.floor() as i32)); let idest: Vec3 = Vec3::from(dest.map(|e| e.floor() as i32)); - let path = WorldPath::get_path(vol, ifrom, idest, get_neighbors); + let path = WorldPath::get_path(vol, ifrom, idest, get_neighbors).ok_or(())?; - Self { from, dest, path } + Ok(Self { + from, + dest, + path: Some(path), + }) } - pub fn get_path( - vol: &V, + pub fn get_path<'a, V: ReadVol, I>( + vol: &'a V, from: Vec3, dest: Vec3, - mut get_neighbors: impl FnMut(&V, &Vec3) -> I, + mut get_neighbors: impl FnMut(&'a V, Vec3) -> I, ) -> Option>> where - I: IntoIterator>, + I: Iterator> + 'a, { let new_start = WorldPath::get_z_walkable_space(vol, from); let new_dest = WorldPath::get_z_walkable_space(vol, dest); @@ -46,7 +54,7 @@ impl WorldPath { new_start, new_dest, euclidean_distance, - |pos| get_neighbors(vol, pos), + |pos| get_neighbors(vol, *pos), transition_cost, ) } else { @@ -55,46 +63,33 @@ impl WorldPath { } fn get_z_walkable_space(vol: &V, pos: Vec3) -> Option> { - if WorldPath::is_walkable_space(vol, pos) { - return Some(pos); + let mut z_incr = 0; + for i in 0..32 { + let test_pos = pos + Vec3::unit_z() * z_incr; + if WorldPath::is_walkable_space(vol, test_pos) { + return Some(test_pos); + } + z_incr = -z_incr + if z_incr <= 0 { 1 } else { 0 }; } - let mut cur_pos_below = pos.clone(); - while !WorldPath::is_walkable_space(vol, cur_pos_below) && cur_pos_below.z > 0 { - cur_pos_below.z -= 1; - } - - let max_z = 1000; - let mut cur_pos_above = pos.clone(); - while !WorldPath::is_walkable_space(vol, cur_pos_above) && cur_pos_above.z <= max_z { - cur_pos_above.z += 1; - } - - if cur_pos_below.z > 0 { - Some(cur_pos_below) - } else if cur_pos_above.z < max_z { - Some(cur_pos_above) - } else { - None - } + None } pub fn is_walkable_space(vol: &V, pos: Vec3) -> bool { - if let (Ok(voxel), Ok(upper_neighbor), Ok(upper_neighbor2)) = ( - vol.get(pos), - vol.get(pos + Vec3::new(0, 0, 1)), - vol.get(pos + Vec3::new(0, 0, 2)), - ) { - !voxel.is_empty() && upper_neighbor.is_empty() && upper_neighbor2.is_empty() - } else { - false - } + vol.get(pos - Vec3::unit_z()) + .map(|v| !v.is_empty()) + .unwrap_or(false) + && (0..2).all(|z| { + vol.get(pos + Vec3::new(0, 0, z)) + .map(|v| v.is_empty()) + .unwrap_or(true) + }) } - pub fn get_neighbors( - vol: &V, - pos: &Vec3, - ) -> impl IntoIterator> { + pub fn get_neighbors<'a, V: ReadVol>( + vol: &'a V, + pos: Vec3, + ) -> impl Iterator> + 'a { let directions = vec![ Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 1), // Forward upward @@ -115,13 +110,10 @@ impl WorldPath { Vec3::new(0, 0, -1), // Downwards ]; - let neighbors: Vec> = directions + directions .into_iter() - .map(|dir| dir + pos) - .filter(|new_pos| Self::is_walkable_space(vol, *new_pos)) - .collect(); - - neighbors.into_iter() + .map(move |dir| dir + pos) + .filter(move |new_pos| Self::is_walkable_space(vol, *new_pos)) } pub fn move_along_path( @@ -130,7 +122,7 @@ impl WorldPath { pos: &Pos, inputs: &mut ControllerInputs, is_destination: impl Fn(Vec3, Vec3) -> bool, - found_destination: impl FnOnce(), + mut found_destination: impl FnMut(), ) { // No path available if self.path == None { @@ -156,10 +148,8 @@ impl WorldPath { self.path = Some(block_path); } - let move_dir = Vec2::::from(next_pos - ipos); - // Move the input towards the next area on the path - inputs.move_dir = Vec2::::new(move_dir.x as f32, move_dir.y as f32); + inputs.move_dir = Vec2::from(next_pos.map(|e| (e as f32).floor() + 0.5) - pos.0); // Need to jump to continue if next_pos.z >= ipos.z + 1 { @@ -171,7 +161,11 @@ impl WorldPath { if next_pos.z - min_z_glide_height < ipos.z { inputs.glide.set_state(true); } + } else { + found_destination(); } + } else { + found_destination(); } } @@ -186,9 +180,9 @@ impl WorldPath { } pub fn euclidean_distance(start: &Vec3, end: &Vec3) -> f32 { - start.map(|e| e as f32).distance(end.map(|e| e as f32)) + start.map(|e| e as f32).distance((*end).map(|e| e as f32)) } pub fn transition_cost(_start: &Vec3, _end: &Vec3) -> f32 { - 1.0f32 + 1.0 } diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 0e4a658917..792ab7ea2e 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -1,9 +1,7 @@ use crate::comp::{ Agent, CharacterState, Controller, MountState, MovementState::Glide, Pos, Stats, }; -use crate::hierarchical::ChunkPath; -use crate::pathfinding::WorldPath; -use crate::terrain::TerrainGrid; +use crate::{hierarchical::ChunkPath, path::Path, pathfinding::WorldPath, terrain::TerrainGrid}; use rand::{seq::SliceRandom, thread_rng}; use specs::{Entities, Join, ReadExpect, ReadStorage, System, WriteStorage}; use vek::*; @@ -75,7 +73,8 @@ impl<'a> System<'a> for Sys { * MAX_TRAVEL_DIST; new_path = Some( ChunkPath::new(&*terrain, pos.0, pos.0 + new_dest) - .get_worldpath(&*terrain), + .get_worldpath(&*terrain) + .unwrap(), ); }; @@ -101,34 +100,39 @@ impl<'a> System<'a> for Sys { inputs.move_dir = bearing.normalized(); } } - Agent::Pet { target, offset } => { + Agent::Pet { target, chaser } => { // Run towards target. - match positions.get(*target) { - Some(tgt_pos) => { - let tgt_pos = tgt_pos.0 + *offset; - - if tgt_pos.z > pos.0.z + 1.0 { - inputs.jump.set_state(true); - } - - // Move towards the target. - let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude(); - inputs.move_dir = if dist > 5.0 { - Vec2::from(tgt_pos - pos.0).normalized() - } else if dist < 1.5 && dist > 0.001 { - Vec2::from(pos.0 - tgt_pos).normalized() - } else { - Vec2::zero() - }; + if let Some(tgt_pos) = positions.get(*target) { + if let Some(bearing) = chaser.chase(&*terrain, tgt_pos.0, pos.0) { + inputs.move_dir = Vec2::from(bearing).normalized(); + inputs.jump.set_state(bearing.z > 0.9); } - _ => inputs.move_dir = Vec2::zero(), - } - // Change offset occasionally. - if rand::random::() < 0.003 { - *offset = - Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) - * 10.0; + /* + const HAPPY_DIST: i32 = 4; + let plot_path = if let Some(dir) = route.traverse(&*terrain, pos.0) { + inputs.move_dir = Vec2::from(dir).normalized(); + inputs.jump.set_state(dir.z > 0.9); + + // Sometimes recalculate to avoid getting stuck + rand::random::() < 0.005 + } else { + true + }; + + if plot_path { + let path: Path = WorldPath::find(&*terrain, pos.0, tgt_pos.0) + .ok() + .and_then(|wp| wp.path.map(|nodes| nodes.into_iter().rev())) + .into_iter() + .flatten() + .collect(); + + *route = path.into(); + } + */ + } else { + inputs.move_dir = Vec2::zero(); } } Agent::Enemy { bearing, target } => { diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index 06f98205ce..d1a981232e 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -4,6 +4,10 @@ version = "0.4.0" authors = ["Joshua Barretto "] edition = "2018" +[features] +worldgen = ["server/worldgen"] +default = ["worldgen"] + [dependencies] server = { package = "veloren-server", path = "../server" } common = { package = "veloren-common", path = "../common" } diff --git a/server/Cargo.toml b/server/Cargo.toml index 996c02eb6c..cf56a3ba51 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -4,6 +4,9 @@ version = "0.4.0" authors = ["Joshua Barretto "] edition = "2018" +[features] +worldgen = [] + [dependencies] common = { package = "veloren-common", path = "../common" } world = { package = "veloren-world", path = "../world" } @@ -19,7 +22,7 @@ scan_fmt = "0.2.4" ron = "0.5.1" serde = "1.0.102" serde_derive = "1.0.102" -rand = "0.7.2" +rand = { version = "0.7.2", features = ["small_rng"] } chrono = "0.4.9" hashbrown = { version = "0.6.2", features = ["rayon", "serde", "nightly"] } crossbeam = "=0.7.2" diff --git a/server/src/chunk_generator.rs b/server/src/chunk_generator.rs index c8cd76d45e..9a4dbf855c 100644 --- a/server/src/chunk_generator.rs +++ b/server/src/chunk_generator.rs @@ -1,4 +1,6 @@ -use common::terrain::TerrainChunk; +#[cfg(not(feature = "worldgen"))] +use crate::test_world::World; +use common::{generation::ChunkSupplement, terrain::TerrainChunk}; use crossbeam::channel; use hashbrown::{hash_map::Entry, HashMap}; use specs::Entity as EcsEntity; @@ -7,7 +9,8 @@ use std::sync::{ Arc, }; use vek::*; -use world::{ChunkSupplement, World}; +#[cfg(feature = "worldgen")] +use world::World; type ChunkGenResult = ( Vec2, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 38b0c11796..d1a3504666 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -537,7 +537,8 @@ fn handle_pathfind(server: &mut Server, player: EcsEntity, args: String, action: { let target = start_pos.0 + Vec3::new(x, y, z); let new_path = ChunkPath::new(&*server.state.terrain(), start_pos.0, target) - .get_worldpath(&*server.state.terrain()); + .get_worldpath(&*server.state.terrain()) + .unwrap(); server.state.write_component( target_entity, @@ -627,10 +628,7 @@ fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: & fn alignment_to_agent(alignment: &str, target: EcsEntity) -> Option { match alignment { "hostile" => Some(comp::Agent::enemy()), - "friendly" => Some(comp::Agent::Pet { - target, - offset: Vec2::zero(), - }), + "friendly" => Some(comp::Agent::pet(target)), "traveler" => Some(comp::Agent::Traveler { path: WorldPath::default(), }), @@ -981,6 +979,20 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch } } +#[cfg(not(feature = "worldgen"))] +fn handle_debug_column( + server: &mut Server, + entity: EcsEntity, + _args: String, + _action: &ChatCommand, +) { + server.notify_client( + entity, + ServerMsg::private(String::from("Unsupported without worldgen enabled")), + ); +} + +#[cfg(feature = "worldgen")] fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { let sim = server.world.sim(); let sampler = server.world.sample_columns(); diff --git a/server/src/lib.rs b/server/src/lib.rs index 63be4560bb..853689f7ee 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -10,6 +10,8 @@ pub mod input; pub mod metrics; pub mod settings; pub mod sys; +#[cfg(not(feature = "worldgen"))] +mod test_world; // Reexports pub use crate::{error::Error, input::Input, settings::ServerSettings}; @@ -46,10 +48,14 @@ use std::{ }; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; +#[cfg(feature = "worldgen")] use world::{ sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, WORLD_SIZE}, World, }; +#[cfg(not(feature = "worldgen"))] +use test_world::World; + const CLIENT_TIMEOUT: f64 = 20.0; // Seconds pub enum Event { @@ -108,6 +114,7 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); + #[cfg(feature = "worldgen")] let world = World::generate( settings.world_seed, WorldOpts { @@ -121,8 +128,14 @@ impl Server { ..WorldOpts::default() }, ); + #[cfg(feature = "worldgen")] let map = world.sim().get_map(); + #[cfg(not(feature = "worldgen"))] + let world = World::generate(settings.world_seed); + let map = Vec::new(); + + #[cfg(feature = "worldgen")] 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) @@ -167,6 +180,9 @@ impl Server { Vec3::new(spawn_location.x, spawn_location.y, z).map(|e| (e as f32)) + 0.5 }; + #[cfg(not(feature = "worldgen"))] + let spawn_point = Vec3::new(0.0, 0.0, 256.0); + // set the spawn point we calculated above state.ecs_mut().insert(SpawnPoint(spawn_point)); diff --git a/server/src/test_world.rs b/server/src/test_world.rs new file mode 100644 index 0000000000..6eb4764b61 --- /dev/null +++ b/server/src/test_world.rs @@ -0,0 +1,41 @@ +use common::{ + generation::{ChunkSupplement, NpcInfo}, + terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, + vol::{ReadVol, RectVolSize, Vox, WriteVol}, +}; +use rand::{prelude::*, rngs::SmallRng}; +use std::time::Duration; +use vek::*; + +pub struct World; + +impl World { + pub fn generate(_seed: u32) -> Self { + Self + } + + pub fn tick(&self, dt: Duration) {} + + pub fn generate_chunk( + &self, + chunk_pos: Vec2, + _should_continue: impl FnMut() -> bool, + ) -> Result<(TerrainChunk, ChunkSupplement), ()> { + let (x, y) = chunk_pos.map(|e| e.to_le_bytes()).into_tuple(); + let mut rng = SmallRng::from_seed([ + x[0], x[1], x[2], x[3], y[0], y[1], y[2], y[3], x[0], x[1], x[2], x[3], y[0], y[1], + y[2], y[3], + ]); + let height = rng.gen::() % 8; + + Ok(( + TerrainChunk::new( + 256 + if rng.gen::() < 64 { height } else { 0 }, + Block::new(BlockKind::Dense, Rgb::new(200, 220, 255)), + Block::empty(), + TerrainChunkMeta::void(), + ), + ChunkSupplement::default(), + )) + } +} diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 81b155c9db..a17a9c6546 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1259,8 +1259,8 @@ impl Terrain { .fold(i32::MIN, |max, (_, chunk)| chunk.get_max_z().max(max)); let aabb = Aabb { - min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 1), - max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 1), + min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 2), + max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 2), }; // Clone various things so that they can be moved into the thread. diff --git a/world/src/lib.rs b/world/src/lib.rs index 61325b49ec..787e038cf1 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -19,6 +19,7 @@ use crate::{ util::Sampler, }; use common::{ + generation::{ChunkSupplement, NpcInfo}, terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::{ReadVol, RectVolSize, Vox, WriteVol}, }; @@ -165,18 +166,3 @@ impl World { Ok((chunk, supplement)) } } - -pub struct NpcInfo { - pub pos: Vec3, - pub boss: bool, -} - -pub struct ChunkSupplement { - pub npcs: Vec, -} - -impl Default for ChunkSupplement { - fn default() -> Self { - Self { npcs: Vec::new() } - } -}