use sha2::{Digest, Sha256}; use std::cell::RefCell; use enum_dispatch::enum_dispatch; use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE}; use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler; pub mod end; pub mod multi_noise; pub mod position_finder; thread_local! { /// A shortcut; check if last used biome is what we should use static LAST_RESULT_NODE: RefCell> = const {RefCell::new(None) }; } #[enum_dispatch] pub trait BiomeSupplier { fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome; } #[derive(Clone, Copy)] pub struct MultiNoiseBiomeSupplier { source: &'static BiomeTree, } impl MultiNoiseBiomeSupplier { pub const OVERWORLD: Self = Self::new(&OVERWORLD_BIOME_SOURCE); pub const NETHER: Self = Self::new(&NETHER_BIOME_SOURCE); const fn new(source: &'static BiomeTree) -> Self { Self { source } } } impl BiomeSupplier for MultiNoiseBiomeSupplier { fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome { let point = noise.sample(x, y, z); let point_list = point.convert_to_list(); LAST_RESULT_NODE.with_borrow_mut(|last_result| self.source.get(&point_list, last_result)) } } #[must_use] pub fn hash_seed(seed: u64) -> i64 { let mut hasher = Sha256::new(); hasher.update(seed.to_le_bytes()); let result = hasher.finalize(); i64::from_le_bytes(result[..8].try_into().unwrap()) } #[cfg(test)] mod test { use pumpkin_data::{chunk::Biome, dimension::Dimension}; use pumpkin_util::read_data_from_file; use serde::Deserialize; use crate::{ ProtoChunk, chunk::palette::BIOME_NETWORK_MAX_BITS, generation::noise::router::multi_noise_sampler::{ MultiNoiseSampler, MultiNoiseSamplerBuilderOptions, }, }; use super::{BiomeSupplier, MultiNoiseBiomeSupplier, hash_seed}; #[test] fn biome_desert() { use crate::generation::generator::{GeneratorInit, VanillaGenerator}; use pumpkin_util::world_seed::Seed; let seed = 13579; let generator = VanillaGenerator::new(Seed(seed as u64), Dimension::OVERWORLD); let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(1, 1, 1); let mut sampler = 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::generation::noise::router::multi_noise_sampler::{ MultiNoiseSampler, MultiNoiseSamplerBuilderOptions, }; use crate::generation::{biome_coords, positions::chunk_pos}; #[derive(Deserialize)] struct BiomeData { x: i32, z: i32, data: Vec<(i32, i32, i32, u8)>, } let expected_data: Vec = read_data_from_file!("../../assets/biome_no_blend_no_beard_0.json"); let seed = 0; 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; let mut chunk = ProtoChunk::new(chunk_x, chunk_z, &generator); // Create MultiNoiseSampler for populate_biomes let start_x = chunk_pos::start_block_x(chunk_x); let start_z = chunk_pos::start_block_z(chunk_z); let horizontal_biome_end = biome_coords::from_block(16); let multi_noise_config = MultiNoiseSamplerBuilderOptions::new( biome_coords::from_block(start_x), biome_coords::from_block(start_z), horizontal_biome_end as usize, ); let mut multi_noise_sampler = MultiNoiseSampler::generate( &generator.base_router.multi_noise, &multi_noise_config, ); 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); assert_eq!( biome_id, calculated_biome.id, "Expected {:?} was {:?} at {},{},{} ({},{})", Biome::from_id(biome_id), calculated_biome, biome_x, biome_y, biome_z, data.x, data.z ); } } } #[test] fn hash_seed_test() { let hashed_seed = hash_seed(0); assert_eq!(8794265229978523055, hashed_seed); let hashed_seed = hash_seed((-777i64) as u64); assert_eq!(-1087248400229165450, hashed_seed); } #[test] fn proper_network_bits_per_entry() { let id_to_test = 1 << BIOME_NETWORK_MAX_BITS; assert!( Biome::from_id(id_to_test).is_none(), "We need to update our constants!" ); } }