Allow to enter other dimensions
a bit broken though
This commit is contained in:
parent
ee32c60427
commit
705ecfe221
58
Cargo.lock
generated
58
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
})
|
||||
},
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -68,6 +68,7 @@ impl PumpkinBlock for CactusBlock {
|
||||
_pos: BlockPos,
|
||||
_block: Block,
|
||||
_state: BlockState,
|
||||
_server: &Server,
|
||||
) {
|
||||
entity.damage(1.0, DamageType::CACTUS).await;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -57,6 +57,7 @@ pub trait PumpkinBlock: Send + Sync {
|
||||
_pos: BlockPos,
|
||||
_block: Block,
|
||||
_state: BlockState,
|
||||
_server: &Server,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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(¤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<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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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,
|
||||
))
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user