Allow to enter other dimensions

a bit broken though
This commit is contained in:
Alexander Medvedev 2025-05-18 14:59:01 +02:00
parent ee32c60427
commit 705ecfe221
37 changed files with 353 additions and 201 deletions

58
Cargo.lock generated
View File

@ -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",
]

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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"

View File

@ -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<Level>, positions: Vec<Vector2<i32>>) {
@ -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
})
},

View File

@ -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<i32>, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome;
fn biome(
at: &Vector3<i32>,
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<i32>, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome {
fn biome(
global_biome_pos: &Vector3<i32>,
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);

View File

@ -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,

View File

@ -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 {

View File

@ -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 {

View File

@ -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(),
}
}

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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 {

View File

@ -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<i32>) -> 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(),

View File

@ -29,9 +29,11 @@ pub use seed::Seed;
use generator::GeneratorInit;
pub fn get_world_gen(seed: Seed) -> Box<dyn WorldGenerator> {
use crate::dimension::Dimension;
pub fn get_world_gen(seed: Seed, dimension: Dimension) -> Box<dyn WorldGenerator> {
// TODO decide which WorldGenerator to pick based on config.
Box::new(VanillaGenerator::new(seed))
Box::new(VanillaGenerator::new(seed, dimension))
}
#[derive(Getters)]

View File

@ -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>) -> i32 {
pub fn top_block_height_exclusive(&self, pos: &Vector2<i32>) -> 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>) -> i32 {
pub fn ocean_floor_height_exclusive(&self, pos: &Vector2<i32>) -> 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>) -> i32 {
pub fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2<i32>) -> 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>) -> i32 {
pub fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2<i32>) -> 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();

View File

@ -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<HashMap<GeneratorSetting, GenerationSet
.expect("Could not parse chunk_gen_settings.json registry.")
});
pub fn gen_settings_from_dimension(dimension: &Dimension) -> &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")]

View File

@ -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<dyn ChunkIO<Data = SyncChunk>> = match advanced_config().chunk.format {
//ChunkFormat::Anvil => (Arc::new(AnvilChunkFormat), Arc::new(AnvilChunkFormat)),

View File

@ -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();
}

View File

@ -68,6 +68,7 @@ impl PumpkinBlock for CactusBlock {
_pos: BlockPos,
_block: Block,
_state: BlockState,
_server: &Server,
) {
entity.damage(1.0, DamageType::CACTUS).await;
}

View File

@ -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 {

View File

@ -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

View File

@ -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<World>,
_entity: &dyn EntityBase,
_pos: BlockPos,
world: &Arc<World>,
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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -57,6 +57,7 @@ pub trait PumpkinBlock: Send + Sync {
_pos: BlockPos,
_block: Block,
_state: BlockState,
_server: &Server,
) {
}

View File

@ -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;
}
}

View File

@ -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<dyn EntityBase>, server: &Server) {
self.entity.tick(caller, server).await;
let age = self

View File

@ -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<dyn EntityBase>, server: &Server) {
let entity = &self.entity;
entity.tick(caller, server).await;
{

View File

@ -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<dyn EntityBase>, 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);

View File

@ -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<dyn EntityBase>, server: &Server) {
self.living_entity.tick(caller, server).await;
let mut goals = self.goals.lock().await;
for (goal, running) in goals.iter_mut() {

View File

@ -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<dyn EntityBase>, 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<Self>,
position: Option<Vector3<f64>>,
yaw: Option<f32>,
pitch: Option<f32>,
world: Arc<World>,
) {
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<Option<Mutex<PortalManager>>>,
}
@ -292,24 +300,6 @@ impl Entity {
.await;
}
pub async fn teleport(&self, position: Vector3<f64>, 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<dyn EntityBase>) {
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<dyn Portal>, pos: BlockPos) {
pub async fn try_use_portal(&self, portal_delay: u32, portal_world: Arc<World>, 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<Vector3<f64>>,
yaw: Option<f32>,
pitch: Option<f32>,
_world: Arc<World>,
) {
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<dyn EntityBase>, _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<Self>,
position: Option<Vector3<f64>>,
yaw: Option<f32>,
pitch: Option<f32>,
world: Arc<World>,
) {
// TODO: handle world change
self.teleport(position, yaw, pitch, world).await;
}
fn get_entity(&self) -> &Entity {
self
}

View File

@ -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<Self>, 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(&current_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<Self>,
position: Option<Vector3<f64>>,
yaw: Option<f32>,
pitch: Option<f32>,
world: Arc<World>,
) {
self.teleport_world(world, position, yaw, pitch).await;
}
fn get_entity(&self) -> &Entity {
&self.living_entity.entity
}

View File

@ -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<dyn EntityBase>, server: &Server) {
let fuse = self.fuse.fetch_sub(1, Relaxed);
if fuse == 0 {
self.entity.remove().await;

View File

@ -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()

View File

@ -155,9 +155,15 @@ impl World {
block_registry: Arc<BlockRegistry>,
) -> 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>) -> 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,
))

View File

@ -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<World>;
}
pub struct PortalManager {
pub portal: Arc<dyn Portal>,
pub portal_delay: u32,
pub portal_world: Arc<World>,
pub pos: BlockPos,
ticks_in_portal: u32,
pub ticks_in_portal: u32,
pub in_portal: bool,
}
impl PortalManager {
pub fn new(portal: Arc<dyn Portal>, pos: BlockPos) -> Self {
pub fn new(portal_delay: u32, portal_world: Arc<World>, 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