fix: tests

This commit is contained in:
Alexander Medvedev 2026-04-29 23:18:24 +02:00
parent 427df56935
commit 1d671504d1
12 changed files with 369 additions and 420 deletions

View File

@ -231,14 +231,15 @@ pub trait RandomDeriverImpl {
/// # Returns
/// A 64-bit hash value.
#[must_use]
pub fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 {
let l = i64::from(x.wrapping_mul(3129871))
^ (i64::from(z).wrapping_mul(116129781i64))
^ i64::from(y);
pub const fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 {
let l =
((x.wrapping_mul(3129871)) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64);
let l = l
.wrapping_mul(l)
.wrapping_mul(42317861i64)
.wrapping_add(l.wrapping_mul(11i64));
l >> 16
}

View File

@ -240,7 +240,7 @@ impl XoroshiroSplitter {
}
#[must_use]
pub fn split_pos(&self, x: i32, y: i32, z: i32) -> Xoroshiro {
pub const fn split_pos(&self, x: i32, y: i32, z: i32) -> Xoroshiro {
let l = hash_block_pos(x, y, z) as u64;
let m = l ^ self.lo;

View File

@ -51,19 +51,15 @@ pub fn hash_seed(seed: u64) -> i64 {
#[cfg(test)]
mod test {
use pumpkin_data::{
chunk::Biome, chunk_gen_settings::GenerationSettings, dimension::Dimension,
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
};
use pumpkin_data::{chunk::Biome, dimension::Dimension};
use pumpkin_util::read_data_from_file;
use serde::Deserialize;
use crate::{
GlobalRandomConfig, ProtoChunk,
ProtoChunk,
chunk::palette::BIOME_NETWORK_MAX_BITS,
generation::noise::router::{
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
proto_noise_router::ProtoNoiseRouters,
generation::noise::router::multi_noise_sampler::{
MultiNoiseSampler, MultiNoiseSamplerBuilderOptions,
},
};
@ -71,20 +67,19 @@ mod test {
#[test]
fn biome_desert() {
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use pumpkin_util::world_seed::Seed;
let seed = 13579;
let random_config = GlobalRandomConfig::new(seed, false);
let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let generator = VanillaGenerator::new(Seed(seed as u64), Dimension::OVERWORLD);
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(1, 1, 1);
let mut sampler =
MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config);
MultiNoiseSampler::generate(&generator.base_router.multi_noise, &multi_noise_config);
let biome = MultiNoiseBiomeSupplier::OVERWORLD.biome(-24, 1, 8, &mut sampler);
assert_eq!(biome, &Biome::DESERT);
}
#[test]
fn wide_area_surface() {
use crate::biome::hash_seed;
use crate::generation::noise::router::multi_noise_sampler::{
MultiNoiseSampler, MultiNoiseSamplerBuilderOptions,
};
@ -100,28 +95,15 @@ mod test {
read_data_from_file!("../../assets/biome_no_blend_no_beard_0.json");
let seed = 0;
let random_config = GlobalRandomConfig::new(seed, false);
let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let surface_settings = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
//let _terrain_cache = TerrainCache::from_random(&random_config);
let default_block = surface_settings.default_block;
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use pumpkin_util::world_seed::Seed;
let generator = VanillaGenerator::new(Seed(seed as u64), Dimension::OVERWORLD);
for data in expected_data {
let chunk_x = data.x;
let chunk_z = data.z;
// Calculate biome mixer seed
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(
chunk_x,
chunk_z,
&Dimension::OVERWORLD,
default_block,
biome_mixer_seed,
);
let mut chunk = ProtoChunk::new(chunk_x, chunk_z, &generator);
// Create MultiNoiseSampler for populate_biomes
@ -134,10 +116,12 @@ mod test {
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config);
let mut multi_noise_sampler = MultiNoiseSampler::generate(
&generator.base_router.multi_noise,
&multi_noise_config,
);
chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler);
chunk.populate_biomes(&generator, &mut multi_noise_sampler);
for (biome_x, biome_y, biome_z, biome_id) in data.data {
let calculated_biome = chunk.get_biome(biome_x, biome_y, biome_z);

View File

@ -32,31 +32,25 @@ impl NoiseValuePoint {
#[cfg(test)]
mod test {
use pumpkin_data::{
chunk::Biome, chunk_gen_settings::GenerationSettings, dimension::Dimension,
noise_router::OVERWORLD_BASE_NOISE_ROUTER,
};
use pumpkin_data::{chunk::Biome, dimension::Dimension};
use pumpkin_util::read_data_from_file;
use crate::{
GlobalRandomConfig, ProtoChunk,
ProtoChunk,
biome::{BiomeSupplier, MultiNoiseBiomeSupplier},
generation::{
noise::router::{
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
proto_noise_router::ProtoNoiseRouters,
},
proto_chunk::TerrainCache,
generation::noise::router::multi_noise_sampler::{
MultiNoiseSampler, MultiNoiseSamplerBuilderOptions,
},
};
#[test]
fn sample_value() {
use crate::biome::hash_seed;
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use crate::generation::noise::router::multi_noise_sampler::{
MultiNoiseSampler, MultiNoiseSamplerBuilderOptions,
};
use crate::generation::{biome_coords, positions::chunk_pos};
use pumpkin_util::world_seed::Seed;
type PosToPoint = (i32, i32, i32, i64, i64, i64, i64, i64, i64);
let expected_data: Vec<PosToPoint> =
read_data_from_file!("../../assets/multi_noise_sample_no_blend_no_beard_0_0_0.json");
@ -65,23 +59,9 @@ mod test {
let chunk_x = 0;
let chunk_z = 0;
let random_config = GlobalRandomConfig::new(seed, false);
let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let generator = VanillaGenerator::new(Seed(seed as u64), Dimension::OVERWORLD);
let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD);
let _terrain_cache = TerrainCache::from_random(&random_config);
// Calculate biome mixer seed
let biome_mixer_seed = hash_seed(random_config.seed);
let _chunk = ProtoChunk::new(
chunk_x,
chunk_z,
&Dimension::OVERWORLD,
surface_config.default_block,
biome_mixer_seed,
);
let _chunk = ProtoChunk::new(chunk_x, chunk_z, &generator);
let start_x = chunk_pos::start_block_x(chunk_x);
let start_z = chunk_pos::start_block_z(chunk_z);
@ -92,7 +72,7 @@ mod test {
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config);
MultiNoiseSampler::generate(&generator.base_router.multi_noise, &multi_noise_config);
for (x, y, z, tem, hum, con, ero, dep, wei) in expected_data {
let point = multi_noise_sampler.sample(x, y, z);
@ -107,16 +87,17 @@ mod test {
#[test]
fn sample_multinoise_biome() {
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use pumpkin_util::world_seed::Seed;
let expected_data: Vec<(i32, i32, i32, u8)> =
read_data_from_file!("../../assets/multi_noise_biome_source_test.json");
let seed = 0;
let random_config = GlobalRandomConfig::new(seed, false);
let noise_router =
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config);
let generator = VanillaGenerator::new(Seed(seed as u64), Dimension::OVERWORLD);
let mut sampler = MultiNoiseSampler::generate(
&noise_router.multi_noise,
&generator.base_router.multi_noise,
&MultiNoiseSamplerBuilderOptions::new(0, 0, 4),
);

View File

@ -1,4 +1,3 @@
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension;
use crate::ProtoChunk;
@ -9,15 +8,14 @@ use pumpkin_config::lighting::LightingEngineConfig;
use super::{Cache, Chunk, StagedChunkEnum};
pub fn generate_single_chunk(
dimension: &Dimension,
biome_mixer_seed: i64,
_dimension: &Dimension,
_biome_mixer_seed: i64,
generator: &VanillaGenerator,
block_registry: &dyn BlockRegistryExt,
chunk_x: i32,
chunk_z: i32,
target_stage: StagedChunkEnum,
) -> Chunk {
let settings = GenerationSettings::from_dimension(dimension);
let radius = target_stage.get_direct_radius();
let mut cache = Cache::new(chunk_x - radius, chunk_z - radius, radius * 2 + 1);
@ -27,13 +25,7 @@ pub fn generate_single_chunk(
let new_x = chunk_x + dx;
let new_z = chunk_z + dz;
let proto_chunk = Box::new(ProtoChunk::new(
new_x,
new_z,
dimension,
generator.default_block,
biome_mixer_seed,
));
let proto_chunk = Box::new(ProtoChunk::new(new_x, new_z, generator));
cache.chunks.push(Chunk::Proto(proto_chunk));
}
@ -57,14 +49,9 @@ pub fn generate_single_chunk(
cache.advance(
stage,
&LightingEngineConfig::Default,
generator,
block_registry,
settings,
&generator.random_config,
&generator.terrain_cache,
&generator.base_router,
*dimension,
&generator.global_structure_cache,
&LightingEngineConfig::Default,
);
}

View File

@ -1,16 +1,14 @@
use super::chunk_state::{Chunk, StagedChunkEnum};
use crate::block::RawBlockState;
use crate::chunk::ChunkHeightmapType;
use crate::generation::generator;
use crate::generation::height_limit::HeightLimitView;
use crate::generation::proto_chunk::{GenerationCache, TerrainCache};
use crate::generation::structure::placement::GlobalStructureCache;
use crate::generation::proto_chunk::GenerationCache;
use crate::world::{BlockAccessor, BlockRegistryExt};
use crate::{BlockStateId, GlobalRandomConfig, ProtoChunk, ProtoNoiseRouters};
use crate::{BlockStateId, ProtoChunk};
use pumpkin_config::lighting::LightingEngineConfig;
use pumpkin_data::biome::Biome;
use pumpkin_data::block_properties::is_air;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::{Fluid, FluidState};
use pumpkin_data::{Block, BlockState};
use pumpkin_nbt::compound::NbtCompound;
@ -340,54 +338,41 @@ impl Cache {
chunks: Vec::with_capacity((size * size) as usize),
}
}
#[expect(clippy::too_many_arguments)]
pub fn advance(
&mut self,
stage: StagedChunkEnum,
lighting_config: &LightingEngineConfig,
generator: &generator::VanillaGenerator,
block_registry: &dyn BlockRegistryExt,
settings: &GenerationSettings,
random_config: &GlobalRandomConfig,
terrain_cache: &TerrainCache,
noise_router: &ProtoNoiseRouters,
dimension: Dimension,
global_structure_cache: &GlobalStructureCache, // <--- NEW PARAMETER HERE
lighting_config: &LightingEngineConfig,
) {
let mid = ((self.size * self.size) >> 1) as usize;
match stage {
StagedChunkEnum::Empty => panic!("empty stage"),
StagedChunkEnum::StructureStart => {
self.chunks[mid].get_proto_chunk_mut().set_structure_starts(
random_config,
settings,
global_structure_cache,
);
self.chunks[mid]
.get_proto_chunk_mut()
.set_structure_starts(generator);
}
StagedChunkEnum::StructureReferences => {
self.chunks[mid]
.get_proto_chunk_mut()
.set_structure_references(generator);
}
StagedChunkEnum::StructureReferences => self.chunks[mid]
.get_proto_chunk_mut()
.set_structure_references(
random_config,
settings,
&dimension,
noise_router,
global_structure_cache,
), // <--- PASSED HERE
StagedChunkEnum::Biomes => self.chunks[mid]
.get_proto_chunk_mut()
.step_to_biomes(dimension, noise_router),
StagedChunkEnum::Noise => self.chunks[mid].get_proto_chunk_mut().step_to_noise(
settings,
random_config,
noise_router,
),
StagedChunkEnum::Surface => self.chunks[mid].get_proto_chunk_mut().step_to_surface(
settings,
random_config,
terrain_cache,
noise_router,
),
.step_to_biomes(generator),
StagedChunkEnum::Noise => self.chunks[mid]
.get_proto_chunk_mut()
.step_to_noise(generator),
StagedChunkEnum::Surface => self.chunks[mid]
.get_proto_chunk_mut()
.step_to_surface(generator),
StagedChunkEnum::Features => {
ProtoChunk::generate_features_and_structure(self, block_registry, random_config);
ProtoChunk::generate_features_and_structure(
self,
block_registry,
&generator.random_config,
);
}
StagedChunkEnum::Lighting => {
let mut engine = crate::lighting::LightEngine::new();
@ -405,7 +390,7 @@ impl Cache {
let chunk = self.chunks[mid].get_proto_chunk_mut();
debug_assert_eq!(chunk.stage, StagedChunkEnum::Lighting);
chunk.stage = StagedChunkEnum::Full;
self.chunks[mid].upgrade_to_level_chunk(&dimension, lighting_config);
self.chunks[mid].upgrade_to_level_chunk(&generator.dimension, lighting_config);
}
StagedChunkEnum::None => {}
}

View File

@ -59,10 +59,7 @@ pub async fn io_read_work(
level: Arc<Level>,
lock: IOLock,
) {
use crate::biome::hash_seed;
debug!("io read thread start");
let biome_mixer_seed = hash_seed(level.world_gen.random_config.seed);
let dimension = &level.world_gen.dimension;
// Cleaner loop and async recv
while let Ok(batch) = recv.recv().await {
@ -108,12 +105,7 @@ pub async fn io_read_work(
);
// Create ProtoChunk using the async method
let mut proto = ProtoChunk::from_chunk_data(
&chunk,
dimension,
level.world_gen.default_block,
biome_mixer_seed,
);
let mut proto = ProtoChunk::from_chunk_data(&chunk, &level.world_gen);
// Clear all lighting data
let section_count = proto.light.sky_light.len();
@ -144,13 +136,9 @@ pub async fn io_read_work(
}
} else {
// Standard ProtoChunk handling for non-full chunks
let val =
RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::from_chunk_data(
&chunk,
dimension,
level.world_gen.default_block,
biome_mixer_seed,
))));
let val = RecvChunk::IO(Chunk::Proto(Box::new(
ProtoChunk::from_chunk_data(&chunk, &level.world_gen),
)));
if send.send((pos, val)).is_err() {
break;
}
@ -163,9 +151,7 @@ pub async fn io_read_work(
RecvChunk::IO(Chunk::Proto(Box::new(ProtoChunk::new(
pos.x,
pos.y,
dimension,
level.world_gen.default_block,
biome_mixer_seed,
&level.world_gen,
)))),
))
.is_err()
@ -241,20 +227,15 @@ pub fn run_generation(
mut cache: Cache,
stage: StagedChunkEnum,
level: &Level,
settings: &GenerationSettings,
_settings: &GenerationSettings,
) -> RecvChunk {
// Run generation with panic catching
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
cache.advance(
stage,
&level.lighting_config,
&level.world_gen,
level.block_registry.as_ref(),
settings,
&level.world_gen.random_config,
&level.world_gen.terrain_cache,
&level.world_gen.base_router,
level.world_gen.dimension,
&level.world_gen.global_structure_cache,
&level.lighting_config,
);
cache // Return cache on success
}));

View File

@ -15,23 +15,29 @@ pub trait GeneratorInit {
fn new(seed: Seed, dimension: Dimension) -> Self;
}
use pumpkin_data::structures::{StructurePlacementCalculator, StructureSet};
use rustc_hash::FxHashMap;
pub struct VanillaGenerator {
pub random_config: GlobalRandomConfig,
pub base_router: ProtoNoiseRouters,
pub dimension: Dimension,
pub settings: &'static GenerationSettings,
pub biome_mixer_seed: i64,
pub terrain_cache: TerrainCache,
pub default_block: &'static BlockState,
pub global_structure_cache: crate::generation::structure::placement::GlobalStructureCache,
pub structure_calculator: StructurePlacementCalculator,
pub structure_allowed_biomes: FxHashMap<usize, Vec<u16>>,
}
impl GeneratorInit for VanillaGenerator {
fn new(seed: Seed, dimension: Dimension) -> Self {
let generation_settings = GenerationSettings::from_dimension(&dimension);
let random_config =
GlobalRandomConfig::new(seed.0, generation_settings.legacy_random_source);
let settings = GenerationSettings::from_dimension(&dimension);
let random_config = GlobalRandomConfig::new(seed.0, settings.legacy_random_source);
// TODO: The generation settings contains (part of?) the noise routers too; do we keep the separate or
// use only the generation settings?
@ -46,16 +52,30 @@ impl GeneratorInit for VanillaGenerator {
};
let terrain_cache = TerrainCache::from_random(&random_config);
let default_block = generation_settings.default_block;
let default_block = settings.default_block;
let base_router = ProtoNoiseRouters::generate(&base, &random_config);
let biome_mixer_seed = crate::biome::hash_seed(seed.0);
let mut structure_allowed_biomes = FxHashMap::default();
for (i, set) in StructureSet::ALL.iter().enumerate() {
structure_allowed_biomes.insert(
i,
crate::generation::proto_chunk::ProtoChunk::get_allowed_biomes(set),
);
}
Self {
random_config,
base_router,
dimension,
settings,
biome_mixer_seed,
terrain_cache,
default_block,
global_structure_cache:
crate::generation::structure::placement::GlobalStructureCache::new(),
structure_calculator: StructurePlacementCalculator::new(seed.0 as i64),
structure_allowed_biomes,
}
}
}

View File

@ -451,149 +451,159 @@ impl WorldAquiferSampler {
density: f64,
) -> Option<&'static BlockState> {
if density > 0f64 {
None
} else {
let sample_x = pos.x;
let sample_y = pos.y;
let sample_z = pos.z;
return None;
}
let fluid_level = self
.fluid_level_sampler
.get_fluid_level(sample_x, sample_y, sample_z);
if fluid_level.get_block(sample_y) == &LAVA_BLOCK {
Some(LAVA_BLOCK.default_state)
} else {
let scaled_x = local_xz!(sample_x - 5);
let scaled_y = local_y!(sample_y + 1);
let scaled_z = local_xz!(sample_z - 5);
let sample_x = pos.x;
let sample_y = pos.y;
let sample_z = pos.z;
let mut random_positions_and_hypot = [(0, i32::MAX); 3];
for packed_random in self.random_positions_for_pos(scaled_x, scaled_y, scaled_z) {
let unpacked_x = block_pos::unpack_x(packed_random);
let unpacked_y = block_pos::unpack_y(packed_random);
let unpacked_z = block_pos::unpack_z(packed_random);
let fluid_level = self
.fluid_level_sampler
.get_fluid_level(sample_x, sample_y, sample_z);
if fluid_level.get_block(sample_y) == &LAVA_BLOCK {
return Some(LAVA_BLOCK.default_state);
}
let local_x = unpacked_x - sample_x;
let local_y = unpacked_y - sample_y;
let local_z = unpacked_z - sample_z;
let scaled_x = local_xz!(sample_x - 5);
let scaled_y = local_y!(sample_y + 1);
let scaled_z = local_xz!(sample_z - 5);
let hypot_squared = local_x * local_x + local_y * local_y + local_z * local_z;
// Inline random_positions_for_pos: read directly from packed_positions with a
// single bounds check instead of stack-allocating and copying a [i64; 12].
let sy = self.size_y;
let syz = sy * self.size_z;
let i00 = self.packed_position_index(scaled_x, scaled_y - 1, scaled_z);
let i01 = i00 + sy;
let i10 = i00 + syz;
let i11 = i10 + sy;
if random_positions_and_hypot[2].1 > hypot_squared {
random_positions_and_hypot[2] = (packed_random, hypot_squared);
}
let p = &self.packed_positions;
// i11 + 2 is the largest index we ever access; all others are strictly smaller.
assert!(i11 + 2 < p.len(), "Index out of bounds");
if random_positions_and_hypot[1].1 > hypot_squared {
random_positions_and_hypot[2] = random_positions_and_hypot[1];
random_positions_and_hypot[1] = (packed_random, hypot_squared);
}
let mut nearest = [(0i64, i32::MAX); 3];
if random_positions_and_hypot[0].1 > hypot_squared {
random_positions_and_hypot[1] = random_positions_and_hypot[0];
random_positions_and_hypot[0] = (packed_random, hypot_squared);
}
// SAFETY: every index passed to this macro is <= i11 + 2, checked by the assert above.
macro_rules! process {
($idx:expr) => {{
let packed = unsafe { *p.get_unchecked($idx) };
let dx = block_pos::unpack_x(packed) - sample_x;
let dy = block_pos::unpack_y(packed) - sample_y;
let dz = block_pos::unpack_z(packed) - sample_z;
let h = dx * dx + dy * dy + dz * dz;
if nearest[2].1 > h {
nearest[2] = (packed, h);
}
let fluid_level2 = self
.get_water_level(
random_positions_and_hypot[0].0,
router,
height_estimator,
sample_options,
)
.clone();
let d = Self::max_distance(
random_positions_and_hypot[0].1,
random_positions_and_hypot[1].1,
);
let block_state = fluid_level2.get_block(sample_y);
if nearest[1].1 > h {
nearest[2] = nearest[1];
nearest[1] = (packed, h);
}
if nearest[0].1 > h {
nearest[1] = nearest[0];
nearest[0] = (packed, h);
}
}};
}
if d <= 0f64 {
// TODO: Handle fluid tick
// Same insertion order as the original array literal — sort behaviour is preserved.
process!(i11 + 2);
process!(i10 + 2);
process!(i01 + 2);
process!(i00 + 2);
process!(i11 + 1);
process!(i10 + 1);
process!(i01 + 1);
process!(i00 + 1);
process!(i11);
process!(i10);
process!(i01);
process!(i00);
Some(block_state.default_state)
} else if block_state == &WATER_BLOCK
&& self
.fluid_level_sampler
.get_fluid_level(sample_x, sample_y - 1, sample_z)
.get_block(sample_y - 1)
== &LAVA_BLOCK
{
Some(block_state.default_state)
} else {
let mut barrier_sample = None;
let fluid_level3 = self
.get_water_level(
random_positions_and_hypot[1].0,
router,
height_estimator,
sample_options,
)
.clone();
let e = d * Self::calculate_density(
// Precompute all three pairwise distances before fetching any water levels so we
// can skip the third get_water_level call entirely when neither f nor g_dist > 0.
let d = Self::max_distance(nearest[0].1, nearest[1].1);
let f = Self::max_distance(nearest[0].1, nearest[2].1);
let g_dist = Self::max_distance(nearest[1].1, nearest[2].1);
let fluid_level2 = self
.get_water_level(nearest[0].0, router, height_estimator, sample_options)
.clone();
let block_state = fluid_level2.get_block(sample_y);
if d <= 0f64 {
// TODO: Handle fluid tick
return Some(block_state.default_state);
}
if block_state == &WATER_BLOCK
&& self
.fluid_level_sampler
.get_fluid_level(sample_x, sample_y - 1, sample_z)
.get_block(sample_y - 1)
== &LAVA_BLOCK
{
return Some(block_state.default_state);
}
let mut barrier_sample = None;
let fluid_level3 = self
.get_water_level(nearest[1].0, router, height_estimator, sample_options)
.clone();
let e = d * Self::calculate_density(
&mut barrier_sample,
pos,
router,
sample_options,
&fluid_level2,
&fluid_level3,
);
if density + e > 0f64 {
return None;
}
// Only pay for the cache/noise lookup when at least one distance weight is positive;
// when both are <= 0 the third centre cannot affect the result.
if f > 0f64 || g_dist > 0f64 {
let fluid_level4 =
self.get_water_level(nearest[2].0, router, height_estimator, sample_options);
if f > 0f64 {
let contrib = d
* f
* Self::calculate_density(
&mut barrier_sample,
pos,
router,
sample_options,
&fluid_level2,
&fluid_level3,
fluid_level4,
);
if density + contrib > 0f64 {
return None;
}
}
if density + e > 0f64 {
None
} else {
let fluid_level4 = self.get_water_level(
random_positions_and_hypot[2].0,
router,
height_estimator,
sample_options,
);
let f = Self::max_distance(
random_positions_and_hypot[0].1,
random_positions_and_hypot[2].1,
);
if f > 0f64 {
let g = d
* f
* Self::calculate_density(
&mut barrier_sample,
pos,
router,
sample_options,
&fluid_level2,
fluid_level4,
);
if density + g > 0f64 {
return None;
}
}
let g = Self::max_distance(
random_positions_and_hypot[1].1,
random_positions_and_hypot[2].1,
);
if g > 0f64 {
let h = d
* g
* Self::calculate_density(
&mut barrier_sample,
pos,
router,
sample_options,
&fluid_level3,
fluid_level4,
);
if density + h > 0f64 {
return None;
}
}
//TODO Handle fluid tick
Some(block_state.default_state)
}
if g_dist > 0f64 {
let contrib = d
* g_dist
* Self::calculate_density(
&mut barrier_sample,
pos,
router,
sample_options,
&fluid_level3,
fluid_level4,
);
if density + contrib > 0f64 {
return None;
}
}
}
// TODO: Handle fluid tick
Some(block_state.default_state)
}
}

View File

@ -32,6 +32,20 @@ impl DoublePerlinNoiseSampler {
)
}
pub fn get_amplitude(amplitudes: &[f64]) -> f64 {
let mut j = i32::MAX;
let mut k = i32::MIN;
for (index, amplitude) in amplitudes.iter().enumerate() {
if *amplitude != 0f64 {
j = i32::min(j, index as i32);
k = i32::max(k, index as i32);
}
}
0.16666666666666666f64 / Self::create_amplitude(k - j)
}
pub fn new(
rand: &mut impl RandomImpl,
first_octave: i32,
@ -76,7 +90,14 @@ mod double_perlin_noise_sampler_test {
assert_eq!(rand.next_i32(), -1302745855);
let mut rand_gen = RandomGenerator::Legacy(rand);
let params = DoublePerlinNoiseParameters::new(0, 0, &[4f64], 1, 1, 1.0);
let params = DoublePerlinNoiseParameters::new(
0,
0,
&[4f64],
0,
0,
DoublePerlinNoiseSampler::get_amplitude(&[4f64]),
);
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, &params, true);
let values = [
@ -174,7 +195,14 @@ mod double_perlin_noise_sampler_test {
let mut rand_gen = RandomGenerator::Xoroshiro(rand);
let params = DoublePerlinNoiseParameters::new(0, 1, &[2f64, 4f64], 1, 1, 1.0);
let params = DoublePerlinNoiseParameters::new(
0,
1,
&[2f64, 4f64],
0,
0,
DoublePerlinNoiseSampler::get_amplitude(&[2f64, 4f64]),
);
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, &params, false);

View File

@ -3,12 +3,10 @@ use std::sync::Arc;
use pumpkin_data::block_properties::is_air;
use pumpkin_data::chunk::DoublePerlinNoiseParameters;
use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::{Fluid, FluidState};
use pumpkin_data::structures::{
Structure, StructureKeys, StructurePlacementCalculator, StructurePlacementType, StructureSet,
WeightedEntry,
Structure, StructureKeys, StructurePlacementType, StructureSet, WeightedEntry,
};
use pumpkin_data::tag;
use pumpkin_data::tag::RegistryKey;
@ -45,7 +43,6 @@ use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSamplerBuil
use crate::generation::noise::router::surface_height_sampler::SurfaceHeightSamplerBuilderOptions;
use crate::generation::noise::{CHUNK_DIM, ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK};
use crate::generation::structure::lazily_generate_structure;
use crate::generation::structure::placement::GlobalStructureCache;
use crate::generation::structure::placement::should_generate_structure;
use crate::generation::structure::structures::{
StructureGeneratorContext, StructureInstance, create_chunk_random,
@ -53,7 +50,7 @@ use crate::generation::structure::structures::{
use crate::generation::structure::try_generate_structure;
use crate::generation::surface::evaluate_surface_rule;
use crate::{
BlockStateId, ProtoNoiseRouters,
BlockStateId,
block::RawBlockState,
chunk::CHUNK_AREA,
generation::{biome, positions::chunk_pos},
@ -205,13 +202,8 @@ impl TerrainCache {
impl ProtoChunk {
#[must_use]
pub fn new(
x: i32,
z: i32,
dimension: &Dimension,
default_block: &'static BlockState,
biome_mixer_seed: i64,
) -> Self {
pub fn new(x: i32, z: i32, generator: &super::generator::VanillaGenerator) -> Self {
let dimension = &generator.dimension;
let height = dimension.logical_height as u16;
let section_count = (height as usize) / 16;
@ -219,7 +211,8 @@ impl ProtoChunk {
Self {
x,
z,
default_block,
default_block: generator.default_block,
biome_mixer_seed: generator.biome_mixer_seed,
flat_block_map: vec![0; CHUNK_AREA * height as usize].into_boxed_slice(),
flat_biome_map: vec![
Biome::PLAINS.id;
@ -228,7 +221,6 @@ impl ProtoChunk {
* biome_coords::from_block(height as i32) as usize
]
.into_boxed_slice(),
biome_mixer_seed,
flat_surface_height_map: default_heightmap.clone(),
flat_ocean_floor_height_map: default_heightmap.clone(),
flat_motion_blocking_height_map: default_heightmap.clone(),
@ -241,38 +233,29 @@ impl ProtoChunk {
sky_light: (0..section_count)
.map(|_| {
if dimension.has_skylight {
// Pre-allocate full arrays for sky light in dimensions with skylight
// Initialize to 0 - lighting engine will calculate proper values
LightContainer::new_filled(0)
// Sky light starts at 0 and is calculated by the lighting engine
LightContainer::new_empty(0)
} else {
// No skylight in Nether/End, can use Empty
// No skylight in Nether/End
LightContainer::new_empty(0)
}
})
.collect(),
block_light: (0..section_count)
// Pre-allocate full arrays for block light
// Initialize to 0 - lighting engine will set emissive blocks
.map(|_| LightContainer::new_filled(0))
// Block light starts at 0 and is set by emissive blocks
.map(|_| LightContainer::new_empty(0))
.collect(),
},
pending_block_entities: Vec::new(),
}
}
#[must_use]
pub fn from_chunk_data(
chunk_data: &ChunkData,
dimension: &Dimension,
default_block: &'static BlockState,
biome_mixer_seed: i64,
generator: &super::generator::VanillaGenerator,
) -> Self {
let mut proto_chunk = Self::new(
chunk_data.x,
chunk_data.z,
dimension,
default_block,
biome_mixer_seed,
);
let mut proto_chunk = Self::new(chunk_data.x, chunk_data.z, generator);
proto_chunk.light = chunk_data.light_engine.lock().unwrap().clone();
let section_data = &chunk_data.section;
@ -549,7 +532,7 @@ impl ProtoChunk {
self.flat_biome_map[index]
}
pub fn step_to_biomes(&mut self, dimension: Dimension, noise_router: &ProtoNoiseRouters) {
pub fn step_to_biomes(&mut self, generator: &super::generator::VanillaGenerator) {
let start_x = start_block_x(self.x);
let start_z = start_block_z(self.z);
let horizontal_biome_end = biome_coords::from_block(16);
@ -560,19 +543,15 @@ impl ProtoChunk {
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config);
self.populate_biomes(dimension, &mut multi_noise_sampler);
MultiNoiseSampler::generate(&generator.base_router.multi_noise, &multi_noise_config);
self.populate_biomes(generator, &mut multi_noise_sampler);
self.stage = StagedChunkEnum::Biomes;
}
pub fn step_to_noise(
&mut self,
settings: &GenerationSettings,
random_config: &GlobalRandomConfig,
noise_router: &ProtoNoiseRouters,
) {
pub fn step_to_noise(&mut self, generator: &super::generator::VanillaGenerator) {
//debug_assert_eq!(self.stage, StagedChunkEnum::Biomes);
let settings = generator.settings;
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let start_x = start_block_x(self.x);
@ -587,8 +566,8 @@ impl ProtoChunk {
);
let mut noise_sampler = ChunkNoiseGenerator::new(
&noise_router.noise,
random_config,
&generator.base_router.noise,
&generator.random_config,
horizontal_cell_count as usize,
start_x,
start_z,
@ -610,30 +589,25 @@ impl ProtoChunk {
generation_shape.vertical_cell_block_count() as usize,
);
let mut surface_height_estimate_sampler = SurfaceHeightEstimateSampler::generate(
&noise_router.surface_estimator,
&generator.base_router.surface_estimator,
&surface_config,
);
self.populate_noise(
generator,
&mut noise_sampler,
&random_config.ore_random_deriver,
&generator.random_config.ore_random_deriver,
&mut surface_height_estimate_sampler,
);
self.stage = StagedChunkEnum::Noise;
}
pub fn step_to_surface(
&mut self,
settings: &GenerationSettings,
random_config: &GlobalRandomConfig,
terrain_cache: &TerrainCache,
noise_router: &ProtoNoiseRouters,
) {
pub fn step_to_surface(&mut self, generator: &super::generator::VanillaGenerator) {
debug_assert_eq!(self.stage, StagedChunkEnum::Noise);
// Build surface
let start_x = start_block_x(self.x);
let start_z = start_block_z(self.z);
let generation_shape = &settings.shape;
let generation_shape = &generator.settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let horizontal_biome_end = biome_coords::from_block(
@ -648,24 +622,20 @@ impl ProtoChunk {
generation_shape.vertical_cell_block_count() as usize,
);
let mut surface_height_estimate_sampler = SurfaceHeightEstimateSampler::generate(
&noise_router.surface_estimator,
&generator.base_router.surface_estimator,
&surface_config,
);
self.build_surface(
settings,
random_config,
terrain_cache,
&mut surface_height_estimate_sampler,
);
self.build_surface(generator, &mut surface_height_estimate_sampler);
self.stage = StagedChunkEnum::Surface;
}
pub fn populate_biomes(
&mut self,
dimension: Dimension,
generator: &super::generator::VanillaGenerator,
multi_noise_sampler: &mut MultiNoiseSampler,
) {
let dimension = generator.dimension;
// Instantiate ONLY the supplier we actually need
let active_supplier = if dimension == Dimension::THE_END {
ActiveSupplier::End(TheEndBiomeSupplier)
@ -721,6 +691,7 @@ impl ProtoChunk {
#[expect(clippy::similar_names)]
pub fn populate_noise(
&mut self,
generator: &super::generator::VanillaGenerator,
noise_sampler: &mut ChunkNoiseGenerator,
ore_random_deriver: &XoroshiroSplitter,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
@ -773,7 +744,7 @@ impl ProtoChunk {
local_z,
surface_height_estimate_sampler,
)
.unwrap_or(self.default_block);
.unwrap_or(generator.default_block);
self.set_block_state(block_x, block_y, block_z, block_state);
}
}
@ -798,6 +769,7 @@ impl ProtoChunk {
self.get_biome_id(seed_biome_pos.x, seed_biome_pos.y, seed_biome_pos.z)
}
#[must_use]
pub fn get_terrain_gen_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome {
Biome::from_id(self.get_terrain_gen_biome_id(x, y, z)).unwrap()
@ -809,15 +781,17 @@ impl ProtoChunk {
/// It is crucial that biome assignments are determined before this process begins.
pub fn build_surface(
&mut self,
settings: &GenerationSettings,
random_config: &GlobalRandomConfig,
terrain_cache: &TerrainCache,
generator: &super::generator::VanillaGenerator,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) {
let start_x = chunk_pos::start_block_x(self.x);
let start_z = chunk_pos::start_block_z(self.z);
let min_y = self.bottom_y();
let settings = generator.settings;
let random_config = &generator.random_config;
let terrain_cache = &generator.terrain_cache;
let random = &random_config.base_random_deriver;
let mut context = MaterialRuleContext::new(
min_y,
@ -1100,7 +1074,7 @@ impl ProtoChunk {
}
}
fn get_allowed_biomes(set: &StructureSet) -> Vec<u16> {
pub fn get_allowed_biomes(set: &StructureSet) -> Vec<u16> {
let mut allowed_biomes = Vec::new();
for entry in set.structures {
let structure = Structure::get(&entry.structure);
@ -1117,26 +1091,25 @@ impl ProtoChunk {
allowed_biomes
}
pub fn set_structure_starts(
&mut self,
random_config: &GlobalRandomConfig,
settings: &GenerationSettings,
global_cache: &GlobalStructureCache,
) {
let seed = random_config.seed;
let calculator = StructurePlacementCalculator::new(seed as i64);
pub fn set_structure_starts(&mut self, generator: &super::generator::VanillaGenerator) {
let random_config = &generator.random_config;
let settings = generator.settings;
let global_cache = &generator.global_structure_cache;
let calculator = &generator.structure_calculator;
for set in StructureSet::ALL {
let allowed_biomes = Self::get_allowed_biomes(set);
let seed = random_config.seed;
for (i, set) in StructureSet::ALL.iter().enumerate() {
let allowed_biomes = &generator.structure_allowed_biomes[&i];
if !should_generate_structure(
&set.placement,
&calculator,
calculator,
self.x,
self.z,
global_cache,
self,
&allowed_biomes,
allowed_biomes,
) {
continue;
}
@ -1205,14 +1178,13 @@ impl ProtoChunk {
false
}
pub fn set_structure_references(
&mut self,
random_config: &GlobalRandomConfig,
settings: &GenerationSettings,
dimension: &Dimension,
noise_router: &ProtoNoiseRouters,
global_cache: &GlobalStructureCache,
) {
pub fn set_structure_references(&mut self, generator: &super::generator::VanillaGenerator) {
let random_config = &generator.random_config;
let settings = generator.settings;
let dimension = &generator.dimension;
let noise_router = &generator.base_router;
let global_cache = &generator.global_structure_cache;
let start_x = chunk_pos::start_block_x(self.x);
let start_z = chunk_pos::start_block_z(self.z);
let end_x = start_x + 15;

View File

@ -55,22 +55,24 @@ use crate::generation::{
};
pub fn bench_create_and_populate_noise(
base_router: &ProtoNoiseRouters,
_base_router: &ProtoNoiseRouters,
random_config: &GlobalRandomConfig,
settings: &GenerationSettings,
_settings: &GenerationSettings,
_terrain_cache: &TerrainCache,
default_block: &'static BlockState,
_default_block: &'static BlockState,
) {
use crate::biome::hash_seed;
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use crate::generation::noise::router::surface_height_sampler::{
SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions,
};
use crate::generation::proto_chunk::StandardChunkFluidLevelSampler;
use pumpkin_util::world_seed::Seed;
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed);
let generator = VanillaGenerator::new(Seed(random_config.seed), Dimension::OVERWORLD);
let mut chunk = ProtoChunk::new(0, 0, &generator);
// Create noise sampler and other required components
let settings = generator.settings;
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let sampler = StandardChunkFluidLevelSampler::new(
@ -85,8 +87,8 @@ pub fn bench_create_and_populate_noise(
let start_z = chunk_pos::start_block_z(0);
let mut noise_sampler = ChunkNoiseGenerator::new(
&base_router.noise,
random_config,
&generator.base_router.noise,
&generator.random_config,
horizontal_cell_count as usize,
start_x,
start_z,
@ -112,63 +114,63 @@ pub fn bench_create_and_populate_noise(
generation_shape.max_y() as i32,
generation_shape.vertical_cell_block_count() as usize,
);
let mut surface_height_estimate_sampler =
SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config);
let mut surface_height_estimate_sampler = SurfaceHeightEstimateSampler::generate(
&generator.base_router.surface_estimator,
&surface_config,
);
chunk.populate_noise(
&generator,
&mut noise_sampler,
&random_config.ore_random_deriver,
&generator.random_config.ore_random_deriver,
&mut surface_height_estimate_sampler,
);
}
pub fn bench_create_and_populate_biome(
base_router: &ProtoNoiseRouters,
_base_router: &ProtoNoiseRouters,
random_config: &GlobalRandomConfig,
settings: &GenerationSettings,
_settings: &GenerationSettings,
_terrain_cache: &TerrainCache,
default_block: &'static BlockState,
_default_block: &'static BlockState,
) {
use crate::biome::hash_seed;
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use crate::generation::noise::router::multi_noise_sampler::{
MultiNoiseSampler, MultiNoiseSamplerBuilderOptions,
};
use crate::generation::{biome_coords, positions::chunk_pos};
use pumpkin_util::world_seed::Seed;
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed);
let generator = VanillaGenerator::new(Seed(random_config.seed), Dimension::OVERWORLD);
let mut chunk = ProtoChunk::new(0, 0, &generator);
// Create multi-noise sampler
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let start_x = chunk_pos::start_block_x(0);
let start_z = chunk_pos::start_block_z(0);
let biome_pos = Vector2::new(
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
);
let horizontal_biome_end = biome_coords::from_block(
horizontal_cell_count as i32 * generation_shape.horizontal_cell_block_count() as i32,
);
let horizontal_biome_end = biome_coords::from_block(16);
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(
biome_pos.x,
biome_pos.y,
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
MultiNoiseSampler::generate(&base_router.multi_noise, &multi_noise_config);
MultiNoiseSampler::generate(&generator.base_router.multi_noise, &multi_noise_config);
chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler);
chunk.populate_biomes(&generator, &mut multi_noise_sampler);
}
pub fn bench_create_and_populate_noise_with_surface(
base_router: &ProtoNoiseRouters,
_base_router: &ProtoNoiseRouters,
random_config: &GlobalRandomConfig,
settings: &GenerationSettings,
terrain_cache: &TerrainCache,
default_block: &'static BlockState,
_settings: &GenerationSettings,
_terrain_cache: &TerrainCache,
_default_block: &'static BlockState,
) {
use crate::biome::hash_seed;
use crate::generation::generator::{GeneratorInit, VanillaGenerator};
use crate::generation::noise::router::{
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
surface_height_sampler::{
@ -176,11 +178,13 @@ pub fn bench_create_and_populate_noise_with_surface(
},
};
use crate::generation::proto_chunk::StandardChunkFluidLevelSampler;
use pumpkin_util::world_seed::Seed;
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(0, 0, &Dimension::OVERWORLD, default_block, biome_mixer_seed);
let generator = VanillaGenerator::new(Seed(random_config.seed), Dimension::OVERWORLD);
let mut chunk = ProtoChunk::new(0, 0, &generator);
// Create all required components
let settings = generator.settings;
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let start_x = chunk_pos::start_block_x(0);
@ -191,16 +195,14 @@ pub fn bench_create_and_populate_noise_with_surface(
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
);
let horizontal_biome_end = biome_coords::from_block(
horizontal_cell_count as i32 * generation_shape.horizontal_cell_block_count() as i32,
);
let horizontal_biome_end = biome_coords::from_block(16);
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(
biome_pos.x,
biome_pos.y,
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
MultiNoiseSampler::generate(&base_router.multi_noise, &multi_noise_config);
MultiNoiseSampler::generate(&generator.base_router.multi_noise, &multi_noise_config);
// Noise sampler
let sampler = StandardChunkFluidLevelSampler::new(
@ -208,12 +210,12 @@ pub fn bench_create_and_populate_noise_with_surface(
settings.sea_level,
Block::from_state_id(settings.default_fluid.id),
),
FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
FluidLevel::new(-54, &Block::LAVA),
);
let mut noise_sampler = ChunkNoiseGenerator::new(
&base_router.noise,
random_config,
&generator.base_router.noise,
&generator.random_config,
horizontal_cell_count as usize,
start_x,
start_z,
@ -232,19 +234,17 @@ pub fn bench_create_and_populate_noise_with_surface(
generation_shape.max_y() as i32,
generation_shape.vertical_cell_block_count() as usize,
);
let mut surface_height_estimate_sampler =
SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config);
let mut surface_height_estimate_sampler = SurfaceHeightEstimateSampler::generate(
&generator.base_router.surface_estimator,
&surface_config,
);
chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler);
chunk.populate_biomes(&generator, &mut multi_noise_sampler);
chunk.populate_noise(
&generator,
&mut noise_sampler,
&random_config.ore_random_deriver,
&mut surface_height_estimate_sampler,
);
chunk.build_surface(
settings,
random_config,
terrain_cache,
&generator.random_config.ore_random_deriver,
&mut surface_height_estimate_sampler,
);
chunk.build_surface(&generator, &mut surface_height_estimate_sampler);
}