chore: make chunk gen usable in debug mode

This commit is contained in:
Alexander Medvedev 2026-01-30 00:57:55 +01:00
parent 4ec9db1e36
commit be322123cf
45 changed files with 279 additions and 13347 deletions

File diff suppressed because one or more lines are too long

View File

@ -307,6 +307,18 @@ pub(crate) fn build() -> TokenStream {
pub spawn_costs: phf::Map<&'static str, SpawnCosts>,
}
impl PartialEq<u8> for Biome {
fn eq(&self, other: &u8) -> bool {
self.id == *other
}
}
impl PartialEq<Biome> for u8 {
fn eq(&self, other: &Biome) -> bool {
*self == other.id
}
}
#[derive(Debug)]
pub struct SpawnGroups {
pub monster: &'static [Spawner],

View File

@ -966,9 +966,9 @@ pub(crate) fn build() -> TokenStream {
#mod_ident::#contains_ident(state_id)
}
pub fn blocks_movement(block_state: &BlockState, block: &Block) -> bool {
pub fn blocks_movement(block_state: &BlockState, block: u16) -> bool {
if block_state.is_solid() {
return block != &Block::COBWEB && block != &Block::BAMBOO_SAPLING;
return block != Block::COBWEB && block != Block::BAMBOO_SAPLING;
}
false
}
@ -982,7 +982,10 @@ pub(crate) fn build() -> TokenStream {
#[doc = r" If you need access to the block use `BlockState::from_id_with_block` instead."]
#[inline]
pub fn from_id(id: u16) -> &'static Self {
Block::STATE_FROM_STATE_ID[id as usize]
// In debug, this avoids the slow range-checking logic
unsafe {
Block::STATE_FROM_STATE_ID.get_unchecked(id as usize)
}
}
#[doc = r" Get a block state from a state id and the corresponding block."]
@ -1041,21 +1044,24 @@ pub(crate) fn build() -> TokenStream {
#[doc = r" Get a raw ID from an State ID."]
#[inline]
pub const fn get_raw_id_from_state_id(state_id: u16) -> u16 {
if state_id as usize >= Self::RAW_ID_FROM_STATE_ID.len() {
pub fn get_raw_id_from_state_id(state_id: u16) -> u16 {
let index = state_id as usize;
if index >= Self::RAW_ID_FROM_STATE_ID.len() {
0
} else {
Self::RAW_ID_FROM_STATE_ID[state_id as usize]
unsafe { *Self::RAW_ID_FROM_STATE_ID.get_unchecked(index) }
}
}
#[doc = r" Get a block from a state id."]
#[inline]
pub const fn from_state_id(id: u16) -> &'static Self {
if id as usize >= Self::RAW_ID_FROM_STATE_ID.len() {
pub fn from_state_id(id: u16) -> &'static Self {
let index = id as usize;
if index >= Self::RAW_ID_FROM_STATE_ID.len() {
return &Self::AIR;
}
Self::from_id(Self::RAW_ID_FROM_STATE_ID[id as usize])
let raw_id = unsafe { *Self::RAW_ID_FROM_STATE_ID.get_unchecked(index) };
Self::from_id(raw_id)
}
#[doc = r" Try to parse a block from an item id."]

View File

@ -37,7 +37,6 @@ pub enum RandomGenerator {
Legacy(LegacyRand),
}
#[derive(Clone)]
#[enum_dispatch(RandomDeriverImpl)]
pub enum RandomDeriver {
Xoroshiro(XoroshiroSplitter),

View File

@ -17,13 +17,13 @@ impl RawBlockState {
#[inline]
#[must_use]
pub const fn to_block(&self) -> &'static Block {
pub fn to_block(&self) -> &'static Block {
Block::from_state_id(self.0)
}
#[inline]
#[must_use]
pub const fn to_block_id(&self) -> u16 {
pub fn to_block_id(&self) -> u16 {
Block::get_raw_id_from_state_id(self.0)
}
}

View File

@ -7,7 +7,6 @@ use pumpkin_data::block_properties::{blocks_movement, is_air};
use pumpkin_data::chunk::ChunkStatus;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::tag::Block::MINECRAFT_LEAVES;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockState};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::nbt_long_array;
@ -536,7 +535,7 @@ impl ChunkData {
let pos = BlockPos::new(x as i32, y, z as i32);
let state_id = self.section.get_block_absolute_y(x, y, z).unwrap();
let block_state = BlockState::from_id(state_id);
let block = Block::from_state_id(state_id);
let block = Block::get_raw_id_from_state_id(state_id);
if !block_state.is_air() && !has_found[ChunkHeightmapType::WorldSurface as usize] {
heightmaps.set(ChunkHeightmapType::WorldSurface, pos, self.section.min_y);
@ -544,8 +543,7 @@ impl ChunkData {
}
let is_motion_blocking = blocks_movement(block_state, block)
|| Fluid::from_registry_key(block.registry_key())
.is_some_and(|fluid| !fluid.states.is_empty());
|| Fluid::from_id(block).is_some_and(|fluid| !fluid.states.is_empty());
if !has_found[ChunkHeightmapType::MotionBlocking as usize] && is_motion_blocking {
heightmaps.set(ChunkHeightmapType::MotionBlocking, pos, self.section.min_y);
@ -554,7 +552,7 @@ impl ChunkData {
if !has_found[ChunkHeightmapType::MotionBlockingNoLeaves as usize]
&& is_motion_blocking
&& !block.has_tag(&MINECRAFT_LEAVES)
&& !MINECRAFT_LEAVES.1.contains(&block)
{
heightmaps.set(
ChunkHeightmapType::MotionBlockingNoLeaves,

View File

@ -803,8 +803,8 @@ impl Chunk {
for z in 0..4 {
for x in 0..4 {
let biome = proto_chunk.get_biome(x as i32, absolute_biome_y, z as i32);
section.biomes.set(x, relative_y, z, biome.id);
let biome = proto_chunk.get_biome_id(x as i32, absolute_biome_y, z as i32);
section.biomes.set(x, relative_y, z, biome);
}
}
}
@ -1131,7 +1131,7 @@ impl GenerationCache for Cache {
)
.unwrap()
}
Chunk::Proto(data) => data.get_biome_for_terrain_gen(x, y, z),
Chunk::Proto(data) => data.get_terrain_gen_biome(x, y, z),
}
}

View File

@ -1,9 +1,11 @@
use itertools::Itertools;
use pumpkin_data::fluid::{Fluid, FluidState};
use pumpkin_data::{Block, BlockDirection, BlockState, tag::Taggable};
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use serde::Deserialize;
use crate::block::RawBlockState;
use crate::generation::proto_chunk::GenerationCache;
use crate::{block::BlockStateCodec, world::BlockRegistryExt};
@ -128,8 +130,10 @@ pub struct MatchingBlockTagPredicate {
impl MatchingBlockTagPredicate {
pub fn test<T: GenerationCache>(&self, chunk: &T, pos: &BlockPos) -> bool {
let block = self.offset.get_block(chunk, pos);
block.is_tagged_with(&self.tag).unwrap()
let block = self.offset.get_raw(chunk, pos);
get_tag_ids(RegistryKey::Block, &self.tag)
.unwrap()
.contains(&block.to_block_id())
}
}
@ -267,6 +271,11 @@ impl OffsetBlocksBlockPredicate {
}
*pos
}
pub fn get_raw<T: GenerationCache>(&self, chunk: &T, pos: &BlockPos) -> RawBlockState {
let pos = self.get(pos);
GenerationCache::get_block_state(chunk, &pos.0)
}
pub fn get_block<T: GenerationCache>(&self, chunk: &T, pos: &BlockPos) -> &'static Block {
let pos = self.get(pos);
GenerationCache::get_block_state(chunk, &pos.0).to_block()

View File

@ -1,4 +1,4 @@
use pumpkin_data::{BlockState, chunk::DoublePerlinNoiseParameters};
use pumpkin_data::BlockState;
use pumpkin_util::{
DoublePerlinNoiseParametersCodec,
math::{
@ -85,10 +85,10 @@ pub struct DualNoiseBlockStateProvider {
impl DualNoiseBlockStateProvider {
pub fn get(&self, pos: BlockPos) -> &'static BlockState {
let noise = perlin_codec_to_static(self.slow_noise.clone());
let sampler = DoublePerlinNoiseSampler::new(
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.base.base.seed as u64)),
&noise,
self.slow_noise.first_octave,
&self.slow_noise.amplitudes,
false,
);
let slow_noise =
@ -103,14 +103,10 @@ impl DualNoiseBlockStateProvider {
let mut list = Vec::with_capacity(mapped as usize);
for i in 0..mapped {
let value = self.get_slow_noise(i as f64 * 54545.0, 0.0, i as f64 * 34234.0, &sampler);
list.push(
self.base
.get_state_by_value(&self.base.states, value)
.clone(),
);
list.push(self.base.get_state_by_value(&self.base.states, value));
}
let value = self.base.base.get_noise(pos);
self.base.get_state_by_value(&list, value).get_state()
self.base.get_state_by_value_2(&list, value).get_state()
}
fn get_slow_noise(&self, x: f64, y: f64, z: f64, sampler: &DoublePerlinNoiseSampler) -> f64 {
@ -151,17 +147,12 @@ pub struct NoiseBlockStateProviderBase {
scale: f32,
}
fn perlin_codec_to_static(noise: DoublePerlinNoiseParametersCodec) -> DoublePerlinNoiseParameters {
let amplitudes_static: &'static [f64] = noise.amplitudes.leak();
DoublePerlinNoiseParameters::new(noise.first_octave, amplitudes_static, "none")
}
impl NoiseBlockStateProviderBase {
pub fn get_noise(&self, pos: BlockPos) -> f64 {
let noise = perlin_codec_to_static(self.noise.clone());
let sampler = DoublePerlinNoiseSampler::new(
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.seed as u64)),
&noise,
self.noise.first_octave,
&self.noise.amplitudes,
false,
);
sampler.sample(
@ -193,6 +184,15 @@ impl NoiseBlockStateProvider {
let val = f64::midpoint(1.0, value).clamp(0.0, 0.9999);
&states[(val * states.len() as f64) as usize]
}
fn get_state_by_value_2<'a>(
&self,
states: &'a [&BlockStateCodec],
value: f64,
) -> &'a BlockStateCodec {
let val = f64::midpoint(1.0, value).clamp(0.0, 0.9999);
states[(val * states.len() as f64) as usize]
}
}
#[derive(Deserialize)]

View File

@ -2,7 +2,6 @@ use pumpkin_data::{
Block, BlockState,
block_properties::{BambooLeaves, BambooLikeProperties, BlockProperties, Integer0To1},
tag,
tag::Taggable,
};
use pumpkin_util::{
math::position::BlockPos,
@ -42,7 +41,7 @@ impl BambooFeature {
let block_below =
BlockPos::new(x, chunk.top_block_height_exclusive(x, z) - 1, z);
let block = GenerationCache::get_block_state(chunk, &block_below.0);
if !block.to_block().has_tag(&tag::Block::MINECRAFT_DIRT) {
if !tag::Block::MINECRAFT_DIRT.1.contains(&block.to_block_id()) {
continue;
}
chunk.set_block_state(&block_below.0, Block::PODZOL.default_state);

View File

@ -2,8 +2,7 @@ use crate::generation::proto_chunk::GenerationCache;
use pumpkin_data::{
Block, BlockDirection, BlockState,
block_properties::{BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties},
tag,
tag::{RegistryKey, Taggable, get_tag_values},
tag::{self, RegistryKey, get_tag_ids},
};
use pumpkin_util::{
math::position::BlockPos,
@ -23,11 +22,11 @@ impl CoralFeature {
state: &BlockState,
pos: BlockPos,
) -> bool {
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block();
let above_block = GenerationCache::get_block_state(chunk, &pos.up().0).to_block();
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
let above_block = GenerationCache::get_block_state(chunk, &pos.up().0).to_block_id();
if block != &Block::WATER && !block.has_tag(&tag::Block::MINECRAFT_CORALS)
|| above_block != &Block::WATER
if block != Block::WATER && !&tag::Block::MINECRAFT_CORALS.1.contains(&block)
|| above_block != Block::WATER
{
return false;
}
@ -48,7 +47,7 @@ impl CoralFeature {
for dir in BlockDirection::horizontal() {
let dir_pos = pos.offset(dir.to_offset());
if random.next_f32() >= 0.2
|| GenerationCache::get_block_state(chunk, &dir_pos.0).to_block() != &Block::WATER
|| GenerationCache::get_block_state(chunk, &dir_pos.0).to_block_id() != Block::WATER
{
continue;
}
@ -84,8 +83,8 @@ impl CoralFeature {
}
pub fn get_random_tag_entry_block(tag: &str, random: &mut RandomGenerator) -> &'static Block {
let values = get_tag_values(RegistryKey::Block, tag).unwrap();
let values = get_tag_ids(RegistryKey::Block, tag).unwrap();
let value = values[random.next_bounded_i32(values.len() as i32) as usize];
Block::from_name(value).unwrap()
Block::from_id(value)
}
}

View File

@ -29,9 +29,9 @@ impl DesertWellFeature {
while chunk.is_air(&block_pos.0) && block_pos.0.y > chunk.bottom_y() as i32 + 2 {
block_pos = block_pos.down();
}
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block();
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
const CAN_GENERATE: Block = Block::SAND;
if CAN_GENERATE.id != block.id {
if CAN_GENERATE.id != block {
return false;
}

View File

@ -1,20 +1,25 @@
use crate::generation::proto_chunk::GenerationCache;
use pumpkin_data::Block;
use pumpkin_data::tag;
use pumpkin_data::{Block, tag::Taggable};
use pumpkin_util::math::position::BlockPos;
pub mod cluster;
pub mod large;
pub mod small;
pub(super) fn can_replace(block: &Block) -> bool {
block == &Block::DRIPSTONE_BLOCK
|| block.has_tag(&tag::Block::MINECRAFT_DRIPSTONE_REPLACEABLE_BLOCKS)
pub(super) fn can_replace(block: u16) -> bool {
block == Block::DRIPSTONE_BLOCK
|| tag::Block::MINECRAFT_DRIPSTONE_REPLACEABLE_BLOCKS
.1
.contains(&block)
}
pub(super) fn gen_dripstone<T: GenerationCache>(chunk: &mut T, pos: BlockPos) -> bool {
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block();
if block.has_tag(&tag::Block::MINECRAFT_DRIPSTONE_REPLACEABLE_BLOCKS) {
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
if tag::Block::MINECRAFT_DRIPSTONE_REPLACEABLE_BLOCKS
.1
.contains(&block)
{
chunk.set_block_state(&pos.0, Block::DRIPSTONE_BLOCK.default_state);
return true;
}

View File

@ -36,9 +36,10 @@ impl SmallDripstoneFeature {
random: &mut RandomGenerator,
) -> Option<BlockDirection> {
let up =
super::can_replace(GenerationCache::get_block_state(chunk, &pos.up().0).to_block());
let down: bool =
super::can_replace(GenerationCache::get_block_state(chunk, &pos.down().0).to_block());
super::can_replace(GenerationCache::get_block_state(chunk, &pos.up().0).to_block_id());
let down: bool = super::can_replace(
GenerationCache::get_block_state(chunk, &pos.down().0).to_block_id(),
);
if up && down {
return if random.next_bool() {
Some(BlockDirection::Down)

View File

@ -1,4 +1,4 @@
use pumpkin_data::{Block, tag, tag::Taggable};
use pumpkin_data::{Block, tag};
use pumpkin_util::{
math::position::BlockPos,
random::{RandomGenerator, RandomImpl},
@ -29,7 +29,10 @@ impl NetherForestVegetationFeature {
) -> bool {
let state = GenerationCache::get_block_state(chunk, &pos.down().0);
if !state.to_block().has_tag(&tag::Block::MINECRAFT_NYLIUM) {
if !tag::Block::MINECRAFT_NYLIUM
.1
.contains(&state.to_block_id())
{
return false;
}
let mut result = false;

View File

@ -44,7 +44,7 @@ impl ReplaceBlobsFeature {
break;
}
let current_state = GenerationCache::get_block_state(chunk, &iter_pos.0);
if current_state.to_block() != target {
if current_state.to_block_id() != target.id {
continue;
}
chunk.set_block_state(&iter_pos.0, state);
@ -61,7 +61,7 @@ impl ReplaceBlobsFeature {
) -> Option<BlockPos> {
while pos.0.y > chunk.bottom_y() as i32 + 1 {
let state = GenerationCache::get_block_state(chunk, &pos.0);
if state.to_block() == target {
if state.to_block_id() == target.id {
return Some(pos);
}

View File

@ -1,14 +1,14 @@
use core::f32;
use pumpkin_data::{BlockDirection, BlockState};
use pumpkin_data::BlockDirection;
use pumpkin_util::{
math::{lerp, position::BlockPos, vector3::Vector3},
random::{RandomGenerator, RandomImpl},
};
use serde::Deserialize;
use crate::generation::proto_chunk::GenerationCache;
use crate::{block::BlockStateCodec, generation::rule::RuleTest, world::BlockRegistryExt};
use crate::{block::RawBlockState, generation::proto_chunk::GenerationCache};
#[derive(Deserialize)]
pub struct OreFeature {
@ -187,13 +187,7 @@ impl OreFeature {
let block_state = GenerationCache::get_block_state(chunk, &pos_vec);
for target in &self.targets {
if self.should_place(
chunk,
block_state.to_state(),
random,
target,
&mutable_pos,
) {
if self.should_place(chunk, block_state, random, target, &mutable_pos) {
chunk.set_block_state(&pos_vec, target.state.get_state());
placed_blocks_count += 1;
break; // Equivalent to 'continue block11;'
@ -209,12 +203,12 @@ impl OreFeature {
fn should_place<T: GenerationCache>(
&self,
chunk: &T,
state: &'static BlockState,
state: RawBlockState,
random: &mut RandomGenerator,
target: &OreTarget,
pos: &BlockPos,
) -> bool {
if !target.target.test(state, random) {
if !target.target.test(&state, random) {
return false;
}
if Self::should_not_discard(random, self.discard_chance_on_air_exposure) {

View File

@ -30,7 +30,7 @@ impl SeaPickleFeature {
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
if GenerationCache::get_block_state(chunk, &pos.0).to_block() != &Block::WATER {
if GenerationCache::get_block_state(chunk, &pos.0).to_block_id() != Block::WATER {
continue;
}
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);

View File

@ -28,11 +28,12 @@ impl SeagrassFeature {
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
let top_pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
if GenerationCache::get_block_state(chunk, &top_pos.0).to_block() == &Block::WATER {
if GenerationCache::get_block_state(chunk, &top_pos.0).to_block_id() == Block::WATER {
let tall = random.next_f64() < self.probability as f64;
if tall {
let tall_pos = top_pos.up();
if GenerationCache::get_block_state(chunk, &tall_pos.0).to_block() == &Block::WATER
if GenerationCache::get_block_state(chunk, &tall_pos.0).to_block_id()
== Block::WATER
{
let mut props = TallSeagrassLikeProperties::default(&Block::TALL_SEAGRASS);
props.half = DoubleBlockHalf::Upper;

View File

@ -25,7 +25,7 @@ impl BlockWrapper {
fn contains(&self, name: &str) -> bool {
match self {
Self::Single(s) => s == name,
Self::Multi(h) => h.contains(&name.to_string()),
Self::Multi(h) => h.iter().any(|s| s == name),
}
}
}

View File

@ -1,10 +1,7 @@
use super::TreeDecorator;
use crate::generation::block_state_provider::BlockStateProvider;
use crate::generation::proto_chunk::GenerationCache;
use pumpkin_data::{
Block,
tag::{Block::MINECRAFT_LEAVES, Taggable},
};
use pumpkin_data::{Block, tag::Block::MINECRAFT_LEAVES};
use pumpkin_util::{
math::{block_box::BlockBox, position::BlockPos},
random::{RandomGenerator, RandomImpl},
@ -72,9 +69,9 @@ impl PlaceOnGroundTreeDecorator {
let up_pos = pos.up();
let up_state = GenerationCache::get_block_state(chunk, &up_pos.0);
// TODO
if (up_state.to_state().is_air() || up_state.to_block() == &Block::VINE)
if (up_state.to_state().is_air() || up_state.to_block_id() == Block::VINE)
&& state.to_state().is_full_cube()
&& !state.to_block().has_tag(&MINECRAFT_LEAVES)
&& !MINECRAFT_LEAVES.1.contains(&state.to_block_id())
// TODO: using heightmap seems not to work
{
chunk.set_block_state(&up_pos.0, self.block_state_provider.get(random, up_pos));

View File

@ -132,7 +132,7 @@ impl FoliagePlacer {
block_state: &BlockState,
) {
let block = GenerationCache::get_block_state(chunk, &pos.0);
if !TreeFeature::can_replace(block.to_state(), block.to_block()) {
if !TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
return;
}
chunk.set_block_state(&pos.0, block_state);

View File

@ -1,7 +1,7 @@
use decorator::TreeDecorator;
use foliage::FoliagePlacer;
use pumpkin_data::tag;
use pumpkin_data::{Block, BlockState, tag::Taggable};
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
use serde::Deserialize;
use trunk::TrunkPlacer;
@ -51,16 +51,19 @@ impl TreeFeature {
true
}
pub fn can_replace_or_log(state: &BlockState, block: &Block) -> bool {
Self::can_replace(state, block) || block.has_tag(&tag::Block::MINECRAFT_LOGS)
pub fn can_replace_or_log(state: &BlockState, block: u16) -> bool {
Self::can_replace(state, block) || tag::Block::MINECRAFT_LOGS.1.contains(&block)
}
pub fn is_air_or_leaves(state: &BlockState, block: &Block) -> bool {
state.is_air() || block.has_tag(&tag::Block::MINECRAFT_LEAVES)
pub fn is_air_or_leaves(state: &BlockState, block: u16) -> bool {
state.is_air() || tag::Block::MINECRAFT_LEAVES.1.contains(&block)
}
pub fn can_replace(state: &BlockState, block: &Block) -> bool {
state.is_air() || block.has_tag(&tag::Block::MINECRAFT_REPLACEABLE_BY_TREES)
pub fn can_replace(state: &BlockState, block: u16) -> bool {
state.is_air()
|| tag::Block::MINECRAFT_REPLACEABLE_BY_TREES
.1
.contains(&block)
}
fn generate_main<T: GenerationCache>(
@ -119,9 +122,9 @@ impl TreeFeature {
for z in -j..=j {
let pos = BlockPos(init_pos.0.add_raw(x, y as i32, z));
let rstate = GenerationCache::get_block_state(chunk, &pos.0);
let block = rstate.to_block();
let block = rstate.to_block_id();
if Self::can_replace_or_log(rstate.to_state(), block)
&& (self.ignore_vines || block != &Block::VINE)
&& (self.ignore_vines || block != Block::VINE)
{
continue;
}

View File

@ -68,7 +68,7 @@ impl DarkOakTrunkPlacer {
let pos = BlockPos::new(x, current_y, z);
// TODO: support multiple chunks
let state = GenerationCache::get_block_state(chunk, &pos.0);
if !TreeFeature::is_air_or_leaves(state.to_state(), state.to_block()) {
if !TreeFeature::is_air_or_leaves(state.to_state(), state.to_block_id()) {
continue;
}
if placer.try_place(chunk, &pos, trunk_block) {

View File

@ -145,7 +145,7 @@ impl FancyTrunkPlacer {
if make {
let axis = Self::get_log_axis(start_pos, block_pos_2.0);
if TreeFeature::can_replace(block.to_state(), block.to_block()) {
if TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
let block = Block::from_state_id(trunk_provider.id);
let original_props = &block.properties(trunk_provider.id).unwrap().to_props();
let axis = axis.to_value();
@ -167,7 +167,7 @@ impl FancyTrunkPlacer {
}
}
if TreeFeature::can_replace_or_log(block.to_state(), block.to_block()) {
if TreeFeature::can_replace_or_log(block.to_state(), block.to_block_id()) {
continue;
}
return (false, logs);

View File

@ -1,6 +1,6 @@
use fancy::FancyTrunkPlacer;
use pumpkin_data::tag;
use pumpkin_data::{Block, BlockState, tag::Taggable};
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{
math::position::BlockPos,
random::{RandomGenerator, RandomImpl},
@ -49,11 +49,11 @@ impl TrunkPlacer {
force_dirt: bool,
dirt_state: &BlockState,
) {
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block();
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
if force_dirt
|| !(block.has_tag(&tag::Block::MINECRAFT_DIRT)
&& block != &Block::GRASS_BLOCK
&& block != &Block::MYCELIUM)
|| !(tag::Block::MINECRAFT_DIRT.1.contains(&block)
&& block != Block::GRASS_BLOCK
&& block != Block::MYCELIUM)
{
chunk.set_block_state(&pos.0, dirt_state);
}
@ -66,7 +66,7 @@ impl TrunkPlacer {
trunk_block: &BlockState,
) -> bool {
let block = GenerationCache::get_block_state(chunk, &pos.0);
if TreeFeature::can_replace(block.to_state(), block.to_block()) {
if TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
chunk.set_block_state(&pos.0, trunk_block);
return true;
}
@ -80,7 +80,7 @@ impl TrunkPlacer {
trunk_block: &BlockState,
) -> bool {
let block = GenerationCache::get_block_state(chunk, &pos.0);
if TreeFeature::can_replace_or_log(block.to_state(), block.to_block()) {
if TreeFeature::can_replace_or_log(block.to_state(), block.to_block_id()) {
return self.place(chunk, pos, trunk_block);
}
false

View File

@ -343,7 +343,7 @@ impl CountOnEveryLayerPlacementModifier {
if !Self::blocks_spawn(&next_block_state)
&& Self::blocks_spawn(&current_block_state)
&& next_block_state.to_block() != &Block::BEDROCK
&& next_block_state.to_block_id() != Block::BEDROCK
{
if found_count == target_y {
return mutable_pos.0.y + 1;
@ -477,7 +477,6 @@ impl ConditionalPlacementModifier for BiomePlacementModifier {
_random: &mut RandomGenerator,
pos: BlockPos,
) -> bool {
// we check if the current feature can be applied to the biome at the pos
let name = format!("minecraft:{this_feature}");
let biome = chunk.get_biome_for_terrain_gen(pos.0.x, pos.0.y, pos.0.z);

View File

@ -46,13 +46,14 @@ impl BlockStateSampler {
pub fn sample(
&mut self,
router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig,
pos: &impl NoisePos,
sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> {
match self {
Self::Aquifer(aquifer) => aquifer.apply(router, pos, sample_options, height_estimator),
Self::Ore(ore) => ore.sample(router, pos, sample_options),
Self::Ore(ore) => ore.sample(router, random_config, pos, sample_options),
}
}
}
@ -70,12 +71,15 @@ impl ChainedBlockStateSampler {
fn sample(
&mut self,
router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig,
pos: &impl NoisePos,
sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> {
for sampler in &mut self.samplers {
if let Some(state) = sampler.sample(router, pos, sample_options, height_estimator) {
if let Some(state) =
sampler.sample(router, random_config, pos, sample_options, height_estimator)
{
return Some(state);
}
}
@ -225,10 +229,9 @@ impl<'a> ChunkNoiseGenerator<'a> {
};
let samplers: Box<[BlockStateSampler]> = if ore_veins {
let ore_sampler = OreVeinSampler::new(random_config.ore_random_deriver.clone());
Box::new([
BlockStateSampler::Aquifer(aquifer_sampler),
BlockStateSampler::Ore(ore_sampler),
BlockStateSampler::Ore(OreVeinSampler),
])
} else {
Box::new([BlockStateSampler::Aquifer(aquifer_sampler)])
@ -369,6 +372,7 @@ impl<'a> ChunkNoiseGenerator<'a> {
#[expect(clippy::too_many_arguments)]
pub fn sample_block_state(
&mut self,
random_config: &GlobalRandomConfig,
start_x: i32,
start_y: i32,
start_z: i32,
@ -394,8 +398,13 @@ impl<'a> ChunkNoiseGenerator<'a> {
0,
);
self.state_sampler
.sample(&mut self.router, &pos, &options, height_estimator)
self.state_sampler.sample(
&mut self.router,
random_config,
&pos,
&options,
height_estimator,
)
}
#[inline]

View File

@ -1,27 +1,24 @@
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{
math::clamped_map,
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
random::{RandomDeriverImpl, RandomImpl},
};
use crate::generation::noise::router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions, chunk_noise_router::ChunkNoiseRouter,
density_function::NoisePos,
use crate::{
GlobalRandomConfig,
generation::noise::router::{
chunk_density_function::ChunkNoiseFunctionSampleOptions,
chunk_noise_router::ChunkNoiseRouter, density_function::NoisePos,
},
};
pub struct OreVeinSampler {
random_deriver: RandomDeriver,
}
pub struct OreVeinSampler;
impl OreVeinSampler {
#[must_use]
pub const fn new(random_deriver: RandomDeriver) -> Self {
Self { random_deriver }
}
pub fn sample(
&self,
router: &mut ChunkNoiseRouter,
random_config: &GlobalRandomConfig,
pos: &impl NoisePos,
sample_options: &ChunkNoiseFunctionSampleOptions,
) -> Option<&'static BlockState> {
@ -40,7 +37,10 @@ impl OreVeinSampler {
let mapped_diff = clamped_map(closest_to_bound as f64, 0f64, 20f64, -0.2f64, 0f64);
let abs_sample = vein_toggle.abs();
if abs_sample + mapped_diff >= 0.4f32 as f64 {
let mut random = self.random_deriver.split_pos(pos.x(), block_y, pos.z());
let mut random =
random_config
.ore_random_deriver
.split_pos(pos.x(), block_y, pos.z());
let vein_ridged_sample = router.vein_ridged(pos, sample_options);
if random.next_f32() <= 0.7f32 && vein_ridged_sample < 0f64 {

View File

@ -18,14 +18,20 @@ impl DoublePerlinNoiseSampler {
self.max_value
}
pub fn new(
pub fn from_params(
rand: &mut RandomGenerator,
parameters: &DoublePerlinNoiseParameters,
legacy: bool,
) -> Self {
let first_octave = parameters.first_octave;
let amplitudes = parameters.amplitudes;
Self::new(rand, parameters.first_octave, parameters.amplitudes, legacy)
}
pub fn new(
rand: &mut RandomGenerator,
first_octave: i32,
amplitudes: &[f64],
legacy: bool,
) -> Self {
let first_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes, legacy);
let second_sampler = OctavePerlinNoiseSampler::new(rand, first_octave, amplitudes, legacy);
@ -75,7 +81,7 @@ mod double_perlin_noise_sampler_test {
let mut rand_gen = RandomGenerator::Legacy(rand);
let params = DoublePerlinNoiseParameters::new(0, &[4f64], "");
let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, &params, true);
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, &params, true);
let values = [
(
@ -174,7 +180,7 @@ mod double_perlin_noise_sampler_test {
let params = DoublePerlinNoiseParameters::new(1, &[2f64, 4f64], "");
let sampler = DoublePerlinNoiseSampler::new(&mut rand_gen, &params, false);
let sampler = DoublePerlinNoiseSampler::from_params(&mut rand_gen, &params, false);
let values = [
(

View File

@ -79,7 +79,7 @@ impl<'a> DoublePerlinNoiseBuilder<'a> {
.random_config
.base_random_deriver
.split_string(parameters.id());
DoublePerlinNoiseSampler::new(&mut random, parameters, false)
DoublePerlinNoiseSampler::from_params(&mut random, parameters, false)
}
}

View File

@ -5,9 +5,7 @@ use pumpkin_data::block_properties::is_air;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::{Fluid, FluidState};
use pumpkin_data::tag;
use pumpkin_data::{
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
};
use pumpkin_data::{Block, BlockState, block_properties::blocks_movement, chunk::Biome};
use pumpkin_util::random::{RandomImpl, get_carver_seed};
use pumpkin_util::{
HeightMap,
@ -474,7 +472,7 @@ impl ProtoChunk {
}
if !block_state.is_air() {
self.maybe_update_surface_height_map(local_x, y, local_z);
let block = Block::from_state_id(block_state.id);
let block = Block::get_raw_id_from_state_id(block_state.id);
let blocks_movement = blocks_movement(block_state, block);
if blocks_movement {
@ -482,7 +480,7 @@ impl ProtoChunk {
}
if blocks_movement || block_state.is_liquid() {
self.maybe_update_motion_blocking_height_map(local_x, y, local_z);
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
if !tag::Block::MINECRAFT_LEAVES.1.contains(&block) {
{
self.maybe_update_motion_blocking_no_leaves_height_map(local_x, y, local_z);
}
@ -572,7 +570,11 @@ impl ProtoChunk {
&noise_router.surface_estimator,
&surface_config,
);
self.populate_noise(&mut noise_sampler, &mut surface_height_estimate_sampler);
self.populate_noise(
&mut noise_sampler,
random_config,
&mut surface_height_estimate_sampler,
);
self.stage = StagedChunkEnum::Noise;
}
@ -673,6 +675,7 @@ impl ProtoChunk {
pub fn populate_noise(
&mut self,
noise_sampler: &mut ChunkNoiseGenerator,
random_config: &GlobalRandomConfig,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) {
let h_count = noise_sampler.horizontal_cell_block_count() as i32;
@ -724,6 +727,7 @@ impl ProtoChunk {
let block_state = noise_sampler
.sample_block_state(
random_config,
sample_start_x,
sample_start_y,
sample_start_z,
@ -744,7 +748,7 @@ impl ProtoChunk {
}
#[must_use]
pub fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome {
pub fn get_terrain_gen_biome_id(&self, x: i32, y: i32, z: i32) -> u8 {
// TODO: See if we can cache this value
let seed_biome_pos = biome::get_biome_blend(
self.bottom_y(),
@ -755,7 +759,11 @@ impl ProtoChunk {
z,
);
self.get_biome(seed_biome_pos.x, seed_biome_pos.y, seed_biome_pos.z)
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()
}
/// Constructs the terrain surface, although "surface" is a misnomer as it also places underground blocks like bedrock and deepslate.
@ -798,8 +806,8 @@ impl ProtoChunk {
top_block
};
let this_biome = self.get_biome_for_terrain_gen(x, biome_y, z);
if this_biome == &Biome::ERODED_BADLANDS {
let this_biome = self.get_terrain_gen_biome_id(x, biome_y, z);
if this_biome == Biome::ERODED_BADLANDS {
terrain_cache
.terrain_builder
.place_badlands_pillar(self, x, z, top_block);
@ -857,7 +865,7 @@ impl ProtoChunk {
// panic!("Blending with biome {:?} at: {:?}", biome, biome_pos);
if state.id == self.default_block.id {
context.biome = self.get_biome_for_terrain_gen(
context.biome = self.get_terrain_gen_biome(
context.block_pos_x,
context.block_pos_y,
context.block_pos_z,
@ -873,13 +881,13 @@ impl ProtoChunk {
}
}
}
if this_biome == &Biome::FROZEN_OCEAN || this_biome == &Biome::DEEP_FROZEN_OCEAN {
if this_biome == Biome::FROZEN_OCEAN || this_biome == Biome::DEEP_FROZEN_OCEAN {
let surface_estimate =
estimate_surface_height(&mut context, surface_height_estimate_sampler);
terrain_cache.terrain_builder.place_iceberg(
self,
this_biome,
Biome::from_id(this_biome).unwrap(),
x,
z,
surface_estimate,

View File

@ -1,6 +1,7 @@
use pumpkin_data::{Block, BlockState};
use serde::Deserialize;
use crate::block::RawBlockState;
#[derive(Deserialize)]
pub struct BlockMatchRuleTest {
// This should be a Block codec, so this is wrong
@ -8,8 +9,7 @@ pub struct BlockMatchRuleTest {
}
impl BlockMatchRuleTest {
pub fn test(&self, state: &BlockState) -> bool {
Block::from_state_id(state.id).name
== self.block.strip_prefix("minecraft:").unwrap_or(&self.block)
pub fn test(&self, state: &RawBlockState) -> bool {
state.to_block().name == self.block.strip_prefix("minecraft:").unwrap_or(&self.block)
}
}

View File

@ -1,7 +1,6 @@
use pumpkin_data::BlockState;
use serde::Deserialize;
use crate::block::BlockStateCodec;
use crate::block::{BlockStateCodec, RawBlockState};
#[derive(Deserialize)]
pub struct BlockStateMatchRuleTest {
@ -9,7 +8,7 @@ pub struct BlockStateMatchRuleTest {
}
impl BlockStateMatchRuleTest {
pub fn test(&self, state: &BlockState) -> bool {
state.id == self.block_state.get_state_id()
pub fn test(&self, state: &RawBlockState) -> bool {
state.0 == self.block_state.get_state_id()
}
}

View File

@ -1,12 +1,13 @@
use block_match::BlockMatchRuleTest;
use block_state_match::BlockStateMatchRuleTest;
use pumpkin_data::BlockState;
use pumpkin_util::random::RandomGenerator;
use random_block_match::RandomBlockMatchRuleTest;
use random_block_state_match::RandomBlockStateMatchRuleTest;
use serde::Deserialize;
use tag_match::TagMatchRuleTest;
use crate::block::RawBlockState;
mod block_match;
mod block_state_match;
mod random_block_match;
@ -31,7 +32,7 @@ pub enum RuleTest {
}
impl RuleTest {
pub fn test(&self, state: &BlockState, random: &mut RandomGenerator) -> bool {
pub fn test(&self, state: &RawBlockState, random: &mut RandomGenerator) -> bool {
match self {
Self::AlwaysTrue => true,
Self::BlockMatch(rule) => rule.test(state),

View File

@ -1,7 +1,8 @@
use pumpkin_data::{Block, BlockState};
use pumpkin_util::random::{RandomGenerator, RandomImpl};
use serde::Deserialize;
use crate::block::RawBlockState;
#[derive(Deserialize)]
pub struct RandomBlockMatchRuleTest {
// This should be a Block codec, so this is wrong
@ -10,9 +11,8 @@ pub struct RandomBlockMatchRuleTest {
}
impl RandomBlockMatchRuleTest {
pub fn test(&self, state: &BlockState, random: &mut RandomGenerator) -> bool {
Block::from_state_id(state.id).name
== self.block.strip_prefix("minecraft:").unwrap_or(&self.block)
pub fn test(&self, state: &RawBlockState, random: &mut RandomGenerator) -> bool {
state.to_block().name == self.block.strip_prefix("minecraft:").unwrap_or(&self.block)
&& random.next_f32() < self.probability
}
}

View File

@ -1,8 +1,7 @@
use pumpkin_data::BlockState;
use pumpkin_util::random::{RandomGenerator, RandomImpl};
use serde::Deserialize;
use crate::block::BlockStateCodec;
use crate::block::{BlockStateCodec, RawBlockState};
#[derive(Deserialize)]
pub struct RandomBlockStateMatchRuleTest {
@ -11,7 +10,7 @@ pub struct RandomBlockStateMatchRuleTest {
}
impl RandomBlockStateMatchRuleTest {
pub fn test(&self, state: &BlockState, random: &mut RandomGenerator) -> bool {
state.id == self.block_state.get_state_id() && random.next_f32() < self.probability
pub fn test(&self, state: &RawBlockState, random: &mut RandomGenerator) -> bool {
state.0 == self.block_state.get_state_id() && random.next_f32() < self.probability
}
}

View File

@ -1,15 +1,16 @@
use pumpkin_data::{Block, BlockState, tag::Taggable};
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
use serde::Deserialize;
use crate::block::RawBlockState;
#[derive(Deserialize)]
pub struct TagMatchRuleTest {
tag: String,
}
impl TagMatchRuleTest {
pub fn test(&self, state: &BlockState) -> bool {
Block::from_state_id(state.id)
.is_tagged_with(&self.tag)
.unwrap()
pub fn test(&self, state: &RawBlockState) -> bool {
let values = get_tag_ids(RegistryKey::Block, &self.tag).unwrap();
values.contains(&state.to_block_id())
}
}

View File

@ -1,6 +1,6 @@
use std::{collections::HashMap, sync::LazyLock};
use pumpkin_data::{chunk::Biome, tag::Taggable};
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
use pumpkin_util::include_json_static;
use serde::Deserialize;
@ -185,14 +185,21 @@ impl StructureKeys {
if let Some(pos) = structure_pos {
// Get the biome at the structure's starting position
let current_biome = chunk.get_biome(
let current_biome = chunk.get_biome_id(
biome_coords::from_block(pos.start_pos.0.x),
biome_coords::from_block(pos.start_pos.0.y),
biome_coords::from_block(pos.start_pos.0.z),
);
let biomes = Biome::get_tag_values(&structure.biomes).unwrap();
) as u16;
let biomes = get_tag_ids(
RegistryKey::WorldgenBiome,
structure
.biomes
.strip_prefix("#")
.unwrap_or(&structure.biomes),
)
.unwrap();
// Check if the biome is allowed for this structure
if biomes.contains(&current_biome.registry_id) {
if biomes.contains(&current_biome) {
return Some(pos);
}
}

View File

@ -170,7 +170,7 @@ impl ConcentricRingsStructurePlacement {
// ^ (salt as i64);
// (seed.abs() % 400) == 0
rand::random_bool(1.0 / 100.0)
rand::random_bool(1.0 / 1000.0)
}
}

View File

@ -73,19 +73,19 @@ impl StructurePieceBase for BuriedTreasurePiece {
let state = chunk.get_block_state(&pos.0);
let down_pos = pos.down();
let down_raw_state = chunk.get_block_state(&down_pos.0);
let down_block = down_raw_state.to_block();
let down_block = down_raw_state.to_block_id();
if down_block == &Block::SANDSTONE
|| down_block == &Block::STONE
|| down_block == &Block::ANDESITE
|| down_block == &Block::GRANITE
|| down_block == &Block::DIORITE
if down_block == Block::SANDSTONE
|| down_block == Block::STONE
|| down_block == Block::ANDESITE
|| down_block == Block::GRANITE
|| down_block == Block::DIORITE
{
for dir in BlockDirection::all() {
let offset_pos = pos.offset(dir.to_offset());
let dir_state = chunk.get_block_state(&offset_pos.0);
if !dir_state.to_state().is_air() && !Self::is_liquid(dir_state.to_block()) {
if !dir_state.to_state().is_air() && !Self::is_liquid(dir_state.to_block_id()) {
continue;
}
@ -93,7 +93,7 @@ impl StructurePieceBase for BuriedTreasurePiece {
let down_offset_state = chunk.get_block_state(&down_offset_pos.0);
if (down_offset_state.to_state().is_air()
|| Self::is_liquid(down_offset_state.to_block()))
|| Self::is_liquid(down_offset_state.to_block_id()))
&& dir != BlockDirection::Up
{
chunk.set_block_state(
@ -105,7 +105,8 @@ impl StructurePieceBase for BuriedTreasurePiece {
continue;
}
let state1 = if state.to_state().is_air() || Self::is_liquid(state.to_block()) {
let state1 =
if state.to_state().is_air() || Self::is_liquid(state.to_block_id()) {
Block::SAND.default_state
} else {
state.to_state()
@ -132,7 +133,7 @@ impl StructurePieceBase for BuriedTreasurePiece {
}
impl BuriedTreasurePiece {
fn is_liquid(block: &Block) -> bool {
block == &Block::WATER || block == &Block::LAVA
fn is_liquid(block: u16) -> bool {
block == Block::WATER || block == Block::LAVA
}
}

View File

@ -239,7 +239,7 @@ impl SurfaceTerrainBuilder {
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
|| (block_state.to_block_id() == WATER_BLOCK
&& y > bottom_block
&& y < sea_level
&& bottom_block != 0

View File

@ -24,6 +24,8 @@ use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use pumpkin_util::world_seed::Seed;
use rand::{RngExt, SeedableRng, rngs::SmallRng};
use std::sync::Mutex;
use std::time::Duration;
use tokio::time::timeout;
use tokio_util::sync::CancellationToken;
// use std::time::Duration;
use std::{
@ -283,13 +285,24 @@ impl Level {
};
log::info!("Joining {} synchronous threads...", handles.len());
tokio::task::spawn_blocking(move || {
let join_task = tokio::task::spawn_blocking(move || {
for handle in handles {
// Attempt to join. If a thread is stuck, this block stays alive.
let _ = handle.join();
}
})
.await
.unwrap();
});
match timeout(Duration::from_secs(3), join_task).await {
Ok(task_result) => {
task_result.unwrap();
log::info!("All threads joined successfully.");
}
Err(_) => {
log::warn!(
"Timed out waiting for synchronous threads to join. Proceeding anyway..."
);
}
}
log::debug!("Awaiting remaining async tasks...");
self.tasks.wait().await;

View File

@ -116,7 +116,11 @@ pub fn bench_create_and_populate_noise(
let mut surface_height_estimate_sampler =
SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config);
chunk.populate_noise(&mut noise_sampler, &mut surface_height_estimate_sampler);
chunk.populate_noise(
&mut noise_sampler,
random_config,
&mut surface_height_estimate_sampler,
);
}
pub fn bench_create_and_populate_biome(
@ -230,7 +234,11 @@ pub fn bench_create_and_populate_noise_with_surface(
SurfaceHeightEstimateSampler::generate(&base_router.surface_estimator, &surface_config);
chunk.populate_biomes(Dimension::OVERWORLD, &mut multi_noise_sampler);
chunk.populate_noise(&mut noise_sampler, &mut surface_height_estimate_sampler);
chunk.populate_noise(
&mut noise_sampler,
random_config,
&mut surface_height_estimate_sampler,
);
chunk.build_surface(
settings,
random_config,

View File

@ -864,12 +864,12 @@ impl World {
let fluid = Fluid::from_state_id(block_state_id).unwrap_or(&Fluid::EMPTY);
if fluid.id == Fluid::EMPTY.id {
let block = Block::from_state_id(block_state_id);
let block = Block::get_raw_id_from_state_id(block_state_id);
let block_state = BlockState::from_id(block_state_id);
let blocks_movement = block_state.is_solid()
&& block != &Block::COBWEB
&& block != &Block::BAMBOO_SAPLING;
&& block != Block::COBWEB
&& block != Block::BAMBOO_SAPLING;
if !blocks_movement {
let down_pos = pos.down();