diff --git a/Cargo.lock b/Cargo.lock index 9c1edefa0..d51ebb544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,9 +233,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.22" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "jobserver", "libc", @@ -412,26 +412,22 @@ dependencies = [ [[package]] name = "criterion" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", + "itertools 0.13.0", "num-traits", - "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "tokio", @@ -725,9 +721,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -993,12 +989,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" - [[package]] name = "hmac" version = "0.12.1" @@ -1263,17 +1253,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1289,6 +1268,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -3311,15 +3299,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings 0.4.1", ] [[package]] @@ -3363,9 +3351,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d" dependencies = [ "windows-link", ] @@ -3381,9 +3369,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a" dependencies = [ "windows-link", ] diff --git a/pumpkin-data/build/noise_router.rs b/pumpkin-data/build/noise_router.rs index ac5bc7196..c0fe3539a 100644 --- a/pumpkin-data/build/noise_router.rs +++ b/pumpkin-data/build/noise_router.rs @@ -1023,6 +1023,8 @@ pub(crate) fn build() -> TokenStream { let _ = reprs.end_islands; let overworld_router = reprs.overworld.into_token_stream(); + let nether_router = reprs.nether.into_token_stream(); + let end_router = reprs.end.into_token_stream(); quote! { pub struct NoiseData { @@ -1326,5 +1328,7 @@ pub(crate) fn build() -> TokenStream { } pub const OVERWORLD_BASE_NOISE_ROUTER: BaseNoiseRouters = #overworld_router; + pub const NETHER_BASE_NOISE_ROUTER: BaseNoiseRouters = #nether_router; + pub const END_BASE_NOISE_ROUTER: BaseNoiseRouters = #end_router; } } diff --git a/pumpkin-protocol/src/client/play/chunk_data.rs b/pumpkin-protocol/src/client/play/chunk_data.rs index a28a21f58..0686d206c 100644 --- a/pumpkin-protocol/src/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/client/play/chunk_data.rs @@ -24,8 +24,8 @@ impl ClientPacket for CChunkData<'_> { write.write_i32_be(self.0.position.z)?; let heightmaps = &self.0.heightmap; - // the heighmap is a map, we put 2 values in so the size is 2 - write.write_var_int(&VarInt(2))?; + // the heighmap is a map, we put 3 values in so the size is 3 + write.write_var_int(&VarInt(3))?; // heighmap index write.write_var_int(&VarInt(1))?; @@ -41,6 +41,13 @@ impl ClientPacket for CChunkData<'_> { for mb in &heightmaps.motion_blocking { write.write_i64_be(*mb)?; } + // heighmap index + write.write_var_int(&VarInt(5))?; + // write long array + write.write_var_int(&VarInt(heightmaps.motion_blocking_no_leaves.len() as i32))?; + for mb in &heightmaps.motion_blocking { + write.write_i64_be(*mb)?; + } { let mut blocks_and_biomes_buf = Vec::new(); diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 1b02f7f9d..77cbf1c9e 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -49,7 +49,7 @@ lru = "0.14.0" tokio-util = { version = "0.7.15", features = ["rt"] } [dev-dependencies] -criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } +criterion = { version = "0.6", features = ["html_reports", "async_tokio"] } temp-dir = "0.1.16" # Print log info inside tests when needed env_logger = "0.11.8" diff --git a/pumpkin-world/benches/chunk_io.rs b/pumpkin-world/benches/chunk_io.rs index 8ef1b2ec0..56c49ca7e 100644 --- a/pumpkin-world/benches/chunk_io.rs +++ b/pumpkin-world/benches/chunk_io.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf, sync::Arc}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use pumpkin_util::math::vector2::Vector2; -use pumpkin_world::{chunk::ChunkData, global_path, level::Level}; +use pumpkin_world::{chunk::ChunkData, dimension::Dimension, global_path, level::Level}; use tokio::{runtime::Runtime, sync::RwLock}; async fn test_reads(level: &Arc, positions: Vec>) { @@ -80,7 +80,11 @@ fn initialize_level( let (send, mut recv) = tokio::sync::mpsc::unbounded_channel(); // Our data dir is empty, so we're generating new chunks here - let level_to_save = Arc::new(Level::from_root_folder(root_dir.clone(), 123)); + let level_to_save = Arc::new(Level::from_root_folder( + root_dir.clone(), + 123, + Dimension::Overworld, + )); println!("Level Seed is: {}", level_to_save.seed.0); let level_to_fetch = level_to_save.clone(); @@ -194,7 +198,11 @@ fn bench_chunk_io(c: &mut Criterion) { &chunks, |b, chunks| { b.to_async(&async_handler).iter(async || { - let level = Arc::new(Level::from_root_folder(root_dir.clone(), 123)); + let level = Arc::new(Level::from_root_folder( + root_dir.clone(), + 123, + Dimension::Overworld, + )); test_writes(&level, chunks.to_vec()).await }) }, @@ -220,7 +228,11 @@ fn bench_chunk_io(c: &mut Criterion) { &positions, |b, positions| { b.to_async(&async_handler).iter(async || { - let level = Arc::new(Level::from_root_folder(root_dir.clone(), 123)); + let level = Arc::new(Level::from_root_folder( + root_dir.clone(), + 123, + Dimension::Overworld, + )); test_reads(&level, positions.to_vec()).await }) }, diff --git a/pumpkin-world/src/biome/mod.rs b/pumpkin-world/src/biome/mod.rs index e10993c48..bed161915 100644 --- a/pumpkin-world/src/biome/mod.rs +++ b/pumpkin-world/src/biome/mod.rs @@ -2,10 +2,12 @@ use sha2::{Digest, Sha256}; use std::cell::RefCell; use enum_dispatch::enum_dispatch; -use pumpkin_data::chunk::{Biome, BiomeTree, OVERWORLD_BIOME_SOURCE}; +use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE}; use pumpkin_util::math::vector3::Vector3; -use crate::generation::noise_router::multi_noise_sampler::MultiNoiseSampler; +use crate::{ + dimension::Dimension, generation::noise_router::multi_noise_sampler::MultiNoiseSampler, +}; pub mod multi_noise; thread_local! { @@ -15,7 +17,11 @@ thread_local! { #[enum_dispatch] pub trait BiomeSupplier { - fn biome(at: &Vector3, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome; + fn biome( + at: &Vector3, + noise: &mut MultiNoiseSampler<'_>, + dimension: Dimension, + ) -> &'static Biome; } pub struct MultiNoiseBiomeSupplier; @@ -23,11 +29,19 @@ pub struct MultiNoiseBiomeSupplier; // TODO: Add Nether & End supplier impl BiomeSupplier for MultiNoiseBiomeSupplier { - fn biome(global_biome_pos: &Vector3, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome { + fn biome( + global_biome_pos: &Vector3, + noise: &mut MultiNoiseSampler<'_>, + dimension: Dimension, + ) -> &'static Biome { + let source: &'static BiomeTree = match dimension { + Dimension::Overworld => &OVERWORLD_BIOME_SOURCE, + Dimension::Nether => &NETHER_BIOME_SOURCE, + Dimension::End => &OVERWORLD_BIOME_SOURCE, // TODO: this is wrong, end has its own thing + }; let point = noise.sample(global_biome_pos.x, global_biome_pos.y, global_biome_pos.z); let point_list = point.convert_to_list(); - LAST_RESULT_NODE - .with_borrow_mut(|last_result| OVERWORLD_BIOME_SOURCE.get(&point_list, last_result)) + LAST_RESULT_NODE.with_borrow_mut(|last_result| source.get(&point_list, last_result)) } } @@ -47,6 +61,7 @@ mod test { use crate::{ GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, chunk::palette::BIOME_NETWORK_MAX_BITS, + dimension::Dimension, generation::noise_router::{ multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, proto_noise_router::ProtoNoiseRouters, @@ -68,6 +83,7 @@ mod test { let biome = MultiNoiseBiomeSupplier::biome( &pumpkin_util::math::vector3::Vector3 { x: -24, y: 1, z: 8 }, &mut sampler, + Dimension::Overworld, ); assert_eq!(biome, &Biome::DESERT) } @@ -96,7 +112,7 @@ mod test { let chunk_pos = Vector2::new(data.x, data.z); let mut chunk = ProtoChunk::new(chunk_pos, &noise_router, &random_config, surface_settings); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); for (biome_x, biome_y, biome_z, biome_id) in data.data { let global_biome_pos = Vector3::new(biome_x, biome_y, biome_z); diff --git a/pumpkin-world/src/biome/multi_noise.rs b/pumpkin-world/src/biome/multi_noise.rs index ae293cf81..8228b6867 100644 --- a/pumpkin-world/src/biome/multi_noise.rs +++ b/pumpkin-world/src/biome/multi_noise.rs @@ -36,6 +36,7 @@ mod test { use crate::{ GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk, biome::{BiomeSupplier, MultiNoiseBiomeSupplier}, + dimension::Dimension, generation::noise_router::{ multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, proto_noise_router::ProtoNoiseRouters, @@ -89,7 +90,11 @@ mod test { for (x, y, z, biome_id) in expected_data { let global_biome_pos = Vector3::new(x, y, z); - let calculated_biome = MultiNoiseBiomeSupplier::biome(&global_biome_pos, &mut sampler); + let calculated_biome = MultiNoiseBiomeSupplier::biome( + &global_biome_pos, + &mut sampler, + Dimension::Overworld, + ); assert_eq!( biome_id, diff --git a/pumpkin-world/src/chunk/format/anvil.rs b/pumpkin-world/src/chunk/format/anvil.rs index 286edebb1..963963bef 100644 --- a/pumpkin-world/src/chunk/format/anvil.rs +++ b/pumpkin-world/src/chunk/format/anvil.rs @@ -912,6 +912,7 @@ mod tests { use crate::chunk::format::anvil::AnvilChunkFile; use crate::chunk::io::chunk_file_manager::ChunkFileManager; use crate::chunk::io::{ChunkIO, LoadedData}; + use crate::dimension::Dimension; use crate::generation::{Seed, get_world_gen}; use crate::level::{LevelFolder, SyncChunk}; @@ -982,7 +983,7 @@ mod tests { let _ = env_logger::try_init(); - let generator = get_world_gen(Seed(0)); + let generator = get_world_gen(Seed(0), Dimension::Overworld); let temp_dir = TempDir::new().unwrap(); let level_folder = LevelFolder { @@ -1250,7 +1251,7 @@ mod tests { let _ = env_logger::try_init(); - let generator = get_world_gen(Seed(0)); + let generator = get_world_gen(Seed(0), Dimension::Overworld); let temp_dir = TempDir::new().unwrap(); let level_folder = LevelFolder { diff --git a/pumpkin-world/src/chunk/format/linear.rs b/pumpkin-world/src/chunk/format/linear.rs index 4b1f8a635..9c1bda5b9 100644 --- a/pumpkin-world/src/chunk/format/linear.rs +++ b/pumpkin-world/src/chunk/format/linear.rs @@ -376,6 +376,7 @@ mod tests { use crate::chunk::format::linear::LinearFile; use crate::chunk::io::chunk_file_manager::ChunkFileManager; use crate::chunk::io::{ChunkIO, LoadedData}; + use crate::dimension::Dimension; use crate::generation::{Seed, get_world_gen}; use crate::level::LevelFolder; @@ -409,7 +410,7 @@ mod tests { async fn test_writing() { let _ = env_logger::try_init(); - let generator = get_world_gen(Seed(0)); + let generator = get_world_gen(Seed(0), Dimension::Overworld); let temp_dir = TempDir::new().unwrap(); let level_folder = LevelFolder { diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 2c4f19ea9..a49bf7fe2 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -179,6 +179,8 @@ pub struct ChunkHeightmaps { pub world_surface: Box<[i64]>, #[serde(serialize_with = "nbt_long_array")] pub motion_blocking: Box<[i64]>, + #[serde(serialize_with = "nbt_long_array")] + pub motion_blocking_no_leaves: Box<[i64]>, } /// The Heightmap for a completely empty chunk @@ -188,6 +190,7 @@ impl Default for ChunkHeightmaps { // 9 bits per entry // 0 packed into an i64 7 times. motion_blocking: vec![0; 37].into_boxed_slice(), + motion_blocking_no_leaves: vec![0; 37].into_boxed_slice(), world_surface: vec![0; 37].into_boxed_slice(), } } diff --git a/pumpkin-world/src/dimension.rs b/pumpkin-world/src/dimension.rs index 51c60e619..1b8a9b2a3 100644 --- a/pumpkin-world/src/dimension.rs +++ b/pumpkin-world/src/dimension.rs @@ -4,7 +4,7 @@ use serde::Deserialize; use crate::level::Level; -#[derive(Clone, Copy, PartialEq, Eq, Hash, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum Dimension { Overworld, @@ -19,6 +19,6 @@ impl Dimension { Dimension::Nether => base_directory.push("DIM-1"), Dimension::End => base_directory.push("DIM1"), } - Level::from_root_folder(base_directory, seed) + Level::from_root_folder(base_directory, seed, *self) } } diff --git a/pumpkin-world/src/generation/chunk_noise.rs b/pumpkin-world/src/generation/chunk_noise.rs index cc7c49c33..8a7930c8d 100644 --- a/pumpkin-world/src/generation/chunk_noise.rs +++ b/pumpkin-world/src/generation/chunk_noise.rs @@ -186,9 +186,10 @@ impl<'a> ChunkNoiseGenerator<'a> { let horizontal_biome_end = biome_coords::from_block( horizontal_cell_count * generation_shape.horizontal_cell_block_count() as usize, ); - - let vertical_cell_count = generation_shape.height as usize - / generation_shape.vertical_cell_block_count() as usize; + let vertical_cell_count = floor_div( + generation_shape.height as usize, + generation_shape.vertical_cell_block_count() as usize, + ); let minimum_cell_y = floor_div( generation_shape.min_y as i32, generation_shape.vertical_cell_block_count() as i32, diff --git a/pumpkin-world/src/generation/generator.rs b/pumpkin-world/src/generation/generator.rs index 5a16dbdc9..41b02d1b1 100644 --- a/pumpkin-world/src/generation/generator.rs +++ b/pumpkin-world/src/generation/generator.rs @@ -1,10 +1,11 @@ use pumpkin_util::math::vector2::Vector2; use crate::chunk::ChunkData; +use crate::dimension::Dimension; use crate::generation::Seed; pub trait GeneratorInit { - fn new(seed: Seed) -> Self; + fn new(seed: Seed, dimension: Dimension) -> Self; } pub trait WorldGenerator: Sync + Send { diff --git a/pumpkin-world/src/generation/implementation/mod.rs b/pumpkin-world/src/generation/implementation/mod.rs index ebc9da4b9..178ea123f 100644 --- a/pumpkin-world/src/generation/implementation/mod.rs +++ b/pumpkin-world/src/generation/implementation/mod.rs @@ -1,13 +1,15 @@ -use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER; +use pumpkin_data::noise_router::{ + END_BASE_NOISE_ROUTER, NETHER_BASE_NOISE_ROUTER, OVERWORLD_BASE_NOISE_ROUTER, +}; use pumpkin_util::math::{vector2::Vector2, vector3::Vector3}; use super::{ - biome_coords, - noise_router::proto_noise_router::ProtoNoiseRouters, - settings::{GENERATION_SETTINGS, GeneratorSetting}, + biome_coords, noise_router::proto_noise_router::ProtoNoiseRouters, + settings::gen_settings_from_dimension, }; -use crate::chunk::ChunkLight; +use crate::chunk::ChunkHeightmaps; use crate::chunk::format::LightContainer; +use crate::{chunk::ChunkLight, dimension::Dimension}; use crate::{ chunk::{ ChunkData, ChunkSections, SubChunk, @@ -21,27 +23,31 @@ use crate::{ pub struct VanillaGenerator { random_config: GlobalRandomConfig, base_router: ProtoNoiseRouters, + dimension: Dimension, } impl GeneratorInit for VanillaGenerator { - fn new(seed: Seed) -> Self { + fn new(seed: Seed, dimension: Dimension) -> Self { let random_config = GlobalRandomConfig::new(seed.0, false); // TODO: The generation settings contains (part of?) the noise routers too; do we keep the separate or // use only the generation settings? - let base_router = ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config); + let base = match dimension { + Dimension::Overworld => OVERWORLD_BASE_NOISE_ROUTER, + Dimension::Nether => NETHER_BASE_NOISE_ROUTER, + Dimension::End => END_BASE_NOISE_ROUTER, + }; + let base_router = ProtoNoiseRouters::generate(&base, &random_config); Self { random_config, base_router, + dimension, } } } impl WorldGenerator for VanillaGenerator { fn generate_chunk(&self, at: &Vector2) -> ChunkData { - // TODO: Dont hardcode this - let generation_settings = GENERATION_SETTINGS - .get(&GeneratorSetting::Overworld) - .unwrap(); + let generation_settings = gen_settings_from_dimension(&self.dimension); let sub_chunks = generation_settings.shape.height as usize / BlockPalette::SIZE; let sections = (0..sub_chunks).map(|_| SubChunk::default()).collect(); @@ -53,7 +59,7 @@ impl WorldGenerator for VanillaGenerator { &self.random_config, generation_settings, ); - proto_chunk.populate_biomes(); + proto_chunk.populate_biomes(self.dimension); proto_chunk.populate_noise(); proto_chunk.build_surface(); @@ -79,7 +85,11 @@ impl WorldGenerator for VanillaGenerator { } } } - + let heightmap = ChunkHeightmaps { + world_surface: proto_chunk.flat_surface_height_map, + motion_blocking: proto_chunk.flat_motion_blocking_height_map, + motion_blocking_no_leaves: proto_chunk.flat_motion_blocking_no_leaves_height_map, + }; ChunkData { light_engine: ChunkLight { sky_light: (0..sections.sections.len() + 2) @@ -90,7 +100,7 @@ impl WorldGenerator for VanillaGenerator { .collect(), }, section: sections, - heightmap: Default::default(), + heightmap, position: *at, dirty: true, block_ticks: Default::default(), diff --git a/pumpkin-world/src/generation/mod.rs b/pumpkin-world/src/generation/mod.rs index 307609e57..5d1dcc046 100644 --- a/pumpkin-world/src/generation/mod.rs +++ b/pumpkin-world/src/generation/mod.rs @@ -29,9 +29,11 @@ pub use seed::Seed; use generator::GeneratorInit; -pub fn get_world_gen(seed: Seed) -> Box { +use crate::dimension::Dimension; + +pub fn get_world_gen(seed: Seed, dimension: Dimension) -> Box { // TODO decide which WorldGenerator to pick based on config. - Box::new(VanillaGenerator::new(seed)) + Box::new(VanillaGenerator::new(seed, dimension)) } #[derive(Getters)] diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index e7d5b70f9..b59e744ce 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -11,6 +11,7 @@ use crate::{ biome::{BiomeSupplier, MultiNoiseBiomeSupplier, hash_seed}, block::RawBlockState, chunk::CHUNK_AREA, + dimension::Dimension, generation::{biome, positions::chunk_pos}, }; @@ -106,10 +107,10 @@ pub struct ProtoChunk<'a> { /// HEIGHTMAPS /// /// Top block that is not air - flat_surface_height_map: Box<[i32]>, - flat_ocean_floor_height_map: Box<[i32]>, - flat_motion_blocking_height_map: Box<[i32]>, - flat_motion_blocking_no_leaves_height_map: Box<[i32]>, + pub flat_surface_height_map: Box<[i64]>, + flat_ocean_floor_height_map: Box<[i64]>, + pub flat_motion_blocking_height_map: Box<[i64]>, + pub flat_motion_blocking_no_leaves_height_map: Box<[i64]>, // may want to use chunk status } @@ -141,8 +142,8 @@ impl<'a> ProtoChunk<'a> { start_z, generation_shape, sampler, - true, - true, + settings.aquifers_enabled, + settings.ore_veins_enabled, ); // TODO: This is duplicate code already in ChunkNoiseGenerator::new let biome_pos = Vector2::new( @@ -172,7 +173,7 @@ impl<'a> ProtoChunk<'a> { SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config); let default_block = RawBlockState::new(&settings.default_block.name).unwrap(); - let default_heightmap = vec![i32::MIN; CHUNK_AREA].into_boxed_slice(); + let default_heightmap = vec![i64::MIN; CHUNK_AREA].into_boxed_slice(); Self { chunk_pos, settings, @@ -208,8 +209,8 @@ impl<'a> ProtoChunk<'a> { let index = Self::local_position_to_height_map_index(local_x, local_z); let current_height = self.flat_surface_height_map[index]; - if pos.y > current_height { - self.flat_surface_height_map[index] = pos.y; + if pos.y > current_height as i32 { + self.flat_surface_height_map[index] = pos.y as i64; } } @@ -219,8 +220,8 @@ impl<'a> ProtoChunk<'a> { let index = Self::local_position_to_height_map_index(local_x, local_z); let current_height = self.flat_ocean_floor_height_map[index]; - if pos.y > current_height { - self.flat_ocean_floor_height_map[index] = pos.y; + if pos.y > current_height as i32 { + self.flat_ocean_floor_height_map[index] = pos.y as i64; } } @@ -230,8 +231,8 @@ impl<'a> ProtoChunk<'a> { let index = Self::local_position_to_height_map_index(local_x, local_z); let current_height = self.flat_motion_blocking_height_map[index]; - if pos.y > current_height { - self.flat_motion_blocking_height_map[index] = pos.y; + if pos.y > current_height as i32 { + self.flat_motion_blocking_height_map[index] = pos.y as i64; } } @@ -241,33 +242,33 @@ impl<'a> ProtoChunk<'a> { let index = Self::local_position_to_height_map_index(local_x, local_z); let current_height = self.flat_motion_blocking_no_leaves_height_map[index]; - if pos.y > current_height { - self.flat_motion_blocking_no_leaves_height_map[index] = pos.y; + if pos.y > current_height as i32 { + self.flat_motion_blocking_no_leaves_height_map[index] = pos.y as i64; } } - pub fn top_block_height_exclusive(&self, pos: &Vector2) -> i32 { + pub fn top_block_height_exclusive(&self, pos: &Vector2) -> i64 { let local_x = (pos.x & 15) as usize; let local_z = (pos.z & 15) as usize; let index = Self::local_position_to_height_map_index(local_x, local_z); self.flat_surface_height_map[index] + 1 } - pub fn ocean_floor_height_exclusive(&self, pos: &Vector2) -> i32 { + pub fn ocean_floor_height_exclusive(&self, pos: &Vector2) -> i64 { let local_x = (pos.x & 15) as usize; let local_z = (pos.z & 15) as usize; let index = Self::local_position_to_height_map_index(local_x, local_z); self.flat_ocean_floor_height_map[index] + 1 } - pub fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2) -> i32 { + pub fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2) -> i64 { let local_x = (pos.x & 15) as usize; let local_z = (pos.z & 15) as usize; let index = Self::local_position_to_height_map_index(local_x, local_z); self.flat_motion_blocking_height_map[index] + 1 } - pub fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2) -> i32 { + pub fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2) -> i64 { let local_x = (pos.x & 15) as usize; let local_z = (pos.z & 15) as usize; let index = Self::local_position_to_height_map_index(local_x, local_z); @@ -366,7 +367,7 @@ impl<'a> ProtoChunk<'a> { self.flat_biome_map[index] } - pub fn populate_biomes(&mut self) { + pub fn populate_biomes(&mut self, dimension: Dimension) { let min_y = self.noise_sampler.min_y(); let bottom_section = section_coords::block_to_section(min_y) as i32; let top_section = @@ -395,6 +396,7 @@ impl<'a> ProtoChunk<'a> { let biome = MultiNoiseBiomeSupplier::biome( &biome_pos, &mut self.multi_noise_sampler, + dimension, ); //panic!("Populating biome: {:?} -> {:?}", biome_pos, biome); @@ -535,7 +537,7 @@ impl<'a> ProtoChunk<'a> { let z = start_z + local_z; let mut top_block = - self.top_block_height_exclusive(&Vector2::new(local_x, local_z)); + self.top_block_height_exclusive(&Vector2::new(local_x, local_z)) as i32; let biome_y = if self.settings.legacy_random_source { 0 @@ -554,7 +556,8 @@ impl<'a> ProtoChunk<'a> { ); // Get the top block again if we placed a pillar! - top_block = self.top_block_height_exclusive(&Vector2::new(local_x, local_z)); + top_block = + self.top_block_height_exclusive(&Vector2::new(local_x, local_z)) as i32; } context.init_horizontal(x, z); @@ -662,6 +665,7 @@ mod test { use pumpkin_util::math::vector2::Vector2; use crate::{ + dimension::Dimension, generation::{ GlobalRandomConfig, noise_router::{ @@ -1151,7 +1155,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); @@ -1183,7 +1187,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); @@ -1215,7 +1219,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); @@ -1247,7 +1251,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); @@ -1279,7 +1283,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); @@ -1312,7 +1316,7 @@ mod test { surface_config, ); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); diff --git a/pumpkin-world/src/generation/settings.rs b/pumpkin-world/src/generation/settings.rs index bacc523f9..e19ac64f0 100644 --- a/pumpkin-world/src/generation/settings.rs +++ b/pumpkin-world/src/generation/settings.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::LazyLock}; use serde::Deserialize; -use crate::block::BlockStateCodec; +use crate::{block::BlockStateCodec, dimension::Dimension}; use super::{biome_coords::to_block, height_limit::HeightLimitView, surface::rule::MaterialRule}; @@ -12,6 +12,16 @@ pub static GENERATION_SETTINGS: LazyLock &GenerationSettings { + match dimension { + Dimension::Overworld => GENERATION_SETTINGS + .get(&GeneratorSetting::Overworld) + .unwrap(), + Dimension::Nether => GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap(), + Dimension::End => GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap(), + } +} + #[derive(Deserialize, Hash, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum GeneratorSetting { @@ -25,6 +35,8 @@ pub enum GeneratorSetting { } #[derive(Deserialize)] pub struct GenerationSettings { + pub aquifers_enabled: bool, + pub ore_veins_enabled: bool, pub legacy_random_source: bool, pub sea_level: i32, #[serde(rename = "noise")] diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 8a9f32418..841843cd6 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -23,6 +23,7 @@ use crate::{ format::{anvil::AnvilChunkFile, linear::LinearFile}, io::{ChunkIO, LoadedData, chunk_file_manager::ChunkFileManager}, }, + dimension::Dimension, generation::{Seed, WorldGenerator, get_world_gen}, world::SimpleWorld, }; @@ -68,7 +69,7 @@ pub struct LevelFolder { } impl Level { - pub fn from_root_folder(root_folder: PathBuf, seed: i64) -> Self { + pub fn from_root_folder(root_folder: PathBuf, seed: i64, dimension: Dimension) -> Self { // If we are using an already existing world we want to read the seed from the level.dat, If not we want to check if there is a seed in the config, if not lets create a random one let region_folder = root_folder.join("region"); if !region_folder.exists() { @@ -81,10 +82,8 @@ impl Level { // TODO: Load info correctly based on world format type - log::info!("Loading world with seed: {}", seed); - let seed = Seed(seed as u64); - let world_gen = get_world_gen(seed).into(); + let world_gen = get_world_gen(seed, dimension).into(); let chunk_saver: Arc> = match advanced_config().chunk.format { //ChunkFormat::Anvil => (Arc::new(AnvilChunkFormat), Arc::new(AnvilChunkFormat)), diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index d5b1a8e42..71a31f8a9 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,3 +1,4 @@ +use dimension::Dimension; use generation::settings::GenerationSettings; use pumpkin_util::math::vector2::Vector2; @@ -8,7 +9,7 @@ pub mod cylindrical_chunk_iterator; pub mod data; pub mod dimension; pub mod entity; -mod generation; +pub mod generation; pub mod inventory; pub mod item; pub mod level; @@ -64,7 +65,7 @@ pub fn bench_create_and_populate_biome( settings: &GenerationSettings, ) { let mut chunk = ProtoChunk::new(Vector2::new(0, 0), base_router, random_config, settings); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); } pub fn bench_create_and_populate_noise_with_surface( @@ -73,7 +74,7 @@ pub fn bench_create_and_populate_noise_with_surface( settings: &GenerationSettings, ) { let mut chunk = ProtoChunk::new(Vector2::new(0, 0), base_router, random_config, settings); - chunk.populate_biomes(); + chunk.populate_biomes(Dimension::Overworld); chunk.populate_noise(); chunk.build_surface(); } diff --git a/pumpkin/src/block/blocks/cactus.rs b/pumpkin/src/block/blocks/cactus.rs index 7af84d131..0350bc316 100644 --- a/pumpkin/src/block/blocks/cactus.rs +++ b/pumpkin/src/block/blocks/cactus.rs @@ -68,6 +68,7 @@ impl PumpkinBlock for CactusBlock { _pos: BlockPos, _block: Block, _state: BlockState, + _server: &Server, ) { entity.damage(1.0, DamageType::CACTUS).await; } diff --git a/pumpkin/src/block/blocks/fire/fire.rs b/pumpkin/src/block/blocks/fire/fire.rs index 22e491ce7..82526bac8 100644 --- a/pumpkin/src/block/blocks/fire/fire.rs +++ b/pumpkin/src/block/blocks/fire/fire.rs @@ -75,6 +75,7 @@ impl PumpkinBlock for FireBlock { _pos: BlockPos, _block: Block, _state: BlockState, + _server: &Server, ) { let base_entity = entity.get_entity(); if !base_entity.entity_type.fire_immune { diff --git a/pumpkin/src/block/blocks/lily_pad.rs b/pumpkin/src/block/blocks/lily_pad.rs index 9070a6886..5cbd4dc4a 100644 --- a/pumpkin/src/block/blocks/lily_pad.rs +++ b/pumpkin/src/block/blocks/lily_pad.rs @@ -6,7 +6,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; -use crate::{block::pumpkin_block::PumpkinBlock, entity::EntityBase, world::World}; +use crate::{block::pumpkin_block::PumpkinBlock, entity::EntityBase, server::Server, world::World}; #[pumpkin_block("minecraft:lily_pad")] pub struct LilyPadBlock; @@ -20,6 +20,7 @@ impl PumpkinBlock for LilyPadBlock { pos: BlockPos, _block: Block, _state: BlockState, + _server: &Server, ) { // Proberbly not the best solution, but works if entity diff --git a/pumpkin/src/block/blocks/nether_portal.rs b/pumpkin/src/block/blocks/nether_portal.rs index 6904943c1..a5455e3dc 100644 --- a/pumpkin/src/block/blocks/nether_portal.rs +++ b/pumpkin/src/block/blocks/nether_portal.rs @@ -2,12 +2,14 @@ use std::sync::Arc; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::EntityBase; +use crate::server::Server; use crate::world::World; use crate::world::portal::nether::NetherPortal; use async_trait::async_trait; use pumpkin_data::block_properties::{Axis, BlockProperties, NetherPortalLikeProperties}; use pumpkin_data::{Block, BlockState}; use pumpkin_macros::pumpkin_block; +use pumpkin_registry::DimensionType; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; @@ -44,11 +46,22 @@ impl PumpkinBlock for NetherPortalBlock { async fn on_entity_collision( &self, - _world: &Arc, - _entity: &dyn EntityBase, - _pos: BlockPos, + world: &Arc, + entity: &dyn EntityBase, + pos: BlockPos, _block: Block, _state: BlockState, + server: &Server, ) { + let world = if world.dimension_type == DimensionType::TheNether { + server + .get_world_from_dimension(DimensionType::Overworld) + .await + } else { + server + .get_world_from_dimension(DimensionType::TheNether) + .await + }; + entity.get_entity().try_use_portal(80, world, pos).await; } } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs index f080de4d0..fac312e0c 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs @@ -12,6 +12,7 @@ use pumpkin_world::{BlockStateId, block::BlockDirection}; use crate::{ block::pumpkin_block::{BlockMetadata, PumpkinBlock}, entity::EntityBase, + server::Server, world::World, }; @@ -48,6 +49,7 @@ impl PumpkinBlock for PressurePlateBlock { pos: BlockPos, block: Block, state: BlockState, + _server: &Server, ) { self.on_entity_collision_pp(world, pos, block, state).await; } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs index 52efc39ef..5924070fa 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs @@ -11,6 +11,7 @@ use pumpkin_world::{BlockStateId, block::BlockDirection}; use crate::{ block::pumpkin_block::{BlockMetadata, PumpkinBlock}, entity::EntityBase, + server::Server, world::World, }; @@ -45,6 +46,7 @@ impl PumpkinBlock for WeightedPressurePlateBlock { pos: BlockPos, block: Block, state: BlockState, + _server: &Server, ) { self.on_entity_collision_pp(world, pos, block, state).await; } diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 0d63b5315..e16928cc1 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -57,6 +57,7 @@ pub trait PumpkinBlock: Send + Sync { _pos: BlockPos, _block: Block, _state: BlockState, + _server: &Server, ) { } diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index 121c7fa88..2b6a322cb 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -63,11 +63,12 @@ impl BlockRegistry { entity: &dyn EntityBase, pos: BlockPos, state: BlockState, + server: &Server, ) { let pumpkin_block = self.get_pumpkin_block(&block); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block - .on_entity_collision(world, entity, pos, block, state) + .on_entity_collision(world, entity, pos, block, state, server) .await; } } diff --git a/pumpkin/src/entity/experience_orb.rs b/pumpkin/src/entity/experience_orb.rs index 87fb23620..792f5c56e 100644 --- a/pumpkin/src/entity/experience_orb.rs +++ b/pumpkin/src/entity/experience_orb.rs @@ -64,7 +64,7 @@ impl ExperienceOrbEntity { #[async_trait] impl EntityBase for ExperienceOrbEntity { - async fn tick(&self, caller: &dyn EntityBase, server: &Server) { + async fn tick(&self, caller: Arc, server: &Server) { self.entity.tick(caller, server).await; let age = self diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 068461735..1af67055e 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -63,7 +63,7 @@ impl ItemEntity { #[async_trait] impl EntityBase for ItemEntity { - async fn tick(&self, caller: &dyn EntityBase, server: &Server) { + async fn tick(&self, caller: Arc, server: &Server) { let entity = &self.entity; entity.tick(caller, server).await; { diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 53a73a495..a2818307c 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -262,7 +262,7 @@ impl LivingEntity { .await; } - async fn tick_move(&self, entity: &dyn EntityBase) { + async fn tick_move(&self, entity: &dyn EntityBase, server: &Server) { let velo = self.entity.velocity.load(); let pos = self.entity.pos.load(); self.entity @@ -272,7 +272,7 @@ impl LivingEntity { self.entity .velocity .store(velo.multiply(multiplier, 1.0, multiplier)); - Entity::check_block_collision(entity).await; + Entity::check_block_collision(entity, server).await; } async fn tick_effects(&self) { @@ -296,9 +296,9 @@ impl LivingEntity { #[async_trait] impl EntityBase for LivingEntity { - async fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.entity.tick(caller, server).await; - self.tick_move(caller).await; + async fn tick(&self, caller: Arc, server: &Server) { + self.entity.tick(caller.clone(), server).await; + self.tick_move(caller.as_ref(), server).await; self.tick_effects().await; if self.time_until_regen.load(Relaxed) > 0 { self.time_until_regen.fetch_sub(1, Relaxed); diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index b6b1ddf40..1d43d7ea2 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -24,7 +24,7 @@ pub struct MobEntity { #[async_trait] impl EntityBase for MobEntity { - async fn tick(&self, caller: &dyn EntityBase, server: &Server) { + async fn tick(&self, caller: Arc, server: &Server) { self.living_entity.tick(caller, server).await; let mut goals = self.goals.lock().await; for (goal, running) in goals.iter_mut() { diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index a56e9afb4..7f121c7f0 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,7 +1,4 @@ -use crate::{ - server::Server, - world::portal::{Portal, PortalManager}, -}; +use crate::{server::Server, world::portal::PortalManager}; use async_trait::async_trait; use bytes::BufMut; use core::f32; @@ -36,7 +33,7 @@ use std::sync::{ Arc, atomic::{ AtomicBool, AtomicI32, AtomicU32, - Ordering::{self, Relaxed, SeqCst}, + Ordering::{self, Relaxed}, }, }; use tokio::sync::{Mutex, RwLock}; @@ -67,7 +64,7 @@ pub trait EntityBase: Send + Sync { /// but in some scenarios (e.g., interactions or events), it might be a different entity. /// /// The `server` parameter provides access to the game server instance. - async fn tick(&self, caller: &dyn EntityBase, server: &Server) { + async fn tick(&self, caller: Arc, server: &Server) { if let Some(living) = self.get_living_entity() { living.tick(caller, server).await; } else { @@ -77,6 +74,18 @@ pub trait EntityBase: Send + Sync { async fn init_data_tracker(&self) {} + async fn teleport( + self: Arc, + position: Option>, + yaw: Option, + pitch: Option, + world: Arc, + ) { + self.get_entity() + .teleport(position, yaw, pitch, world) + .await; + } + /// Returns if damage was successful or not async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { if let Some(living) = self.get_living_entity() { @@ -143,7 +152,6 @@ pub struct Entity { pub portal_cooldown: AtomicU32, - // :c pub portal_manager: Mutex>>, } @@ -292,24 +300,6 @@ impl Entity { .await; } - pub async fn teleport(&self, position: Vector3, yaw: f32, pitch: f32) { - self.world - .read() - .await - .broadcast_packet_all(&CEntityPositionSync::new( - self.entity_id.into(), - position, - Vector3::new(0.0, 0.0, 0.0), - yaw, - pitch, - // TODO - self.on_ground.load(SeqCst), - )) - .await; - self.set_pos(position); - self.set_rotation(yaw, pitch); - } - fn default_portal_cooldown(&self) -> u32 { if self.entity_type == EntityType::PLAYER { 10 @@ -318,7 +308,7 @@ impl Entity { } } - async fn tick_portal(&self) { + async fn tick_portal(&self, caller: &Arc) { if self.portal_cooldown.load(Ordering::Relaxed) > 0 { self.portal_cooldown.fetch_sub(1, Ordering::Relaxed); } @@ -331,8 +321,11 @@ impl Entity { // reset cooldown self.portal_cooldown .store(self.default_portal_cooldown(), Ordering::Relaxed); - //let world = portal_manager.portal.get_world(self); - } else { + caller + .clone() + .teleport(None, None, None, portal_manager.portal_world.clone()) + .await; + } else if portal_manager.ticks_in_portal == 0 { should_remove = true; } } @@ -341,7 +334,7 @@ impl Entity { } } - pub async fn try_use_portal(&self, portal: Arc, pos: BlockPos) { + pub async fn try_use_portal(&self, portal_delay: u32, portal_world: Arc, pos: BlockPos) { if self.portal_cooldown.load(Ordering::Relaxed) > 0 { self.portal_cooldown .store(self.default_portal_cooldown(), Ordering::Relaxed); @@ -349,7 +342,11 @@ impl Entity { } let mut manager = self.portal_manager.lock().await; if manager.is_none() { - *manager = Some(Mutex::new(PortalManager::new(portal, pos))); + *manager = Some(Mutex::new(PortalManager::new( + portal_delay, + portal_world, + pos, + ))); } else if let Some(manager) = manager.as_ref() { let mut manager = manager.lock().await; manager.pos = pos; @@ -575,7 +572,7 @@ impl Entity { // } } - pub async fn check_block_collision(entity: &dyn EntityBase) { + pub async fn check_block_collision(entity: &dyn EntityBase, server: &Server) { let aabb = entity.get_entity().bounding_box.load(); let blockpos = BlockPos::new( (aabb.min.x + 0.001).floor() as i32, @@ -596,7 +593,7 @@ impl Entity { if let Ok((block, state)) = world.get_block_and_block_state(&pos).await { world .block_registry - .on_entity_collision(block, &world, entity, pos, state) + .on_entity_collision(block, &world, entity, pos, state, server) .await; } if let Ok(fluid) = world.get_fluid(&pos).await { @@ -609,6 +606,29 @@ impl Entity { } } } + + async fn teleport( + &self, + position: Option>, + yaw: Option, + pitch: Option, + _world: Arc, + ) { + dbg!("aa"); + // TODO: handle world change + self.world + .read() + .await + .broadcast_packet_all(&CEntityPositionSync::new( + self.entity_id.into(), + position.unwrap_or(Vector3::new(0.0, 0.0, 0.0)), + Vector3::new(0.0, 0.0, 0.0), + yaw.unwrap_or(0.0), + pitch.unwrap_or(0.0), + self.on_ground.load(Ordering::SeqCst), + )) + .await; + } } #[async_trait] @@ -617,8 +637,8 @@ impl EntityBase for Entity { false } - async fn tick(&self, caller: &dyn EntityBase, _: &Server) { - self.tick_portal().await; + async fn tick(&self, caller: Arc, _server: &Server) { + self.tick_portal(&caller).await; let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); if fire_ticks > 0 { if self.entity_type.fire_immune { @@ -639,6 +659,17 @@ impl EntityBase for Entity { // TODO: Tick } + async fn teleport( + self: Arc, + position: Option>, + yaw: Option, + pitch: Option, + world: Arc, + ) { + // TODO: handle world change + self.teleport(position, yaw, pitch, world).await; + } + fn get_entity(&self) -> &Entity { self } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 92fbc1d0c..143899f24 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -100,10 +100,11 @@ use pumpkin_util::{ permission::PermissionLvl, text::TextComponent, }; -use pumpkin_world::entity::entity_data_flags::{ - DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION, -}; use pumpkin_world::inventory::Inventory; +use pumpkin_world::{ + biome, + entity::entity_data_flags::{DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION}, +}; use pumpkin_world::{cylindrical_chunk_iterator::Cylindrical, item::ItemStack, level::SyncChunk}; use tokio::sync::RwLock; use tokio::{sync::Mutex, task::JoinHandle}; @@ -583,7 +584,7 @@ impl Player { .await; } - pub async fn tick(&self, server: &Server) { + pub async fn tick(self: &Arc, server: &Server) { self.current_screen_handler .lock() .await @@ -663,8 +664,8 @@ impl Player { self.last_attacked_ticks.fetch_add(1, Relaxed); - self.living_entity.tick(self, server).await; - self.hunger_manager.tick(self).await; + self.living_entity.tick(self.clone(), server).await; + self.hunger_manager.tick(self.as_ref()).await; // experience handling self.tick_experience().await; @@ -930,6 +931,7 @@ impl Player { *self.living_entity.entity.world.write().await = new_world.clone(); new_world.players.write().await.insert(uuid, self.clone()); self.unload_watched_chunks(¤t_world).await; + let last_pos = self.living_entity.last_pos.load(); let death_dimension = self.world().await.dimension_type.name(); let death_location = BlockPos(Vector3::new( @@ -941,21 +943,21 @@ impl Player { .send_packet_now(&CRespawn::new( (new_world.dimension_type as u8).into(), new_world.dimension_type.name(), - 0, // seed + biome::hash_seed(new_world.level.seed.0), // seed self.gamemode.load() as u8, self.gamemode.load() as i8, false, false, Some((death_dimension, death_location)), - 0.into(), - 0.into(), + VarInt(self.get_entity().portal_cooldown.load(Relaxed) as i32), + new_world.sea_level.into(), 1, )).await ; - self.send_abilities_update().await; self.send_permission_lvl_update().await; self.clone().request_teleport(position, yaw, pitch).await; self.living_entity.last_pos.store(position); + self.send_abilities_update().await; new_world.send_world_info(self, position, yaw, pitch).await; } @@ -1818,6 +1820,16 @@ impl EntityBase for Player { result } + async fn teleport( + self: Arc, + position: Option>, + yaw: Option, + pitch: Option, + world: Arc, + ) { + self.teleport_world(world, position, yaw, pitch).await; + } + fn get_entity(&self) -> &Entity { &self.living_entity.entity } diff --git a/pumpkin/src/entity/tnt.rs b/pumpkin/src/entity/tnt.rs index e78058476..876f13397 100644 --- a/pumpkin/src/entity/tnt.rs +++ b/pumpkin/src/entity/tnt.rs @@ -8,7 +8,10 @@ use pumpkin_protocol::{ use pumpkin_util::math::vector3::Vector3; use std::{ f64::consts::TAU, - sync::atomic::{AtomicU32, Ordering::Relaxed}, + sync::{ + Arc, + atomic::{AtomicU32, Ordering::Relaxed}, + }, }; use super::{Entity, EntityBase, living::LivingEntity}; @@ -31,7 +34,7 @@ impl TNTEntity { #[async_trait] impl EntityBase for TNTEntity { - async fn tick(&self, _caller: &dyn EntityBase, server: &Server) { + async fn tick(&self, _caller: Arc, server: &Server) { let fuse = self.fuse.fetch_sub(1, Relaxed); if fuse == 0 { self.entity.remove().await; diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index d825e7854..91d837f46 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -139,24 +139,28 @@ impl Server { } let level_info = Arc::new(level_info.unwrap_or_default()); // TODO: Improve error handling + let seed = level_info.world_gen_settings.seed; + log::info!("Loading Overworld: {seed}"); let overworld = World::load( - Dimension::Overworld.into_level(world_path.clone(), level_info.world_gen_settings.seed), + Dimension::Overworld.into_level(world_path.clone(), seed), level_info.clone(), DimensionType::Overworld, block_registry.clone(), ); + log::info!("Loading Nether: {seed}"); let nether = World::load( - Dimension::Nether.into_level(world_path.clone(), level_info.world_gen_settings.seed), + Dimension::Nether.into_level(world_path.clone(), seed), level_info.clone(), DimensionType::TheNether, block_registry.clone(), ); - let end = World::load( - Dimension::End.into_level(world_path.clone(), level_info.world_gen_settings.seed), - level_info.clone(), - DimensionType::TheEnd, - block_registry.clone(), - ); + // log::info!("Loading End: {}", seed); + // let end = World::load( + // Dimension::End.into_level(world_path.clone(), seed), + // level_info.clone(), + // DimensionType::TheEnd, + // block_registry.clone(), + // ); // if we fail to lock, lets crash ???. maybe not the best solution when we have a large server with many worlds and one is locked. // So TODO @@ -167,7 +171,7 @@ impl Server { Self { cached_registry: Registry::get_synced(), container_id: 0.into(), - worlds: RwLock::new(vec![Arc::new(overworld), Arc::new(nether), Arc::new(end)]), + worlds: RwLock::new(vec![Arc::new(overworld), Arc::new(nether)]), dimensions: vec![ DimensionType::Overworld, DimensionType::OverworldCaves, @@ -226,7 +230,7 @@ impl Server { let world = match dimension { DimensionType::Overworld => world_guard.first(), DimensionType::OverworldCaves => todo!(), - DimensionType::TheEnd => world_guard.get(2), + DimensionType::TheEnd => todo!(), DimensionType::TheNether => world_guard.get(1), }; world.cloned().unwrap() diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 1c2159eb0..dbe56d978 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -155,9 +155,15 @@ impl World { block_registry: Arc, ) -> Self { // TODO - let generation_settings = GENERATION_SETTINGS - .get(&GeneratorSetting::Overworld) - .unwrap(); + let generation_settings = match dimension_type { + DimensionType::Overworld => GENERATION_SETTINGS + .get(&GeneratorSetting::Overworld) + .unwrap(), + DimensionType::OverworldCaves => todo!(), + DimensionType::TheEnd => GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap(), + DimensionType::TheNether => GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap(), + }; + Self { level: Arc::new(level), level_info, @@ -393,8 +399,10 @@ impl World { self.tick_scheduled_block_ticks().await; + let players_to_tick: Vec<_> = self.players.read().await.values().cloned().collect(); + // player ticks - for player in self.players.read().await.values() { + for player in players_to_tick { player.tick(server).await; } @@ -402,7 +410,7 @@ impl World { // Entity ticks for entity in entities_to_tick { - entity.tick(entity.as_ref(), server).await; + entity.tick(entity.clone(), server).await; for player in self.players.read().await.values() { if player .living_entity @@ -483,7 +491,19 @@ impl World { /// Gets the y position of the first non air block from the top down pub async fn get_top_block(&self, position: Vector2) -> i32 { - for y in (-64..=319).rev() { + // TODO: this is bad + let generation_settings = match self.dimension_type { + DimensionType::Overworld => GENERATION_SETTINGS + .get(&GeneratorSetting::Overworld) + .unwrap(), + DimensionType::OverworldCaves => todo!(), + DimensionType::TheEnd => GENERATION_SETTINGS.get(&GeneratorSetting::End).unwrap(), + DimensionType::TheNether => GENERATION_SETTINGS.get(&GeneratorSetting::Nether).unwrap(), + }; + for y in (i32::from(generation_settings.shape.min_y) + ..=i32::from(generation_settings.shape.height)) + .rev() + { let pos = BlockPos(Vector3::new(position.x, y, position.z)); let block = self.get_block_state(&pos).await; if let Ok(block) = block { @@ -880,7 +900,7 @@ impl World { false, false, Some((death_dimension, death_location)), - 0.into(), + VarInt(player.get_entity().portal_cooldown.load(Ordering::Relaxed) as i32), self.sea_level.into(), data_kept, )) diff --git a/pumpkin/src/world/portal/mod.rs b/pumpkin/src/world/portal/mod.rs index 13e98538c..2a6b6af12 100644 --- a/pumpkin/src/world/portal/mod.rs +++ b/pumpkin/src/world/portal/mod.rs @@ -2,30 +2,23 @@ use std::sync::Arc; use pumpkin_util::math::position::BlockPos; -use crate::entity::Entity; - use super::World; pub mod nether; -pub trait Portal: Send + Sync { - fn get_delay(&self) -> u32 { - 0 - } - fn get_world(&self, entity: &Entity) -> Arc; -} - pub struct PortalManager { - pub portal: Arc, + pub portal_delay: u32, + pub portal_world: Arc, pub pos: BlockPos, - ticks_in_portal: u32, + pub ticks_in_portal: u32, pub in_portal: bool, } impl PortalManager { - pub fn new(portal: Arc, pos: BlockPos) -> Self { + pub fn new(portal_delay: u32, portal_world: Arc, pos: BlockPos) -> Self { Self { - portal, + portal_delay, + portal_world, pos, ticks_in_portal: 0, in_portal: true, @@ -36,7 +29,7 @@ impl PortalManager { if self.in_portal { self.in_portal = false; self.ticks_in_portal += 1; - self.ticks_in_portal >= self.portal.get_delay() + self.ticks_in_portal >= self.portal_delay } else { self.ticks_in_portal -= 4; false