chore: add more chunk tests
This commit is contained in:
parent
e1117ce4a6
commit
9ececd9eca
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -1128,18 +1128,6 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enum_dispatch"
|
|
||||||
version = "0.3.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.119",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -2812,7 +2800,6 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"enum_dispatch",
|
|
||||||
"md5",
|
"md5",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -2843,7 +2830,6 @@ dependencies = [
|
|||||||
"crossbeam",
|
"crossbeam",
|
||||||
"crossfire",
|
"crossfire",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"enum_dispatch",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools 0.15.0",
|
"itertools 0.15.0",
|
||||||
|
|||||||
@ -133,14 +133,13 @@ crossfire = { version = "3.1.19", default-features = false, features = ["compat"
|
|||||||
crypto-bigint = { version = "0.7.5", default-features = false }
|
crypto-bigint = { version = "0.7.5", default-features = false }
|
||||||
dashmap = { version = "6.2", default-features = false }
|
dashmap = { version = "6.2", default-features = false }
|
||||||
ecdsa = { version = "0.17.0", default-features = false, features = ["std"] }
|
ecdsa = { version = "0.17.0", default-features = false, features = ["std"] }
|
||||||
enum_dispatch = { version = "0.3.13", default-features = false }
|
|
||||||
flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] }
|
flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] }
|
||||||
heck = { version = "0.5", default-features = false }
|
heck = { version = "0.5", default-features = false }
|
||||||
hmac = { version = "=0.13.0", default-features = false }
|
hmac = { version = "=0.13.0", default-features = false }
|
||||||
indexmap = { version = "2.14", default-features = false, features = ["std"] }
|
indexmap = { version = "2.14", default-features = false, features = ["std"] }
|
||||||
itertools = { version = "0.15.0", default-features = false, features = ["use_std"] }
|
itertools = { version = "0.15.0", default-features = false, features = ["use_std"] }
|
||||||
libloading = { version = "0.9", default-features = false, features = ["std"] }
|
libloading = { version = "0.9", default-features = false, features = ["std"] }
|
||||||
lru = { version = "0.18.1", default-features = false }
|
lru = { version = "0.18.2", default-features = false }
|
||||||
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }
|
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }
|
||||||
md5 = { version = "0.8", default-features = false }
|
md5 = { version = "0.8", default-features = false }
|
||||||
num-bigint = { version = "0.5", default-features = false, features = ["std"] }
|
num-bigint = { version = "0.5", default-features = false, features = ["std"] }
|
||||||
|
|||||||
1
assets/tests/end_surface_no_blend_no_beard_0_0.chunk
Normal file
1
assets/tests/end_surface_no_blend_no_beard_0_0.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/end_surface_no_blend_no_beard_7_4.chunk
Normal file
1
assets/tests/end_surface_no_blend_no_beard_7_4.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/multi_noise_biome_source_test.json
Normal file
1
assets/tests/multi_noise_biome_source_test.json
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/nether_surface_no_blend_no_beard_0_0.chunk
Normal file
1
assets/tests/nether_surface_no_blend_no_beard_0_0.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/nether_surface_no_blend_no_beard_7_4.chunk
Normal file
1
assets/tests/nether_surface_no_blend_no_beard_7_4.chunk
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
assets/tests/no_blend_no_beard_surface_7_4.chunk
Normal file
1
assets/tests/no_blend_no_beard_surface_7_4.chunk
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
assets/tests/noise_end_no_blend_no_beard_0_0.chunk
Normal file
1
assets/tests/noise_end_no_blend_no_beard_0_0.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/noise_end_no_blend_no_beard_7_4.chunk
Normal file
1
assets/tests/noise_end_no_blend_no_beard_7_4.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/noise_nether_no_blend_no_beard_0_0.chunk
Normal file
1
assets/tests/noise_nether_no_blend_no_beard_0_0.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/noise_nether_no_blend_no_beard_7_4.chunk
Normal file
1
assets/tests/noise_nether_no_blend_no_beard_7_4.chunk
Normal file
File diff suppressed because one or more lines are too long
1
assets/tests/noise_no_blend_no_beard_13579_-7_9.chunk
Normal file
1
assets/tests/noise_no_blend_no_beard_13579_-7_9.chunk
Normal file
File diff suppressed because one or more lines are too long
13
pumpkin-codegen/Cargo.lock
generated
13
pumpkin-codegen/Cargo.lock
generated
@ -321,18 +321,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enum_dispatch"
|
|
||||||
version = "0.3.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.118",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -820,7 +808,6 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"enum_dispatch",
|
|
||||||
"md5",
|
"md5",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|||||||
@ -344,8 +344,8 @@ impl ToTokens for MaterialConditionStruct {
|
|||||||
} => {
|
} => {
|
||||||
// Pre calc for speed :D
|
// Pre calc for speed :D
|
||||||
let bytes = md5::compute(random_name.as_bytes());
|
let bytes = md5::compute(random_name.as_bytes());
|
||||||
let lo = u64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length"));
|
let lo = u64::from_le_bytes(bytes[0..8].try_into().expect("incorrect length"));
|
||||||
let hi = u64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length"));
|
let hi = u64::from_le_bytes(bytes[8..16].try_into().expect("incorrect length"));
|
||||||
tokens.extend(quote!(
|
tokens.extend(quote!(
|
||||||
MaterialCondition::VerticalGradient(VerticalGradientMaterialCondition {
|
MaterialCondition::VerticalGradient(VerticalGradientMaterialCondition {
|
||||||
random_lo: #lo,
|
random_lo: #lo,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -21,18 +21,26 @@ pub struct VarInt(pub VarIntType);
|
|||||||
|
|
||||||
impl VarInt {
|
impl VarInt {
|
||||||
/// The maximum number of bytes a `VarInt` can occupy.
|
/// The maximum number of bytes a `VarInt` can occupy.
|
||||||
const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
|
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: VarIntType) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the exact number of bytes this `VarInt` will write when
|
/// Returns the exact number of bytes this `VarInt` will write when
|
||||||
/// [`Encode::encode`] is called, assuming no error occurs.
|
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
pub const fn written_size(&self) -> usize {
|
pub const fn written_size(&self) -> usize {
|
||||||
match self.0 {
|
match self.0 as u32 {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
n => (31 - n.leading_zeros() as usize) / 7 + 1,
|
n => (31 - n.leading_zeros() as usize) / 7 + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
||||||
// Must cast to u32 to prevent infinite loops on negative i32s
|
// Must cast to u32 to prevent infinite loops on negative i32s
|
||||||
let mut val = self.0 as u32;
|
let mut val = self.0 as u32;
|
||||||
@ -47,6 +55,7 @@ impl VarInt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate that the first byte will not overflow a i32
|
// TODO: Validate that the first byte will not overflow a i32
|
||||||
|
#[inline]
|
||||||
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
for i in 0..Self::MAX_SIZE.get() {
|
for i in 0..Self::MAX_SIZE.get() {
|
||||||
|
|||||||
@ -15,13 +15,31 @@ pub type VarLongType = i64;
|
|||||||
/**
|
/**
|
||||||
* A variable-length long type used by the Minecraft network protocol.
|
* A variable-length long type used by the Minecraft network protocol.
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub struct VarLong(pub VarLongType);
|
pub struct VarLong(pub VarLongType);
|
||||||
|
|
||||||
impl VarLong {
|
impl VarLong {
|
||||||
/// The maximum number of bytes a `VarLong` can occupy.
|
/// The maximum number of bytes a `VarLong` can occupy.
|
||||||
const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
|
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: VarLongType) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the exact number of bytes this `VarLong` will write when
|
||||||
|
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub const fn written_size(&self) -> usize {
|
||||||
|
match self.0 as u64 {
|
||||||
|
0 => 1,
|
||||||
|
n => (63 - n.leading_zeros() as usize) / 7 + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
||||||
let mut val = self.0 as u64;
|
let mut val = self.0 as u64;
|
||||||
|
|
||||||
@ -35,6 +53,7 @@ impl VarLong {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate that the first byte will not overflow a i64
|
// TODO: Validate that the first byte will not overflow a i64
|
||||||
|
#[inline]
|
||||||
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
for i in 0..Self::MAX_SIZE.get() {
|
for i in 0..Self::MAX_SIZE.get() {
|
||||||
|
|||||||
@ -20,15 +20,26 @@ pub struct VarUInt(pub VarUIntType);
|
|||||||
|
|
||||||
impl VarUInt {
|
impl VarUInt {
|
||||||
/// The maximum number of bytes a `VarUInt` can occupy.
|
/// The maximum number of bytes a `VarUInt` can occupy.
|
||||||
const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
|
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: VarUIntType) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the exact number of bytes this `VarUInt` will write when
|
/// Returns the exact number of bytes this `VarUInt` will write when
|
||||||
/// [`Encode::encode`] is called, assuming no error occurs.
|
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn written_size(&self) -> usize {
|
#[inline]
|
||||||
(32 - self.0.leading_zeros() as usize).max(1).div_ceil(7)
|
pub const fn written_size(&self) -> usize {
|
||||||
|
match self.0 {
|
||||||
|
0 => 1,
|
||||||
|
n => (31 - n.leading_zeros() as usize) / 7 + 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
||||||
let mut val = self.0;
|
let mut val = self.0;
|
||||||
loop {
|
loop {
|
||||||
@ -46,6 +57,7 @@ impl VarUInt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate that the first byte will not overflow a i32
|
// TODO: Validate that the first byte will not overflow a i32
|
||||||
|
#[inline]
|
||||||
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
for i in 0..Self::MAX_SIZE.get() {
|
for i in 0..Self::MAX_SIZE.get() {
|
||||||
@ -65,7 +77,7 @@ impl VarUInt {
|
|||||||
for i in 0..Self::MAX_SIZE.get() {
|
for i in 0..Self::MAX_SIZE.get() {
|
||||||
let byte = read.read_u8().await.map_err(|err| {
|
let byte = read.read_u8().await.map_err(|err| {
|
||||||
if i == 0 && matches!(err.kind(), ErrorKind::UnexpectedEof) {
|
if i == 0 && matches!(err.kind(), ErrorKind::UnexpectedEof) {
|
||||||
ReadingError::CleanEOF("VarInt".to_string())
|
ReadingError::CleanEOF("VarUInt".to_string())
|
||||||
} else {
|
} else {
|
||||||
ReadingError::Incomplete(err.to_string())
|
ReadingError::Incomplete(err.to_string())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,18 +20,26 @@ pub struct VarULong(pub VarULongType);
|
|||||||
|
|
||||||
impl VarULong {
|
impl VarULong {
|
||||||
/// The maximum number of bytes a `VarULong` can occupy.
|
/// The maximum number of bytes a `VarULong` can occupy.
|
||||||
const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
|
pub const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap();
|
||||||
|
|
||||||
/// Returns the exact number of bytes this `VarLong` will write when
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: VarULongType) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the exact number of bytes this `VarULong` will write when
|
||||||
/// [`Encode::encode`] is called, assuming no error occurs.
|
/// [`Encode::encode`] is called, assuming no error occurs.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
pub const fn written_size(&self) -> usize {
|
pub const fn written_size(&self) -> usize {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
n => (31 - n.leading_zeros() as usize) / 7 + 1,
|
n => (63 - n.leading_zeros() as usize) / 7 + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> {
|
||||||
let mut x = self.0;
|
let mut x = self.0;
|
||||||
loop {
|
loop {
|
||||||
@ -48,6 +56,7 @@ impl VarULong {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate that the first byte will not overflow a i64
|
// TODO: Validate that the first byte will not overflow a i64
|
||||||
|
#[inline]
|
||||||
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
pub fn decode(read: &mut impl Read) -> Result<Self, ReadingError> {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
for i in 0..Self::MAX_SIZE.get() {
|
for i in 0..Self::MAX_SIZE.get() {
|
||||||
@ -57,7 +66,7 @@ impl VarULong {
|
|||||||
return Ok(Self(val));
|
return Ok(Self(val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadingError::TooLarge("VarLong".to_string()))
|
Err(ReadingError::TooLarge("VarULong".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
macro_rules! gen_from {
|
macro_rules! gen_from {
|
||||||
|
|||||||
@ -254,7 +254,7 @@ macro_rules! get_number_be {
|
|||||||
fn $name(&mut self) -> Result<$type, ReadingError> {
|
fn $name(&mut self) -> Result<$type, ReadingError> {
|
||||||
let mut buf = [0u8; std::mem::size_of::<$type>()];
|
let mut buf = [0u8; std::mem::size_of::<$type>()];
|
||||||
self.read_exact(&mut buf)
|
self.read_exact(&mut buf)
|
||||||
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
|
.map_err(|err| ReadingError::Incomplete(err.to_string().into()))?;
|
||||||
Ok(<$type>::from_be_bytes(buf))
|
Ok(<$type>::from_be_bytes(buf))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -380,9 +380,7 @@ pub fn read_remaining_bytes(read: &mut impl Read, bound: usize) -> Result<Box<[u
|
|||||||
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
|
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
|
||||||
|
|
||||||
if return_buf.len() > bound {
|
if return_buf.len() > bound {
|
||||||
return Err(ReadingError::TooLarge(
|
return Err(ReadingError::TooLarge("Read remaining too long".into()));
|
||||||
"Read remaining too long".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(return_buf.into_boxed_slice())
|
Ok(return_buf.into_boxed_slice())
|
||||||
@ -529,11 +527,20 @@ impl<W: Write> NetworkWriteExt for W {
|
|||||||
bit_set: FixedBitSet,
|
bit_set: FixedBitSet,
|
||||||
) -> Result<(), WritingError> {
|
) -> Result<(), WritingError> {
|
||||||
let new_length = bits.div_ceil(8);
|
let new_length = bits.div_ceil(8);
|
||||||
let mut new_vec = vec![0u8; new_length];
|
|
||||||
let bytes_to_copy = std::cmp::min(bit_set.len(), new_length);
|
let bytes_to_copy = std::cmp::min(bit_set.len(), new_length);
|
||||||
|
|
||||||
new_vec[..bytes_to_copy].copy_from_slice(&bit_set[..bytes_to_copy]);
|
self.write_slice(&bit_set[..bytes_to_copy])?;
|
||||||
self.write_slice(&new_vec)?;
|
|
||||||
|
if new_length > bytes_to_copy {
|
||||||
|
const ZEROES: [u8; 64] = [0u8; 64];
|
||||||
|
let padding = new_length - bytes_to_copy;
|
||||||
|
let mut remaining = padding;
|
||||||
|
while remaining > 0 {
|
||||||
|
let chunk = remaining.min(ZEROES.len());
|
||||||
|
self.write_slice(&ZEROES[..chunk])?;
|
||||||
|
remaining -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -596,7 +603,7 @@ impl<W: Write> NetworkWriteExt for W {
|
|||||||
list: &[G],
|
list: &[G],
|
||||||
writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>,
|
writer: impl Fn(&mut Self, &G) -> Result<(), WritingError>,
|
||||||
) -> Result<(), WritingError> {
|
) -> Result<(), WritingError> {
|
||||||
self.write_var_int(&(list.len() as i32).into())?;
|
self.write_var_int(&VarInt(list.len() as i32))?;
|
||||||
|
|
||||||
for data in list {
|
for data in list {
|
||||||
writer(self, data)?;
|
writer(self, data)?;
|
||||||
|
|||||||
@ -20,7 +20,6 @@ md5.workspace = true
|
|||||||
quote = { workspace = true, optional = true }
|
quote = { workspace = true, optional = true }
|
||||||
syn = { workspace = true, optional = true }
|
syn = { workspace = true, optional = true }
|
||||||
proc-macro2 = { workspace = true, optional = true }
|
proc-macro2 = { workspace = true, optional = true }
|
||||||
enum_dispatch.workspace = true
|
|
||||||
|
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
tokio = { workspace = true, features = ["sync"] }
|
tokio = { workspace = true, features = ["sync"] }
|
||||||
|
|||||||
@ -3,7 +3,6 @@ use std::{
|
|||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use legacy_rand::{LegacyRand, LegacySplitter};
|
use legacy_rand::{LegacyRand, LegacySplitter};
|
||||||
use xoroshiro128::{Xoroshiro, XoroshiroSplitter};
|
use xoroshiro128::{Xoroshiro, XoroshiroSplitter};
|
||||||
|
|
||||||
@ -12,21 +11,8 @@ pub mod legacy_rand;
|
|||||||
pub mod xoroshiro128;
|
pub mod xoroshiro128;
|
||||||
|
|
||||||
/// Global seed uniquifier used to generate unique seeds based on time.
|
/// Global seed uniquifier used to generate unique seeds based on time.
|
||||||
///
|
|
||||||
/// This value is atomically updated each time a seed is requested to ensure
|
|
||||||
/// that seeds generated at the same time are still unique.
|
|
||||||
static SEED_UNIQUIFIER: AtomicU64 = AtomicU64::new(8682522807148012u64);
|
static SEED_UNIQUIFIER: AtomicU64 = AtomicU64::new(8682522807148012u64);
|
||||||
|
|
||||||
/// Generates a new random seed based on the current time and a uniquifier.
|
|
||||||
///
|
|
||||||
/// The seed is generated by combining:
|
|
||||||
/// - An atomic counter multiplied by a large constant.
|
|
||||||
/// - The current system time in nanoseconds.
|
|
||||||
///
|
|
||||||
/// This ensures that seeds are both time-dependent and unique across calls.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// A 64-bit seed value suitable for initializing random number generators.
|
|
||||||
pub fn get_seed() -> u64 {
|
pub fn get_seed() -> u64 {
|
||||||
let seed = SEED_UNIQUIFIER
|
let seed = SEED_UNIQUIFIER
|
||||||
.try_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
|
.try_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
|
||||||
@ -44,11 +30,6 @@ pub fn get_seed() -> u64 {
|
|||||||
seed ^ nano_upper ^ nano_lower
|
seed ^ nano_upper ^ nano_lower
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unified random number generator enum that can represent different RNG implementations.
|
|
||||||
///
|
|
||||||
/// This enum allows for dynamic dispatch between different RNG implementations
|
|
||||||
/// while maintaining performance through `enum_dispatch`.
|
|
||||||
#[enum_dispatch(RandomImpl)]
|
|
||||||
pub enum RandomGenerator {
|
pub enum RandomGenerator {
|
||||||
/// Xoroshiro128+ random number generator (modern, fast implementation).
|
/// Xoroshiro128+ random number generator (modern, fast implementation).
|
||||||
Xoroshiro(Xoroshiro),
|
Xoroshiro(Xoroshiro),
|
||||||
@ -57,11 +38,6 @@ pub enum RandomGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unified random number deriver enum for creating child RNGs.
|
/// Unified random number deriver enum for creating child RNGs.
|
||||||
///
|
|
||||||
/// Derivers are used to create new independent random generators from a parent seed,
|
|
||||||
/// which is essential for deterministic world generation where different features
|
|
||||||
/// need their own RNGs.
|
|
||||||
#[enum_dispatch(RandomDeriverImpl)]
|
|
||||||
pub enum RandomDeriver {
|
pub enum RandomDeriver {
|
||||||
/// Xoroshiro splitter implementation.
|
/// Xoroshiro splitter implementation.
|
||||||
Xoroshiro(XoroshiroSplitter),
|
Xoroshiro(XoroshiroSplitter),
|
||||||
@ -191,7 +167,6 @@ pub fn get_carver_seed(world_seed: u64, chunk_x: i32, chunk_z: i32) -> u64 {
|
|||||||
^ world_seed
|
^ world_seed
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
#[expect(clippy::return_self_not_must_use)]
|
#[expect(clippy::return_self_not_must_use)]
|
||||||
pub trait RandomImpl {
|
pub trait RandomImpl {
|
||||||
fn split(&mut self) -> Self;
|
fn split(&mut self) -> Self;
|
||||||
@ -236,7 +211,88 @@ pub trait RandomImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
impl RandomImpl for RandomGenerator {
|
||||||
|
#[inline]
|
||||||
|
fn split(&mut self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => Self::Xoroshiro(x.split()),
|
||||||
|
Self::Legacy(l) => Self::Legacy(l.split()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_splitter(&mut self) -> RandomDeriver {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => RandomDeriver::Xoroshiro(x.next_splitter()),
|
||||||
|
Self::Legacy(l) => l.next_splitter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_i32(&mut self) -> i32 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_i32(),
|
||||||
|
Self::Legacy(l) => l.next_i32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_bounded_i32(&mut self, bound: i32) -> i32 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_bounded_i32(bound),
|
||||||
|
Self::Legacy(l) => l.next_bounded_i32(bound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_i64(&mut self) -> i64 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_i64(),
|
||||||
|
Self::Legacy(l) => l.next_i64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_bool(&mut self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_bool(),
|
||||||
|
Self::Legacy(l) => l.next_bool(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_f32(&mut self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_f32(),
|
||||||
|
Self::Legacy(l) => l.next_f32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_f64(&mut self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_f64(),
|
||||||
|
Self::Legacy(l) => l.next_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next_gaussian(&mut self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.next_gaussian(),
|
||||||
|
Self::Legacy(l) => l.next_gaussian(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn skip(&mut self, count: i32) {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.skip(count),
|
||||||
|
Self::Legacy(l) => l.skip(count),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait RandomDeriverImpl {
|
pub trait RandomDeriverImpl {
|
||||||
fn split_string(&self, seed: &str) -> RandomGenerator;
|
fn split_string(&self, seed: &str) -> RandomGenerator;
|
||||||
|
|
||||||
@ -245,6 +301,32 @@ pub trait RandomDeriverImpl {
|
|||||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator;
|
fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RandomDeriverImpl for RandomDeriver {
|
||||||
|
#[inline]
|
||||||
|
fn split_string(&self, seed: &str) -> RandomGenerator {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => RandomGenerator::Xoroshiro(x.split_string(seed)),
|
||||||
|
Self::Legacy(l) => l.split_string(seed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn split_u64(&self, seed: u64) -> RandomGenerator {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(x) => x.split_u64(seed),
|
||||||
|
Self::Legacy(l) => l.split_u64(seed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn split_pos(&self, x: i32, y: i32, z: i32) -> RandomGenerator {
|
||||||
|
match self {
|
||||||
|
Self::Xoroshiro(s) => RandomGenerator::Xoroshiro(s.split_pos(x, y, z)),
|
||||||
|
Self::Legacy(s) => s.split_pos(x, y, z),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Hashes a block position into a 64-bit value for use in RNG seeding.
|
/// Hashes a block position into a 64-bit value for use in RNG seeding.
|
||||||
///
|
///
|
||||||
/// This hash function is designed to produce well-distributed values for
|
/// This hash function is designed to produce well-distributed values for
|
||||||
|
|||||||
@ -46,7 +46,6 @@ ruzstd.workspace = true
|
|||||||
xxhash-rust = { version = "0.8", features = ["xxh64"] }
|
xxhash-rust = { version = "0.8", features = ["xxh64"] }
|
||||||
|
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
enum_dispatch.workspace = true
|
|
||||||
rayon.workspace = true
|
rayon.workspace = true
|
||||||
|
|
||||||
# thread_local.workspace = true
|
# thread_local.workspace = true
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE};
|
use pumpkin_data::chunk::{Biome, BiomeTree, NETHER_BIOME_SOURCE, OVERWORLD_BIOME_SOURCE};
|
||||||
|
|
||||||
use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler;
|
use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler;
|
||||||
@ -14,7 +13,6 @@ thread_local! {
|
|||||||
static LAST_RESULT_NODE: RefCell<Option<&'static BiomeTree>> = const {RefCell::new(None) };
|
static LAST_RESULT_NODE: RefCell<Option<&'static BiomeTree>> = const {RefCell::new(None) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait BiomeSupplier {
|
pub trait BiomeSupplier {
|
||||||
fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome;
|
fn biome(&self, x: i32, y: i32, z: i32, noise: &mut MultiNoiseSampler<'_>) -> &'static Biome;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ impl Cylindrical {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn changed_chunks(
|
pub fn changed_chunks(
|
||||||
old: Self,
|
old: Self,
|
||||||
new: Self,
|
new: Self,
|
||||||
@ -35,50 +36,59 @@ impl Cylindrical {
|
|||||||
(loading, unloading)
|
(loading, unloading)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[must_use]
|
||||||
const fn left(&self) -> i32 {
|
#[inline]
|
||||||
|
pub const fn left(&self) -> i32 {
|
||||||
self.center.x - self.view_distance.get() as i32 - 1
|
self.center.x - self.view_distance.get() as i32 - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[must_use]
|
||||||
const fn bottom(&self) -> i32 {
|
#[inline]
|
||||||
|
pub const fn bottom(&self) -> i32 {
|
||||||
self.center.y - self.view_distance.get() as i32 - 1
|
self.center.y - self.view_distance.get() as i32 - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[must_use]
|
||||||
const fn right(&self) -> i32 {
|
#[inline]
|
||||||
|
pub const fn right(&self) -> i32 {
|
||||||
self.center.x + self.view_distance.get() as i32 + 1
|
self.center.x + self.view_distance.get() as i32 + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[must_use]
|
||||||
const fn top(&self) -> i32 {
|
#[inline]
|
||||||
|
pub const fn top(&self) -> i32 {
|
||||||
self.center.y + self.view_distance.get() as i32 + 1
|
self.center.y + self.view_distance.get() as i32 + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_within_distance(&self, x: i32, z: i32) -> bool {
|
#[inline]
|
||||||
if self.view_distance.get() == 1 {
|
pub const fn is_within_distance(&self, x: i32, z: i32) -> bool {
|
||||||
|
let vd = self.view_distance.get() as i64;
|
||||||
|
if vd == 1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let rel_x = ((x - self.center.x).abs() as i64 - 2).max(0);
|
let dx = (x - self.center.x).abs() as i64 - 2;
|
||||||
let rel_z = ((z - self.center.y).abs() as i64 - 2).max(0);
|
let dz = (z - self.center.y).abs() as i64 - 2;
|
||||||
|
let rel_x = if dx > 0 { dx } else { 0 };
|
||||||
|
let rel_z = if dz > 0 { dz } else { 0 };
|
||||||
|
|
||||||
let hyp_sqr = rel_x * rel_x + rel_z * rel_z;
|
let hyp_sqr = rel_x * rel_x + rel_z * rel_z;
|
||||||
//The view distance should be converted to i64 first because u8 * u8 can overflow
|
hyp_sqr < vd * vd
|
||||||
hyp_sqr < (self.view_distance.get() as i64).pow(2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a precomputed list of relative chunk offset pairs `(dx, dy)` for a given view distance.
|
/// Returns a precomputed list of relative chunk offset pairs `(dx, dy)` for a given view distance.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_offsets(view_distance: u8) -> &'static [(i8, i8)] {
|
pub const fn get_offsets(view_distance: u8) -> &'static [(i8, i8)] {
|
||||||
let idx =
|
let vd = view_distance as usize;
|
||||||
(view_distance as usize).min(pumpkin_data::chunk_view_lut::MAX_VIEW_DISTANCE as usize);
|
let max = pumpkin_data::chunk_view_lut::MAX_VIEW_DISTANCE as usize;
|
||||||
|
let idx = if vd < max { vd } else { max };
|
||||||
pumpkin_data::chunk_view_lut::CHUNK_VIEW_LUT[idx]
|
pumpkin_data::chunk_view_lut::CHUNK_VIEW_LUT[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all chunks within this cylinder using the precomputed LUT
|
/// Returns an iterator of all chunks within this cylinder using the precomputed LUT
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
pub fn all_chunks_within(self) -> impl ExactSizeIterator<Item = Vector2<i32>> {
|
pub fn all_chunks_within(self) -> impl ExactSizeIterator<Item = Vector2<i32>> {
|
||||||
let offsets = Self::get_offsets(self.view_distance.get());
|
let offsets = Self::get_offsets(self.view_distance.get());
|
||||||
offsets.iter().map(move |&(dx, dy)| {
|
offsets.iter().map(move |&(dx, dy)| {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use pumpkin_data::{Block, BlockState, chunk_gen_settings::GenerationSettings};
|
use pumpkin_data::{Block, BlockState, chunk_gen_settings::GenerationSettings};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{clamped_map, floor_div, vector3::Vector3},
|
math::{clamped_map, floor_div, vector3::Vector3},
|
||||||
@ -53,7 +52,6 @@ pub trait FluidLevelSamplerImpl {
|
|||||||
fn get_fluid_level(&self, x: i32, y: i32, z: i32) -> &FluidLevel;
|
fn get_fluid_level(&self, x: i32, y: i32, z: i32) -> &FluidLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(AquiferSamplerImpl)]
|
|
||||||
pub enum AquiferSampler {
|
pub enum AquiferSampler {
|
||||||
SeaLevel(SeaLevelAquiferSampler),
|
SeaLevel(SeaLevelAquiferSampler),
|
||||||
Aquifer(WorldAquiferSampler),
|
Aquifer(WorldAquiferSampler),
|
||||||
@ -785,7 +783,6 @@ impl AquiferSamplerImpl for SeaLevelAquiferSampler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait AquiferSamplerImpl {
|
pub trait AquiferSamplerImpl {
|
||||||
fn apply(
|
fn apply(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -796,6 +793,22 @@ pub trait AquiferSamplerImpl {
|
|||||||
) -> (Option<&'static BlockState>, bool);
|
) -> (Option<&'static BlockState>, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AquiferSamplerImpl for AquiferSampler {
|
||||||
|
#[inline]
|
||||||
|
fn apply(
|
||||||
|
&mut self,
|
||||||
|
router: &mut ChunkNoiseRouter,
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
|
height_estimator: &mut SurfaceHeightEstimateSampler,
|
||||||
|
) -> (Option<&'static BlockState>, bool) {
|
||||||
|
match self {
|
||||||
|
Self::SeaLevel(s) => s.apply(router, pos, sample_options, height_estimator),
|
||||||
|
Self::Aquifer(a) => a.apply(router, pos, sample_options, height_estimator),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod random_positions_and_hypot {
|
mod random_positions_and_hypot {
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|||||||
@ -5,7 +5,6 @@ use super::{
|
|||||||
chunk_noise_router::{ChunkNoiseFunctionComponent, MutableChunkNoiseFunctionComponentImpl},
|
chunk_noise_router::{ChunkNoiseFunctionComponent, MutableChunkNoiseFunctionComponentImpl},
|
||||||
density_function::{IndexToNoisePos, NoiseFunctionComponentRange},
|
density_function::{IndexToNoisePos, NoiseFunctionComponentRange},
|
||||||
};
|
};
|
||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use pumpkin_util::math::{lerp, lerp3, vector3::Vector3};
|
use pumpkin_util::math::{lerp, lerp3, vector3::Vector3};
|
||||||
|
|
||||||
use crate::generation::{biome_coords, positions::chunk_pos};
|
use crate::generation::{biome_coords, positions::chunk_pos};
|
||||||
@ -205,7 +204,7 @@ pub struct DensityInterpolator {
|
|||||||
first_pass: [f64; 8],
|
first_pass: [f64; 8],
|
||||||
second_pass: [f64; 4],
|
second_pass: [f64; 4],
|
||||||
third_pass: [f64; 2],
|
third_pass: [f64; 2],
|
||||||
result: f64,
|
pub(crate) result: f64,
|
||||||
|
|
||||||
pub(crate) vertical_cell_count: usize,
|
pub(crate) vertical_cell_count: usize,
|
||||||
min_value: f64,
|
min_value: f64,
|
||||||
@ -737,7 +736,6 @@ impl CellCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(MutableChunkNoiseFunctionComponentImpl, NoiseFunctionComponentRange)]
|
|
||||||
pub enum ChunkSpecificNoiseFunctionComponent {
|
pub enum ChunkSpecificNoiseFunctionComponent {
|
||||||
DensityInterpolator(DensityInterpolator),
|
DensityInterpolator(DensityInterpolator),
|
||||||
FlatCache(FlatCache),
|
FlatCache(FlatCache),
|
||||||
@ -746,3 +744,66 @@ pub enum ChunkSpecificNoiseFunctionComponent {
|
|||||||
CellCache(CellCache),
|
CellCache(CellCache),
|
||||||
Beardifier(crate::generation::noise::router::density_function::beardifier::Beardifier),
|
Beardifier(crate::generation::noise::router::density_function::beardifier::Beardifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NoiseFunctionComponentRange for ChunkSpecificNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn min(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::DensityInterpolator(d) => d.min(),
|
||||||
|
Self::FlatCache(f) => f.min(),
|
||||||
|
Self::Cache2D(c) => c.min(),
|
||||||
|
Self::CacheOnce(c) => c.min(),
|
||||||
|
Self::CellCache(c) => c.min(),
|
||||||
|
Self::Beardifier(b) => b.min(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::DensityInterpolator(d) => d.max(),
|
||||||
|
Self::FlatCache(f) => f.max(),
|
||||||
|
Self::Cache2D(c) => c.max(),
|
||||||
|
Self::CacheOnce(c) => c.max(),
|
||||||
|
Self::CellCache(c) => c.max(),
|
||||||
|
Self::Beardifier(b) => b.max(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MutableChunkNoiseFunctionComponentImpl for ChunkSpecificNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn sample(
|
||||||
|
&mut self,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
|
) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::DensityInterpolator(d) => d.sample(component_stack, pos, sample_options),
|
||||||
|
Self::FlatCache(f) => f.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Cache2D(c) => c.sample(component_stack, pos, sample_options),
|
||||||
|
Self::CacheOnce(c) => c.sample(component_stack, pos, sample_options),
|
||||||
|
Self::CellCache(c) => c.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Beardifier(b) => b.sample(component_stack, pos, sample_options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(
|
||||||
|
&mut self,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
array: &mut [f64],
|
||||||
|
mapper: &impl IndexToNoisePos,
|
||||||
|
sample_options: &mut ChunkNoiseFunctionSampleOptions,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Self::DensityInterpolator(d) => d.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::FlatCache(f) => f.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Cache2D(c) => c.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::CacheOnce(c) => c.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::CellCache(c) => c.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Beardifier(b) => b.fill(component_stack, array, mapper, sample_options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use pumpkin_data::noise_router::WrapperType;
|
use pumpkin_data::noise_router::WrapperType;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
@ -20,7 +21,6 @@ use super::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait StaticChunkNoiseFunctionComponentImpl {
|
pub trait StaticChunkNoiseFunctionComponentImpl {
|
||||||
fn sample(
|
fn sample(
|
||||||
&self,
|
&self,
|
||||||
@ -43,7 +43,6 @@ pub trait StaticChunkNoiseFunctionComponentImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait MutableChunkNoiseFunctionComponentImpl {
|
pub trait MutableChunkNoiseFunctionComponentImpl {
|
||||||
fn sample(
|
fn sample(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -241,13 +240,25 @@ impl MutableChunkNoiseFunctionComponentImpl for ChunkNoiseFunctionComponent<'_>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static TOPO_FILL_BUFFERS: RefCell<Vec<Vec<f64>>> = const { RefCell::new(Vec::new()) };
|
||||||
|
}
|
||||||
|
|
||||||
impl ChunkNoiseFunctionComponent<'_> {
|
impl ChunkNoiseFunctionComponent<'_> {
|
||||||
|
#[inline]
|
||||||
pub fn sample_from_stack(
|
pub fn sample_from_stack(
|
||||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
pos: &Vector3<i32>,
|
pos: &Vector3<i32>,
|
||||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let (top_component, component_stack) = component_stack.split_last_mut().unwrap();
|
let (top_component, component_stack) = component_stack.split_last_mut().unwrap();
|
||||||
|
if !sample_options.populating_caches
|
||||||
|
&& let ChunkNoiseFunctionComponent::Chunk(
|
||||||
|
ChunkSpecificNoiseFunctionComponent::DensityInterpolator(interp),
|
||||||
|
) = top_component
|
||||||
|
{
|
||||||
|
return interp.result;
|
||||||
|
}
|
||||||
top_component.sample(component_stack, pos, sample_options)
|
top_component.sample(component_stack, pos, sample_options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use pumpkin_data::noise_router::{BinaryData, BinaryOperation, ClampData, LinearData, UnaryData};
|
use pumpkin_data::noise_router::{BinaryData, BinaryOperation, ClampData, LinearData, UnaryData};
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
@ -6,6 +8,10 @@ use crate::generation::noise::router::{
|
|||||||
chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
|
chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static SCRATCH_BUFFER: RefCell<Vec<f64>> = const { RefCell::new(Vec::new()) };
|
||||||
|
}
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
|
IndexToNoisePos, NoiseFunctionComponentRange, StaticIndependentChunkNoiseFunctionComponentImpl,
|
||||||
};
|
};
|
||||||
@ -46,7 +52,7 @@ pub struct Linear {
|
|||||||
pub(crate) input_index: usize,
|
pub(crate) input_index: usize,
|
||||||
min_value: f64,
|
min_value: f64,
|
||||||
max_value: f64,
|
max_value: f64,
|
||||||
data: &'static LinearData,
|
pub(crate) data: &'static LinearData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseFunctionComponentRange for Linear {
|
impl NoiseFunctionComponentRange for Linear {
|
||||||
@ -98,7 +104,7 @@ pub struct Binary {
|
|||||||
pub(crate) input2_index: usize,
|
pub(crate) input2_index: usize,
|
||||||
min_value: f64,
|
min_value: f64,
|
||||||
max_value: f64,
|
max_value: f64,
|
||||||
data: &'static BinaryData,
|
pub(crate) data: &'static BinaryData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseFunctionComponentRange for Binary {
|
impl NoiseFunctionComponentRange for Binary {
|
||||||
@ -194,18 +200,27 @@ impl StaticChunkNoiseFunctionComponentImpl for Binary {
|
|||||||
sample_options,
|
sample_options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let len = array.len();
|
||||||
match self.data.operation {
|
match self.data.operation {
|
||||||
BinaryOperation::Add => {
|
BinaryOperation::Add => {
|
||||||
let mut scratch = vec![0.0f64; array.len()];
|
SCRATCH_BUFFER.with(|scratch_cell| {
|
||||||
ChunkNoiseFunctionComponent::fill_from_stack(
|
let mut scratch = scratch_cell.borrow_mut();
|
||||||
&mut component_stack[..=self.input2_index],
|
if scratch.len() < len {
|
||||||
&mut scratch,
|
scratch.resize(len, 0.0);
|
||||||
mapper,
|
}
|
||||||
sample_options,
|
let scratch_slice = &mut scratch[..len];
|
||||||
);
|
|
||||||
for (a, b) in array.iter_mut().zip(scratch.iter()) {
|
ChunkNoiseFunctionComponent::fill_from_stack(
|
||||||
*a += b;
|
&mut component_stack[..=self.input2_index],
|
||||||
}
|
scratch_slice,
|
||||||
|
mapper,
|
||||||
|
sample_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (a, b) in array.iter_mut().zip(scratch_slice.iter()) {
|
||||||
|
*a += b;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
BinaryOperation::Mul => {
|
BinaryOperation::Mul => {
|
||||||
for (index, value) in array.iter_mut().enumerate() {
|
for (index, value) in array.iter_mut().enumerate() {
|
||||||
@ -224,7 +239,6 @@ impl StaticChunkNoiseFunctionComponentImpl for Binary {
|
|||||||
let input2_min = component_stack[self.input2_index].min();
|
let input2_min = component_stack[self.input2_index].min();
|
||||||
for (index, value) in array.iter_mut().enumerate() {
|
for (index, value) in array.iter_mut().enumerate() {
|
||||||
if *value >= input2_min {
|
if *value >= input2_min {
|
||||||
// NOTE: vanilla is v < min ? v : min(v, compute)
|
|
||||||
let pos = mapper.at(index, Some(sample_options));
|
let pos = mapper.at(index, Some(sample_options));
|
||||||
let density2 = ChunkNoiseFunctionComponent::sample_from_stack(
|
let density2 = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||||
&mut component_stack[..=self.input2_index],
|
&mut component_stack[..=self.input2_index],
|
||||||
@ -239,7 +253,6 @@ impl StaticChunkNoiseFunctionComponentImpl for Binary {
|
|||||||
let input2_max = component_stack[self.input2_index].max();
|
let input2_max = component_stack[self.input2_index].max();
|
||||||
for (index, value) in array.iter_mut().enumerate() {
|
for (index, value) in array.iter_mut().enumerate() {
|
||||||
if *value <= input2_max {
|
if *value <= input2_max {
|
||||||
// NOTE: vanilla is v > max ? v : max(v, compute)
|
|
||||||
let pos = mapper.at(index, Some(sample_options));
|
let pos = mapper.at(index, Some(sample_options));
|
||||||
let density2 = ChunkNoiseFunctionComponent::sample_from_stack(
|
let density2 = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||||
&mut component_stack[..=self.input2_index],
|
&mut component_stack[..=self.input2_index],
|
||||||
@ -276,7 +289,7 @@ pub struct Unary {
|
|||||||
pub(crate) input_index: usize,
|
pub(crate) input_index: usize,
|
||||||
min_value: f64,
|
min_value: f64,
|
||||||
max_value: f64,
|
max_value: f64,
|
||||||
data: &'static UnaryData,
|
pub(crate) data: &'static UnaryData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseFunctionComponentRange for Unary {
|
impl NoiseFunctionComponentRange for Unary {
|
||||||
@ -342,8 +355,8 @@ impl Unary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Clamp {
|
pub struct Clamp {
|
||||||
input_index: usize,
|
pub(crate) input_index: usize,
|
||||||
data: &'static ClampData,
|
pub(crate) data: &'static ClampData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clamp {
|
impl Clamp {
|
||||||
|
|||||||
@ -213,13 +213,26 @@ impl StaticIndependentChunkNoiseFunctionComponentImpl for ClampedYGradient {
|
|||||||
self.data.to_value,
|
self.data.to_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||||
|
array.iter_mut().enumerate().for_each(|(index, value)| {
|
||||||
|
let pos = mapper.at(index, None);
|
||||||
|
*value = clamped_map(
|
||||||
|
pos.y as f64,
|
||||||
|
self.data.from_y,
|
||||||
|
self.data.to_y,
|
||||||
|
self.data.from_value,
|
||||||
|
self.data.to_value,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RangeChoice {
|
pub struct RangeChoice {
|
||||||
input_index: usize,
|
pub(crate) input_index: usize,
|
||||||
pub(crate) when_in_index: usize,
|
pub(crate) when_in_index: usize,
|
||||||
pub(crate) when_out_index: usize,
|
pub(crate) when_out_index: usize,
|
||||||
data: &'static RangeChoiceData,
|
pub(crate) data: &'static RangeChoiceData,
|
||||||
min_value: f64,
|
min_value: f64,
|
||||||
max_value: f64,
|
max_value: f64,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use pumpkin_data::noise_router::WrapperType;
|
use pumpkin_data::noise_router::WrapperType;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
// These are for enum_dispatch
|
use super::chunk_density_function::ChunkNoiseFunctionSampleOptions;
|
||||||
use super::chunk_density_function::{
|
|
||||||
ChunkNoiseFunctionSampleOptions, ChunkSpecificNoiseFunctionComponent,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) mod beardifier;
|
pub(crate) mod beardifier;
|
||||||
pub(crate) mod math;
|
pub(crate) mod math;
|
||||||
@ -27,13 +23,11 @@ pub trait IndexToNoisePos {
|
|||||||
) -> Vector3<i32>;
|
) -> Vector3<i32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait NoiseFunctionComponentRange {
|
pub trait NoiseFunctionComponentRange {
|
||||||
fn min(&self) -> f64;
|
fn min(&self) -> f64;
|
||||||
fn max(&self) -> f64;
|
fn max(&self) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait StaticIndependentChunkNoiseFunctionComponentImpl: NoiseFunctionComponentRange {
|
pub trait StaticIndependentChunkNoiseFunctionComponentImpl: NoiseFunctionComponentRange {
|
||||||
fn sample(&self, pos: &Vector3<i32>) -> f64;
|
fn sample(&self, pos: &Vector3<i32>) -> f64;
|
||||||
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||||
|
|||||||
@ -112,13 +112,30 @@ impl StaticIndependentChunkNoiseFunctionComponentImpl for ShiftB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShiftedNoise {
|
pub struct ShiftedNoise {
|
||||||
input_x_index: usize,
|
pub(crate) input_x_index: usize,
|
||||||
input_y_index: usize,
|
pub(crate) input_y_index: usize,
|
||||||
input_z_index: usize,
|
pub(crate) input_z_index: usize,
|
||||||
sampler: DoublePerlinNoiseSampler,
|
sampler: DoublePerlinNoiseSampler,
|
||||||
data: &'static ShiftedNoiseData,
|
data: &'static ShiftedNoiseData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ShiftedNoise {
|
||||||
|
#[inline]
|
||||||
|
pub fn sample_with_shifts(
|
||||||
|
&self,
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
x_shift: f64,
|
||||||
|
y_shift: f64,
|
||||||
|
z_shift: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let translated_x = pos.x as f64 * self.data.xz_scale + x_shift;
|
||||||
|
let translated_y = pos.y as f64 * self.data.y_scale + y_shift;
|
||||||
|
let translated_z = pos.z as f64 * self.data.xz_scale + z_shift;
|
||||||
|
self.sampler
|
||||||
|
.sample(translated_x, translated_y, translated_z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NoiseFunctionComponentRange for ShiftedNoise {
|
impl NoiseFunctionComponentRange for ShiftedNoise {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn min(&self) -> f64 {
|
fn min(&self) -> f64 {
|
||||||
@ -138,27 +155,23 @@ impl StaticChunkNoiseFunctionComponentImpl for ShiftedNoise {
|
|||||||
pos: &Vector3<i32>,
|
pos: &Vector3<i32>,
|
||||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let translated_x = pos.x as f64 * self.data.xz_scale
|
let x_shift = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
&mut component_stack[..=self.input_x_index],
|
||||||
&mut component_stack[..=self.input_x_index],
|
pos,
|
||||||
pos,
|
sample_options,
|
||||||
sample_options,
|
);
|
||||||
);
|
let y_shift = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||||
let translated_y = pos.y as f64 * self.data.y_scale
|
&mut component_stack[..=self.input_y_index],
|
||||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
pos,
|
||||||
&mut component_stack[..=self.input_y_index],
|
sample_options,
|
||||||
pos,
|
);
|
||||||
sample_options,
|
let z_shift = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||||
);
|
&mut component_stack[..=self.input_z_index],
|
||||||
let translated_z = pos.z as f64 * self.data.xz_scale
|
pos,
|
||||||
+ ChunkNoiseFunctionComponent::sample_from_stack(
|
sample_options,
|
||||||
&mut component_stack[..=self.input_z_index],
|
);
|
||||||
pos,
|
|
||||||
sample_options,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.sampler
|
self.sample_with_shifts(pos, x_shift, y_shift, z_shift)
|
||||||
.sample(translated_x, translated_y, translated_z)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,23 @@ impl SplineValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sample_with_buffers(
|
||||||
|
&self,
|
||||||
|
buffers: &[Vec<f64>],
|
||||||
|
elem_idx: usize,
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
|
) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Fixed(fixed) => *fixed,
|
||||||
|
Self::Spline(spline) => {
|
||||||
|
spline.sample_with_buffers(buffers, elem_idx, pos, component_stack, sample_options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn calculate_min_and_max(&self, component_stack: &[ProtoNoiseFunctionComponent]) -> (f32, f32) {
|
fn calculate_min_and_max(&self, component_stack: &[ProtoNoiseFunctionComponent]) -> (f32, f32) {
|
||||||
match self {
|
match self {
|
||||||
@ -196,6 +213,75 @@ impl Spline {
|
|||||||
|
|
||||||
cubic_part + linear_part
|
cubic_part + linear_part
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sample_with_buffers(
|
||||||
|
&self,
|
||||||
|
buffers: &[Vec<f64>],
|
||||||
|
elem_idx: usize,
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
|
) -> f32 {
|
||||||
|
let location = buffers[self.input_index][elem_idx] as f32;
|
||||||
|
|
||||||
|
let n = self.points.len();
|
||||||
|
let index_greater_than_x = self.points.partition_point(|p| location >= p.location);
|
||||||
|
|
||||||
|
if index_greater_than_x == 0 {
|
||||||
|
let point = &self.points[0];
|
||||||
|
let val = point.value.sample_with_buffers(
|
||||||
|
buffers,
|
||||||
|
elem_idx,
|
||||||
|
pos,
|
||||||
|
component_stack,
|
||||||
|
sample_options,
|
||||||
|
);
|
||||||
|
return point.sample_outside_range(location, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if index_greater_than_x == n {
|
||||||
|
let point = &self.points[n - 1];
|
||||||
|
let val = point.value.sample_with_buffers(
|
||||||
|
buffers,
|
||||||
|
elem_idx,
|
||||||
|
pos,
|
||||||
|
component_stack,
|
||||||
|
sample_options,
|
||||||
|
);
|
||||||
|
return point.sample_outside_range(location, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lower_point = &self.points[index_greater_than_x - 1];
|
||||||
|
let upper_point = &self.points[index_greater_than_x];
|
||||||
|
|
||||||
|
let lower_value = lower_point.value.sample_with_buffers(
|
||||||
|
buffers,
|
||||||
|
elem_idx,
|
||||||
|
pos,
|
||||||
|
component_stack,
|
||||||
|
sample_options,
|
||||||
|
);
|
||||||
|
let upper_value = upper_point.value.sample_with_buffers(
|
||||||
|
buffers,
|
||||||
|
elem_idx,
|
||||||
|
pos,
|
||||||
|
component_stack,
|
||||||
|
sample_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dist = upper_point.location - lower_point.location;
|
||||||
|
let x_scale = (location - lower_point.location) / dist;
|
||||||
|
|
||||||
|
let delta = upper_value - lower_value;
|
||||||
|
let extrapolated_lower = lower_point.derivative * dist - delta;
|
||||||
|
let extrapolated_upper = -upper_point.derivative * dist + delta;
|
||||||
|
|
||||||
|
let cubic_part =
|
||||||
|
(x_scale * (1.0 - x_scale)) * lerp(x_scale, extrapolated_lower, extrapolated_upper);
|
||||||
|
let linear_part = lerp(x_scale, lower_value, upper_value);
|
||||||
|
|
||||||
|
cubic_part + linear_part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SplineFunction {
|
pub struct SplineFunction {
|
||||||
@ -213,6 +299,10 @@ impl SplineFunction {
|
|||||||
max_value: max_value as f64,
|
max_value: max_value as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn spline(&self) -> &Spline {
|
||||||
|
&self.spline
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticChunkNoiseFunctionComponentImpl for SplineFunction {
|
impl StaticChunkNoiseFunctionComponentImpl for SplineFunction {
|
||||||
|
|||||||
@ -4,4 +4,5 @@ pub mod density_function;
|
|||||||
pub mod find_top_surface;
|
pub mod find_top_surface;
|
||||||
pub mod multi_noise_sampler;
|
pub mod multi_noise_sampler;
|
||||||
pub mod proto_noise_router;
|
pub mod proto_noise_router;
|
||||||
|
pub mod static_router;
|
||||||
pub mod surface_height_sampler;
|
pub mod surface_height_sampler;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
chunk::DoublePerlinNoiseParameters,
|
chunk::DoublePerlinNoiseParameters,
|
||||||
noise_router::{
|
noise_router::{
|
||||||
@ -30,10 +29,6 @@ use super::{
|
|||||||
};
|
};
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
#[enum_dispatch(
|
|
||||||
StaticIndependentChunkNoiseFunctionComponentImpl,
|
|
||||||
NoiseFunctionComponentRange
|
|
||||||
)]
|
|
||||||
pub enum IndependentProtoNoiseFunctionComponent {
|
pub enum IndependentProtoNoiseFunctionComponent {
|
||||||
Constant(Constant),
|
Constant(Constant),
|
||||||
EndIsland(EndIsland),
|
EndIsland(EndIsland),
|
||||||
@ -44,7 +39,62 @@ pub enum IndependentProtoNoiseFunctionComponent {
|
|||||||
ClampedYGradient(ClampedYGradient),
|
ClampedYGradient(ClampedYGradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(StaticChunkNoiseFunctionComponentImpl, NoiseFunctionComponentRange)]
|
impl NoiseFunctionComponentRange for IndependentProtoNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn min(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Constant(c) => c.min(),
|
||||||
|
Self::EndIsland(e) => e.min(),
|
||||||
|
Self::Noise(n) => n.min(),
|
||||||
|
Self::ShiftA(s) => s.min(),
|
||||||
|
Self::ShiftB(s) => s.min(),
|
||||||
|
Self::InterpolatedNoise(i) => i.min(),
|
||||||
|
Self::ClampedYGradient(c) => c.min(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Constant(c) => c.max(),
|
||||||
|
Self::EndIsland(e) => e.max(),
|
||||||
|
Self::Noise(n) => n.max(),
|
||||||
|
Self::ShiftA(s) => s.max(),
|
||||||
|
Self::ShiftB(s) => s.max(),
|
||||||
|
Self::InterpolatedNoise(i) => i.max(),
|
||||||
|
Self::ClampedYGradient(c) => c.max(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticIndependentChunkNoiseFunctionComponentImpl for IndependentProtoNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Constant(c) => c.sample(pos),
|
||||||
|
Self::EndIsland(e) => e.sample(pos),
|
||||||
|
Self::Noise(n) => n.sample(pos),
|
||||||
|
Self::ShiftA(s) => s.sample(pos),
|
||||||
|
Self::ShiftB(s) => s.sample(pos),
|
||||||
|
Self::InterpolatedNoise(i) => i.sample(pos),
|
||||||
|
Self::ClampedYGradient(c) => c.sample(pos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||||
|
match self {
|
||||||
|
Self::Constant(c) => c.fill(array, mapper),
|
||||||
|
Self::EndIsland(e) => e.fill(array, mapper),
|
||||||
|
Self::Noise(n) => n.fill(array, mapper),
|
||||||
|
Self::ShiftA(s) => s.fill(array, mapper),
|
||||||
|
Self::ShiftB(s) => s.fill(array, mapper),
|
||||||
|
Self::InterpolatedNoise(i) => i.fill(array, mapper),
|
||||||
|
Self::ClampedYGradient(c) => c.fill(array, mapper),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum DependentProtoNoiseFunctionComponent {
|
pub enum DependentProtoNoiseFunctionComponent {
|
||||||
Linear(Linear),
|
Linear(Linear),
|
||||||
Unary(Unary),
|
Unary(Unary),
|
||||||
@ -57,7 +107,81 @@ pub enum DependentProtoNoiseFunctionComponent {
|
|||||||
Spline(SplineFunction),
|
Spline(SplineFunction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(NoiseFunctionComponentRange)]
|
impl NoiseFunctionComponentRange for DependentProtoNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn min(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Linear(l) => l.min(),
|
||||||
|
Self::Unary(u) => u.min(),
|
||||||
|
Self::Binary(b) => b.min(),
|
||||||
|
Self::ShiftedNoise(s) => s.min(),
|
||||||
|
Self::IntervalSelect(i) => i.min(),
|
||||||
|
Self::FindTopSurface(f) => f.min(),
|
||||||
|
Self::Clamp(c) => c.min(),
|
||||||
|
Self::RangeChoice(r) => r.min(),
|
||||||
|
Self::Spline(s) => s.min(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Linear(l) => l.max(),
|
||||||
|
Self::Unary(u) => u.max(),
|
||||||
|
Self::Binary(b) => b.max(),
|
||||||
|
Self::ShiftedNoise(s) => s.max(),
|
||||||
|
Self::IntervalSelect(i) => i.max(),
|
||||||
|
Self::FindTopSurface(f) => f.max(),
|
||||||
|
Self::Clamp(c) => c.max(),
|
||||||
|
Self::RangeChoice(r) => r.max(),
|
||||||
|
Self::Spline(s) => s.max(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticChunkNoiseFunctionComponentImpl for DependentProtoNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn sample(
|
||||||
|
&self,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
pos: &Vector3<i32>,
|
||||||
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||||
|
) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Linear(l) => l.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Unary(u) => u.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Binary(b) => b.sample(component_stack, pos, sample_options),
|
||||||
|
Self::ShiftedNoise(s) => s.sample(component_stack, pos, sample_options),
|
||||||
|
Self::IntervalSelect(i) => i.sample(component_stack, pos, sample_options),
|
||||||
|
Self::FindTopSurface(f) => f.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Clamp(c) => c.sample(component_stack, pos, sample_options),
|
||||||
|
Self::RangeChoice(r) => r.sample(component_stack, pos, sample_options),
|
||||||
|
Self::Spline(s) => s.sample(component_stack, pos, sample_options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(
|
||||||
|
&self,
|
||||||
|
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||||
|
array: &mut [f64],
|
||||||
|
mapper: &impl IndexToNoisePos,
|
||||||
|
sample_options: &mut ChunkNoiseFunctionSampleOptions,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Self::Linear(l) => l.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Unary(u) => u.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Binary(b) => b.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::ShiftedNoise(s) => s.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::IntervalSelect(i) => i.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::FindTopSurface(f) => f.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Clamp(c) => c.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::RangeChoice(r) => r.fill(component_stack, array, mapper, sample_options),
|
||||||
|
Self::Spline(s) => s.fill(component_stack, array, mapper, sample_options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ProtoNoiseFunctionComponent {
|
pub enum ProtoNoiseFunctionComponent {
|
||||||
Independent(IndependentProtoNoiseFunctionComponent),
|
Independent(IndependentProtoNoiseFunctionComponent),
|
||||||
Dependent(DependentProtoNoiseFunctionComponent),
|
Dependent(DependentProtoNoiseFunctionComponent),
|
||||||
@ -66,6 +190,30 @@ pub enum ProtoNoiseFunctionComponent {
|
|||||||
Beardifier(Beardifier),
|
Beardifier(Beardifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NoiseFunctionComponentRange for ProtoNoiseFunctionComponent {
|
||||||
|
#[inline]
|
||||||
|
fn min(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Independent(i) => i.min(),
|
||||||
|
Self::Dependent(d) => d.min(),
|
||||||
|
Self::Wrapper(w) => w.min(),
|
||||||
|
Self::PassThrough(p) => p.min(),
|
||||||
|
Self::Beardifier(b) => b.min(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn max(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Independent(i) => i.max(),
|
||||||
|
Self::Dependent(d) => d.max(),
|
||||||
|
Self::Wrapper(w) => w.max(),
|
||||||
|
Self::PassThrough(p) => p.max(),
|
||||||
|
Self::Beardifier(b) => b.max(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DoublePerlinNoiseBuilder;
|
pub struct DoublePerlinNoiseBuilder;
|
||||||
|
|
||||||
impl DoublePerlinNoiseBuilder {
|
impl DoublePerlinNoiseBuilder {
|
||||||
|
|||||||
139
pumpkin-world/src/generation/noise/router/static_router.rs
Normal file
139
pumpkin-world/src/generation/noise/router/static_router.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
|
use super::density_function::IndexToNoisePos;
|
||||||
|
|
||||||
|
/// Zero-cost static trait for monomorphized density function pipeline evaluation.
|
||||||
|
pub trait StaticDensityFunction {
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||||
|
array.iter_mut().enumerate().for_each(|(index, value)| {
|
||||||
|
let pos = mapper.at(index, None);
|
||||||
|
*value = self.sample(&pos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Constant(pub f64);
|
||||||
|
|
||||||
|
impl StaticDensityFunction for Constant {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, _pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(&self, array: &mut [f64], _mapper: &impl IndexToNoisePos) {
|
||||||
|
array.fill(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Add<A: StaticDensityFunction, B: StaticDensityFunction>(pub A, pub B);
|
||||||
|
|
||||||
|
impl<A: StaticDensityFunction, B: StaticDensityFunction> StaticDensityFunction for Add<A, B> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.0.sample(pos) + self.1.sample(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Mul<A: StaticDensityFunction, B: StaticDensityFunction>(pub A, pub B);
|
||||||
|
|
||||||
|
impl<A: StaticDensityFunction, B: StaticDensityFunction> StaticDensityFunction for Mul<A, B> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
let v1 = self.0.sample(pos);
|
||||||
|
if v1 == 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
v1 * self.1.sample(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Min<A: StaticDensityFunction, B: StaticDensityFunction>(pub A, pub B);
|
||||||
|
|
||||||
|
impl<A: StaticDensityFunction, B: StaticDensityFunction> StaticDensityFunction for Min<A, B> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.0.sample(pos).min(self.1.sample(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Max<A: StaticDensityFunction, B: StaticDensityFunction>(pub A, pub B);
|
||||||
|
|
||||||
|
impl<A: StaticDensityFunction, B: StaticDensityFunction> StaticDensityFunction for Max<A, B> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.0.sample(pos).max(self.1.sample(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Clamp<I: StaticDensityFunction> {
|
||||||
|
pub input: I,
|
||||||
|
pub min: f64,
|
||||||
|
pub max: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: StaticDensityFunction> StaticDensityFunction for Clamp<I> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.input.sample(pos).clamp(self.min, self.max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Linear<I: StaticDensityFunction> {
|
||||||
|
pub input: I,
|
||||||
|
pub argument: f64,
|
||||||
|
pub addend: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: StaticDensityFunction> StaticDensityFunction for Linear<I> {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
self.input.sample(pos) * self.argument + self.addend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ClampedYGradient {
|
||||||
|
pub from_y: f64,
|
||||||
|
pub to_y: f64,
|
||||||
|
pub from_value: f64,
|
||||||
|
pub to_value: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticDensityFunction for ClampedYGradient {
|
||||||
|
#[inline]
|
||||||
|
fn sample(&self, pos: &Vector3<i32>) -> f64 {
|
||||||
|
pumpkin_util::math::clamped_map(
|
||||||
|
pos.y as f64,
|
||||||
|
self.from_y,
|
||||||
|
self.to_y,
|
||||||
|
self.from_value,
|
||||||
|
self.to_value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fill(&self, array: &mut [f64], mapper: &impl IndexToNoisePos) {
|
||||||
|
array.iter_mut().enumerate().for_each(|(index, value)| {
|
||||||
|
let pos = mapper.at(index, None);
|
||||||
|
*value = pumpkin_util::math::clamped_map(
|
||||||
|
pos.y as f64,
|
||||||
|
self.from_y,
|
||||||
|
self.to_y,
|
||||||
|
self.from_value,
|
||||||
|
self.to_value,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -179,8 +179,16 @@ impl TerrainCache {
|
|||||||
impl ProtoChunk {
|
impl ProtoChunk {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(x: i32, z: i32, generator: &super::generator::WorldGenerator) -> Self {
|
pub fn new(x: i32, z: i32, generator: &super::generator::WorldGenerator) -> Self {
|
||||||
let dimension = generator.dimension();
|
let (height, bottom_y) = match generator {
|
||||||
let height = dimension.logical_height as u16;
|
super::generator::WorldGenerator::Noise(noise_gen) => (
|
||||||
|
noise_gen.settings.shape.height,
|
||||||
|
noise_gen.settings.shape.min_y,
|
||||||
|
),
|
||||||
|
super::generator::WorldGenerator::Flat(flat_gen) => (
|
||||||
|
flat_gen.dimension.logical_height as u16,
|
||||||
|
flat_gen.dimension.min_y as i8,
|
||||||
|
),
|
||||||
|
};
|
||||||
let section_count = (height as usize) / 16;
|
let section_count = (height as usize) / 16;
|
||||||
|
|
||||||
let default_block = match generator {
|
let default_block = match generator {
|
||||||
@ -215,7 +223,7 @@ impl ProtoChunk {
|
|||||||
flat_motion_blocking_no_leaves_height_map: default_heightmap,
|
flat_motion_blocking_no_leaves_height_map: default_heightmap,
|
||||||
structure_starts: FxHashMap::default(),
|
structure_starts: FxHashMap::default(),
|
||||||
height,
|
height,
|
||||||
bottom_y: dimension.min_y as i8,
|
bottom_y,
|
||||||
stage: StagedChunkEnum::Empty,
|
stage: StagedChunkEnum::Empty,
|
||||||
light: ChunkLight {
|
light: ChunkLight {
|
||||||
sky_light: (0..section_count)
|
sky_light: (0..section_count)
|
||||||
@ -227,7 +235,7 @@ impl ProtoChunk {
|
|||||||
},
|
},
|
||||||
carving_mask: crate::generation::carver::mask::CarvingMask::new(
|
carving_mask: crate::generation::carver::mask::CarvingMask::new(
|
||||||
height as i32,
|
height as i32,
|
||||||
dimension.min_y,
|
bottom_y as i32,
|
||||||
),
|
),
|
||||||
blending_data: None,
|
blending_data: None,
|
||||||
pending_block_entities: Vec::new(),
|
pending_block_entities: Vec::new(),
|
||||||
@ -513,9 +521,9 @@ impl ProtoChunk {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_biome_id(&self, x: i32, y: i32, z: i32) -> u8 {
|
pub fn get_biome_id(&self, x: i32, y: i32, z: i32) -> u8 {
|
||||||
let index = self.local_biome_pos_to_biome_index(
|
let index = self.local_biome_pos_to_biome_index(
|
||||||
x & biome_coords::from_block(15),
|
x & 3,
|
||||||
y - biome_coords::from_block(self.bottom_y() as i32),
|
y - biome_coords::from_block(self.bottom_y() as i32),
|
||||||
z & biome_coords::from_block(15),
|
z & 3,
|
||||||
);
|
);
|
||||||
self.flat_biome_map[index]
|
self.flat_biome_map[index]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
#![allow(clippy::print_stdout, clippy::needless_pass_by_value)]
|
||||||
use crate::chunk_system::chunk_state::StagedChunkEnum;
|
use crate::chunk_system::chunk_state::StagedChunkEnum;
|
||||||
use crate::generation::{generator::WorldGenerator, get_world_gen, proto_chunk::ProtoChunk};
|
use crate::generation::{generator::WorldGenerator, get_world_gen, proto_chunk::ProtoChunk};
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
@ -92,11 +93,17 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn verify_chunk_noise(
|
||||||
fn no_blend_no_beard_0_0() {
|
seed: u64,
|
||||||
let seed = Seed(0);
|
dimension: Dimension,
|
||||||
let world_gen = get_world_gen(seed, Dimension::OVERWORLD, false, Vec::new(), String::new());
|
chunk_x: i32,
|
||||||
let mut chunk = ProtoChunk::new(0, 0, &world_gen);
|
chunk_z: i32,
|
||||||
|
expected_data: &[u16],
|
||||||
|
test_name: &str,
|
||||||
|
) {
|
||||||
|
let seed = Seed(seed);
|
||||||
|
let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new());
|
||||||
|
let mut chunk = ProtoChunk::new(chunk_x, chunk_z, &world_gen);
|
||||||
let WorldGenerator::Noise(generator) = &*world_gen else {
|
let WorldGenerator::Noise(generator) = &*world_gen else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
@ -105,48 +112,55 @@ mod test {
|
|||||||
chunk.stage = StagedChunkEnum::StructureReferences;
|
chunk.stage = StagedChunkEnum::StructureReferences;
|
||||||
chunk.step_to_noise(generator);
|
chunk.step_to_noise(generator);
|
||||||
|
|
||||||
let mut non_air_count = 0;
|
assert_eq!(chunk.flat_block_map.len(), expected_data.len());
|
||||||
for block in &chunk.flat_block_map {
|
let min_y = chunk.bottom_y() as i32;
|
||||||
if !block.to_state().id.to_block().name.eq("air") {
|
let height = chunk.height() as usize;
|
||||||
non_air_count += 1;
|
let mut mismatches = 0;
|
||||||
|
for (i, (&actual, &expected)) in chunk
|
||||||
|
.flat_block_map
|
||||||
|
.iter()
|
||||||
|
.zip(expected_data.iter())
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if actual.as_u16() != expected {
|
||||||
|
if mismatches < 10 {
|
||||||
|
let x = i / (height * 16);
|
||||||
|
let rem = i % (height * 16);
|
||||||
|
let y_local = rem / 16;
|
||||||
|
let z = rem % 16;
|
||||||
|
let y = y_local as i32 + min_y;
|
||||||
|
let act_block = pumpkin_data::BlockState::from_id(actual).id.to_block().name;
|
||||||
|
let exp_block = pumpkin_data::BlockState::from_id(
|
||||||
|
pumpkin_data::BlockStateId::new(expected).unwrap(),
|
||||||
|
)
|
||||||
|
.id
|
||||||
|
.to_block()
|
||||||
|
.name;
|
||||||
|
println!(
|
||||||
|
"[{test_name}] Mismatch at local ({x}, {y}, {z}) index {i}: got {act_block} ({}), expected {exp_block} ({expected})",
|
||||||
|
actual.as_u16()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mismatches += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(
|
assert_eq!(
|
||||||
non_air_count > 0,
|
mismatches, 0,
|
||||||
"Chunk should generate non-air noise blocks"
|
"[{test_name}] Chunk noise generation mismatches vanilla!"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn verify_chunk_surface(
|
||||||
fn no_blend_no_beard_7_4() {
|
seed: u64,
|
||||||
let seed = Seed(0);
|
dimension: Dimension,
|
||||||
let world_gen = get_world_gen(seed, Dimension::OVERWORLD, false, Vec::new(), String::new());
|
chunk_x: i32,
|
||||||
let mut chunk = ProtoChunk::new(7, 4, &world_gen);
|
chunk_z: i32,
|
||||||
let WorldGenerator::Noise(generator) = &*world_gen else {
|
expected_data: &[u16],
|
||||||
unreachable!()
|
test_name: &str,
|
||||||
};
|
) {
|
||||||
|
let seed = Seed(seed);
|
||||||
chunk.step_to_biomes(generator);
|
let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new());
|
||||||
chunk.stage = StagedChunkEnum::StructureReferences;
|
let mut chunk = ProtoChunk::new(chunk_x, chunk_z, &world_gen);
|
||||||
chunk.step_to_noise(generator);
|
|
||||||
|
|
||||||
let mut non_air_count = 0;
|
|
||||||
for block in &chunk.flat_block_map {
|
|
||||||
if !block.to_state().id.to_block().name.eq("air") {
|
|
||||||
non_air_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert!(
|
|
||||||
non_air_count > 0,
|
|
||||||
"Chunk should generate non-air noise blocks"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_blend_no_beard_surface_0_0() {
|
|
||||||
let seed = Seed(0);
|
|
||||||
let world_gen = get_world_gen(seed, Dimension::OVERWORLD, false, Vec::new(), String::new());
|
|
||||||
let mut chunk = ProtoChunk::new(0, 0, &world_gen);
|
|
||||||
let WorldGenerator::Noise(generator) = &*world_gen else {
|
let WorldGenerator::Noise(generator) = &*world_gen else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
@ -156,39 +170,331 @@ mod test {
|
|||||||
chunk.step_to_noise(generator);
|
chunk.step_to_noise(generator);
|
||||||
chunk.step_to_surface(generator);
|
chunk.step_to_surface(generator);
|
||||||
|
|
||||||
let bottom_block = chunk.get_block_state_raw(0, 0, 0);
|
assert_eq!(chunk.flat_block_map.len(), expected_data.len());
|
||||||
assert_eq!(
|
let min_y = chunk.bottom_y() as i32;
|
||||||
bottom_block.to_state().id.to_block().name,
|
let height = chunk.height() as usize;
|
||||||
"bedrock",
|
let mut mismatches = 0;
|
||||||
"Bottom of the world must be bedrock"
|
for (i, (&actual, &expected)) in chunk
|
||||||
);
|
.flat_block_map
|
||||||
|
.iter()
|
||||||
let mut has_deepslate_or_stone = false;
|
.zip(expected_data.iter())
|
||||||
for y in 10..100 {
|
.enumerate()
|
||||||
let block = chunk.get_block_state_raw(8, y, 8);
|
{
|
||||||
let name = block.to_state().id.to_block().name;
|
if actual.as_u16() != expected {
|
||||||
if name.contains("deepslate") || name.eq("stone") {
|
if mismatches < 10 {
|
||||||
has_deepslate_or_stone = true;
|
let x = i / (height * 16);
|
||||||
break;
|
let rem = i % (height * 16);
|
||||||
|
let y_local = rem / 16;
|
||||||
|
let z = rem % 16;
|
||||||
|
let y = y_local as i32 + min_y;
|
||||||
|
let act_block = pumpkin_data::BlockState::from_id(actual).id.to_block().name;
|
||||||
|
let exp_block = pumpkin_data::BlockState::from_id(
|
||||||
|
pumpkin_data::BlockStateId::new(expected).unwrap(),
|
||||||
|
)
|
||||||
|
.id
|
||||||
|
.to_block()
|
||||||
|
.name;
|
||||||
|
println!(
|
||||||
|
"[{test_name}] Mismatch at local ({x}, {y}, {z}) index {i}: got {act_block} ({}), expected {exp_block} ({expected})",
|
||||||
|
actual.as_u16()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mismatches += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let allowed_mismatches = if dimension == Dimension::THE_NETHER {
|
||||||
|
850
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
assert!(
|
assert!(
|
||||||
has_deepslate_or_stone,
|
mismatches <= allowed_mismatches,
|
||||||
"Middle of the world must contain deepslate or stone"
|
"[{test_name}] Chunk surface generation mismatches vanilla! (got {mismatches} mismatches, allowed {allowed_mismatches})"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut has_surface_blocks = false;
|
#[test]
|
||||||
for y in 100..384 {
|
fn no_blend_no_beard_0_0() {
|
||||||
let block = chunk.get_block_state_raw(8, y, 8);
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
let name = block.to_state().id.to_block().name;
|
"../../../assets/tests/noise_no_blend_no_beard_0_0.chunk"
|
||||||
if name.eq("grass_block") || name.eq("dirt") || name.eq("sand") || name.eq("water") {
|
);
|
||||||
has_surface_blocks = true;
|
verify_chunk_noise(
|
||||||
break;
|
0,
|
||||||
}
|
Dimension::OVERWORLD,
|
||||||
}
|
0,
|
||||||
assert!(
|
0,
|
||||||
has_surface_blocks,
|
&expected,
|
||||||
"Top of the world must contain surface blocks (grass/dirt/sand/water)"
|
"no_blend_no_beard_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_7_4() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_7_4.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_7_4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_only_cell_cache_interpolated_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_only_cell_cache_interpolated_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_only_cell_cache_interpolated_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_badlands_minus595_544() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_-595_544.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-595,
|
||||||
|
544,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_badlands_minus595_544",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_frozen_ocean_minus119_183() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_-119_183.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-119,
|
||||||
|
183,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_frozen_ocean_minus119_183",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_13579_minus6_11() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_13579_-6_11.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
13579,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-6,
|
||||||
|
11,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_13579_minus6_11",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_13579_minus2_15() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_13579_-2_15.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
13579,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-2,
|
||||||
|
15,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_13579_minus2_15",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_13579_minus7_9() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_no_blend_no_beard_13579_-7_9.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
13579,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-7,
|
||||||
|
9,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_13579_minus7_9",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_noise_no_blend_no_beard_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_nether_no_blend_no_beard_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::THE_NETHER,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"nether_noise_no_blend_no_beard_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_noise_no_blend_no_beard_7_4() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_nether_no_blend_no_beard_7_4.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::THE_NETHER,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
&expected,
|
||||||
|
"nether_noise_no_blend_no_beard_7_4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn end_noise_no_blend_no_beard_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_end_no_blend_no_beard_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::THE_END,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"end_noise_no_blend_no_beard_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn end_noise_no_blend_no_beard_7_4() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/noise_end_no_blend_no_beard_7_4.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_noise(
|
||||||
|
0,
|
||||||
|
Dimension::THE_END,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
&expected,
|
||||||
|
"end_noise_no_blend_no_beard_7_4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_surface_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/no_blend_no_beard_surface_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_surface_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_surface_badlands_minus595_544() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/no_blend_no_beard_surface_badlands_-595_544.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-595,
|
||||||
|
544,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_surface_badlands_minus595_544",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_blend_no_beard_surface_frozen_ocean_minus119_183() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/no_blend_no_beard_surface_frozen_ocean_-119_183.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::OVERWORLD,
|
||||||
|
-119,
|
||||||
|
183,
|
||||||
|
&expected,
|
||||||
|
"no_blend_no_beard_surface_frozen_ocean_minus119_183",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_surface_no_blend_no_beard_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/nether_surface_no_blend_no_beard_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::THE_NETHER,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"nether_surface_no_blend_no_beard_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nether_surface_no_blend_no_beard_7_4() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/nether_surface_no_blend_no_beard_7_4.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::THE_NETHER,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
&expected,
|
||||||
|
"nether_surface_no_blend_no_beard_7_4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn end_surface_no_blend_no_beard_0_0() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/end_surface_no_blend_no_beard_0_0.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::THE_END,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&expected,
|
||||||
|
"end_surface_no_blend_no_beard_0_0",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn end_surface_no_blend_no_beard_7_4() {
|
||||||
|
let expected: Vec<u16> = pumpkin_util::read_data_from_file!(
|
||||||
|
"../../../assets/tests/end_surface_no_blend_no_beard_7_4.chunk"
|
||||||
|
);
|
||||||
|
verify_chunk_surface(
|
||||||
|
0,
|
||||||
|
Dimension::THE_END,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
&expected,
|
||||||
|
"end_surface_no_blend_no_beard_7_4",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user