- fixed nether again, not the dead lock or whatever it is but the server crash - further replace Notify with CancellationToken - fixed chunk loading on inital spawn - more stronghold work
271 lines
10 KiB
Rust
271 lines
10 KiB
Rust
use pumpkin_data::{Block, BlockState, chunk::Biome};
|
|
use pumpkin_util::{
|
|
math::vector3::Vector3,
|
|
random::{RandomDeriver, RandomDeriverImpl, RandomGenerator, RandomImpl},
|
|
};
|
|
|
|
use crate::{
|
|
ProtoChunk,
|
|
block::RawBlockState,
|
|
generation::noise::{
|
|
WATER_BLOCK, perlin::DoublePerlinNoiseSampler,
|
|
router::proto_noise_router::DoublePerlinNoiseBuilder,
|
|
},
|
|
};
|
|
|
|
pub struct SurfaceTerrainBuilder {
|
|
// Badlands stuff
|
|
terracotta_bands: Box<[RawBlockState]>,
|
|
terracotta_bands_offset_noise: DoublePerlinNoiseSampler,
|
|
badlands_pillar_noise: DoublePerlinNoiseSampler,
|
|
badlands_surface_noise: DoublePerlinNoiseSampler,
|
|
badlands_pillar_roof_noise: DoublePerlinNoiseSampler,
|
|
// Iceberg stuff
|
|
iceberg_pillar_noise: DoublePerlinNoiseSampler,
|
|
iceberg_pillar_roof_noise: DoublePerlinNoiseSampler,
|
|
iceberg_surface_noise: DoublePerlinNoiseSampler,
|
|
}
|
|
|
|
impl SurfaceTerrainBuilder {
|
|
pub fn new(noise_builder: &DoublePerlinNoiseBuilder, random_deriver: &RandomDeriver) -> Self {
|
|
Self {
|
|
terracotta_bands: Self::create_terracotta_bands(
|
|
random_deriver.split_string("minecraft:clay_bands"),
|
|
),
|
|
terracotta_bands_offset_noise: noise_builder
|
|
.get_noise_sampler_for_id("clay_bands_offset"),
|
|
badlands_pillar_noise: noise_builder.get_noise_sampler_for_id("badlands_pillar"),
|
|
badlands_surface_noise: noise_builder.get_noise_sampler_for_id("badlands_surface"),
|
|
badlands_pillar_roof_noise: noise_builder
|
|
.get_noise_sampler_for_id("badlands_pillar_roof"),
|
|
iceberg_pillar_noise: noise_builder.get_noise_sampler_for_id("iceberg_pillar"),
|
|
iceberg_pillar_roof_noise: noise_builder
|
|
.get_noise_sampler_for_id("iceberg_pillar_roof"),
|
|
iceberg_surface_noise: noise_builder.get_noise_sampler_for_id("iceberg_surface"),
|
|
}
|
|
}
|
|
|
|
const ORANGE_TERRACOTTA: RawBlockState =
|
|
RawBlockState(Block::ORANGE_TERRACOTTA.default_state.id);
|
|
const YELLOW_TERRACOTTA: RawBlockState =
|
|
RawBlockState(Block::YELLOW_TERRACOTTA.default_state.id);
|
|
const BROWN_TERRACOTTA: RawBlockState = RawBlockState(Block::BROWN_TERRACOTTA.default_state.id);
|
|
const RED_TERRACOTTA: RawBlockState = RawBlockState(Block::RED_TERRACOTTA.default_state.id);
|
|
const WHITE_TERRACOTTA: RawBlockState = RawBlockState(Block::WHITE_TERRACOTTA.default_state.id);
|
|
const LIGHT_GRAY_TERRACOTTA: RawBlockState =
|
|
RawBlockState(Block::LIGHT_GRAY_TERRACOTTA.default_state.id);
|
|
const TERRACOTTA: RawBlockState = RawBlockState(Block::TERRACOTTA.default_state.id);
|
|
|
|
fn create_terracotta_bands(mut random: RandomGenerator) -> Box<[RawBlockState]> {
|
|
let mut block_states = [Self::TERRACOTTA; 192];
|
|
|
|
let mut i = 0;
|
|
while i < block_states.len() {
|
|
i += random.next_bounded_i32(5) as usize + 1;
|
|
if i >= block_states.len() {
|
|
break;
|
|
}
|
|
block_states[i] = Self::ORANGE_TERRACOTTA;
|
|
i += 1;
|
|
}
|
|
|
|
Self::add_terracotta_bands(&mut random, &mut block_states, 1, Self::YELLOW_TERRACOTTA);
|
|
Self::add_terracotta_bands(&mut random, &mut block_states, 2, Self::BROWN_TERRACOTTA);
|
|
Self::add_terracotta_bands(&mut random, &mut block_states, 1, Self::RED_TERRACOTTA);
|
|
|
|
let band_count = random.next_inbetween_i32(9, 15);
|
|
let mut current_band = 0;
|
|
let mut index = 0;
|
|
|
|
while current_band < band_count && index < block_states.len() {
|
|
block_states[index] = Self::WHITE_TERRACOTTA;
|
|
|
|
if index > 1 && random.next_bool() {
|
|
block_states[index - 1] = Self::LIGHT_GRAY_TERRACOTTA;
|
|
}
|
|
|
|
if index + 1 < block_states.len() && random.next_bool() {
|
|
block_states[index + 1] = Self::LIGHT_GRAY_TERRACOTTA;
|
|
}
|
|
|
|
index += random.next_bounded_i32(16) as usize + 4;
|
|
current_band += 1;
|
|
}
|
|
|
|
Box::new(block_states)
|
|
}
|
|
|
|
fn add_terracotta_bands(
|
|
random: &mut RandomGenerator,
|
|
terracotta_bands: &mut [RawBlockState],
|
|
min_band_size: i32,
|
|
state: RawBlockState,
|
|
) {
|
|
let band_count = random.next_inbetween_i32(6, 15);
|
|
|
|
for _ in 0..band_count {
|
|
let band_width = min_band_size + random.next_bounded_i32(3);
|
|
let start_index = random.next_bounded_i32(terracotta_bands.len() as i32);
|
|
|
|
for m in 0..band_width {
|
|
if (start_index + m < terracotta_bands.len() as i32) && (m < band_width) {
|
|
terracotta_bands[(start_index + m) as usize] = state;
|
|
} else {
|
|
break; // Stop if we reach the end of the array
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn place_badlands_pillar(
|
|
&self,
|
|
chunk: &mut ProtoChunk,
|
|
global_x: i32,
|
|
global_z: i32,
|
|
surface_y: i32,
|
|
) {
|
|
let surface_noise =
|
|
(self
|
|
.badlands_surface_noise
|
|
.sample(global_x as f64, 0.0, global_z as f64)
|
|
* 8.25)
|
|
.abs();
|
|
let pillar_noise =
|
|
self.badlands_pillar_noise
|
|
.sample(global_x as f64 * 0.2, 0.0, global_z as f64 * 0.2)
|
|
* 15.0;
|
|
|
|
let threshold = surface_noise.min(pillar_noise);
|
|
|
|
if threshold > 0.0 {
|
|
let pillar_roof_noise = (self.badlands_pillar_roof_noise.sample(
|
|
global_x as f64 * 0.75,
|
|
0.0,
|
|
global_z as f64 * 0.75,
|
|
) * 1.5)
|
|
.abs();
|
|
|
|
let scaled_threshold = threshold * threshold * 2.5;
|
|
let transformed_roof = (pillar_roof_noise * 50.0).ceil() + 24.0;
|
|
let elevation = 64.0 + scaled_threshold.min(transformed_roof);
|
|
let elevation_y = elevation.floor() as i32;
|
|
if surface_y <= elevation_y {
|
|
for y in (chunk.bottom_y() as i32..=elevation_y).rev() {
|
|
let pos = Vector3::new(global_x, y, global_z);
|
|
let block_id = chunk.get_block_state(&pos).to_block_id();
|
|
if block_id == Block::get_raw_id_from_state_id(chunk.default_block.id) {
|
|
break;
|
|
}
|
|
|
|
if block_id == Block::WATER {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for y in (chunk.bottom_y() as i32..=elevation_y).rev() {
|
|
let pos = Vector3::new(global_x, y, global_z);
|
|
let block_state = chunk.get_block_state(&pos).to_state();
|
|
if !block_state.is_air() {
|
|
break;
|
|
}
|
|
|
|
let default_block = &chunk.default_block;
|
|
chunk.set_block_state(global_x, y, global_z, default_block);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const SNOW_BLOCK: Block = Block::SNOW_BLOCK;
|
|
const PACKED_ICE: Block = Block::PACKED_ICE;
|
|
|
|
#[expect(clippy::too_many_arguments)]
|
|
pub fn place_iceberg(
|
|
&self,
|
|
chunk: &mut ProtoChunk,
|
|
biome: &Biome,
|
|
x: i32,
|
|
z: i32,
|
|
estimated_surface_y: i32,
|
|
current_top_y: i32,
|
|
sea_level: i32,
|
|
random_deriver: &RandomDeriver,
|
|
) {
|
|
let iceburg_surface_noise =
|
|
(self.iceberg_surface_noise.sample(x as f64, 0.0, z as f64) * 8.25).abs();
|
|
|
|
let iceburg_pillar_noise =
|
|
self.iceberg_pillar_noise
|
|
.sample(x as f64 * 1.28, 0.0, z as f64 * 1.28)
|
|
* 15.0;
|
|
|
|
let threshold = iceburg_surface_noise.min(iceburg_pillar_noise);
|
|
if threshold > 1.8 {
|
|
let iceburg_pillar_roof_noise =
|
|
(self
|
|
.iceberg_pillar_roof_noise
|
|
.sample(x as f64 * 1.17, 0.0, z as f64 * 1.17)
|
|
* 1.5)
|
|
.abs();
|
|
|
|
let scaled_threshold = threshold * threshold * 1.2;
|
|
let scaled_roof_noise = (iceburg_pillar_roof_noise * 40.0).ceil() + 14.0;
|
|
|
|
let mut block_threshold = scaled_threshold.min(scaled_roof_noise);
|
|
|
|
// TODO: Cache this
|
|
let temperature = biome
|
|
.weather
|
|
.compute_temperature(x as f64, sea_level, z as f64, sea_level);
|
|
if temperature > 0.1f32 {
|
|
block_threshold -= 2.0;
|
|
}
|
|
|
|
let (top_block, bottom_block) = if block_threshold > 2.0 {
|
|
let value = sea_level as f64 - block_threshold - 7.0;
|
|
(block_threshold as i32 + sea_level, value as i32)
|
|
} else {
|
|
(0, 0)
|
|
};
|
|
|
|
let mut rand = random_deriver.split_pos(x, 0, z);
|
|
let snow_block_count = 2 + rand.next_bounded_i32(4);
|
|
let snow_bottom = sea_level + 18 + rand.next_bounded_i32(10);
|
|
let mut snow_blocks = 0;
|
|
|
|
let top_y = current_top_y.max(top_block + 1);
|
|
|
|
for y in (estimated_surface_y..=top_y).rev() {
|
|
let pos = Vector3::new(x, y, z);
|
|
let block_state = chunk.get_block_state(&pos);
|
|
if (block_state.to_state().is_air() && y < top_block && rand.next_f64() > 0.01)
|
|
|| (block_state.to_block() == &WATER_BLOCK
|
|
&& y > bottom_block
|
|
&& y < sea_level
|
|
&& bottom_block != 0
|
|
&& rand.next_f64() > 0.15)
|
|
{
|
|
if snow_blocks <= snow_block_count && y > snow_bottom {
|
|
chunk.set_block_state(x, y, z, Self::SNOW_BLOCK.default_state);
|
|
snow_blocks += 1;
|
|
} else {
|
|
chunk.set_block_state(x, y, z, Self::PACKED_ICE.default_state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_terracotta_block(&self, x: i32, y: i32, z: i32) -> &'static BlockState {
|
|
let offset = (self
|
|
.terracotta_bands_offset_noise
|
|
.sample(x as f64, 0.0, z as f64)
|
|
* 4.0)
|
|
.round() as i32;
|
|
let offset = y + offset;
|
|
self.terracotta_bands[((offset as u64 + self.terracotta_bands.len() as u64)
|
|
% self.terracotta_bands.len() as u64) as usize]
|
|
.to_state()
|
|
}
|
|
}
|