ci: add Windows Arm and macOS Intel

This commit is contained in:
Alexander Medvedev
2025-12-09 19:29:57 +01:00
parent 92fe87e921
commit fba4d405ac
64 changed files with 716 additions and 786 deletions

View File

@@ -41,7 +41,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, ubuntu-24.04-arm]
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-latest, macos-15-intel]
toolchain:
- stable
steps:
@@ -55,7 +55,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, ubuntu-24.04-arm]
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-latest, macos-15-intel]
toolchain:
- stable
steps:

View File

@@ -886,9 +886,8 @@ pub(crate) fn build() -> TokenStream {
matches!(state_id, #random_tick_state_ids)
}
pub fn blocks_movement(block_state: &BlockState) -> bool {
pub fn blocks_movement(block_state: &BlockState, block: &Block) -> bool {
if block_state.is_solid() {
let block = Block::from_state_id(block_state.id);
return block != &Block::COBWEB && block != &Block::BAMBOO_SAPLING;
}
false

View File

@@ -22,8 +22,8 @@ pub struct CLevelChunk<'a> {
impl<'a> PacketWrite for CLevelChunk<'a> {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
VarInt(self.chunk.position.x).write(writer)?;
VarInt(self.chunk.position.y).write(writer)?;
VarInt(self.chunk.x).write(writer)?;
VarInt(self.chunk.z).write(writer)?;
VarInt(self.dimension).write(writer)?;
let sub_chunk_count = self.chunk.section.sections.len() as u32;

View File

@@ -134,7 +134,7 @@ impl UDPNetworkDecoder {
err => PacketDecodeError::MalformedLength(err.to_string()),
})?;
let packet_len = packet_len.0 as u64;
let packet_len = packet_len.0 as usize;
let var_header = VarUInt::decode(&mut reader)?;
@@ -157,7 +157,7 @@ impl UDPNetworkDecoder {
let gamepacket_id = (header & 0x3FF) as u16; // 0x3FF is 10 bits set to 1
let payload = reader
.read_boxed_slice(packet_len as usize - var_header.written_size())
.read_boxed_slice(packet_len - var_header.written_size())
.map_err(|err| PacketDecodeError::FailedDecompression(err.to_string()))?;
Ok(RawPacket {

View File

@@ -17,9 +17,9 @@ impl ClientPacket for CChunkData<'_> {
let mut write = write;
// Chunk X
write.write_i32_be(self.0.position.x)?;
write.write_i32_be(self.0.x)?;
// Chunk Z
write.write_i32_be(self.0.position.y)?;
write.write_i32_be(self.0.z)?;
let heightmaps = &self.0.heightmap;
write.write_var_int(&VarInt(3))?; // Map size

View File

@@ -43,7 +43,7 @@ impl Serialize for CMultiBlockUpdate {
))?;
for (position, state_id) in &self.positions_to_state_ids {
let long = ((*state_id as u64) << 12) | (*position as u64);
let long = (*state_id as u64) << 12 | (*position as u64);
let var_long = VarLong::from(long as i64);
tuple.serialize_element(&var_long)?;
}

View File

@@ -2,10 +2,7 @@ use std::sync::LazyLock;
use serde::Deserialize;
use crate::{
math::vector3::Vector3, noise::simplex::OctaveSimplexNoiseSampler,
random::legacy_rand::LegacyRand,
};
use crate::{noise::simplex::OctaveSimplexNoiseSampler, random::legacy_rand::LegacyRand};
pub static TEMPERATURE_NOISE: LazyLock<OctaveSimplexNoiseSampler> = LazyLock::new(|| {
let mut rand = LegacyRand::from_seed(1234);
@@ -30,20 +27,17 @@ pub enum TemperatureModifier {
}
impl TemperatureModifier {
pub fn convert_temperature(&self, pos: &Vector3<i32>, temperature: f32) -> f32 {
pub fn convert_temperature(&self, x: f64, z: f64, temperature: f32) -> f32 {
match self {
TemperatureModifier::None => temperature,
TemperatureModifier::Frozen => {
let frozen_ocean_sample =
FROZEN_OCEAN_NOISE.sample(pos.x as f64 * 0.05, pos.z as f64 * 0.05, false)
* 7.0;
let foliage_sample =
FOLIAGE_NOISE.sample(pos.x as f64 * 0.2, pos.z as f64 * 0.2, false);
FROZEN_OCEAN_NOISE.sample(x * 0.05, z * 0.05, false) * 7.0;
let foliage_sample = FOLIAGE_NOISE.sample(x * 0.2, z * 0.2, false);
let threshold = frozen_ocean_sample + foliage_sample;
if threshold < 0.3 {
let foliage_sample =
FOLIAGE_NOISE.sample(pos.x as f64 * 0.09, pos.z as f64 * 0.09, false);
let foliage_sample = FOLIAGE_NOISE.sample(x * 0.09, z * 0.09, false);
if foliage_sample < 0.8 {
return 0.2f32;
}
@@ -81,19 +75,18 @@ impl Weather {
}
/// This is an expensive function and should be cached
pub fn compute_temperature(&self, pos: &Vector3<i32>, sea_level: i32) -> f32 {
let modified_temperature = self
.temperature_modifier
.convert_temperature(pos, self.temperature);
pub fn compute_temperature(&self, x: f64, y: i32, z: f64, sea_level: i32) -> f32 {
let modified_temperature =
self.temperature_modifier
.convert_temperature(x, z, self.temperature);
let offset_sea_level = sea_level + 17;
if pos.y > offset_sea_level {
if y > offset_sea_level {
let temperature_noise =
(TEMPERATURE_NOISE.sample(pos.x as f64 / 8.0, pos.z as f64 / 8.0, false) * 8.0)
as f32;
(TEMPERATURE_NOISE.sample(x / 8.0, z / 8.0, false) * 8.0) as f32;
modified_temperature
- (temperature_noise + pos.y as f32 - offset_sea_level as f32) * 0.05f32 / 40.0f32
- (temperature_noise + y as f32 - offset_sea_level as f32) * 0.05f32 / 40.0f32
} else {
modified_temperature
}

View File

@@ -71,13 +71,7 @@ pub const fn floor_log2(value: u32) -> u8 {
}
pub const fn smallest_encompassing_power_of_two(value: u32) -> u32 {
let mut i = value - 1;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i + 1
value.next_power_of_two()
}
#[inline]

View File

@@ -82,7 +82,7 @@ pub struct TextComponentBase {
/// Style of the text. Bold, Italic, underline, Color...
/// Also has `ClickEvent
#[serde(flatten)]
pub style: Style,
pub style: Box<Style>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
/// Extra text components
pub extra: Vec<TextComponentBase>,
@@ -144,7 +144,7 @@ impl TextComponentBase {
// Divide the translation into slices and inserts the substitutions
let component = match self.content {
TextContent::Custom { key, with, locale } => {
let translation = get_translation(key, locale);
let translation = get_translation(&key, locale);
let mut translation_parent = translation.clone();
let mut translation_slices = vec![];
@@ -171,7 +171,7 @@ impl TextComponentBase {
)
},
},
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
});
}
@@ -242,7 +242,7 @@ impl TextComponent {
pub fn text<P: Into<Cow<'static, str>>>(plain: P) -> Self {
Self(TextComponentBase {
content: TextContent::Text { text: plain.into() },
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
}
@@ -256,7 +256,7 @@ impl TextComponent {
translate: key.into(),
with: with.into().into_iter().map(|x| x.0).collect(),
},
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
}
@@ -275,7 +275,7 @@ impl TextComponent {
locale,
with: with.into().into_iter().map(|x| x.0).collect(),
},
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
}
@@ -288,7 +288,7 @@ impl TextComponent {
pub fn from_content(content: TextContent) -> Self {
Self(TextComponentBase {
content,
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
}
@@ -296,7 +296,7 @@ impl TextComponent {
pub fn add_text<P: Into<Cow<'static, str>>>(mut self, text: P) -> Self {
self.0.extra.push(TextComponentBase {
content: TextContent::Text { text: text.into() },
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
});
self
@@ -317,7 +317,7 @@ impl TextComponent {
content: TextContent::Text {
text: Cow::Owned(with_resolved_fields),
},
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
}

View File

@@ -57,9 +57,9 @@ pub fn add_translation_file<P: Into<String>>(namespace: P, file_path: P, locale:
}
}
pub fn get_translation<P: Into<String>>(key: P, locale: Locale) -> String {
pub fn get_translation(key: &str, locale: Locale) -> String {
let translations = TRANSLATIONS.lock().unwrap();
let key = key.into().to_lowercase();
let key = key.to_lowercase();
match translations[locale as usize].get(&key) {
Some(translation) => translation.clone(),
None => match translations[Locale::EnUs as usize].get(&key) {
@@ -96,7 +96,7 @@ pub fn reorder_substitutions(
.iter()
.map(|_| TextComponentBase {
content: TextContent::Text { text: "".into() },
style: Style::default(),
style: Box::new(Style::default()),
extra: vec![],
})
.collect();
@@ -138,7 +138,7 @@ pub fn translation_to_pretty<P: Into<Cow<'static, str>>>(
locale: Locale,
with: Vec<TextComponentBase>,
) -> String {
let mut translation = get_translation(namespaced_key.into(), locale);
let mut translation = get_translation(&namespaced_key.into(), locale);
if with.is_empty() || !translation.contains('%') {
return translation;
}
@@ -162,7 +162,7 @@ pub fn get_translation_text<P: Into<Cow<'static, str>>>(
locale: Locale,
with: Vec<TextComponentBase>,
) -> String {
let mut translation = get_translation(namespaced_key.into(), locale);
let mut translation = get_translation(&namespaced_key.into(), locale);
if with.is_empty() || !translation.contains('%') {
return translation;
}

View File

@@ -1,5 +1,4 @@
use pumpkin_data::chunk::Biome;
use pumpkin_util::math::vector3::Vector3;
use crate::{
biome::BiomeSupplier,
@@ -21,13 +20,15 @@ impl TheEndBiomeSupplier {
impl BiomeSupplier for TheEndBiomeSupplier {
fn biome(
global_biome_pos: &Vector3<i32>,
x: i32,
y: i32,
z: i32,
noise: &mut MultiNoiseSampler<'_>,
_dimension: Dimension,
) -> &'static Biome {
let x = biome_coords::to_block(global_biome_pos.x);
let y = biome_coords::to_block(global_biome_pos.y);
let z = biome_coords::to_block(global_biome_pos.z);
let x = biome_coords::to_block(x);
let y = biome_coords::to_block(y);
let z = biome_coords::to_block(z);
let section_x = section_coords::block_to_section(x);
let section_z = section_coords::block_to_section(z);
if section_x * section_x + section_z * section_z <= 4096 {

View File

@@ -3,7 +3,6 @@ use std::cell::RefCell;
use enum_dispatch::enum_dispatch;
use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE};
use pumpkin_util::math::vector3::Vector3;
use crate::{
dimension::Dimension, generation::noise::router::multi_noise_sampler::MultiNoiseSampler,
@@ -19,7 +18,9 @@ thread_local! {
#[enum_dispatch]
pub trait BiomeSupplier {
fn biome(
at: &Vector3<i32>,
x: i32,
y: i32,
z: i32,
noise: &mut MultiNoiseSampler<'_>,
dimension: Dimension,
) -> &'static Biome;
@@ -29,7 +30,9 @@ pub struct MultiNoiseBiomeSupplier;
impl BiomeSupplier for MultiNoiseBiomeSupplier {
fn biome(
global_biome_pos: &Vector3<i32>,
x: i32,
y: i32,
z: i32,
noise: &mut MultiNoiseSampler<'_>,
dimension: Dimension,
) -> &'static Biome {
@@ -38,7 +41,7 @@ impl BiomeSupplier for MultiNoiseBiomeSupplier {
Dimension::Nether => &NETHER_BIOME_SOURCE,
Dimension::End => unreachable!(), // Use TheEndBiomeSupplier
};
let point = noise.sample(global_biome_pos.x, global_biome_pos.y, global_biome_pos.z);
let point = noise.sample(x, y, z);
let point_list = point.convert_to_list();
LAST_RESULT_NODE.with_borrow_mut(|last_result| source.get(&point_list, last_result))
}
@@ -54,10 +57,7 @@ pub fn hash_seed(seed: u64) -> i64 {
#[cfg(test)]
mod test {
use pumpkin_data::{chunk::Biome, noise_router::OVERWORLD_BASE_NOISE_ROUTER};
use pumpkin_util::{
math::{vector2::Vector2, vector3::Vector3},
read_data_from_file,
};
use pumpkin_util::read_data_from_file;
use serde::Deserialize;
use crate::{
@@ -84,11 +84,7 @@ mod test {
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(1, 1, 1);
let mut sampler =
MultiNoiseSampler::generate(&noise_router.multi_noise, &multi_noise_config);
let biome = MultiNoiseBiomeSupplier::biome(
&pumpkin_util::math::vector3::Vector3 { x: -24, y: 1, z: 8 },
&mut sampler,
Dimension::Overworld,
);
let biome = MultiNoiseBiomeSupplier::biome(-24, 1, 8, &mut sampler, Dimension::Overworld);
assert_eq!(biome, &Biome::DESERT)
}
@@ -115,14 +111,20 @@ mod test {
let default_block = surface_settings.default_block.get_state();
for data in expected_data.into_iter() {
let chunk_pos = Vector2::new(data.x, data.z);
let chunk_x = data.x;
let chunk_z = data.z;
// Calculate biome mixer seed
use crate::biome::hash_seed;
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk =
ProtoChunk::new(chunk_pos, surface_settings, default_block, biome_mixer_seed);
let mut chunk = ProtoChunk::new(
chunk_x,
chunk_z,
surface_settings,
default_block,
biome_mixer_seed,
);
// Create MultiNoiseSampler for populate_biomes
use crate::generation::noise::router::multi_noise_sampler::{
@@ -130,16 +132,13 @@ mod test {
};
use crate::generation::{biome_coords, positions::chunk_pos};
let start_x = chunk_pos::start_block_x(&chunk_pos);
let start_z = chunk_pos::start_block_z(&chunk_pos);
let biome_pos = Vector2::new(
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
);
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_pos.x,
biome_pos.y,
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
@@ -148,8 +147,7 @@ mod test {
chunk.populate_biomes(Dimension::Overworld, &mut multi_noise_sampler);
for (biome_x, biome_y, biome_z, biome_id) in data.data {
let global_biome_pos = Vector3::new(biome_x, biome_y, biome_z);
let calculated_biome = chunk.get_biome(&global_biome_pos);
let calculated_biome = chunk.get_biome(biome_x, biome_y, biome_z);
assert_eq!(
biome_id,

View File

@@ -31,10 +31,7 @@ impl NoiseValuePoint {
#[cfg(test)]
mod test {
use pumpkin_data::{chunk::Biome, noise_router::OVERWORLD_BASE_NOISE_ROUTER};
use pumpkin_util::{
math::{vector2::Vector2, vector3::Vector3},
read_data_from_file,
};
use pumpkin_util::read_data_from_file;
use crate::{
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk,
@@ -56,7 +53,9 @@ mod test {
read_data_from_file!("../../assets/multi_noise_sample_no_blend_no_beard_0_0_0.json");
let seed = 0;
let chunk_pos = Vector2::new(0, 0);
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);
@@ -70,7 +69,8 @@ mod test {
let biome_mixer_seed = hash_seed(random_config.seed);
let _chunk = ProtoChunk::new(
chunk_pos,
chunk_x,
chunk_z,
surface_config,
surface_config.default_block.get_state(),
biome_mixer_seed,
@@ -82,16 +82,12 @@ mod test {
};
use crate::generation::{biome_coords, positions::chunk_pos};
let start_x = chunk_pos::start_block_x(&chunk_pos);
let start_z = chunk_pos::start_block_z(&chunk_pos);
let biome_pos = Vector2::new(
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
);
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_pos.x,
biome_pos.y,
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
@@ -124,12 +120,8 @@ mod test {
);
for (x, y, z, biome_id) in expected_data {
let global_biome_pos = Vector3::new(x, y, z);
let calculated_biome = MultiNoiseBiomeSupplier::biome(
&global_biome_pos,
&mut sampler,
Dimension::Overworld,
);
let calculated_biome =
MultiNoiseBiomeSupplier::biome(x, y, z, &mut sampler, Dimension::Overworld);
assert_eq!(
biome_id,

View File

@@ -342,9 +342,9 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
(at.x >> SUBREGION_BITS, at.y >> SUBREGION_BITS)
}
pub const fn get_chunk_index(pos: &Vector2<i32>) -> usize {
let local_x = pos.x & SUBREGION_AND;
let local_z = pos.y & SUBREGION_AND;
pub const fn get_chunk_index(x: i32, z: i32) -> usize {
let local_x = x & SUBREGION_AND;
let local_z = z & SUBREGION_AND;
let index = (local_z << SUBREGION_BITS) + local_x;
index as usize
}
@@ -518,7 +518,7 @@ pub trait SingleChunkDataSerializer: Send + Sync + Sized + Dirtiable {
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, ChunkSerializingError>> + Send + '_>>;
fn from_bytes(bytes: &Bytes, pos: Vector2<i32>) -> Result<Self, ChunkReadingError>;
fn position(&self) -> &Vector2<i32>;
fn position(&self) -> (i32, i32);
}
impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
@@ -629,7 +629,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
.unwrap()
.as_secs() as u32;
let index = AnvilChunkFile::<S>::get_chunk_index(chunk.position());
let index = AnvilChunkFile::<S>::get_chunk_index(chunk.position().0, chunk.position().1);
// Default to the compression type read from the file
let compression_type = self.chunks_data[index]
.as_ref()
@@ -797,13 +797,13 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
async fn get_chunks(
&self,
chunks: &[Vector2<i32>],
chunks: Vec<Vector2<i32>>,
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
) {
// Don't par iter here so we can prevent backpressure with the await in the async
// runtime
for chunk in chunks.iter().cloned() {
let index = AnvilChunkFile::<S>::get_chunk_index(&chunk);
for chunk in chunks.into_iter() {
let index = AnvilChunkFile::<S>::get_chunk_index(chunk.x, chunk.y);
let is_ok = match &self.chunks_data[index] {
None => stream.send(LoadedData::Missing(chunk)).await.is_ok(),
Some(chunk_metadata) => {

View File

@@ -139,8 +139,8 @@ impl LinearFileHeader {
}
impl<S: SingleChunkDataSerializer> LinearFile<S> {
const fn get_chunk_index(at: &Vector2<i32>) -> usize {
AnvilChunkFile::<S>::get_chunk_index(at)
const fn get_chunk_index(x: i32, z: i32) -> usize {
AnvilChunkFile::<S>::get_chunk_index(x, z)
}
fn check_signature(bytes: &[u8]) -> Result<(), ChunkReadingError> {
@@ -327,7 +327,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
chunk: &Self::Data,
_chunk_config: &Self::ChunkConfig,
) -> Result<(), ChunkWritingError> {
let index = LinearFile::<S>::get_chunk_index(chunk.position());
let index = LinearFile::<S>::get_chunk_index(chunk.position().0, chunk.position().1);
let chunk_raw: Bytes = chunk
.to_bytes()
.await
@@ -348,13 +348,13 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearFile<S> {
async fn get_chunks(
&self,
chunks: &[Vector2<i32>],
chunks: Vec<Vector2<i32>>,
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
) {
// Don't par iter here so we can prevent backpressure with the await in the async
// runtime
for chunk in chunks.iter().cloned() {
let index = LinearFile::<S>::get_chunk_index(&chunk);
for chunk in chunks.into_iter() {
let index = LinearFile::<S>::get_chunk_index(chunk.x, chunk.y);
let linear_chunk_data = &self.chunks_data[index];
let result = if let Some(data) = linear_chunk_data {

View File

@@ -43,8 +43,8 @@ impl SingleChunkDataSerializer for ChunkData {
}
#[inline]
fn position(&self) -> &Vector2<i32> {
&self.position
fn position(&self) -> (i32, i32) {
(self.x, self.z)
}
}
@@ -163,7 +163,8 @@ impl ChunkData {
Ok(ChunkData {
section,
heightmap: chunk_data.heightmaps,
position,
x: position.x,
z: position.y,
// This chunk is read from disk, so it has not been modified
dirty: false,
block_ticks: ChunkTickScheduler::from_vec(&chunk_data.block_ticks),
@@ -206,8 +207,8 @@ impl ChunkData {
let nbt = ChunkNbt {
data_version: WORLD_DATA_VERSION,
x_pos: self.position.x,
z_pos: self.position.y,
x_pos: self.x,
z_pos: self.z,
min_y_section: section_coords::block_to_section(self.section.min_y),
status: self.status,
heightmaps: self.heightmap.clone(),
@@ -264,8 +265,8 @@ impl SingleChunkDataSerializer for ChunkEntityData {
}
#[inline]
fn position(&self) -> &Vector2<i32> {
&self.chunk_position
fn position(&self) -> (i32, i32) {
(self.x, self.z)
}
}
@@ -310,7 +311,8 @@ impl ChunkEntityData {
}
Ok(ChunkEntityData {
chunk_position: position,
x: position.x,
z: position.y,
data: map,
dirty: false,
})
@@ -319,7 +321,7 @@ impl ChunkEntityData {
fn internal_to_bytes(&self) -> Result<Bytes, ChunkSerializingError> {
let nbt = EntityNbt {
data_version: WORLD_DATA_VERSION,
position: [self.chunk_position.x, self.chunk_position.y],
position: [self.x, self.z],
entities: self.data.values().cloned().collect(),
};

View File

@@ -273,7 +273,7 @@ where
// This minimizes the time we block other operations
let reader = async move {
let serializer = chunk_serializer.read().await;
serializer.get_chunks(&chunks, send).await;
serializer.get_chunks(chunks, send).await;
};
join!(intermediary, reader);

View File

@@ -125,7 +125,7 @@ pub trait ChunkSerializer: Send + Sync + Default {
/// Get the chunks data from the serializer
fn get_chunks(
&self,
chunks: &[Vector2<i32>],
chunks: Vec<Vector2<i32>>,
stream: tokio::sync::mpsc::Sender<LoadedData<Self::Data, ChunkReadingError>>,
) -> impl Future<Output = ()> + Send;
}

View File

@@ -11,7 +11,7 @@ use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockState};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::nbt_long_array;
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use pumpkin_util::math::position::BlockPos;
use serde::{Deserialize, Serialize};
use std::ops::{BitAnd, BitOr};
use std::{collections::HashMap, sync::Arc};
@@ -72,7 +72,8 @@ pub struct ChunkData {
pub section: ChunkSections,
/// See `https://minecraft.wiki/w/Heightmap` for more info
pub heightmap: ChunkHeightmaps,
pub position: Vector2<i32>,
pub x: i32,
pub z: i32,
pub block_ticks: ChunkTickScheduler<&'static Block>,
pub fluid_ticks: ChunkTickScheduler<&'static Fluid>,
pub block_entities: HashMap<BlockPos, Arc<dyn BlockEntity>>,
@@ -83,7 +84,10 @@ pub struct ChunkData {
#[derive(Clone)]
pub struct ChunkEntityData {
pub chunk_position: Vector2<i32>,
/// Chunk X
pub x: i32,
/// Chunk Z
pub z: i32,
pub data: HashMap<uuid::Uuid, NbtCompound>,
pub dirty: bool,
@@ -528,7 +532,7 @@ impl ChunkData {
has_found[ChunkHeightmapType::WorldSurface as usize] = true;
}
let is_motion_blocking = blocks_movement(block_state)
let is_motion_blocking = blocks_movement(block_state, block)
|| Fluid::from_registry_key(block.registry_key())
.is_some_and(|fluid| !fluid.states.is_empty());

View File

@@ -779,8 +779,7 @@ impl Chunk {
let absolute_y =
biome_coords::from_block(generation_settings.shape.min_y as i32)
+ y as i32;
let biome =
proto_chunk.get_biome(&Vector3::new(x as i32, absolute_y, z as i32));
let biome = proto_chunk.get_biome(x as i32, absolute_y, z as i32);
section.biomes.set(x, relative_y, z, biome.id);
}
}
@@ -793,8 +792,7 @@ impl Chunk {
if let Some(section) = sections.sections.get_mut(section_index) {
for z in 0..BlockPalette::SIZE {
for x in 0..BlockPalette::SIZE {
let block = proto_chunk
.get_block_state_raw(&Vector3::new(x as i32, y as i32, z as i32));
let block = proto_chunk.get_block_state_raw(x as i32, y as i32, z as i32);
section.block_states.set(x, relative_y, z, block);
}
}
@@ -811,7 +809,8 @@ impl Chunk {
},
section: sections,
heightmap: Default::default(),
position: proto_chunk.chunk_pos,
x: proto_chunk.x,
z: proto_chunk.z,
dirty: true,
block_ticks: Default::default(),
fluid_ticks: Default::default(),
@@ -971,22 +970,22 @@ impl GenerationCache for Cache {
}
}
fn get_top_y(&self, heightmap: &HeightMap, pos: &Vector2<i32>) -> i32 {
fn get_top_y(&self, heightmap: &HeightMap, x: i32, z: i32) -> i32 {
match heightmap {
HeightMap::WorldSurfaceWg => self.top_block_height_exclusive(pos),
HeightMap::WorldSurface => self.top_block_height_exclusive(pos),
HeightMap::OceanFloorWg => self.ocean_floor_height_exclusive(pos),
HeightMap::OceanFloor => self.ocean_floor_height_exclusive(pos),
HeightMap::MotionBlocking => self.top_motion_blocking_block_height_exclusive(pos),
HeightMap::WorldSurfaceWg => self.top_block_height_exclusive(x, z),
HeightMap::WorldSurface => self.top_block_height_exclusive(x, z),
HeightMap::OceanFloorWg => self.ocean_floor_height_exclusive(x, z),
HeightMap::OceanFloor => self.ocean_floor_height_exclusive(x, z),
HeightMap::MotionBlocking => self.top_motion_blocking_block_height_exclusive(x, z),
HeightMap::MotionBlockingNoLeaves => {
self.top_motion_blocking_block_no_leaves_height_exclusive(pos)
self.top_motion_blocking_block_no_leaves_height_exclusive(x, z)
}
}
}
fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let dx = (pos.x >> 4) - self.x;
let dy = (pos.y >> 4) - self.y;
fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
let dx = (x >> 4) - self.x;
let dy = (z >> 4) - self.y;
debug_assert!(dx < self.size && dy < self.size);
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
@@ -994,18 +993,18 @@ impl GenerationCache for Cache {
let chunk = data.blocking_read();
chunk.heightmap.get_height(
ChunkHeightmapType::MotionBlocking,
pos.x,
pos.y,
x,
z,
chunk.section.min_y,
)
}
Chunk::Proto(data) => data.top_motion_blocking_block_height_exclusive(pos),
Chunk::Proto(data) => data.top_motion_blocking_block_height_exclusive(x, z),
}
}
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let dx = (pos.x >> 4) - self.x;
let dy = (pos.y >> 4) - self.y;
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32 {
let dx = (x >> 4) - self.x;
let dy = (z >> 4) - self.y;
debug_assert!(dx < self.size && dy < self.size);
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
@@ -1013,18 +1012,18 @@ impl GenerationCache for Cache {
let chunk = data.blocking_read();
chunk.heightmap.get_height(
ChunkHeightmapType::MotionBlockingNoLeaves,
pos.x,
pos.y,
x,
z,
chunk.section.min_y,
)
}
Chunk::Proto(data) => data.top_motion_blocking_block_no_leaves_height_exclusive(pos),
Chunk::Proto(data) => data.top_motion_blocking_block_no_leaves_height_exclusive(x, z),
}
}
fn top_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let dx = (pos.x >> 4) - self.x;
let dy = (pos.y >> 4) - self.y;
fn top_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
let dx = (x >> 4) - self.x;
let dy = (z >> 4) - self.y;
debug_assert!(dx < self.size && dy < self.size);
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
@@ -1032,31 +1031,31 @@ impl GenerationCache for Cache {
let chunk = data.blocking_read();
chunk.heightmap.get_height(
ChunkHeightmapType::WorldSurface,
pos.x,
pos.y,
x,
z,
chunk.section.min_y,
) // can we return this?
}
Chunk::Proto(data) => data.top_block_height_exclusive(pos),
Chunk::Proto(data) => data.top_block_height_exclusive(x, z),
}
}
fn ocean_floor_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let dx = (pos.x >> 4) - self.x;
let dy = (pos.y >> 4) - self.y;
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32 {
let dx = (x >> 4) - self.x;
let dy = (z >> 4) - self.y;
debug_assert!(dx < self.size && dy < self.size);
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
Chunk::Level(_data) => {
0 // todo missing
}
Chunk::Proto(data) => data.ocean_floor_height_exclusive(pos),
Chunk::Proto(data) => data.ocean_floor_height_exclusive(x, z),
}
}
fn get_biome_for_terrain_gen(&self, global_block_pos: &Vector3<i32>) -> &'static Biome {
let dx = (global_block_pos.x >> 4) - self.x;
let dy = (global_block_pos.z >> 4) - self.y;
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome {
let dx = (x >> 4) - self.x;
let dy = (z >> 4) - self.y;
debug_assert!(dx < self.size && dy < self.size);
debug_assert!(dx >= 0 && dy >= 0);
match &self.chunks[(dx * self.size + dy) as usize] {
@@ -1065,16 +1064,12 @@ impl GenerationCache for Cache {
Biome::from_id(
data.blocking_read()
.section
.get_rough_biome_absolute_y(
(global_block_pos.x & 15) as usize,
global_block_pos.y,
(global_block_pos.z & 15) as usize,
)
.get_rough_biome_absolute_y((x & 15) as usize, y, (z & 15) as usize)
.unwrap_or(0),
)
.unwrap()
}
Chunk::Proto(data) => data.get_biome_for_terrain_gen(global_block_pos),
Chunk::Proto(data) => data.get_biome_for_terrain_gen(x, y, z),
}
}
@@ -1616,7 +1611,8 @@ impl GenerationSchedule {
.send((
pos,
RecvChunk::IO(Proto(Box::new(ProtoChunk::new(
pos,
pos.x,
pos.y,
generation_setting,
level.world_gen.default_block,
biome_mixer_seed,

View File

@@ -1,7 +1,7 @@
use enum_dispatch::enum_dispatch;
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{
math::{clamped_map, floor_div, vector2::Vector2, vector3::Vector3},
math::{clamped_map, floor_div, vector3::Vector3},
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
};
@@ -45,7 +45,7 @@ impl FluidLevel {
#[enum_dispatch(FluidLevelSamplerImpl)]
pub enum FluidLevelSampler {
Static(StaticFluidLevelSampler),
Chunk(Box<StandardChunkFluidLevelSampler>),
Chunk(StandardChunkFluidLevelSampler),
}
impl FluidLevelSamplerImpl for FluidLevelSampler {
@@ -106,7 +106,7 @@ macro_rules! local_y {
pub struct WorldAquiferSampler {
fluid_level_sampler: FluidLevelSampler,
start_x: i32,
start_y: i8,
start_y: i32,
start_z: i32,
size_y: usize,
size_z: usize,
@@ -115,40 +115,40 @@ pub struct WorldAquiferSampler {
}
impl WorldAquiferSampler {
const CHUNK_POS_OFFSETS: [Vector2<i8>; 13] = [
Vector2::new(0, 0),
Vector2::new(-2, -1),
Vector2::new(-1, -1),
Vector2::new(0, -1),
Vector2::new(1, -1),
Vector2::new(-3, 0),
Vector2::new(-2, 0),
Vector2::new(-1, 0),
Vector2::new(1, 0),
Vector2::new(-2, 1),
Vector2::new(-1, 1),
Vector2::new(0, 1),
Vector2::new(1, 1),
const CHUNK_POS_OFFSETS: [(i8, i8); 13] = [
(0, 0),
(-2, -1),
(-1, -1),
(0, -1),
(1, -1),
(-3, 0),
(-2, 0),
(-1, 0),
(1, 0),
(-2, 1),
(-1, 1),
(0, 1),
(1, 1),
];
#[allow(clippy::too_many_arguments)]
pub fn new(
chunk_pos: Vector2<i32>,
chunk_x: i32,
chunk_z: i32,
random_deriver: &RandomDeriver,
minimum_y: i8,
height: u16,
fluid_level: FluidLevelSampler,
) -> Self {
let start_x = local_xz!(chunk_pos::start_block_x(&chunk_pos)) - 1;
let end_x = local_xz!(chunk_pos::end_block_x(&chunk_pos)) + 1;
let start_x = local_xz!(chunk_pos::start_block_x(chunk_x)) - 1;
let end_x = local_xz!(chunk_pos::end_block_x(chunk_x)) + 1;
let size_x = (end_x - start_x) as usize + 1;
let start_y = local_y!(minimum_y) - 1;
let end_y = local_y!(minimum_y as i32 + height as i32) + 1;
let size_y = (end_y - start_y as i32) as usize + 1;
let start_z = local_xz!(chunk_pos::start_block_z(&chunk_pos)) - 1;
let end_z = local_xz!(chunk_pos::end_block_z(&chunk_pos)) + 1;
let start_z = local_xz!(chunk_pos::start_block_z(chunk_z)) - 1;
let end_z = local_xz!(chunk_pos::end_block_z(chunk_z)) + 1;
let size_z = (end_z - start_z) as usize + 1;
let cache_size = size_x * size_y * size_z;
@@ -178,7 +178,7 @@ impl WorldAquiferSampler {
Self {
fluid_level_sampler: fluid_level,
start_x,
start_y,
start_y: start_y as i32,
start_z,
size_y,
size_z,
@@ -189,7 +189,7 @@ impl WorldAquiferSampler {
fn packed_position_index(&self, x: i32, y: i32, z: i32) -> usize {
let local_x = (x - self.start_x) as usize;
let local_y = (y - self.start_y as i32) as usize;
let local_y = (y - self.start_y) as usize;
let local_z = (z - self.start_z) as usize;
packed_position_index!(local_x, local_y, local_z, self.size_y, self.size_z)
@@ -288,10 +288,8 @@ impl WorldAquiferSampler {
let local_z = local_xz!(z);
let index = self.packed_position_index(local_x, local_y, local_z);
let entry = self
.levels
.get_mut(index)
.expect("Index calculated by packed_position_index is out of bounds for self.levels");
let entry = &mut self.levels[index];
let fluid_level = &self.fluid_level_sampler;
entry.get_or_insert_with(|| {
Self::get_fluid_level(
@@ -321,13 +319,13 @@ impl WorldAquiferSampler {
let mut bl = false;
let mut min_surface_estimate = i32::MAX;
for offset in Self::CHUNK_POS_OFFSETS {
let x = block_x + section_coords::section_to_block(offset.x as i32);
let z = block_z + section_coords::section_to_block(offset.y as i32);
for (offset_x, offset_z) in Self::CHUNK_POS_OFFSETS {
let x = block_x + section_coords::section_to_block(offset_x as i32);
let z = block_z + section_coords::section_to_block(offset_z as i32);
let n = height_estimator.estimate_height(x, z);
let o = n + 8;
let bl2 = offset.x == 0 && offset.y == 0;
let bl2 = offset_x == 0 && offset_z == 0;
if bl2 && k > o {
return fluid_level.clone();
@@ -691,7 +689,6 @@ mod random_positions_and_hypot {
use std::{mem, sync::LazyLock};
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
use pumpkin_util::math::vector2::Vector2;
use crate::{
block::RawBlockState,
@@ -738,18 +735,20 @@ mod random_positions_and_hypot {
.get(&GeneratorSetting::Overworld)
.unwrap();
let shape = &surface_config.shape;
let chunk_pos = Vector2::new(7, 4);
let sampler = FluidLevelSampler::Chunk(Box::new(StandardChunkFluidLevelSampler::new(
let chunk_x = 7;
let chunk_z = 4;
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
FluidLevel::new(63, &WATER_BLOCK),
FluidLevel::new(-54, &LAVA_BLOCK),
)));
));
const CHUNK_WIDTH: usize = 16;
let noise = ChunkNoiseGenerator::new(
&base_router.noise,
&RANDOM_CONFIG,
CHUNK_WIDTH / shape.horizontal_cell_block_count() as usize,
chunk_pos::start_block_x(&chunk_pos),
chunk_pos::start_block_z(&chunk_pos),
chunk_pos::start_block_x(chunk_x),
chunk_pos::start_block_z(chunk_z),
shape,
sampler,
true,
@@ -771,7 +770,7 @@ mod random_positions_and_hypot {
BlockStateSampler::Aquifer(aquifer) => aquifer,
_ => unreachable!(),
};
let aquifer = match *sampler {
let aquifer = match sampler {
AquiferSampler::Aquifer(aquifer) => aquifer,
_ => unreachable!(),
};
@@ -783,8 +782,8 @@ mod random_positions_and_hypot {
);
let surface_height_estimator_options = SurfaceHeightSamplerBuilderOptions::new(
chunk_pos.x,
chunk_pos.y,
chunk_x,
chunk_z,
horizontal_biome_end,
shape.min_y as i32,
shape.max_y() as i32,

View File

@@ -7,12 +7,14 @@ pub fn get_biome_blend(
bottom_y: i8,
height: u16,
seed: i64,
global_block_pos: &Vector3<i32>,
x: i32,
y: i32,
z: i32,
) -> Vector3<i32> {
// This is the "left" side of the biome boundary
let offset_x = global_block_pos.x - 2;
let offset_y = global_block_pos.y - 2;
let offset_z = global_block_pos.z - 2;
let offset_x = x - 2;
let offset_y = y - 2;
let offset_z = z - 2;
let biome_x = biome_coords::from_block(offset_x);
let biome_y = biome_coords::from_block(offset_y);
let biome_z = biome_coords::from_block(offset_z);
@@ -174,7 +176,7 @@ mod test {
#[test]
fn test_biome_blend() {
let biome_pos = get_biome_blend(-64, 384, 1234567890, &Vector3::new(123, 123, 123));
let biome_pos = get_biome_blend(-64, 384, 1234567890, 123, 123, 123);
assert_eq!(biome_pos, Vector3::new(31, 30, 30));
}
@@ -191,7 +193,7 @@ mod test {
let seed = hash_seed((-777i64) as u64);
for (i, (x, y, z, result_x, result_y, result_z)) in data.into_iter().enumerate() {
let result = get_biome_blend(i8::MIN, u16::MAX, seed, &Vector3::new(x, y, z));
let result = get_biome_blend(i8::MIN, u16::MAX, seed, x, y, z);
let expected = Vector3::new(result_x, result_y, result_z);
assert_eq!(
result, expected,

View File

@@ -6,7 +6,6 @@ use pumpkin_util::{
int_provider::IntProvider,
pool::{Pool, Weighted},
position::BlockPos,
vector3::Vector3,
},
random::{RandomGenerator, RandomImpl, legacy_rand::LegacyRand},
};
@@ -81,7 +80,7 @@ pub struct DualNoiseBlockStateProvider {
base: NoiseBlockStateProvider,
variety: [u32; 2],
slow_noise: DoublePerlinNoiseParametersCodec,
slow_scale: f32,
slow_scale: f64,
}
impl DualNoiseBlockStateProvider {
@@ -92,7 +91,8 @@ impl DualNoiseBlockStateProvider {
&noise,
false,
);
let slow_noise = self.get_slow_noise(&pos, &sampler);
let slow_noise =
self.get_slow_noise(pos.0.x as f64, pos.0.y as f64, pos.0.z as f64, &sampler);
let mapped = clamped_map(
slow_noise,
-1.0,
@@ -102,10 +102,7 @@ impl DualNoiseBlockStateProvider {
) as i32;
let mut list = Vec::with_capacity(mapped as usize);
for i in 0..mapped {
let value = self.get_slow_noise(
&BlockPos(pos.0.add(&Vector3::new(i * 54545, 0, i * 34234))),
&sampler,
);
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)
@@ -116,11 +113,11 @@ impl DualNoiseBlockStateProvider {
self.base.get_state_by_value(&list, value).get_state()
}
fn get_slow_noise(&self, pos: &BlockPos, sampler: &DoublePerlinNoiseSampler) -> f64 {
fn get_slow_noise(&self, x: f64, y: f64, z: f64, sampler: &DoublePerlinNoiseSampler) -> f64 {
sampler.sample(
pos.0.x as f64 * self.slow_scale as f64,
pos.0.y as f64 * self.slow_scale as f64,
pos.0.z as f64 * self.slow_scale as f64,
x * self.slow_scale,
y * self.slow_scale,
z * self.slow_scale,
)
}
}

View File

@@ -1,5 +1,5 @@
use pumpkin_data::{Block, BlockState};
use pumpkin_util::math::{floor_div, floor_mod, vector2::Vector2, vector3::Vector3};
use pumpkin_util::math::{floor_div, floor_mod, vector2::Vector2};
use crate::generation::section_coords;
@@ -30,7 +30,7 @@ pub const WATER_BLOCK: Block = Block::WATER;
pub const CHUNK_DIM: u8 = 16;
pub enum BlockStateSampler {
Aquifer(Box<AquiferSampler>),
Aquifer(AquiferSampler),
Ore(OreVeinSampler),
Chained(ChainedBlockStateSampler),
}
@@ -142,7 +142,8 @@ impl IndexToNoisePos for ChunkIndexMapper {
pub struct ChunkNoiseGenerator<'a> {
pub state_sampler: BlockStateSampler,
generation_shape: &'a GenerationShapeConfig,
start_cell_pos: Vector2<i32>,
start_cell_pos_x: i32,
start_cell_pos_z: i32,
vertical_cell_count: usize,
minimum_cell_y: i32,
@@ -166,15 +167,13 @@ impl<'a> ChunkNoiseGenerator<'a> {
aquifers: bool,
ore_veins: bool,
) -> Self {
let start_cell_pos = Vector2::new(
floor_div(
start_block_x,
generation_shape.horizontal_cell_block_count() as i32,
),
floor_div(
start_block_z,
generation_shape.horizontal_cell_block_count() as i32,
),
let start_cell_pos_x = floor_div(
start_block_x,
generation_shape.horizontal_cell_block_count() as i32,
);
let start_cell_pos_z = floor_div(
start_block_z,
generation_shape.horizontal_cell_block_count() as i32,
);
let biome_pos = Vector2::new(
@@ -209,7 +208,8 @@ impl<'a> ChunkNoiseGenerator<'a> {
let section_x = section_coords::block_to_section(start_block_x);
let section_z = section_coords::block_to_section(start_block_z);
AquiferSampler::Aquifer(WorldAquiferSampler::new(
Vector2::new(section_x, section_z),
section_x,
section_z,
&random_config.aquifer_random_deriver,
generation_shape.min_y,
generation_shape.height,
@@ -219,23 +219,28 @@ impl<'a> ChunkNoiseGenerator<'a> {
AquiferSampler::SeaLevel(SeaLevelAquiferSampler::new(level_sampler))
};
let mut samplers = vec![BlockStateSampler::Aquifer(Box::new(aquifer_sampler))];
if ore_veins {
let state_sampler = if ore_veins {
let ore_sampler = OreVeinSampler::new(random_config.ore_random_deriver.clone());
samplers.push(BlockStateSampler::Ore(ore_sampler));
};
let samplers: Box<[BlockStateSampler]> = Box::new([
BlockStateSampler::Aquifer(aquifer_sampler),
BlockStateSampler::Ore(ore_sampler),
]);
let state_sampler =
BlockStateSampler::Chained(ChainedBlockStateSampler::new(samplers.into_boxed_slice()));
BlockStateSampler::Chained(ChainedBlockStateSampler::new(samplers))
} else {
let samplers: Box<[BlockStateSampler]> =
Box::new([BlockStateSampler::Aquifer(aquifer_sampler)]);
BlockStateSampler::Chained(ChainedBlockStateSampler::new(samplers))
};
let router = ChunkNoiseRouter::generate(noise_router_base, &builder_options);
Self {
state_sampler,
generation_shape,
start_cell_pos,
start_cell_pos_x,
start_cell_pos_z,
vertical_cell_count,
minimum_cell_y,
@@ -249,19 +254,19 @@ impl<'a> ChunkNoiseGenerator<'a> {
#[inline]
pub fn sample_start_density(&mut self) {
self.cache_result_unique_id = 0;
self.sample_density(true, self.start_cell_pos.x);
self.sample_density(true, self.start_cell_pos_x);
}
#[inline]
pub fn sample_end_density(&mut self, cell_x: u8) {
self.sample_density(false, self.start_cell_pos.x + cell_x as i32 + 1);
self.sample_density(false, self.start_cell_pos_x + cell_x as i32 + 1);
}
fn sample_density(&mut self, start: bool, current_x: i32) {
let x = current_x * self.horizontal_cell_block_count() as i32;
for cell_z in 0..=(16 / self.horizontal_cell_block_count()) {
let current_cell_z_pos = self.start_cell_pos.y + cell_z as i32;
let current_cell_z_pos = self.start_cell_pos_z + cell_z as i32;
let z = current_cell_z_pos * self.horizontal_cell_block_count() as i32;
self.cache_fill_unique_id += 1;
@@ -331,11 +336,11 @@ impl<'a> ChunkNoiseGenerator<'a> {
self.cache_fill_unique_id += 1;
let start_x =
(self.start_cell_pos.x + cell_x as i32) * self.horizontal_cell_block_count() as i32;
(self.start_cell_pos_x + cell_x as i32) * self.horizontal_cell_block_count() as i32;
let start_y =
(cell_y as i32 + self.minimum_cell_y) * self.vertical_cell_block_count() as i32;
let start_z =
(self.start_cell_pos.y + cell_z as i32) * self.horizontal_cell_block_count() as i32;
(self.start_cell_pos_z + cell_z as i32) * self.horizontal_cell_block_count() as i32;
let mapper = ChunkIndexMapper {
start_x,
@@ -363,25 +368,26 @@ impl<'a> ChunkNoiseGenerator<'a> {
self.cache_fill_unique_id += 1;
}
#[expect(clippy::too_many_arguments)]
pub fn sample_block_state(
&mut self,
start_pos: Vector3<i32>,
cell_pos: Vector3<i32>,
start_x: i32,
start_y: i32,
start_z: i32,
cell_x: i32,
cell_y: i32,
cell_z: i32,
height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> {
//TODO: Fix this when Blender is added
let pos = UnblendedNoisePos::new(
start_pos.x + cell_pos.x,
start_pos.y + cell_pos.y,
start_pos.z + cell_pos.z,
);
let pos = UnblendedNoisePos::new(start_x + cell_x, start_y + cell_y, start_z + cell_z);
let options = ChunkNoiseFunctionSampleOptions::new(
false,
SampleAction::CellCaches(WrapperData::new(
cell_pos.x as usize,
cell_pos.y as usize,
cell_pos.z as usize,
cell_x as usize,
cell_y as usize,
cell_z as usize,
self.horizontal_cell_block_count() as usize,
self.vertical_cell_block_count() as usize,
)),

View File

@@ -5,7 +5,7 @@ use pumpkin_data::{
tag::Taggable,
};
use pumpkin_util::{
math::{position::BlockPos, vector2::Vector2},
math::position::BlockPos,
random::{RandomGenerator, RandomImpl},
};
use serde::Deserialize;
@@ -38,11 +38,8 @@ impl BambooFeature {
let rnd = random.next_bounded_i32(4) + 1;
for x in pos.0.x - rnd..pos.0.x + rnd {
for z in pos.0.z - rnd..pos.0.z + rnd {
let block_below = BlockPos::new(
x,
chunk.top_block_height_exclusive(&Vector2::new(x, z)) - 1,
z,
);
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) {
continue;

View File

@@ -57,7 +57,7 @@ impl OreFeature {
for _ in n..=(n + q) {
for _ in p..=(p + q) {
if o > chunk.ocean_floor_height_exclusive(&pos.0.to_vec2_i32()) {
if o > chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z) {
continue;
}
return self.generate_vein_part(chunk, random, d, e, h, j, l, m, n, o, p, q, r);

View File

@@ -4,7 +4,7 @@ use pumpkin_data::{
block_properties::{BlockProperties, EnumVariants, Integer1To4, SeaPickleLikeProperties},
};
use pumpkin_util::{
math::{int_provider::IntProvider, position::BlockPos, vector2::Vector2},
math::{int_provider::IntProvider, position::BlockPos},
random::{RandomGenerator, RandomImpl},
};
use serde::Deserialize;
@@ -29,7 +29,7 @@ impl SeaPickleFeature {
for _ in 0..count {
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(&Vector2::new(pos.0.x + x, pos.0.z + z));
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 {
continue;
}

View File

@@ -4,7 +4,7 @@ use pumpkin_data::{
block_properties::{BlockProperties, DoubleBlockHalf, TallSeagrassLikeProperties},
};
use pumpkin_util::{
math::{position::BlockPos, vector2::Vector2},
math::position::BlockPos,
random::{RandomGenerator, RandomImpl},
};
use serde::Deserialize;
@@ -26,7 +26,7 @@ impl SeagrassFeature {
) -> bool {
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(&Vector2::new(pos.0.x + x, pos.0.z + z));
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 {
let tall = random.next_f64() < self.probability as f64;

View File

@@ -9,7 +9,6 @@ use std::sync::LazyLock;
use pumpkin_util::biome::FOLIAGE_NOISE;
use pumpkin_util::math::int_provider::IntProvider;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector2::Vector2;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::{RandomGenerator, RandomImpl};
@@ -28,7 +27,7 @@ pub static PLACED_FEATURES: LazyLock<HashMap<String, PlacedFeature>> = LazyLock:
#[derive(Deserialize)]
#[serde(untagged)]
pub enum PlacedFeatureWrapper {
Direct(Box<PlacedFeature>),
Direct(PlacedFeature),
Named(String),
}
@@ -325,7 +324,7 @@ impl CountOnEveryLayerPlacementModifier {
for _j in 0..self.count.get(random) {
let x = random.next_bounded_i32(16) + pos.0.x;
let z = random.next_bounded_i32(16) + pos.0.z;
let y = chunk.top_motion_blocking_block_height_exclusive(&Vector2::new(x, z));
let y = chunk.top_motion_blocking_block_height_exclusive(x, z);
let n = Self::find_pos(chunk, x, y, z, i);
@@ -406,7 +405,7 @@ impl ConditionalPlacementModifier for SurfaceThresholdFilterPlacementModifier {
_random: &mut RandomGenerator,
pos: BlockPos,
) -> bool {
let y = chunk.get_top_y(&self.heightmap, &pos.0.to_vec2_i32());
let y = chunk.get_top_y(&self.heightmap, pos.0.x, pos.0.z);
let min = y.saturating_add(self.min_inclusive.unwrap_or(i32::MIN));
let max = y.saturating_add(self.max_inclusive.unwrap_or(i32::MAX));
min <= pos.0.y && pos.0.y <= max
@@ -470,8 +469,8 @@ impl ConditionalPlacementModifier for SurfaceWaterDepthFilterPlacementModifier {
_random: &mut RandomGenerator,
pos: BlockPos,
) -> bool {
let world_top = chunk.top_block_height_exclusive(&Vector2::new(pos.0.x, pos.0.z));
let ocean = chunk.ocean_floor_height_exclusive(&Vector2::new(pos.0.x, pos.0.z));
let world_top = chunk.top_block_height_exclusive(pos.0.x, pos.0.z);
let ocean = chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z);
world_top - ocean <= self.max_water_depth
}
}
@@ -490,7 +489,7 @@ impl ConditionalPlacementModifier for BiomePlacementModifier {
) -> 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);
let biome = chunk.get_biome_for_terrain_gen(pos.0.x, pos.0.y, pos.0.z);
for feature in biome.features {
if feature.contains(&name.deref()) {
@@ -536,7 +535,7 @@ impl HeightmapPlacementModifier {
) -> Box<dyn Iterator<Item = BlockPos>> {
let x = pos.0.x;
let z = pos.0.z;
let top = chunk.get_top_y(&self.heightmap, &Vector2::new(x, z));
let top = chunk.get_top_y(&self.heightmap, x, z);
if top > min_y as i32 {
return Box::new(iter::once(BlockPos(Vector3::new(x, top, z))));
}

View File

@@ -33,8 +33,8 @@ pub struct VeryBiasedToBottomHeightProvider {
impl VeryBiasedToBottomHeightProvider {
pub fn get(&self, random: &mut RandomGenerator, min_y: i8, height: u16) -> i32 {
let min = self.min_inclusive.get_y(min_y, height) as i32;
let max = self.max_inclusive.get_y(min_y, height) as i32;
let min = self.min_inclusive.get_y(min_y as i16, height);
let max = self.max_inclusive.get_y(min_y as i16, height);
let inner = self.inner.unwrap_or(1) as i32;
let min_rnd = random.next_inbetween_i32(min + inner, max);
@@ -52,8 +52,8 @@ pub struct UniformHeightProvider {
impl UniformHeightProvider {
pub fn get(&self, random: &mut RandomGenerator, min_y: i8, height: u16) -> i32 {
let min = self.min_inclusive.get_y(min_y, height) as i32;
let max = self.max_inclusive.get_y(min_y, height) as i32;
let min = self.min_inclusive.get_y(min_y as i16, height);
let max = self.max_inclusive.get_y(min_y as i16, height);
random.next_inbetween_i32(min, max)
}
@@ -69,21 +69,21 @@ pub struct TrapezoidHeightProvider {
impl TrapezoidHeightProvider {
pub fn get(&self, random: &mut RandomGenerator, min_y: i8, height: u16) -> i32 {
let plateau = self.plateau.unwrap_or(0);
let i = self.min_inclusive.get_y(min_y, height);
let j = self.max_inclusive.get_y(min_y, height);
let i = self.min_inclusive.get_y(min_y as i16, height);
let j = self.max_inclusive.get_y(min_y as i16, height);
if i > j {
log::warn!("Empty height range");
return i as i32;
return i;
}
let k = j - i;
if plateau >= k as i32 {
return random.next_inbetween_i32(i as i32, j as i32);
if plateau >= k {
return random.next_inbetween_i32(i, j);
}
let l = (k as i32 - plateau) / 2;
let m = k as i32 - l;
i as i32 + random.next_inbetween_i32(0, m) + random.next_inbetween_i32(0, l)
let l = (k - plateau) / 2;
let m = k - l;
i + random.next_inbetween_i32(0, m) + random.next_inbetween_i32(0, l)
}
}

View File

@@ -6,7 +6,7 @@ use super::{
density_function::{IndexToNoisePos, NoiseFunctionComponentRange, NoisePos},
};
use enum_dispatch::enum_dispatch;
use pumpkin_util::math::{lerp, lerp3, vector2::Vector2};
use pumpkin_util::math::{lerp, lerp3};
use crate::generation::{biome_coords, positions::chunk_pos};
@@ -486,7 +486,7 @@ impl MutableChunkNoiseFunctionComponentImpl for Cache2D {
pos: &impl NoisePos,
sample_options: &ChunkNoiseFunctionSampleOptions,
) -> f64 {
let packed_column = chunk_pos::packed(&Vector2::new(pos.x(), pos.z()));
let packed_column = chunk_pos::packed(pos.x() as u64, pos.z() as u64);
if packed_column == self.last_sample_column {
self.last_sample_result
} else {

View File

@@ -70,7 +70,7 @@ pub enum ChunkNoiseFunctionComponent<'a> {
Dependent(&'a DependentProtoNoiseFunctionComponent),
// NOTE: The box here is intentional: we want to bring down the size to keep the component stack
// smaller
Chunk(Box<ChunkSpecificNoiseFunctionComponent>),
Chunk(ChunkSpecificNoiseFunctionComponent),
PassThrough(PassThrough),
//Panic(String),
}
@@ -360,7 +360,7 @@ impl<'a> ChunkNoiseRouter<'a> {
match wrapper.wrapper_type {
WrapperType::Interpolated => {
interpolator_indices.push(component_index);
ChunkNoiseFunctionComponent::Chunk(Box::new(
ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::DensityInterpolator(
DensityInterpolator::new(
wrapper.input_index,
@@ -369,33 +369,33 @@ impl<'a> ChunkNoiseRouter<'a> {
build_options,
),
),
))
)
}
WrapperType::CellCache => {
cell_cache_indices.push(component_index);
ChunkNoiseFunctionComponent::Chunk(Box::new(
ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::CellCache(CellCache::new(
wrapper.input_index,
min_value,
max_value,
build_options,
)),
))
)
}
WrapperType::CacheOnce => ChunkNoiseFunctionComponent::Chunk(Box::new(
WrapperType::CacheOnce => ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::CacheOnce(CacheOnce::new(
wrapper.input_index,
min_value,
max_value,
)),
)),
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(Box::new(
),
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::Cache2D(Cache2D::new(
wrapper.input_index,
min_value,
max_value,
)),
)),
),
WrapperType::CacheFlat => {
let mut flat_cache = FlatCache::new(
wrapper.input_index,
@@ -445,9 +445,9 @@ impl<'a> ChunkNoiseRouter<'a> {
}
}
ChunkNoiseFunctionComponent::Chunk(Box::new(
ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::FlatCache(flat_cache),
))
)
}
}
}
@@ -485,7 +485,7 @@ impl<'a> ChunkNoiseRouter<'a> {
let ChunkNoiseFunctionComponent::Chunk(chunk) = component.first_mut().unwrap() else {
unreachable!();
};
let ChunkSpecificNoiseFunctionComponent::CellCache(cell_cache) = chunk.as_mut() else {
let ChunkSpecificNoiseFunctionComponent::CellCache(cell_cache) = chunk else {
unreachable!();
};
@@ -514,7 +514,7 @@ impl<'a> ChunkNoiseRouter<'a> {
unreachable!();
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};
@@ -547,7 +547,7 @@ impl<'a> ChunkNoiseRouter<'a> {
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};
@@ -566,7 +566,7 @@ impl<'a> ChunkNoiseRouter<'a> {
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};
@@ -584,7 +584,7 @@ impl<'a> ChunkNoiseRouter<'a> {
unreachable!();
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};
@@ -602,7 +602,7 @@ impl<'a> ChunkNoiseRouter<'a> {
unreachable!();
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};
@@ -620,7 +620,7 @@ impl<'a> ChunkNoiseRouter<'a> {
unreachable!();
};
let ChunkSpecificNoiseFunctionComponent::DensityInterpolator(density_interpolator) =
chunk.as_mut()
chunk
else {
unreachable!();
};

View File

@@ -181,9 +181,9 @@ impl ShiftedNoise {
}
pub struct InterpolatedNoiseSampler {
lower_noise: Box<OctavePerlinNoiseSampler>,
upper_noise: Box<OctavePerlinNoiseSampler>,
noise: Box<OctavePerlinNoiseSampler>,
lower_noise: OctavePerlinNoiseSampler,
upper_noise: OctavePerlinNoiseSampler,
noise: OctavePerlinNoiseSampler,
data: &'static InterpolatedNoiseSamplerData,
fractions: [f64; 16],
max_value: f64,
@@ -197,24 +197,9 @@ impl InterpolatedNoiseSampler {
let little_start = -7;
let little_amplitudes = [1.0; 8];
let lower_noise = Box::new(OctavePerlinNoiseSampler::new(
random,
big_start,
&big_amplitudes,
true,
));
let upper_noise = Box::new(OctavePerlinNoiseSampler::new(
random,
big_start,
&big_amplitudes,
true,
));
let noise = Box::new(OctavePerlinNoiseSampler::new(
random,
little_start,
&little_amplitudes,
true,
));
let lower_noise = OctavePerlinNoiseSampler::new(random, big_start, &big_amplitudes, true);
let upper_noise = OctavePerlinNoiseSampler::new(random, big_start, &big_amplitudes, true);
let noise = OctavePerlinNoiseSampler::new(random, little_start, &little_amplitudes, true);
let max_value = lower_noise.get_total_amplitude(data.scaled_y_scale + 2.0);

View File

@@ -7,7 +7,6 @@ use pumpkin_data::noise_router::{self, BaseNoiseFunctionComponent};
use serde::Deserialize;
// I do a lot of leaking here, but its only for testing
pub struct HashableF32(pub f32);
// Normally this is bad, but we just care about checking if components are the same

View File

@@ -140,13 +140,13 @@ impl<'a> MultiNoiseSampler<'a> {
let max_value = component_stack[wrapper.input_index].max();
match wrapper.wrapper_type {
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(Box::new(
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::Cache2D(Cache2D::new(
wrapper.input_index,
min_value,
max_value,
)),
)),
),
WrapperType::CacheFlat => {
let mut flat_cache = FlatCache::new(
wrapper.input_index,
@@ -196,9 +196,9 @@ impl<'a> MultiNoiseSampler<'a> {
}
}
ChunkNoiseFunctionComponent::Chunk(Box::new(
ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::FlatCache(flat_cache),
))
)
}
// Java passes thru if the noise pos is not the chunk itself, which it is
// never for the MultiNoiseSampler

View File

@@ -68,7 +68,7 @@ impl<'a> DoublePerlinNoiseBuilder<'a> {
}
}
pub fn get_noise_sampler_for_id(&mut self, id: &str) -> DoublePerlinNoiseSampler {
pub fn get_noise_sampler_for_id(&self, id: &str) -> DoublePerlinNoiseSampler {
let parameters = DoublePerlinNoiseParameters::id_to_parameters(id)
.unwrap_or_else(|| panic!("Unknown noise id: {id}"));
@@ -140,7 +140,7 @@ impl ProtoNoiseRouters {
base_stack: &[BaseNoiseFunctionComponent],
random_config: &GlobalRandomConfig,
) -> Box<[ProtoNoiseFunctionComponent]> {
let mut perlin_noise_builder = DoublePerlinNoiseBuilder::new(random_config);
let perlin_noise_builder = DoublePerlinNoiseBuilder::new(random_config);
// Contiguous memory for our function components
let mut stack = Vec::<ProtoNoiseFunctionComponent>::with_capacity(base_stack.len());

View File

@@ -1,7 +1,6 @@
use std::collections::HashMap;
use pumpkin_data::noise_router::WrapperType;
use pumpkin_util::math::vector2::Vector2;
use crate::generation::{biome_coords, positions::chunk_pos};
@@ -68,7 +67,7 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
let biome_aligned_x = biome_coords::to_block(biome_coords::from_block(block_x));
let biome_aligned_z = biome_coords::to_block(biome_coords::from_block(block_z));
let packed_column = chunk_pos::packed(&Vector2::new(biome_aligned_x, biome_aligned_z));
let packed_column = chunk_pos::packed(biome_aligned_x as u64, biome_aligned_z as u64);
if let Some(estimate) = self.cache.get(&packed_column) {
*estimate
} else {
@@ -125,13 +124,13 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
let max_value = component_stack[wrapper.input_index].max();
match wrapper.wrapper_type {
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(Box::new(
WrapperType::Cache2D => ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::Cache2D(Cache2D::new(
wrapper.input_index,
min_value,
max_value,
)),
)),
),
WrapperType::CacheFlat => {
let mut flat_cache = FlatCache::new(
wrapper.input_index,
@@ -181,9 +180,9 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
}
}
ChunkNoiseFunctionComponent::Chunk(Box::new(
ChunkNoiseFunctionComponent::Chunk(
ChunkSpecificNoiseFunctionComponent::FlatCache(flat_cache),
))
)
}
// Java passes thru if the noise pos is not the chunk itself, which it is
// never for the Height estimator

View File

@@ -39,10 +39,10 @@ pub mod chunk_pos {
use crate::generation::section_coords::get_offset_pos;
// A chunk outside of normal bounds
pub const MARKER: u64 = packed(&Vector2::new(1875066, 1875066));
pub const MARKER: u64 = packed(1875066, 1875066);
pub const fn packed(vec: &Vector2<i32>) -> u64 {
(vec.x as u64 & 4294967295u64) | ((vec.y as u64 & 4294967295u64) << 32)
pub const fn packed(x: u64, y: u64) -> u64 {
(x & 4294967295u64) | ((y & 4294967295u64) << 32)
}
pub const fn unpack_x(packed: u64) -> i32 {
@@ -69,20 +69,20 @@ pub mod chunk_pos {
get_offset_pos(coord, offset)
}
pub const fn start_block_x(vec: &Vector2<i32>) -> i32 {
vec.x << 4
pub const fn start_block_x(x: i32) -> i32 {
x << 4
}
pub const fn end_block_x(vec: &Vector2<i32>) -> i32 {
start_block_x(vec) + 15
pub const fn end_block_x(x: i32) -> i32 {
start_block_x(x) + 15
}
pub const fn start_block_z(vec: &Vector2<i32>) -> i32 {
vec.y << 4
pub const fn start_block_z(z: i32) -> i32 {
z << 4
}
pub const fn end_block_z(vec: &Vector2<i32>) -> i32 {
start_block_z(vec) + 15
pub const fn end_block_z(z: i32) -> i32 {
start_block_z(z) + 15
}
pub const fn to_chunk_pos(vec: &Vector2<i32>) -> Vector2<i32> {
@@ -107,17 +107,18 @@ pub const MIN_HEIGHT_CELL: i32 = MIN_HEIGHT << 4;
#[cfg(test)]
mod test {
use pumpkin_util::math::{vector2::Vector2, vector3::Vector3};
use pumpkin_util::math::vector3::Vector3;
use super::{block_pos, chunk_pos};
#[test]
fn test_chunk_packing() {
let pos = Vector2::new(305135135, -1351513511);
let packed = chunk_pos::packed(&pos);
let x = 305135135_i32;
let y = -1351513511_i32;
let packed = chunk_pos::packed(x as u64, y as u64);
assert_eq!(packed as i64, -5804706329542001121i64);
assert_eq!(pos.x, chunk_pos::unpack_x(packed));
assert_eq!(pos.y, chunk_pos::unpack_z(packed));
assert_eq!(x, chunk_pos::unpack_x(packed));
assert_eq!(y, chunk_pos::unpack_z(packed));
}
#[test]

View File

@@ -9,7 +9,7 @@ use pumpkin_data::{
use pumpkin_util::math::block_box::BlockBox;
use pumpkin_util::{
HeightMap,
math::{position::BlockPos, vector2::Vector2, vector3::Vector3},
math::{position::BlockPos, vector3::Vector3},
random::{RandomGenerator, get_decorator_seed, xoroshiro128::Xoroshiro},
};
@@ -52,13 +52,13 @@ pub trait GenerationCache: HeightLimitView + BlockAccessor {
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState;
fn get_fluid_and_fluid_state(&self, position: &Vector3<i32>) -> (Fluid, FluidState);
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState);
fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32;
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2<i32>) -> i32;
fn get_top_y(&self, heightmap: &HeightMap, pos: &Vector2<i32>) -> i32;
fn top_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32;
fn ocean_floor_height_exclusive(&self, pos: &Vector2<i32>) -> i32;
fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32;
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32;
fn get_top_y(&self, heightmap: &HeightMap, x: i32, z: i32) -> i32;
fn top_block_height_exclusive(&self, x: i32, z: i32) -> i32;
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32;
fn is_air(&self, local_pos: &Vector3<i32>) -> bool;
fn get_biome_for_terrain_gen(&self, global_block_pos: &Vector3<i32>) -> &'static Biome;
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome;
}
const AIR_BLOCK: Block = Block::AIR;
@@ -120,7 +120,8 @@ impl FluidLevelSamplerImpl for StandardChunkFluidLevelSampler {
///
#[derive(Debug, Clone)]
pub struct ProtoChunk {
pub chunk_pos: Vector2<i32>,
pub x: i32,
pub z: i32,
pub default_block: &'static BlockState,
biome_mixer_seed: i64,
// These are local positions
@@ -150,8 +151,8 @@ pub struct TerrainCache {
impl TerrainCache {
pub fn from_random(random_config: &GlobalRandomConfig) -> Self {
let random = &random_config.base_random_deriver;
let mut noise_builder = DoublePerlinNoiseBuilder::new(random_config);
let terrain_builder = SurfaceTerrainBuilder::new(&mut noise_builder, random);
let noise_builder = DoublePerlinNoiseBuilder::new(random_config);
let terrain_builder = SurfaceTerrainBuilder::new(&noise_builder, random);
let surface_noise = noise_builder.get_noise_sampler_for_id("surface");
let secondary_noise = noise_builder.get_noise_sampler_for_id("surface_secondary");
Self {
@@ -164,7 +165,8 @@ impl TerrainCache {
impl ProtoChunk {
pub fn new(
chunk_pos: Vector2<i32>,
x: i32,
z: i32,
settings: &GenerationSettings,
default_block: &'static BlockState,
biome_mixer_seed: i64,
@@ -174,7 +176,8 @@ impl ProtoChunk {
let default_heightmap = vec![i16::MIN; CHUNK_AREA].into_boxed_slice();
Self {
chunk_pos,
x,
z,
default_block,
flat_block_map: vec![0; CHUNK_AREA * height as usize].into_boxed_slice(),
flat_biome_map: vec![
@@ -203,7 +206,8 @@ impl ProtoChunk {
biome_mixer_seed: i64,
) -> Self {
let mut proto_chunk = ProtoChunk::new(
chunk_data.position,
chunk_data.x,
chunk_data.z,
settings,
default_block,
biome_mixer_seed,
@@ -233,12 +237,11 @@ impl ProtoChunk {
let biome = Biome::from_id(biome_id).unwrap();
let relative_y_block = (section_y as i32 * 16) + (y as i32 * 4);
let local_biome_pos = Vector3::new(
let index = proto_chunk.local_biome_pos_to_biome_index(
x as i32,
biome_coords::from_block(relative_y_block),
z as i32,
);
let index = proto_chunk.local_biome_pos_to_biome_index(&local_biome_pos);
proto_chunk.flat_biome_map[index] = biome;
}
}
@@ -290,87 +293,84 @@ impl ProtoChunk {
self.bottom_y
}
fn maybe_update_surface_height_map(&mut self, pos: &Vector3<i32>) {
let local_x = pos.x & 15;
let local_z = pos.z & 15;
fn maybe_update_surface_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_surface_height_map[index];
if pos.y > current_height as i32 {
self.flat_surface_height_map[index] = pos.y as _;
if y > current_height as i32 {
self.flat_surface_height_map[index] = y as _;
}
}
fn maybe_update_ocean_floor_height_map(&mut self, pos: &Vector3<i32>) {
let local_x = pos.x & 15;
let local_z = pos.z & 15;
fn maybe_update_ocean_floor_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_ocean_floor_height_map[index];
if pos.y > current_height as i32 {
self.flat_ocean_floor_height_map[index] = pos.y as _;
if y > current_height as i32 {
self.flat_ocean_floor_height_map[index] = y as _;
}
}
fn maybe_update_motion_blocking_height_map(&mut self, pos: &Vector3<i32>) {
let local_x = pos.x & 15;
let local_z = pos.z & 15;
fn maybe_update_motion_blocking_height_map(&mut self, local_x: i32, y: i32, local_z: i32) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_motion_blocking_height_map[index];
if pos.y > current_height as i32 {
self.flat_motion_blocking_height_map[index] = pos.y as _;
if y > current_height as i32 {
self.flat_motion_blocking_height_map[index] = y as _;
}
}
fn maybe_update_motion_blocking_no_leaves_height_map(&mut self, pos: &Vector3<i32>) {
let local_x = pos.x & 15;
let local_z = pos.z & 15;
fn maybe_update_motion_blocking_no_leaves_height_map(
&mut self,
local_x: i32,
y: i32,
local_z: i32,
) {
let index = Self::local_position_to_height_map_index(local_x, local_z);
let current_height = self.flat_motion_blocking_no_leaves_height_map[index];
if pos.y > current_height as i32 {
self.flat_motion_blocking_no_leaves_height_map[index] = pos.y as _;
if y > current_height as i32 {
self.flat_motion_blocking_no_leaves_height_map[index] = y as _;
}
}
pub fn get_top_y(&self, heightmap: &HeightMap, pos: &Vector2<i32>) -> i32 {
pub fn get_top_y(&self, heightmap: &HeightMap, x: i32, z: i32) -> i32 {
match heightmap {
HeightMap::WorldSurfaceWg => self.top_block_height_exclusive(pos),
HeightMap::WorldSurface => self.top_block_height_exclusive(pos),
HeightMap::OceanFloorWg => self.ocean_floor_height_exclusive(pos),
HeightMap::OceanFloor => self.ocean_floor_height_exclusive(pos),
HeightMap::MotionBlocking => self.top_motion_blocking_block_height_exclusive(pos),
HeightMap::WorldSurfaceWg => self.top_block_height_exclusive(x, z),
HeightMap::WorldSurface => self.top_block_height_exclusive(x, z),
HeightMap::OceanFloorWg => self.ocean_floor_height_exclusive(x, z),
HeightMap::OceanFloor => self.ocean_floor_height_exclusive(x, z),
HeightMap::MotionBlocking => self.top_motion_blocking_block_height_exclusive(x, z),
HeightMap::MotionBlockingNoLeaves => {
self.top_motion_blocking_block_no_leaves_height_exclusive(pos)
self.top_motion_blocking_block_no_leaves_height_exclusive(x, z)
}
}
}
pub fn top_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let local_x = pos.x & 15;
let local_z = pos.y & 15;
pub fn top_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
let local_x = x & 15;
let local_z = z & 15;
let index = Self::local_position_to_height_map_index(local_x, local_z);
self.flat_surface_height_map[index] as i32 + 1
}
pub fn ocean_floor_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let local_x = pos.x & 15;
let local_z = pos.y & 15;
pub fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32 {
let local_x = x & 15;
let local_z = z & 15;
let index = Self::local_position_to_height_map_index(local_x, local_z);
self.flat_ocean_floor_height_map[index] as i32 + 1
}
pub fn top_motion_blocking_block_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let local_x = pos.x & 15;
let local_z = pos.y & 15;
pub fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
let local_x = x & 15;
let local_z = z & 15;
let index = Self::local_position_to_height_map_index(local_x, local_z);
self.flat_motion_blocking_height_map[index] as i32 + 1
}
pub fn top_motion_blocking_block_no_leaves_height_exclusive(&self, pos: &Vector2<i32>) -> i32 {
let local_x = pos.x & 15;
let local_z = pos.y & 15;
pub fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32 {
let local_x = x & 15;
let local_z = z & 15;
let index = Self::local_position_to_height_map_index(local_x, local_z);
self.flat_motion_blocking_no_leaves_height_map[index] as i32 + 1
}
@@ -381,40 +381,39 @@ impl ProtoChunk {
}
#[inline]
fn local_pos_to_block_index(&self, local_pos: &Vector3<i32>) -> usize {
fn local_pos_to_block_index(&self, x: i32, y: i32, z: i32) -> usize {
#[cfg(debug_assertions)]
{
assert!(local_pos.x >= 0 && local_pos.x <= 15);
assert!(local_pos.y < self.height() as i32);
assert!(local_pos.y >= 0);
assert!(local_pos.z >= 0 && local_pos.z <= 15);
assert!((0..=15).contains(&x));
assert!(y < self.height() as i32);
assert!(y >= 0);
assert!((0..=15).contains(&z));
}
self.height() as usize * CHUNK_DIM as usize * local_pos.x as usize
+ CHUNK_DIM as usize * local_pos.y as usize
+ local_pos.z as usize
self.height() as usize * CHUNK_DIM as usize * x as usize
+ CHUNK_DIM as usize * y as usize
+ z as usize
}
#[inline]
fn local_biome_pos_to_biome_index(&self, local_biome_pos: &Vector3<i32>) -> usize {
fn local_biome_pos_to_biome_index(&self, x: i32, y: i32, z: i32) -> usize {
#[cfg(debug_assertions)]
{
assert!(local_biome_pos.x >= 0 && local_biome_pos.x <= 3);
assert!((0..=3).contains(&x));
assert!(
local_biome_pos.y < biome_coords::from_chunk(self.height() as i32)
&& local_biome_pos.y >= 0,
y < biome_coords::from_chunk(self.height() as i32) && y >= 0,
"{} - {} vs {}",
0,
biome_coords::from_chunk(self.height() as i32),
local_biome_pos.y
y
);
assert!(local_biome_pos.z >= 0 && local_biome_pos.z <= 3);
assert!((0..=3).contains(&z));
}
biome_coords::from_block(self.height() as usize)
* biome_coords::from_block(CHUNK_DIM as usize)
* local_biome_pos.x as usize
+ biome_coords::from_block(CHUNK_DIM as usize) * local_biome_pos.y as usize
+ local_biome_pos.z as usize
* x as usize
+ biome_coords::from_block(CHUNK_DIM as usize) * y as usize
+ z as usize
}
#[inline]
@@ -424,76 +423,70 @@ impl ProtoChunk {
}
#[inline]
pub fn get_block_state_raw(&self, local_pos: &Vector3<i32>) -> u16 {
let index = self.local_pos_to_block_index(local_pos);
pub fn get_block_state_raw(&self, x: i32, y: i32, z: i32) -> u16 {
let index = self.local_pos_to_block_index(x, y, z);
self.flat_block_map[index]
}
#[inline]
pub fn get_block_state(&self, local_pos: &Vector3<i32>) -> RawBlockState {
let local_pos = Vector3::new(
local_pos.x & 15,
local_pos.y - self.bottom_y() as i32,
local_pos.z & 15,
);
if local_pos.y < 0 || local_pos.y >= self.height() as i32 {
let local_y = local_pos.y - self.bottom_y() as i32;
if local_y < 0 || local_y >= self.height() as i32 {
return RawBlockState(Block::VOID_AIR.default_state.id);
}
RawBlockState(self.get_block_state_raw(&local_pos))
RawBlockState(self.get_block_state_raw(local_pos.x & 15, local_y, local_pos.z & 15))
}
pub fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState) {
let local_pos = Vector3::new(pos.x & 15, pos.y - self.bottom_y() as i32, pos.z & 15);
if local_pos.y < 0 || local_pos.y >= self.height() as i32 {
let local_x = pos.x & 15;
let local_y = pos.y - self.bottom_y() as i32;
let local_z = pos.z & 15;
if local_y < 0 || local_y >= self.height() as i32 {
return;
}
if !block_state.is_air() {
self.maybe_update_surface_height_map(pos);
}
if blocks_movement(block_state) {
self.maybe_update_ocean_floor_height_map(pos);
}
if blocks_movement(block_state) || block_state.is_liquid() {
self.maybe_update_motion_blocking_height_map(pos);
self.maybe_update_surface_height_map(local_x, pos.y, local_z);
let block = Block::from_state_id(block_state.id);
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
{
self.maybe_update_motion_blocking_no_leaves_height_map(pos);
let blocks_movement = blocks_movement(block_state, block);
if blocks_movement {
self.maybe_update_ocean_floor_height_map(local_x, pos.y, local_z);
} else if blocks_movement || block_state.is_liquid() {
self.maybe_update_motion_blocking_height_map(local_x, pos.y, local_z);
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
{
self.maybe_update_motion_blocking_no_leaves_height_map(
local_x, pos.y, local_z,
);
}
}
}
}
let index = self.local_pos_to_block_index(&local_pos);
let index = self.local_pos_to_block_index(local_x, local_y, local_z);
self.flat_block_map[index] = block_state.id;
}
#[inline]
pub fn get_biome(&self, global_biome_pos: &Vector3<i32>) -> &'static Biome {
let local_pos = Vector3::new(
global_biome_pos.x & biome_coords::from_block(15),
global_biome_pos.y - biome_coords::from_block(self.bottom_y() as i32),
global_biome_pos.z & biome_coords::from_block(15),
pub fn get_biome(&self, x: i32, y: i32, z: i32) -> &'static Biome {
let index = self.local_biome_pos_to_biome_index(
x & biome_coords::from_block(15),
y - biome_coords::from_block(self.bottom_y() as i32),
z & biome_coords::from_block(15),
);
let index = self.local_biome_pos_to_biome_index(&local_pos);
self.flat_biome_map[index]
}
pub fn step_to_biomes(&mut self, dimension: Dimension, noise_router: &ProtoNoiseRouters) {
debug_assert_eq!(self.stage, StagedChunkEnum::Empty);
let chunk_pos = self.chunk_pos;
let start_x = start_block_x(&chunk_pos);
let start_z = start_block_z(&chunk_pos);
let biome_pos = Vector2::new(
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
);
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);
let multi_noise_config =
super::noise::router::multi_noise_sampler::MultiNoiseSamplerBuilderOptions::new(
biome_pos.x,
biome_pos.y,
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
);
let mut multi_noise_sampler =
@@ -510,16 +503,15 @@ impl ProtoChunk {
) {
debug_assert_eq!(self.stage, StagedChunkEnum::Biomes);
let chunk_pos = self.chunk_pos;
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let start_x = start_block_x(&chunk_pos);
let start_z = start_block_z(&chunk_pos);
let start_x = start_block_x(self.x);
let start_z = start_block_z(self.z);
let sampler = FluidLevelSampler::Chunk(Box::new(StandardChunkFluidLevelSampler::new(
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
FluidLevel::new(-54, &Block::LAVA),
)));
));
let mut noise_sampler = ChunkNoiseGenerator::new(
&noise_router.noise,
@@ -532,16 +524,13 @@ impl ProtoChunk {
settings.aquifers_enabled,
settings.ore_veins_enabled,
);
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 * generation_shape.horizontal_cell_block_count(),
);
let surface_config = SurfaceHeightSamplerBuilderOptions::new(
biome_pos.x,
biome_pos.y,
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
generation_shape.min_y as i32,
generation_shape.max_y() as i32,
@@ -565,22 +554,17 @@ impl ProtoChunk {
) {
debug_assert_eq!(self.stage, StagedChunkEnum::Noise);
// Build surface
let chunk_pos = self.chunk_pos;
let start_x = start_block_x(&chunk_pos);
let start_z = start_block_z(&chunk_pos);
let start_x = start_block_x(self.x);
let start_z = start_block_z(self.z);
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
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 * generation_shape.horizontal_cell_block_count(),
);
let surface_config = SurfaceHeightSamplerBuilderOptions::new(
biome_pos.x,
biome_pos.y,
biome_coords::from_block(start_x),
biome_coords::from_block(start_z),
horizontal_biome_end as usize,
generation_shape.min_y as i32,
generation_shape.max_y() as i32,
@@ -609,8 +593,8 @@ impl ProtoChunk {
let bottom_section = section_coords::block_to_section(min_y) as i32;
let top_section = section_coords::block_to_section(min_y as i32 + self.height() as i32 - 1);
let start_block_x = chunk_pos::start_block_x(&self.chunk_pos);
let start_block_z = chunk_pos::start_block_z(&self.chunk_pos);
let start_block_x = start_block_x(self.x);
let start_block_z = start_block_z(self.z);
let start_biome_x = biome_coords::from_block(start_block_x);
let start_biome_z = biome_coords::from_block(start_block_z);
@@ -623,26 +607,30 @@ impl ProtoChunk {
for x in 0..biomes_per_section {
for y in 0..biomes_per_section {
for z in 0..biomes_per_section {
let biome_pos =
Vector3::new(start_biome_x + x, start_biome_y + y, start_biome_z + z);
let biome = if dimension == Dimension::End {
TheEndBiomeSupplier::biome(&biome_pos, multi_noise_sampler, dimension)
TheEndBiomeSupplier::biome(
start_biome_x + x,
start_biome_y + y,
start_biome_z + z,
multi_noise_sampler,
dimension,
)
} else {
MultiNoiseBiomeSupplier::biome(
&biome_pos,
start_biome_x + x,
start_biome_y + y,
start_biome_z + z,
multi_noise_sampler,
dimension,
)
};
//dbg!("Populating biome: {:?} -> {:?}", biome_pos, biome);
let local_biome_pos = Vector3 {
let index = self.local_biome_pos_to_biome_index(
x,
// Make the y start from 0
y: start_biome_y + y - biome_coords::from_block(min_y as i32),
start_biome_y + y - biome_coords::from_block(min_y as i32),
z,
};
let index = self.local_biome_pos_to_biome_index(&local_biome_pos);
);
self.flat_biome_map[index] = biome;
}
@@ -722,12 +710,12 @@ impl ProtoChunk {
let block_state = noise_sampler
.sample_block_state(
Vector3::new(
sample_start_x,
sample_start_y,
sample_start_z,
),
Vector3::new(cell_offset_x, cell_offset_y, cell_offset_z),
sample_start_x,
sample_start_y,
sample_start_z,
cell_offset_x,
cell_offset_y,
cell_offset_z,
surface_height_estimate_sampler,
)
.unwrap_or(self.default_block);
@@ -744,16 +732,18 @@ impl ProtoChunk {
}
}
pub fn get_biome_for_terrain_gen(&self, global_block_pos: &Vector3<i32>) -> &'static Biome {
pub fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome {
// TODO: See if we can cache this value
let seed_biome_pos = biome::get_biome_blend(
self.bottom_y(),
self.height(),
self.biome_mixer_seed,
global_block_pos,
x,
y,
z,
);
self.get_biome(&seed_biome_pos)
self.get_biome(seed_biome_pos.x, seed_biome_pos.y, seed_biome_pos.z)
}
/// Constructs the terrain surface, although "surface" is a misnomer as it also places underground blocks like bedrock and deepslate.
@@ -767,8 +757,8 @@ impl ProtoChunk {
terrain_cache: &TerrainCache,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) {
let start_x = chunk_pos::start_block_x(&self.chunk_pos);
let start_z = chunk_pos::start_block_z(&self.chunk_pos);
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 random = &random_config.base_random_deriver;
@@ -788,8 +778,7 @@ impl ProtoChunk {
let x = start_x + local_x;
let z = start_z + local_z;
let mut top_block =
self.top_block_height_exclusive(&Vector2::new(local_x, local_z));
let mut top_block = self.top_block_height_exclusive(local_x, local_z);
let biome_y = if settings.legacy_random_source {
0
@@ -797,14 +786,14 @@ impl ProtoChunk {
top_block
};
let this_biome = self.get_biome_for_terrain_gen(&Vector3::new(x, biome_y, z));
let this_biome = self.get_biome_for_terrain_gen(x, biome_y, z);
if this_biome == &Biome::ERODED_BADLANDS {
terrain_cache
.terrain_builder
.place_badlands_pillar(self, x, z, top_block);
// Get the top block again if we placed a pillar!
top_block = self.top_block_height_exclusive(&Vector2::new(local_x, local_z));
top_block = self.top_block_height_exclusive(local_x, local_z);
}
context.init_horizontal(x, z);
@@ -858,7 +847,11 @@ 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.block_pos);
context.biome = self.get_biome_for_terrain_gen(
context.block_pos_x,
context.block_pos_y,
context.block_pos_z,
);
let new_state = settings.surface_rule.try_apply(
self,
&mut context,
@@ -905,15 +898,14 @@ impl ProtoChunk {
) {
let chunk = cache.get_center_chunk_mut();
debug_assert_eq!(chunk.stage, StagedChunkEnum::Surface);
let chunk_pos = chunk.chunk_pos;
let min_y = chunk.bottom_y();
let height = chunk.height();
let bottom_section = section_coords::block_to_section(min_y) as i32;
let block_pos = BlockPos(Vector3::new(
section_coords::section_to_block(chunk_pos.x),
section_coords::section_to_block(chunk.x),
bottom_section,
section_coords::section_to_block(chunk_pos.y),
section_coords::section_to_block(chunk.z),
));
let population_seed =
@@ -953,7 +945,7 @@ impl ProtoChunk {
// for structure in &set.structures {
// let start = self.structure_starts.get(STRUCTURES.get(name).unwrap());
// }
if !set.placement.should_generate(calculator, self.chunk_pos) {
if !set.placement.should_generate(calculator, self.x, self.z) {
continue; // ??
}
@@ -976,9 +968,8 @@ impl ProtoChunk {
}
fn get_block_box_for_chunk(&self) -> BlockBox {
let pos = self.chunk_pos;
let x = start_block_x(&pos);
let z = start_block_z(&pos);
let x = start_block_x(self.x);
let z = start_block_z(self.z);
let bottom = self.bottom_y() as i32 + 1;
let top = self.top_y() as i32;
BlockBox::new(x, bottom, z, x + 15, top, z + 15)
@@ -993,11 +984,11 @@ impl ProtoChunk {
}
fn start_block_x(&self) -> i32 {
start_block_x(&self.chunk_pos)
start_block_x(self.x)
}
fn start_block_z(&self) -> i32 {
start_block_z(&self.chunk_pos)
start_block_z(self.z)
}
}

View File

@@ -48,7 +48,11 @@ impl StructureType {
StructureType::NetherFortress(generator) => generator.get_structure_position(chunk),
};
if let Some(structure) = STRUCTURES.get(name) {
let current_biome = chunk.get_biome(&position.position.0);
let current_biome = chunk.get_biome(
position.position.0.x,
position.position.0.y,
position.position.0.z,
);
if Biome::get_tag_values(&structure.biomes)
.unwrap()
.contains(&current_biome.registry_id)

View File

@@ -1,5 +1,5 @@
use pumpkin_util::{
math::{floor_div, vector2::Vector2},
math::floor_div,
random::{
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, xoroshiro128::Xoroshiro,
},
@@ -19,22 +19,23 @@ impl StructurePlacement {
pub fn should_generate(
&self,
calculator: StructurePlacementCalculator,
chunk_pos: Vector2<i32>,
chunk_x: i32,
chunk_z: i32,
) -> bool {
self.r#type
.is_start_chunk(&calculator, chunk_pos, self.salt)
&& self.apply_frequency_reduction(calculator.seed, chunk_pos)
.is_start_chunk(&calculator, chunk_x, chunk_z, self.salt)
&& self.apply_frequency_reduction(calculator.seed, chunk_x, chunk_z)
// TODO: add exclusion_zone, only used for pillager_outposts
}
fn apply_frequency_reduction(&self, seed: i64, chunk_pos: Vector2<i32>) -> bool {
fn apply_frequency_reduction(&self, seed: i64, chunk_x: i32, chunk_z: i32) -> bool {
let frequency = self.frequency.unwrap_or(1.0);
frequency >= 1.0
|| self
.frequency_reduction_method
.as_ref()
.unwrap_or(&FrequencyReductionMethod::Default)
.should_generate(seed, chunk_pos, self.salt, frequency)
.should_generate(seed, chunk_x, chunk_z, self.salt, frequency)
}
}
@@ -51,19 +52,20 @@ impl FrequencyReductionMethod {
pub fn should_generate(
&self,
seed: i64,
chunk_pos: Vector2<i32>,
chunk_x: i32,
chunk_z: i32,
salt: i32,
frequency: f32,
) -> bool {
match self {
FrequencyReductionMethod::Default => {
let region_seed = get_region_seed(seed as u64, chunk_pos.x, chunk_pos.y, salt);
let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, salt);
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed));
random.next_f32() < frequency
}
FrequencyReductionMethod::LegacyType1 => {
let x = chunk_pos.x >> 4;
let z = chunk_pos.y >> 4;
let x = chunk_x >> 4;
let z = chunk_z >> 4;
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(
(x ^ z << 4) as u64 ^ seed as u64,
));
@@ -71,15 +73,14 @@ impl FrequencyReductionMethod {
random.next_bounded_i32((1.0 / frequency) as i32) == 0
}
FrequencyReductionMethod::LegacyType2 => {
let region_seed = get_region_seed(seed as u64, chunk_pos.x, chunk_pos.y, 10387320);
let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, 10387320);
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed));
random.next_f32() < frequency
}
FrequencyReductionMethod::LegacyType3 => {
let mut random: RandomGenerator =
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64));
let carver_seed =
get_carver_seed(&mut random, seed as u64, chunk_pos.x, chunk_pos.y);
let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z);
let mut random: RandomGenerator =
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
@@ -102,12 +103,13 @@ impl StructurePlacementType {
pub fn is_start_chunk(
&self,
calculator: &StructurePlacementCalculator,
chunk_pos: Vector2<i32>,
chunk_x: i32,
chunk_z: i32,
salt: i32,
) -> bool {
match self {
StructurePlacementType::RandomSpread(placement) => {
placement.is_start_chunk(calculator, chunk_pos, salt)
placement.is_start_chunk(calculator, chunk_x, chunk_z, salt)
}
StructurePlacementType::ConcentricRings => false, // TODO, This is needed for Stronghold, since it is placed in rings
}
@@ -140,25 +142,26 @@ impl SpreadType {
}
impl RandomSpreadStructurePlacement {
fn get_start_chunk(&self, seed: i64, chunk_pos: Vector2<i32>, salt: i32) -> Vector2<i32> {
let x = floor_div(chunk_pos.x, self.spacing);
let z = floor_div(chunk_pos.y, self.spacing);
fn get_start_chunk(&self, seed: i64, chunk_x: i32, chunk_z: i32, salt: i32) -> (i32, i32) {
let x = floor_div(chunk_x, self.spacing);
let z = floor_div(chunk_z, self.spacing);
let region_seed = get_region_seed(seed as u64, x, z, salt);
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed));
let bound = self.spacing - self.separation;
let rand_x = self.spread_type.get(&mut random, bound);
let rand_z = self.spread_type.get(&mut random, bound);
Vector2::new(x * self.spacing + rand_x, z * self.spacing + rand_z)
(x * self.spacing + rand_x, z * self.spacing + rand_z)
}
pub fn is_start_chunk(
&self,
calculator: &StructurePlacementCalculator,
chunk_pos: Vector2<i32>,
chunk_x: i32,
chunk_z: i32,
salt: i32,
) -> bool {
let pos = self.get_start_chunk(calculator.seed, chunk_pos, salt);
pos == chunk_pos
let pos = self.get_start_chunk(calculator.seed, chunk_x, chunk_z, salt);
(chunk_x == pos.0) && (chunk_z == pos.1)
}
}

View File

@@ -1,7 +1,7 @@
use pumpkin_data::{Block, BlockDirection};
use pumpkin_util::{
HeightMap,
math::{block_box::BlockBox, position::BlockPos, vector2::Vector2, vector3::Vector3},
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
};
use serde::Deserialize;
@@ -18,14 +18,14 @@ pub struct BuriedTreasureGenerator;
impl StructureGenerator for BuriedTreasureGenerator {
fn get_structure_position(&self, chunk: &ProtoChunk) -> super::StructurePosition {
let x = get_center_x(chunk.chunk_pos.x);
let z = get_center_z(chunk.chunk_pos.y);
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, &Vector2::new(x, z)) - 1;
let x = get_center_x(chunk.x);
let z = get_center_z(chunk.z);
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, x, z) - 1;
let generator = StructurePiecesCollector {
pieces_positions: vec![BlockBox::from_pos(BlockPos::new(
get_offset_x(chunk.chunk_pos.x, 9),
get_offset_x(chunk.x, 9),
90,
get_offset_z(chunk.chunk_pos.y, 9),
get_offset_z(chunk.z, 9),
))],
};
StructurePosition {
@@ -37,7 +37,8 @@ impl StructureGenerator for BuriedTreasureGenerator {
fn generate(&self, bounding_box: BlockBox, chunk: &mut crate::ProtoChunk) {
let y = chunk.get_top_y(
&HeightMap::OceanFloorWg,
&Vector2::new(bounding_box.min.x, bounding_box.min.z),
bounding_box.min.x,
bounding_box.min.z,
);
let mut pos = BlockPos::new(bounding_box.min.x, y, bounding_box.min.z);
for _ in y..chunk.bottom_y() as i32 {

View File

@@ -17,9 +17,8 @@ pub struct NetherFortressGenerator;
impl StructureGenerator for NetherFortressGenerator {
fn get_structure_position(&self, chunk: &ProtoChunk) -> StructurePosition {
let chunk_pos = chunk.chunk_pos;
let start_x = chunk_pos::start_block_x(&chunk_pos);
let start_z = chunk_pos::start_block_z(&chunk_pos);
let start_x = chunk_pos::start_block_x(chunk.x);
let start_z = chunk_pos::start_block_z(chunk.z);
let generator = StructurePiecesCollector {
pieces_positions: vec![], // TODO
};

View File

@@ -3,7 +3,7 @@ use std::{cell::RefCell, num::NonZeroUsize};
use lru::LruCache;
use pumpkin_data::chunk::Biome;
use pumpkin_util::{
math::{lerp2, vector2::Vector2, vector3::Vector3, vertical_surface_type::VerticalSurfaceType},
math::{lerp2, vertical_surface_type::VerticalSurfaceType},
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
};
use serde::Deserialize;
@@ -33,7 +33,9 @@ pub struct MaterialRuleContext<'a> {
pub height: u16,
pub random_deriver: &'a RandomDeriver,
fluid_height: i32,
pub block_pos: Vector3<i32>,
pub block_pos_x: i32,
pub block_pos_y: i32,
pub block_pos_z: i32,
pub biome: &'a Biome,
pub run_depth: i32,
pub secondary_depth: f64,
@@ -77,7 +79,9 @@ impl<'a> MaterialRuleContext<'a> {
random_deriver,
terrain_builder,
fluid_height: 0,
block_pos: Vector3::new(0, 0, 0),
block_pos_x: 0,
block_pos_y: 0,
block_pos_z: 0,
biome: &Biome::PLAINS,
run_depth: 0,
secondary_depth: 0.0,
@@ -93,20 +97,20 @@ impl<'a> MaterialRuleContext<'a> {
fn sample_run_depth(&self) -> i32 {
let noise =
self.surface_noise
.sample(self.block_pos.x as f64, 0.0, self.block_pos.z as f64);
.sample(self.block_pos_x as f64, 0.0, self.block_pos_z as f64);
(noise * 2.75
+ 3.0
+ self
.random_deriver
.split_pos(self.block_pos.x, 0, self.block_pos.z)
.split_pos(self.block_pos_x, 0, self.block_pos_z)
.next_f64()
* 0.25) as i32
}
pub fn init_horizontal(&mut self, x: i32, z: i32) {
self.unique_horizontal_pos_value += 1;
self.block_pos.x = x;
self.block_pos.z = z;
self.block_pos_x = x;
self.block_pos_z = z;
self.run_depth = self.sample_run_depth();
}
@@ -117,7 +121,7 @@ impl<'a> MaterialRuleContext<'a> {
y: i32,
fluid_height: i32,
) {
self.block_pos.y = y;
self.block_pos_y = y;
self.fluid_height = fluid_height;
self.stone_depth_below = stone_depth_below;
self.stone_depth_above = stone_depth_above;
@@ -128,7 +132,7 @@ impl<'a> MaterialRuleContext<'a> {
self.last_unique_horizontal_pos_value = self.unique_horizontal_pos_value;
self.secondary_depth =
self.secondary_noise
.sample(self.block_pos.x as f64, 0.0, self.block_pos.z as f64)
.sample(self.block_pos_x as f64, 0.0, self.block_pos_z as f64)
}
self.secondary_depth
}
@@ -178,23 +182,23 @@ impl MaterialCondition {
MaterialCondition::YAbove(above_y) => above_y.test(context),
MaterialCondition::Water(water) => water.test(context),
MaterialCondition::Temperature => {
let temperature = context
.biome
.weather
.compute_temperature(&context.block_pos, context.sea_level);
let temperature = context.biome.weather.compute_temperature(
context.block_pos_x as f64,
context.block_pos_y,
context.block_pos_z as f64,
context.sea_level,
);
temperature < 0.15f32
}
MaterialCondition::Steep => {
let local_x = context.block_pos.x & 15;
let local_z = context.block_pos.z & 15;
let local_x = context.block_pos_x & 15;
let local_z = context.block_pos_z & 15;
let local_z_sub = 0.max(local_z - 1);
let local_z_add = 15.min(local_z + 1);
let sub_height =
chunk.top_block_height_exclusive(&Vector2::new(local_x, local_z_sub));
let add_height =
chunk.top_block_height_exclusive(&Vector2::new(local_x, local_z_add));
let sub_height = chunk.top_block_height_exclusive(local_x, local_z_sub);
let add_height = chunk.top_block_height_exclusive(local_x, local_z_add);
if add_height >= sub_height + 4 {
true
@@ -202,10 +206,8 @@ impl MaterialCondition {
let local_x_sub = 0.max(local_x - 1);
let local_x_add = 15.min(local_x + 1);
let sub_height =
chunk.top_block_height_exclusive(&Vector2::new(local_x_sub, local_z));
let add_height =
chunk.top_block_height_exclusive(&Vector2::new(local_x_add, local_z));
let sub_height = chunk.top_block_height_exclusive(local_x_sub, local_z);
let add_height = chunk.top_block_height_exclusive(local_x_add, local_z);
sub_height >= add_height + 4
}
@@ -240,13 +242,13 @@ pub struct AboveYMaterialCondition {
impl AboveYMaterialCondition {
pub fn test(&self, context: &MaterialRuleContext) -> bool {
context.block_pos.y
context.block_pos_y
+ if self.add_stone_depth {
context.stone_depth_above
} else {
0
}
>= self.anchor.get_y(context.min_y, context.height) as i32
>= self.anchor.get_y(context.min_y as i16, context.height)
+ context.run_depth * self.surface_depth_multiplier
}
}
@@ -279,7 +281,7 @@ impl SurfaceMaterialCondition {
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) -> bool {
// TODO
context.block_pos.y >= estimate_surface_height(context, surface_height_estimate_sampler)
context.block_pos_y >= estimate_surface_height(context, surface_height_estimate_sampler)
}
}
@@ -289,9 +291,9 @@ pub fn estimate_surface_height(
) -> i32 {
if context.last_est_heiht_unique_horizontal_pos_value != context.unique_horizontal_pos_value {
context.last_est_heiht_unique_horizontal_pos_value = context.unique_horizontal_pos_value;
let x = section_coords::block_to_section(context.block_pos.x);
let z = section_coords::block_to_section(context.block_pos.z);
let packed = chunk_pos::packed(&Vector2::new(x, z)) as i64;
let x = section_coords::block_to_section(context.block_pos_x);
let z = section_coords::block_to_section(context.block_pos_z);
let packed = chunk_pos::packed(x as u64, z as u64) as i64;
if context.packed_chunk_pos != packed {
context.packed_chunk_pos = packed;
context.estimated_surface_heights[0] = surface_height_estimate_sampler.estimate_height(
@@ -312,8 +314,8 @@ pub fn estimate_surface_height(
);
}
let surface = lerp2(
((context.block_pos.x & 15) as f32 / 16.0) as f64,
((context.block_pos.z & 15) as f32 / 16.0) as f64,
((context.block_pos_x & 15) as f32 / 16.0) as f64,
((context.block_pos_z & 15) as f32 / 16.0) as f64,
context.estimated_surface_heights[0] as f64,
context.estimated_surface_heights[1] as f64,
context.estimated_surface_heights[2] as f64,
@@ -349,7 +351,7 @@ impl NoiseThresholdMaterialCondition {
let sampler = context
.noise_builder
.get_noise_sampler_for_id(self.noise.strip_prefix("minecraft:").unwrap());
let value = sampler.sample(context.block_pos.x as f64, 0.0, context.block_pos.z as f64);
let value = sampler.sample(context.block_pos_x as f64, 0.0, context.block_pos_z as f64);
value >= self.min_threshold && value <= self.max_threshold
}
}
@@ -398,7 +400,7 @@ pub struct WaterMaterialCondition {
impl WaterMaterialCondition {
pub fn test(&self, context: &MaterialRuleContext) -> bool {
context.fluid_height == i32::MIN
|| context.block_pos.y
|| context.block_pos_y
+ (if self.add_stone_depth {
context.stone_depth_above
} else {
@@ -421,8 +423,12 @@ pub struct VerticalGradientMaterialCondition {
impl VerticalGradientMaterialCondition {
pub fn test(&self, context: &MaterialRuleContext) -> bool {
let true_at = self.true_at_and_below.get_y(context.min_y, context.height);
let false_at = self.false_at_and_above.get_y(context.min_y, context.height);
let true_at = self
.true_at_and_below
.get_y(context.min_y as i16, context.height);
let false_at = self
.false_at_and_above
.get_y(context.min_y as i16, context.height);
let context_pointer: *const RandomDeriver = context.random_deriver;
let key = context_pointer.addr();
@@ -443,16 +449,16 @@ impl VerticalGradientMaterialCondition {
.next_splitter()
});
let block_y = context.block_pos.y;
if block_y <= true_at as i32 {
let block_y = context.block_pos_y;
if block_y <= true_at {
return true;
}
if block_y >= false_at as i32 {
if block_y >= false_at {
return false;
}
let mapped =
pumpkin_util::math::map(block_y as f32, true_at as f32, false_at as f32, 1.0, 0.0);
let mut random = splitter.split_pos(context.block_pos.x, block_y, context.block_pos.z);
let mut random = splitter.split_pos(context.block_pos_x, block_y, context.block_pos_z);
random.next_f32() < mapped
}
}

View File

@@ -17,7 +17,7 @@ pub enum MaterialRule {
#[serde(rename = "minecraft:sequence")]
Sequence(SequenceMaterialRule),
#[serde(rename = "minecraft:condition")]
Condition(Box<ConditionMaterialRule>),
Condition(ConditionMaterialRule),
}
impl MaterialRule {
@@ -45,11 +45,11 @@ pub struct BadLandsMaterialRule;
impl BadLandsMaterialRule {
pub fn try_apply(&self, context: &mut MaterialRuleContext) -> Option<&'static BlockState> {
Some(
context
.terrain_builder
.get_terracotta_block(&context.block_pos),
)
Some(context.terrain_builder.get_terracotta_block(
context.block_pos_x,
context.block_pos_y,
context.block_pos_z,
))
}
}

View File

@@ -27,10 +27,7 @@ pub struct SurfaceTerrainBuilder {
}
impl SurfaceTerrainBuilder {
pub fn new(
noise_builder: &mut DoublePerlinNoiseBuilder,
random_deriver: &RandomDeriver,
) -> Self {
pub fn new(noise_builder: &DoublePerlinNoiseBuilder, random_deriver: &RandomDeriver) -> Self {
Self {
terracotta_bands: Self::create_terracotta_bands(
random_deriver.split_string("minecraft:clay_bands"),
@@ -217,8 +214,9 @@ impl SurfaceTerrainBuilder {
let mut block_threshold = scaled_threshold.min(scaled_roof_noise);
// TODO: Cache this
let pos = Vector3::new(x, sea_level, z);
let temperature = biome.weather.compute_temperature(&pos, sea_level);
let temperature = biome
.weather
.compute_temperature(x as f64, sea_level, z as f64, sea_level);
if temperature > 0.1f32 {
block_threshold -= 2.0;
}
@@ -258,13 +256,13 @@ impl SurfaceTerrainBuilder {
}
}
pub fn get_terracotta_block(&self, pos: &Vector3<i32>) -> &'static BlockState {
pub fn get_terracotta_block(&self, x: i32, y: i32, z: i32) -> &'static BlockState {
let offset = (self
.terracotta_bands_offset_noise
.sample(pos.x as f64, 0.0, pos.z as f64)
.sample(x as f64, 0.0, z as f64)
* 4.0)
.round() as i32;
let offset = pos.y + offset;
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()

View File

@@ -9,13 +9,13 @@ pub enum YOffset {
}
impl YOffset {
pub fn get_y(&self, min_y: i8, height: u16) -> i16 {
pub fn get_y(&self, min_y: i16, height: u16) -> i32 {
match self {
YOffset::AboveBottom(above_bottom) => min_y as i16 + above_bottom.above_bottom as i16,
YOffset::AboveBottom(above_bottom) => min_y as i32 + above_bottom.above_bottom as i32,
YOffset::BelowTop(below_top) => {
height as i16 - 1 + min_y as i16 - below_top.below_top as i16
height as i32 - 1 + min_y as i32 - below_top.below_top as i32
}
YOffset::Absolute(absolute) => absolute.absolute,
YOffset::Absolute(absolute) => absolute.absolute as i32,
}
}
}

View File

@@ -237,7 +237,8 @@ impl Level {
// );
let chunk = ChunkEntityData {
chunk_position: pos,
x: pos.x,
z: pos.y,
data: HashMap::new(),
dirty: true,
};
@@ -508,8 +509,8 @@ impl Level {
let chunk = chunk.downgrade();
let chunk_x_base = chunk.position.x * 16;
let chunk_z_base = chunk.position.y * 16;
let chunk_x_base = chunk.x * 16;
let chunk_z_base = chunk.z * 16;
let mut section_blocks = Vec::new();
for i in 0..chunk.section.sections.len() {
@@ -669,7 +670,9 @@ impl Level {
while let Some(data) = rx.recv().await {
match data {
LoadedData::Loaded(chunk) => {
let pos = chunk.read().await.chunk_position;
let tmp_chunk = chunk.read().await;
let pos = Vector2::new(tmp_chunk.x, tmp_chunk.z);
drop(tmp_chunk);
level.loaded_entity_chunks.insert(pos, chunk.clone());
let _ = sender.send((chunk, false));
}

View File

@@ -67,23 +67,18 @@ pub fn bench_create_and_populate_noise(
};
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(
Vector2::new(0, 0),
settings,
default_block,
biome_mixer_seed,
);
let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed);
// Create noise sampler and other required components
let generation_shape = &settings.shape;
let horizontal_cell_count = CHUNK_DIM / generation_shape.horizontal_cell_block_count();
let sampler = FluidLevelSampler::Chunk(Box::new(StandardChunkFluidLevelSampler::new(
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
)));
));
let start_x = chunk_pos::start_block_x(&Vector2::new(0, 0));
let start_z = chunk_pos::start_block_z(&Vector2::new(0, 0));
let start_x = chunk_pos::start_block_x(0);
let start_z = chunk_pos::start_block_z(0);
let mut noise_sampler = ChunkNoiseGenerator::new(
&base_router.noise,
@@ -133,18 +128,13 @@ pub fn bench_create_and_populate_biome(
use crate::generation::{biome_coords, positions::chunk_pos};
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(
Vector2::new(0, 0),
settings,
default_block,
biome_mixer_seed,
);
let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed);
// 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(&Vector2::new(0, 0));
let start_z = chunk_pos::start_block_z(&Vector2::new(0, 0));
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),
@@ -186,18 +176,13 @@ pub fn bench_create_and_populate_noise_with_surface(
};
let biome_mixer_seed = hash_seed(random_config.seed);
let mut chunk = ProtoChunk::new(
Vector2::new(0, 0),
settings,
default_block,
biome_mixer_seed,
);
let mut chunk = ProtoChunk::new(0, 0, settings, default_block, biome_mixer_seed);
// Create all required components
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(&Vector2::new(0, 0));
let start_z = chunk_pos::start_block_z(&Vector2::new(0, 0));
let start_x = chunk_pos::start_block_x(0);
let start_z = chunk_pos::start_block_z(0);
// Multi-noise sampler for biomes
let biome_pos = Vector2::new(
@@ -216,10 +201,10 @@ pub fn bench_create_and_populate_noise_with_surface(
MultiNoiseSampler::generate(&base_router.multi_noise, &multi_noise_config);
// Noise sampler
let sampler = FluidLevelSampler::Chunk(Box::new(StandardChunkFluidLevelSampler::new(
let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler::new(
FluidLevel::new(settings.sea_level, settings.default_fluid.name),
FluidLevel::new(-54, &pumpkin_data::Block::LAVA),
)));
));
let mut noise_sampler = ChunkNoiseGenerator::new(
&base_router.noise,

View File

@@ -376,12 +376,12 @@ pub async fn calc_block_breaking(
return 0.0;
}
let i = if player.can_harvest(state, block).await {
30
30.0
} else {
100
100.0
};
player.get_mining_speed(block).await / hardness / i as f32
player.get_mining_speed(block).await / hardness / i
}
#[derive(PartialEq)]

View File

@@ -55,18 +55,14 @@ impl<'a> FindArg<'a> for BlockArgumentConsumer {
Some(Arg::Block(name)) => Block::from_name(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text((*name).to_string())],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
)))
}
},
@@ -125,18 +121,14 @@ impl<'a> FindArg<'a> for BlockPredicateArgumentConsumer {
Block::from_name(name).map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text((*name).to_string())],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.block.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
)))
}
},
@@ -146,11 +138,9 @@ impl<'a> FindArg<'a> for BlockPredicateArgumentConsumer {
|tag| {
get_tag_ids(RegistryKey::Block, tag).map_or_else(
|| {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"arguments.block.tag.unknown",
[TextComponent::text((*tag).to_string())],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"arguments.block.tag.unknown",
[TextComponent::text((*tag).to_string())],
)))
},
|blocks| Ok(Some(BlockPredicate::Tag(blocks.to_vec()))),

View File

@@ -90,22 +90,18 @@ pub enum NotInBounds {
impl From<NotInBounds> for CommandError {
fn from(value: NotInBounds) -> Self {
match value {
NotInBounds::LowerBound(val, min) => {
Self::CommandFailed(Box::new(TextComponent::text(format!(
"{} must not be less than {}, found {}",
val.qualifier(),
min,
val
))))
}
NotInBounds::UpperBound(val, max) => {
Self::CommandFailed(Box::new(TextComponent::text(format!(
"{} must not be more than {}, found {}",
val.qualifier(),
max,
val
))))
}
NotInBounds::LowerBound(val, min) => Self::CommandFailed(TextComponent::text(format!(
"{} must not be less than {}, found {}",
val.qualifier(),
min,
val
))),
NotInBounds::UpperBound(val, max) => Self::CommandFailed(TextComponent::text(format!(
"{} must not be more than {}, found {}",
val.qualifier(),
max,
val
))),
}
}
}

View File

@@ -56,18 +56,14 @@ impl<'a> FindArg<'a> for ItemArgumentConsumer {
.map_or_else(
|| {
if name.starts_with("minecraft:") {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.item.id.invalid",
[TextComponent::text((*name).to_string())],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.item.id.invalid",
[TextComponent::text((*name).to_string())],
)))
} else {
Err(CommandError::CommandFailed(Box::new(
TextComponent::translate(
"argument.item.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
),
Err(CommandError::CommandFailed(TextComponent::translate(
"argument.item.id.invalid",
[TextComponent::text("minecraft:".to_string() + *name)],
)))
}
},

View File

@@ -54,8 +54,8 @@ impl<'a> FindArg<'a> for SoundArgumentConsumer {
Some(Arg::Block(name)) => {
Sound::from_name(name.strip_prefix("minecraft:").unwrap_or(name)).map_or_else(
|| {
Err(CommandError::CommandFailed(Box::new(TextComponent::text(
format!("Sound {name} does not exist."),
Err(CommandError::CommandFailed(TextComponent::text(format!(
"Sound {name} does not exist."
))))
},
Result::Ok,

View File

@@ -54,9 +54,9 @@ impl<'a> FindArg<'a> for SummonableEntitiesArgumentConsumer {
Some(Arg::Block(name)) => {
EntityType::from_name(name.strip_prefix("minecraft:").unwrap_or(name)).map_or_else(
|| {
Err(CommandError::CommandFailed(Box::new(TextComponent::text(
Err(CommandError::CommandFailed(TextComponent::text(
"Can't find Entity",
))))
)))
},
Result::Ok,
)

View File

@@ -62,9 +62,9 @@ impl CommandExecutor for ListExecutor {
handle_banlist(entries, sender).await;
}
_ => {
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
"Incorrect argument for command".to_string(),
))));
return Err(CommandError::CommandFailed(TextComponent::text(
"Incorrect argument for command",
)));
}
}

View File

@@ -225,7 +225,7 @@ async fn display_data(
let mut nbt = NbtCompound::new();
storage.write_nbt(&mut nbt).await;
let display = snbt_colorful_display(&NbtTag::Compound(nbt), 0)
.map_err(|string| CommandError::CommandFailed(Box::new(TextComponent::text(string))))?;
.map_err(|string| CommandError::CommandFailed(TextComponent::text(string)))?;
Ok(TextComponent::translate(
"commands.data.entity.query",
[target_name, display],

View File

@@ -46,7 +46,7 @@ impl CommandExecutor for Executor {
),
};
return Err(CommandError::CommandFailed(Box::new(err_msg)));
return Err(CommandError::CommandFailed(err_msg));
}
};
@@ -58,7 +58,7 @@ impl CommandExecutor for Executor {
TextComponent::text(enchantment.max_level.to_string()),
],
);
return Err(CommandError::CommandFailed(Box::new(msg)));
return Err(CommandError::CommandFailed(msg));
}
let only_one = targets.len() == 1;
@@ -88,7 +88,7 @@ impl CommandExecutor for Executor {
"commands.enchant.failed.itemless",
[targets[0].get_display_name().await],
);
return Err(CommandError::CommandFailed(Box::new(msg)));
return Err(CommandError::CommandFailed(msg));
}
continue;
}
@@ -98,7 +98,7 @@ impl CommandExecutor for Executor {
"commands.enchant.failed.incompatible",
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(Box::new(msg)));
return Err(CommandError::CommandFailed(msg));
}
continue;
}
@@ -111,7 +111,7 @@ impl CommandExecutor for Executor {
"commands.enchant.failed.incompatible",
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(Box::new(msg)));
return Err(CommandError::CommandFailed(msg));
}
} else {
item.enchant(enchantment, level);
@@ -120,7 +120,7 @@ impl CommandExecutor for Executor {
}
if success == 0 {
let msg = TextComponent::translate("commands.enchant.failed", []);
return Err(CommandError::CommandFailed(Box::new(msg)));
return Err(CommandError::CommandFailed(msg));
}
if only_one {
let msg = TextComponent::translate(

View File

@@ -27,9 +27,9 @@ impl CommandExecutor for Executor {
_ => match server.worlds.read().await.first() {
Some(world) => world.level.seed.0,
None => {
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
return Err(CommandError::CommandFailed(TextComponent::text(
"Unable to get Seed",
))));
)));
}
},
};

View File

@@ -102,9 +102,9 @@ async fn setworldspawn(
yaw: f32,
) -> Result<(), CommandError> {
let Some(world) = sender.world() else {
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
return Err(CommandError::CommandFailed(TextComponent::text(
"Failed to get world.",
))));
)));
};
match world.dimension_type {

View File

@@ -66,8 +66,9 @@ impl CommandExecutor for EntitiesToEntityExecutor {
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
let pos = destination.get_entity().pos.load();
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
for target in targets {
@@ -100,8 +101,9 @@ impl CommandExecutor for EntitiesToPosFacingPosExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?;
@@ -140,8 +142,9 @@ impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?;
@@ -179,8 +182,9 @@ impl CommandExecutor for EntitiesToPosWithRotationExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?;
@@ -213,8 +217,9 @@ impl CommandExecutor for EntitiesToPosExecutor {
let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
// todo command context
@@ -257,8 +262,9 @@ impl CommandExecutor for SelfToEntityExecutor {
let yaw = player.living_entity.entity.yaw.load();
let pitch = player.living_entity.entity.pitch.load();
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
player
@@ -293,8 +299,9 @@ impl CommandExecutor for SelfToPosExecutor {
let yaw = player.living_entity.entity.yaw.load();
let pitch = player.living_entity.entity.pitch.load();
if !World::is_valid(pos) {
return Err(CommandError::CommandFailed(Box::new(
TextComponent::translate("argument.pos.outofbounds", []),
return Err(CommandError::CommandFailed(TextComponent::translate(
"argument.pos.outofbounds",
[],
)));
}
player

View File

@@ -24,7 +24,7 @@ pub enum CommandError {
PermissionDenied,
/// A general error occurred during command execution that doesn't fit into
/// more specific `CommandError` variants.
CommandFailed(Box<TextComponent>),
CommandFailed(TextComponent),
}
impl CommandError {
@@ -49,7 +49,7 @@ impl CommandError {
"I'm sorry, but you do not have permission to perform this command. Please contact the server administrator if you believe this is an error.",
)
}
CommandFailed(s) => *s,
CommandFailed(s) => s,
}
}
}
@@ -138,9 +138,7 @@ impl CommandDispatcher {
pub(crate) fn split_parts(cmd: &str) -> Result<(&str, Vec<&str>), CommandError> {
if cmd.is_empty() {
return Err(CommandFailed(Box::new(TextComponent::text(
"Empty Command",
))));
return Err(CommandFailed(TextComponent::text("Empty Command")));
}
let mut args = Vec::new();
let mut current_arg_start = 0usize;
@@ -167,9 +165,7 @@ impl CommandDispatcher {
'}' => {
if !in_single_quotes && !in_double_quotes {
if in_braces == 0 {
return Err(CommandFailed(Box::new(TextComponent::text(
"Unmatched braces",
))));
return Err(CommandFailed(TextComponent::text("Unmatched braces")));
}
in_braces -= 1;
}
@@ -182,9 +178,7 @@ impl CommandDispatcher {
']' => {
if !in_single_quotes && !in_double_quotes {
if in_brackets == 0 {
return Err(CommandFailed(Box::new(TextComponent::text(
"Unmatched brackets",
))));
return Err(CommandFailed(TextComponent::text("Unmatched brackets")));
}
in_brackets -= 1;
}
@@ -216,24 +210,22 @@ impl CommandDispatcher {
args.push(&cmd[current_arg_start..]);
}
if in_single_quotes || in_double_quotes {
return Err(CommandFailed(Box::new(TextComponent::text(
return Err(CommandFailed(TextComponent::text(
"Unmatched quotes at the end",
))));
)));
}
if in_braces != 0 {
return Err(CommandFailed(Box::new(TextComponent::text(
return Err(CommandFailed(TextComponent::text(
"Unmatched braces at the end",
))));
)));
}
if in_brackets != 0 {
return Err(CommandFailed(Box::new(TextComponent::text(
return Err(CommandFailed(TextComponent::text(
"Unmatched brackets at the end",
))));
)));
}
if args.is_empty() {
return Err(CommandFailed(Box::new(TextComponent::text(
"Empty Command",
))));
return Err(CommandFailed(TextComponent::text("Empty Command")));
}
let key = args.remove(0);
Ok((key, args.into_iter().rev().collect()))
@@ -249,15 +241,15 @@ impl CommandDispatcher {
let (key, raw_args) = Self::split_parts(cmd)?;
if !self.commands.contains_key(key) {
return Err(CommandFailed(Box::new(TextComponent::text(format!(
return Err(CommandFailed(TextComponent::text(format!(
"Command {key} does not exist"
)))));
))));
}
let Some(permission) = self.permissions.get(key) else {
return Err(CommandFailed(Box::new(TextComponent::text(
return Err(CommandFailed(TextComponent::text(
"Permission for Command not found".to_string(),
))));
)));
};
if !src.has_permission(permission.as_str()).await {
@@ -272,18 +264,16 @@ impl CommandDispatcher {
return Ok(());
}
}
Err(CommandFailed(Box::new(TextComponent::text(format!(
Err(CommandFailed(TextComponent::text(format!(
"Invalid Syntax. Usage: {tree}"
)))))
))))
}
pub fn get_tree<'a>(&'a self, key: &str) -> Result<&'a CommandTree, CommandError> {
let command =
self.commands
.get(key)
.ok_or(CommandFailed(Box::new(TextComponent::text(
"Command not found",
))))?;
let command = self
.commands
.get(key)
.ok_or(CommandFailed(TextComponent::text("Command not found")))?;
match command {
Command::Tree(tree) => Ok(tree),
@@ -292,9 +282,9 @@ impl CommandDispatcher {
log::error!(
"Error while parsing command alias \"{key}\": pointing to \"{target}\" which is not a valid tree"
);
return Err(CommandFailed(Box::new(TextComponent::text(
return Err(CommandFailed(TextComponent::text(
"Internal Error (See logs for details)",
))));
)));
};
Ok(tree)
}

View File

@@ -1935,7 +1935,9 @@ impl World {
let Some((chunk, _first_load)) = recv_result else {
break;
};
let position = chunk.read().await.chunk_position;
let tmp_chunk = chunk.read().await;
let position = Vector2::new(tmp_chunk.x, tmp_chunk.z);
drop(tmp_chunk);
let chunk = if level.is_chunk_watched(&position) {
chunk

View File

@@ -373,10 +373,6 @@ pub async fn spawn_category_for_position(
new_pos = BlockPos::new(new_x, new_pos.0.y, new_z);
let new_pos_center = new_pos.to_centered_f64();
let player_distance = get_nearest_player(&new_pos_center, world).await;
if player_distance == f64::MAX {
// debug!("player_distance infinity");
return;
}
if !is_right_distance_to_player_and_spawn_point(
&new_pos,
player_distance,