chore: less ugly code
and less allocations
This commit is contained in:
parent
ccf62b842a
commit
ac4c85192f
@ -119,8 +119,7 @@ impl SplineRepr {
|
||||
.into_iter()
|
||||
.zip(values)
|
||||
.zip(derivatives)
|
||||
.map(|((location, value), derivative)| (location, value, derivative))
|
||||
.collect::<Vec<_>>();
|
||||
.map(|((location, value), derivative)| (location, value, derivative));
|
||||
|
||||
let function_index =
|
||||
location_function.get_index_for_component(stack, hash_to_index_map);
|
||||
|
||||
@ -118,36 +118,41 @@ impl BlockState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_block_collision_shapes(&self) -> Vec<CollisionShape> {
|
||||
pub fn is_waterlogged(&self) -> bool {
|
||||
let block = Block::from_state_id(self.id);
|
||||
|
||||
if let Some(props) = block.properties(self.id) {
|
||||
props
|
||||
.to_props()
|
||||
.iter()
|
||||
.any(|(k, v)| k == &"waterlogged" && v == &"true")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_block_collision_shapes(&self) -> impl Iterator<Item = CollisionShape> + '_ {
|
||||
self.collision_shapes
|
||||
.iter()
|
||||
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_block_outline_shapes(&self) -> Option<Vec<CollisionShape>> {
|
||||
let mut shapes: Vec<CollisionShape> = self
|
||||
pub fn get_block_outline_shapes(&self) -> impl Iterator<Item = CollisionShape> + '_ {
|
||||
let base_shapes = self
|
||||
.outline_shapes
|
||||
.iter()
|
||||
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||
.collect();
|
||||
.map(|&id| COLLISION_SHAPES[id as usize]);
|
||||
|
||||
let block = Block::from_state_id(self.id);
|
||||
if let Some(props) = block.properties(self.id) {
|
||||
let is_waterlogged = props
|
||||
.to_props()
|
||||
.iter()
|
||||
.any(|(k, v)| *k == "waterlogged" && *v == "true");
|
||||
|
||||
if is_waterlogged {
|
||||
shapes.push(CollisionShape::new(
|
||||
let water_shape = if self.is_waterlogged() {
|
||||
Some(CollisionShape::new(
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.875, 1.0),
|
||||
));
|
||||
}
|
||||
}
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(shapes)
|
||||
base_shapes.chain(water_shape)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -73,10 +73,10 @@ impl NbtCompound {
|
||||
Ok(compound)
|
||||
}
|
||||
|
||||
pub fn serialize_content<W: Write>(&self, w: &mut WriteAdaptor<W>) -> Result<(), Error> {
|
||||
for (name, tag) in &self.child_tags {
|
||||
pub fn serialize_content<W: Write>(self, w: &mut WriteAdaptor<W>) -> Result<(), Error> {
|
||||
for (name, tag) in self.child_tags {
|
||||
w.write_u8_be(tag.get_type_id())?;
|
||||
NbtTag::String(name.clone()).serialize_data(w)?;
|
||||
NbtTag::write_string(&name, w)?;
|
||||
tag.serialize_data(w)?;
|
||||
}
|
||||
w.write_u8_be(END_ID)?;
|
||||
|
||||
@ -110,11 +110,11 @@ impl Nbt {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write(&self) -> Bytes {
|
||||
pub fn write(self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
let mut writer = WriteAdaptor::new(&mut bytes);
|
||||
writer.write_u8_be(COMPOUND_ID).unwrap();
|
||||
NbtTag::String(self.name.to_string())
|
||||
NbtTag::String(self.name)
|
||||
.serialize_data(&mut writer)
|
||||
.unwrap();
|
||||
self.root_tag.serialize_content(&mut writer).unwrap();
|
||||
@ -122,13 +122,13 @@ impl Nbt {
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
pub fn write_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||
pub fn write_to_writer<W: Write>(self, mut writer: W) -> Result<(), io::Error> {
|
||||
writer.write_all(&self.write())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an NBT tag without a root `Compound` name.
|
||||
pub fn write_unnamed(&self) -> Bytes {
|
||||
pub fn write_unnamed(self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
let mut writer = WriteAdaptor::new(&mut bytes);
|
||||
|
||||
@ -138,7 +138,7 @@ impl Nbt {
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
pub fn write_unnamed_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||
pub fn write_unnamed_to_writer<W: Write>(self, mut writer: W) -> Result<(), io::Error> {
|
||||
writer.write_all(&self.write_unnamed())?;
|
||||
Ok(())
|
||||
}
|
||||
@ -178,7 +178,7 @@ pub fn get_nbt_string<R: Read + Seek>(bytes: &mut NbtReadHelper<R>) -> Result<St
|
||||
let len = bytes.get_u16_be()? as usize;
|
||||
let string_bytes = bytes.read_boxed_slice(len)?;
|
||||
let string = cesu8::from_java_cesu8(&string_bytes).map_err(|_| Error::Cesu8DecodingError)?;
|
||||
Ok(string.to_string())
|
||||
Ok(string.into_owned())
|
||||
}
|
||||
|
||||
// TODO: This is a bit hacky
|
||||
|
||||
@ -36,12 +36,12 @@ pub fn read_gzip_compound_tag(input: impl Read + Seek) -> Result<NbtCompound, Er
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result containing either the compressed data as a byte vector or an Error
|
||||
pub fn write_gzip_compound_tag(compound: &NbtCompound, output: impl Write) -> Result<(), Error> {
|
||||
pub fn write_gzip_compound_tag(compound: NbtCompound, output: impl Write) -> Result<(), Error> {
|
||||
// Create a GZip encoder that writes to the output
|
||||
let mut encoder = GzEncoder::new(output, Compression::default());
|
||||
|
||||
// Create an NBT wrapper and write directly to the encoder
|
||||
let nbt = Nbt::new(String::new(), compound.clone());
|
||||
let nbt = Nbt::new(String::new(), compound);
|
||||
nbt.write_to_writer(&mut encoder)
|
||||
.map_err(Error::Incomplete)?;
|
||||
|
||||
@ -52,7 +52,7 @@ pub fn write_gzip_compound_tag(compound: &NbtCompound, output: impl Write) -> Re
|
||||
}
|
||||
|
||||
/// Convenience function that returns compressed bytes
|
||||
pub fn write_gzip_compound_tag_to_bytes(compound: &NbtCompound) -> Result<Vec<u8>, Error> {
|
||||
pub fn write_gzip_compound_tag_to_bytes(compound: NbtCompound) -> Result<Vec<u8>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(compound, &mut buffer)?;
|
||||
Ok(buffer)
|
||||
@ -135,7 +135,7 @@ mod tests {
|
||||
|
||||
// Write to GZip using streaming
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(&compound, &mut buffer).expect("Failed to compress compound");
|
||||
write_gzip_compound_tag(compound, &mut buffer).expect("Failed to compress compound");
|
||||
|
||||
// Read from GZip using streaming
|
||||
let read_compound =
|
||||
@ -170,7 +170,7 @@ mod tests {
|
||||
|
||||
// Test convenience method for writing
|
||||
let buffer =
|
||||
write_gzip_compound_tag_to_bytes(&compound).expect("Failed to compress compound");
|
||||
write_gzip_compound_tag_to_bytes(compound).expect("Failed to compress compound");
|
||||
|
||||
// Test streaming read from the buffer
|
||||
let read_compound =
|
||||
@ -183,7 +183,7 @@ mod tests {
|
||||
fn test_gzip_empty_compound() {
|
||||
let compound = NbtCompound::new();
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(&compound, &mut buffer).expect("Failed to compress empty compound");
|
||||
write_gzip_compound_tag(compound, &mut buffer).expect("Failed to compress empty compound");
|
||||
let read_compound = read_gzip_compound_tag(Cursor::new(buffer))
|
||||
.expect("Failed to decompress empty compound");
|
||||
|
||||
@ -200,7 +200,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(&compound, &mut buffer).expect("Failed to compress large compound");
|
||||
write_gzip_compound_tag(compound, &mut buffer).expect("Failed to compress large compound");
|
||||
let read_compound = read_gzip_compound_tag(Cursor::new(buffer))
|
||||
.expect("Failed to decompress large compound");
|
||||
|
||||
@ -272,7 +272,7 @@ mod tests {
|
||||
|
||||
let uncompressed = compound.child_tags.len() * 100; // rough estimate
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(&compound, &mut buffer).expect("Failed to compress compound");
|
||||
write_gzip_compound_tag(compound, &mut buffer).expect("Failed to compress compound");
|
||||
|
||||
println!("Uncompressed size (est): {uncompressed} bytes");
|
||||
println!("Compressed size: {} bytes", buffer.len());
|
||||
@ -356,7 +356,7 @@ mod tests {
|
||||
compound.put_int("test_value", 42);
|
||||
|
||||
let file = File::create(&file_path).expect("Failed to create temp file");
|
||||
write_gzip_compound_tag(&compound, file).expect("Failed to write compound to file");
|
||||
write_gzip_compound_tag(compound, file).expect("Failed to write compound to file");
|
||||
|
||||
let file = File::open(&file_path).expect("Failed to open temp file");
|
||||
let read_compound =
|
||||
|
||||
@ -92,7 +92,7 @@ impl<W: Write> Serializer<W> {
|
||||
match &mut self.state {
|
||||
State::Named(name) | State::Array { name, .. } => {
|
||||
self.output.write_u8_be(tag)?;
|
||||
NbtTag::String(name.clone()).serialize_data(&mut self.output)?;
|
||||
NbtTag::write_string(name, &mut self.output)?;
|
||||
}
|
||||
State::FirstListElement { len } => {
|
||||
self.output.write_u8_be(tag)?;
|
||||
@ -131,7 +131,7 @@ impl<W: Write> Serializer<W> {
|
||||
self.handled_root = true;
|
||||
self.output.write_u8_be(tag)?;
|
||||
if let Some(root_name) = root_name {
|
||||
NbtTag::String(root_name.clone()).serialize_data(&mut self.output)?;
|
||||
NbtTag::write_string(root_name, &mut self.output)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -246,7 +246,7 @@ impl<W: Write> ser::Serializer for &mut Serializer<W> {
|
||||
if self.state == State::MapKey {
|
||||
self.state = State::Named(v.to_string());
|
||||
} else {
|
||||
NbtTag::String(v.to_string()).serialize_data(&mut self.output)?;
|
||||
NbtTag::write_string(v, &mut self.output)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -32,31 +32,13 @@ impl NbtTag {
|
||||
unsafe { *(self as *const Self as *const u8) }
|
||||
}
|
||||
|
||||
pub fn serialize<W: Write>(&self, w: &mut WriteAdaptor<W>) -> serializer::Result<()> {
|
||||
pub fn serialize<W: Write>(self, w: &mut WriteAdaptor<W>) -> serializer::Result<()> {
|
||||
w.write_u8_be(self.get_type_id())?;
|
||||
self.serialize_data(w)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize_data<W: Write>(&self, w: &mut WriteAdaptor<W>) -> serializer::Result<()> {
|
||||
match self {
|
||||
NbtTag::End => {}
|
||||
NbtTag::Byte(byte) => w.write_i8_be(*byte)?,
|
||||
NbtTag::Short(short) => w.write_i16_be(*short)?,
|
||||
NbtTag::Int(int) => w.write_i32_be(*int)?,
|
||||
NbtTag::Long(long) => w.write_i64_be(*long)?,
|
||||
NbtTag::Float(float) => w.write_f32_be(*float)?,
|
||||
NbtTag::Double(double) => w.write_f64_be(*double)?,
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
let len = byte_array.len();
|
||||
if len > i32::MAX as usize {
|
||||
return Err(Error::LargeLength(len));
|
||||
}
|
||||
|
||||
w.write_i32_be(len as i32)?;
|
||||
w.write_slice(byte_array)?;
|
||||
}
|
||||
NbtTag::String(string) => {
|
||||
pub fn write_string<W: Write>(string: &str, w: &mut WriteAdaptor<W>) -> serializer::Result<()> {
|
||||
let java_string = cesu8::to_java_cesu8(string);
|
||||
let len = java_string.len();
|
||||
if len > u16::MAX as usize {
|
||||
@ -65,6 +47,29 @@ impl NbtTag {
|
||||
|
||||
w.write_u16_be(len as u16)?;
|
||||
w.write_slice(&java_string)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize_data<W: Write>(self, w: &mut WriteAdaptor<W>) -> serializer::Result<()> {
|
||||
match self {
|
||||
NbtTag::End => {}
|
||||
NbtTag::Byte(byte) => w.write_i8_be(byte)?,
|
||||
NbtTag::Short(short) => w.write_i16_be(short)?,
|
||||
NbtTag::Int(int) => w.write_i32_be(int)?,
|
||||
NbtTag::Long(long) => w.write_i64_be(long)?,
|
||||
NbtTag::Float(float) => w.write_f32_be(float)?,
|
||||
NbtTag::Double(double) => w.write_f64_be(double)?,
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
let len = byte_array.len();
|
||||
if len > i32::MAX as usize {
|
||||
return Err(Error::LargeLength(len));
|
||||
}
|
||||
|
||||
w.write_i32_be(len as i32)?;
|
||||
w.write_slice(&byte_array)?;
|
||||
}
|
||||
NbtTag::String(string) => {
|
||||
Self::write_string(&string, w)?;
|
||||
}
|
||||
NbtTag::List(list) => {
|
||||
let len = list.len();
|
||||
@ -89,7 +94,7 @@ impl NbtTag {
|
||||
|
||||
w.write_i32_be(len as i32)?;
|
||||
for int in int_array {
|
||||
w.write_i32_be(*int)?;
|
||||
w.write_i32_be(int)?;
|
||||
}
|
||||
}
|
||||
NbtTag::LongArray(long_array) => {
|
||||
@ -101,7 +106,7 @@ impl NbtTag {
|
||||
w.write_i32_be(len as i32)?;
|
||||
|
||||
for long in long_array {
|
||||
w.write_i64_be(*long)?;
|
||||
w.write_i64_be(long)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -307,9 +312,9 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_byte_array(&self) -> Option<Box<[u8]>> {
|
||||
pub fn extract_byte_array(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array.clone()),
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ impl Ack {
|
||||
|
||||
let mut start = self.sequences[0];
|
||||
let mut end = start;
|
||||
for seq in self.sequences.clone() {
|
||||
for seq in self.sequences.iter().copied() {
|
||||
if seq == end + 1 {
|
||||
end = seq
|
||||
} else {
|
||||
|
||||
@ -128,7 +128,7 @@ impl ClientPacket for CChunkData<'_> {
|
||||
write.write_var_int(&VarInt(block_entity.get_id() as i32))?;
|
||||
|
||||
if let Some(nbt) = block_entity.chunk_data_nbt() {
|
||||
write.write_nbt(&nbt.into())?;
|
||||
write.write_nbt(nbt.into())?;
|
||||
} else {
|
||||
write.write_u8(END_ID)?;
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@ pub trait NetworkWriteExt {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_nbt(&mut self, data: &NbtTag) -> Result<(), WritingError>;
|
||||
fn write_nbt(&mut self, data: NbtTag) -> Result<(), WritingError>;
|
||||
}
|
||||
|
||||
macro_rules! write_number_be {
|
||||
@ -400,7 +400,7 @@ impl<W: Write> NetworkWriteExt for W {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_nbt(&mut self, data: &NbtTag) -> Result<(), WritingError> {
|
||||
fn write_nbt(&mut self, data: NbtTag) -> Result<(), WritingError> {
|
||||
let mut write_adaptor = WriteAdaptor::new(self);
|
||||
data.serialize(&mut write_adaptor)
|
||||
.map_err(|e| WritingError::Message(e.to_string()))?;
|
||||
|
||||
@ -363,8 +363,8 @@ impl BlockPos {
|
||||
x + y + z
|
||||
}
|
||||
|
||||
pub fn squared_distance(&self, other: Self) -> i32 {
|
||||
self.0.squared_distance_to_vec(other.0)
|
||||
pub fn squared_distance(&self, other: &Self) -> i32 {
|
||||
self.0.squared_distance_to_vec(&other.0)
|
||||
}
|
||||
|
||||
pub fn as_long(&self) -> i64 {
|
||||
|
||||
@ -151,7 +151,7 @@ impl<T: Math + PartialOrd + Copy> Vector3<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn squared_distance_to_vec(&self, other: Self) -> T {
|
||||
pub fn squared_distance_to_vec(&self, other: &Self) -> T {
|
||||
self.squared_distance_to(other.x, other.y, other.z)
|
||||
}
|
||||
|
||||
|
||||
@ -244,13 +244,11 @@ impl OctavePerlinNoiseSampler {
|
||||
result
|
||||
})
|
||||
.collect();
|
||||
let lacunarities: Vec<f64> = (0..amplitudes.len())
|
||||
.map(|_| {
|
||||
let lacunarities = (0..amplitudes.len()).map(|_| {
|
||||
let result = lacunarity;
|
||||
lacunarity *= 2.0;
|
||||
result
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
|
||||
let max_value = Self::get_total_amplitude_generic(2.0, &persistences, amplitudes);
|
||||
|
||||
|
||||
@ -123,31 +123,24 @@ impl Compression {
|
||||
const CUSTOM_ID: u8 = 127;
|
||||
|
||||
fn decompress_data(&self, compressed_data: &[u8]) -> Result<Box<[u8]>, CompressionError> {
|
||||
let initial_capacity = compressed_data.len();
|
||||
|
||||
fn decode<R: std::io::Read>(mut reader: R, capacity: usize) -> std::io::Result<Box<[u8]>> {
|
||||
let mut buf = Vec::with_capacity(capacity);
|
||||
reader.read_to_end(&mut buf)?;
|
||||
Ok(buf.into_boxed_slice())
|
||||
}
|
||||
|
||||
match self {
|
||||
Compression::GZip => {
|
||||
let mut decoder = GzDecoder::new(compressed_data);
|
||||
let mut chunk_data = Vec::new();
|
||||
decoder
|
||||
.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::GZipError)?;
|
||||
Ok(chunk_data.into_boxed_slice())
|
||||
}
|
||||
Compression::ZLib => {
|
||||
let mut decoder = ZlibDecoder::new(compressed_data);
|
||||
let mut chunk_data = Vec::new();
|
||||
decoder
|
||||
.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::ZlibError)?;
|
||||
Ok(chunk_data.into_boxed_slice())
|
||||
}
|
||||
Compression::LZ4 => {
|
||||
let mut decoder = lz4_java_wrc::Lz4BlockInput::new(compressed_data);
|
||||
let mut decompressed_data = Vec::new();
|
||||
decoder
|
||||
.read_to_end(&mut decompressed_data)
|
||||
.map_err(CompressionError::LZ4Error)?;
|
||||
Ok(decompressed_data.into_boxed_slice())
|
||||
}
|
||||
Compression::GZip => decode(GzDecoder::new(compressed_data), initial_capacity)
|
||||
.map_err(CompressionError::GZipError),
|
||||
Compression::ZLib => decode(ZlibDecoder::new(compressed_data), initial_capacity)
|
||||
.map_err(CompressionError::ZlibError),
|
||||
Compression::LZ4 => decode(
|
||||
lz4_java_wrc::Lz4BlockInput::new(compressed_data),
|
||||
initial_capacity,
|
||||
)
|
||||
.map_err(CompressionError::LZ4Error),
|
||||
Compression::Custom => todo!(),
|
||||
}
|
||||
}
|
||||
@ -349,7 +342,10 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
|
||||
index as usize
|
||||
}
|
||||
|
||||
async fn write_indices(&self, path: &Path, indices: &[usize]) -> Result<(), std::io::Error> {
|
||||
async fn write_indices<I>(&self, path: &Path, indices: I) -> Result<(), std::io::Error>
|
||||
where
|
||||
I: IntoIterator<Item = usize>,
|
||||
{
|
||||
log::trace!("Writing in place: {}", path.display());
|
||||
|
||||
let file = tokio::fs::OpenOptions::new()
|
||||
@ -392,11 +388,11 @@ impl<S: SingleChunkDataSerializer> AnvilChunkFile<S> {
|
||||
}
|
||||
|
||||
let mut chunks = indices
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|index| {
|
||||
(
|
||||
index,
|
||||
self.chunks_data[*index]
|
||||
self.chunks_data[index]
|
||||
.as_ref()
|
||||
.expect("We are trying to write a chunk, but it does not exist!"),
|
||||
)
|
||||
@ -547,10 +543,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for AnvilChunkFile<S> {
|
||||
Ok(())
|
||||
}
|
||||
WriteAction::All => self.write_all(path).await,
|
||||
WriteAction::Parts(parts) => {
|
||||
self.write_indices(path, Vec::from_iter(parts.iter().cloned()).as_slice())
|
||||
.await
|
||||
}
|
||||
WriteAction::Parts(parts) => self.write_indices(path, parts.iter().copied()).await,
|
||||
}?;
|
||||
|
||||
// If we still are in memory after this, we don't need to write again!
|
||||
|
||||
@ -71,15 +71,12 @@ impl LinearChunkHeader {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes(self) -> Box<[u8]> {
|
||||
let mut bytes = Vec::with_capacity(LinearChunkHeader::CHUNK_HEADER_SIZE);
|
||||
pub fn to_bytes(self) -> [u8; LinearChunkHeader::CHUNK_HEADER_SIZE] {
|
||||
let mut bytes = [0u8; LinearChunkHeader::CHUNK_HEADER_SIZE];
|
||||
bytes[0..4].copy_from_slice(&self.size.to_be_bytes());
|
||||
bytes[4..8].copy_from_slice(&self.timestamp.to_be_bytes());
|
||||
|
||||
bytes.put_u32(self.size);
|
||||
bytes.put_u32(self.timestamp);
|
||||
|
||||
// This should be a clear code error if the size of the header is not the expected
|
||||
// so we can unwrap the conversion safely or panic the entire program if not
|
||||
bytes.into_boxed_slice()
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -167,8 +167,8 @@ impl ChunkData {
|
||||
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),
|
||||
fluid_ticks: ChunkTickScheduler::from_vec(&chunk_data.fluid_ticks),
|
||||
block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks),
|
||||
fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks),
|
||||
block_entities: {
|
||||
let mut block_entities = HashMap::new();
|
||||
for nbt in chunk_data.block_entities {
|
||||
|
||||
@ -166,11 +166,9 @@ where
|
||||
folder: &'a LevelFolder,
|
||||
chunks: &'a [Vector2<i32>],
|
||||
) -> BoxFuture<'a, ()> {
|
||||
let paths: Vec<_> = chunks
|
||||
let paths = chunks
|
||||
.iter()
|
||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)))
|
||||
.collect();
|
||||
|
||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)));
|
||||
Box::pin(async move {
|
||||
let mut watchers = self.watchers.write().await;
|
||||
for path in paths {
|
||||
@ -187,10 +185,9 @@ where
|
||||
folder: &'a LevelFolder,
|
||||
chunks: &'a [Vector2<i32>],
|
||||
) -> BoxFuture<'a, ()> {
|
||||
let paths: Vec<_> = chunks
|
||||
let paths = chunks
|
||||
.iter()
|
||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)))
|
||||
.collect();
|
||||
.map(|chunk| P::file_path(folder, &S::get_chunk_key(chunk)));
|
||||
Box::pin(async move {
|
||||
let mut watchers = self.watchers.write().await;
|
||||
|
||||
|
||||
@ -138,20 +138,20 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
|
||||
}
|
||||
|
||||
pub fn from_palette_and_packed_data(
|
||||
palette_slice: &[V],
|
||||
palette: Vec<V>,
|
||||
packed_data: &[i64],
|
||||
minimum_bits_per_entry: u8,
|
||||
) -> Self {
|
||||
if palette_slice.is_empty() {
|
||||
if palette.is_empty() {
|
||||
log::warn!("No palette data! Defaulting...");
|
||||
return Self::Homogeneous(V::default());
|
||||
}
|
||||
|
||||
if palette_slice.len() == 1 {
|
||||
return Self::Homogeneous(palette_slice[0]);
|
||||
if palette.len() == 1 {
|
||||
return Self::Homogeneous(palette[0]);
|
||||
}
|
||||
|
||||
let bits_per_key = encompassing_bits(palette_slice.len()).max(minimum_bits_per_entry);
|
||||
let bits_per_key = encompassing_bits(palette.len()).max(minimum_bits_per_entry);
|
||||
let index_mask = (1 << bits_per_key) - 1;
|
||||
let keys_per_i64 = 64 / bits_per_key;
|
||||
|
||||
@ -174,7 +174,7 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
|
||||
>> (bit_index_in_word as u64 * bits_per_key as u64))
|
||||
& index_mask;
|
||||
|
||||
let value = palette_slice
|
||||
let value = palette
|
||||
.get(lookup_index as usize)
|
||||
.copied()
|
||||
.unwrap_or_else(|| {
|
||||
@ -186,12 +186,12 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
|
||||
}
|
||||
|
||||
// Now, with all decompressed values, build the counts.
|
||||
let mut counts = vec![0; palette_slice.len()];
|
||||
let mut counts = vec![0; palette.len()];
|
||||
|
||||
for &value in &decompressed_values {
|
||||
// This is the key optimization: find the index in the palette Vec
|
||||
// and increment the corresponding count.
|
||||
if let Some(index) = palette_slice.iter().position(|v| v == &value) {
|
||||
if let Some(index) = palette.iter().position(|v| v == &value) {
|
||||
counts[index] += 1;
|
||||
} else {
|
||||
// This case should ideally not happen if the palette is complete.
|
||||
@ -204,11 +204,9 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
|
||||
.as_flattened_mut()
|
||||
.copy_from_slice(&decompressed_values);
|
||||
|
||||
let palette_vec: Vec<V> = palette_slice.to_vec();
|
||||
|
||||
Self::Heterogeneous(Box::new(HeterogeneousPaletteData {
|
||||
cube,
|
||||
palette: palette_vec,
|
||||
palette,
|
||||
counts,
|
||||
}))
|
||||
}
|
||||
@ -336,7 +334,7 @@ impl BiomePalette {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self::from_palette_and_packed_data(
|
||||
&palette,
|
||||
palette,
|
||||
nbt.data.as_ref().unwrap_or(&Box::default()),
|
||||
BIOME_DISK_MIN_BITS,
|
||||
)
|
||||
@ -515,7 +513,7 @@ impl BlockPalette {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self::from_palette_and_packed_data(
|
||||
&palette,
|
||||
palette,
|
||||
nbt.data.as_ref().unwrap_or(&Box::default()),
|
||||
BLOCK_DISK_MIN_BITS,
|
||||
)
|
||||
|
||||
@ -138,7 +138,7 @@ impl PlayerDataStorage {
|
||||
// Create the file and write directly with GZip compression
|
||||
match File::create(&path) {
|
||||
Ok(file) => {
|
||||
if let Err(e) = pumpkin_nbt::nbt_compress::write_gzip_compound_tag(&data, file) {
|
||||
if let Err(e) = pumpkin_nbt::nbt_compress::write_gzip_compound_tag(data, file) {
|
||||
log::error!("Failed to write compressed player data for {uuid}: {e}");
|
||||
Err(PlayerDataError::Nbt(e.to_string()))
|
||||
} else {
|
||||
|
||||
@ -30,10 +30,7 @@ impl CoralClawFeature {
|
||||
let direction = BlockDirection::horizontal()
|
||||
[random.next_bounded_i32(BlockDirection::horizontal().len() as i32 - 1) as usize];
|
||||
// TODO: Shuffle
|
||||
let directions: Vec<_> = BlockDirection::horizontal()
|
||||
.into_iter()
|
||||
.take(i as usize)
|
||||
.collect();
|
||||
let directions = BlockDirection::horizontal().into_iter().take(i as usize);
|
||||
'block0: for direction2 in directions {
|
||||
let mut pos = pos;
|
||||
let j = random.next_bounded_i32(2) + 1;
|
||||
|
||||
@ -34,10 +34,7 @@ impl CoralTreeFeature {
|
||||
let i = random.next_bounded_i32(3) + 2;
|
||||
|
||||
// TODO: Shuffle
|
||||
let directions: Vec<_> = BlockDirection::horizontal()
|
||||
.into_iter()
|
||||
.take(i as usize)
|
||||
.collect();
|
||||
let directions = BlockDirection::horizontal().into_iter().take(i as usize);
|
||||
for dir in directions {
|
||||
pos = pos.offset(dir.to_offset());
|
||||
let times = random.next_bounded_i32(5) + 2;
|
||||
|
||||
@ -12,6 +12,7 @@ pub mod height_provider;
|
||||
pub mod noise;
|
||||
pub mod positions;
|
||||
pub mod proto_chunk;
|
||||
pub mod proto_chunk_test;
|
||||
pub mod rule;
|
||||
mod rule_test;
|
||||
pub mod settings;
|
||||
|
||||
@ -1164,684 +1164,3 @@ impl BlockAccessor for ProtoChunk {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] // TODO: Fix tests to work with new ProtoChunk API
|
||||
mod test {
|
||||
/*
|
||||
TODO: Update all tests to work with the new ProtoChunk API that doesn't use lifetimes.
|
||||
The new API requires passing noise samplers and other dependencies as parameters to methods
|
||||
instead of storing them in the struct.
|
||||
|
||||
const SEED: u64 = 0;
|
||||
static RANDOM_CONFIG: LazyLock<GlobalRandomConfig> =
|
||||
LazyLock::new(|| GlobalRandomConfig::new(SEED, false)); // TODO: use legacy when needed
|
||||
static TERRAIN_CACHE: LazyLock<TerrainCache> =
|
||||
LazyLock::new(|| TerrainCache::from_random(&RANDOM_CONFIG));
|
||||
static BASE_NOISE_ROUTER: LazyLock<ProtoNoiseRouters> =
|
||||
LazyLock::new(|| ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG));
|
||||
|
||||
const SEED2: u64 = 13579;
|
||||
static RANDOM_CONFIG2: LazyLock<GlobalRandomConfig> =
|
||||
LazyLock::new(|| GlobalRandomConfig::new(SEED2, false)); // TODO: use legacy when needed
|
||||
static BASE_NOISE_ROUTER2: LazyLock<ProtoNoiseRouters> = LazyLock::new(|| {
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG2)
|
||||
});
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_cache() {
|
||||
// We say no wrapper, but it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_only_cell_cache_0_0.chunk");
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_2d_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
// should be the same as only cell_cache
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_only_cell_cache_0_0.chunk");
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::Cache2D => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_flat_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_flat_cache_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::CacheFlat => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_once_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_once_cache_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::CacheOnce => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_interpolated() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_interpolated_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::Interpolated => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard() {
|
||||
let _expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_0_0.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
// TODO: Create ProtoChunk and call populate_noise with proper parameters
|
||||
let _chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
surface_config,
|
||||
surface_config.default_block.get_state(),
|
||||
0, // biome_mixer_seed
|
||||
);
|
||||
|
||||
// assert_eq!(
|
||||
// expected_data,
|
||||
// chunk.flat_block_map.into_iter().collect::<Vec<u16>>()
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_aquifer() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_7_4.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(7, 4),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
assert_eq!(
|
||||
expected_data,
|
||||
chunk.flat_block_map.into_iter().collect::<Vec<u16>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_-595_544.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-595, 544),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_frozen_ocean() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_-119_183.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-119, 183),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands2() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_13579_-6_11.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-6, 11),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands3() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_13579_-2_15.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-2, 15),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_0_0.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_badlands_-595_544.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-595, 544),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands2() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-6_11.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-6, 11),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands3() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-7_9.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-7, 9),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_biome_blend() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-2_15.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-2, 15),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_frozen_ocean() {
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_surface_frozen_ocean_-119_183.chunk"
|
||||
);
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-119, 183),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
680
pumpkin-world/src/generation/proto_chunk_test.rs
Normal file
680
pumpkin-world/src/generation/proto_chunk_test.rs
Normal file
@ -0,0 +1,680 @@
|
||||
#[cfg(test)] // TODO: Fix tests to work with new ProtoChunk API
|
||||
mod test {
|
||||
/*
|
||||
TODO: Update all tests to work with the new ProtoChunk API that doesn't use lifetimes.
|
||||
The new API requires passing noise samplers and other dependencies as parameters to methods
|
||||
instead of storing them in the struct.
|
||||
|
||||
const SEED: u64 = 0;
|
||||
static RANDOM_CONFIG: LazyLock<GlobalRandomConfig> =
|
||||
LazyLock::new(|| GlobalRandomConfig::new(SEED, false)); // TODO: use legacy when needed
|
||||
static TERRAIN_CACHE: LazyLock<TerrainCache> =
|
||||
LazyLock::new(|| TerrainCache::from_random(&RANDOM_CONFIG));
|
||||
static BASE_NOISE_ROUTER: LazyLock<ProtoNoiseRouters> =
|
||||
LazyLock::new(|| ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG));
|
||||
|
||||
const SEED2: u64 = 13579;
|
||||
static RANDOM_CONFIG2: LazyLock<GlobalRandomConfig> =
|
||||
LazyLock::new(|| GlobalRandomConfig::new(SEED2, false)); // TODO: use legacy when needed
|
||||
static BASE_NOISE_ROUTER2: LazyLock<ProtoNoiseRouters> = LazyLock::new(|| {
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG2)
|
||||
});
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_cache() {
|
||||
// We say no wrapper, but it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_only_cell_cache_0_0.chunk");
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_2d_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
// should be the same as only cell_cache
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_only_cell_cache_0_0.chunk");
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::Cache2D => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_flat_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_flat_cache_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::CacheFlat => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_once_cache() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_once_cache_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::CacheOnce => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_only_cell_interpolated() {
|
||||
// it technically has a top-level cell cache
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_only_cell_cache_interpolated_0_0.chunk"
|
||||
);
|
||||
|
||||
let mut base_router =
|
||||
ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &RANDOM_CONFIG);
|
||||
|
||||
macro_rules! set_wrappers {
|
||||
($stack: expr) => {
|
||||
$stack.iter_mut().for_each(|component| {
|
||||
if let ProtoNoiseFunctionComponent::Wrapper(wrapper) = component {
|
||||
match wrapper.wrapper_type {
|
||||
WrapperType::CellCache => (),
|
||||
WrapperType::Interpolated => (),
|
||||
_ => {
|
||||
*component =
|
||||
ProtoNoiseFunctionComponent::PassThrough(PassThrough::new(
|
||||
wrapper.input_index,
|
||||
wrapper.min(),
|
||||
wrapper.max(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
set_wrappers!(base_router.noise.full_component_stack);
|
||||
set_wrappers!(base_router.surface_estimator.full_component_stack);
|
||||
set_wrappers!(base_router.multi_noise.full_component_stack);
|
||||
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&base_router,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("{expected} vs {actual} ({index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard() {
|
||||
let _expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_0_0.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
// TODO: Create ProtoChunk and call populate_noise with proper parameters
|
||||
let _chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
surface_config,
|
||||
surface_config.default_block.get_state(),
|
||||
0, // biome_mixer_seed
|
||||
);
|
||||
|
||||
// assert_eq!(
|
||||
// expected_data,
|
||||
// chunk.flat_block_map.into_iter().collect::<Vec<u16>>()
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_aquifer() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_7_4.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(7, 4),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
assert_eq!(
|
||||
expected_data,
|
||||
chunk.flat_block_map.into_iter().collect::<Vec<u16>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_-595_544.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-595, 544),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_frozen_ocean() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_-119_183.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-119, 183),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands2() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_13579_-6_11.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-6, 11),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_badlands3() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_13579_-2_15.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-2, 15),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
chunk.populate_noise();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_0_0.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(0, 0),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_badlands_-595_544.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-595, 544),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands2() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-6_11.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-6, 11),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_badlands3() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-7_9.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-7, 9),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_biome_blend() {
|
||||
let expected_data: Vec<u16> =
|
||||
read_data_from_file!("../../assets/no_blend_no_beard_surface_13579_-2_15.chunk");
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let terrain_cache = TerrainCache::from_random(&RANDOM_CONFIG2);
|
||||
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-2, 15),
|
||||
&BASE_NOISE_ROUTER2,
|
||||
&RANDOM_CONFIG2,
|
||||
surface_config,
|
||||
&terrain_cache,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Update this test to work with new API
|
||||
fn test_no_blend_no_beard_surface_frozen_ocean() {
|
||||
let expected_data: Vec<u16> = read_data_from_file!(
|
||||
"../../assets/no_blend_no_beard_surface_frozen_ocean_-119_183.chunk"
|
||||
);
|
||||
let surface_config = GENERATION_SETTINGS
|
||||
.get(&GeneratorSetting::Overworld)
|
||||
.unwrap();
|
||||
let mut chunk = ProtoChunk::new(
|
||||
Vector2::new(-119, 183),
|
||||
&BASE_NOISE_ROUTER,
|
||||
&RANDOM_CONFIG,
|
||||
surface_config,
|
||||
&TERRAIN_CACHE,
|
||||
surface_config.default_block.get_state(),
|
||||
);
|
||||
|
||||
chunk.populate_biomes(Dimension::Overworld);
|
||||
chunk.populate_noise();
|
||||
chunk.build_surface();
|
||||
|
||||
expected_data
|
||||
.into_iter()
|
||||
.zip(chunk.flat_block_map)
|
||||
.enumerate()
|
||||
.for_each(|(index, (expected, actual))| {
|
||||
if expected != actual {
|
||||
panic!("expected {expected}, was {actual} (at {index})");
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
@ -435,12 +435,7 @@ impl Level {
|
||||
};
|
||||
|
||||
let mut rng = SmallRng::from_rng(&mut rand::rng());
|
||||
let chunks = self
|
||||
.loaded_chunks
|
||||
.iter()
|
||||
.map(|x| x.value().clone())
|
||||
.collect::<Vec<_>>();
|
||||
for chunk_sync in chunks {
|
||||
for chunk_sync in self.loaded_chunks.iter() {
|
||||
// Try to get a READ lock first.
|
||||
// Most chunks won't have pending ticks, so this is very fast.
|
||||
let chunk = chunk_sync.read().await;
|
||||
@ -595,14 +590,18 @@ impl Level {
|
||||
let cancel_notifier = level.cancel_token.cancelled();
|
||||
|
||||
let fetch_task = async {
|
||||
let mut to_fetch = Vec::new();
|
||||
for pos in &chunks {
|
||||
let to_fetch: Vec<_> = chunks
|
||||
.iter()
|
||||
.filter(|pos| {
|
||||
if let Some(chunk) = level.loaded_entity_chunks.get(pos) {
|
||||
let _ = sender.send((chunk.clone(), false));
|
||||
false // Don't fetch
|
||||
} else {
|
||||
to_fetch.push(*pos);
|
||||
}
|
||||
true // Needs fetch
|
||||
}
|
||||
})
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
if !to_fetch.is_empty() {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<
|
||||
|
||||
@ -53,11 +53,22 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_vec(ticks: &[ScheduledTick<&'a T>]) -> Self {
|
||||
impl<'a, T: std::hash::Hash + Eq + 'static> FromIterator<ScheduledTick<&'a T>>
|
||||
for ChunkTickScheduler<&'a T>
|
||||
{
|
||||
fn from_iter<I: IntoIterator<Item = ScheduledTick<&'a T>>>(iter: I) -> Self {
|
||||
let mut scheduler = Self::default();
|
||||
for tick in ticks {
|
||||
scheduler.schedule_tick(tick, 0);
|
||||
let iter = iter.into_iter();
|
||||
|
||||
let (lower, _) = iter.size_hint();
|
||||
if lower > 0 {
|
||||
scheduler.queued_ticks.reserve(lower);
|
||||
}
|
||||
|
||||
for tick in iter {
|
||||
scheduler.schedule_tick(&tick, 0);
|
||||
}
|
||||
scheduler
|
||||
}
|
||||
|
||||
@ -135,12 +135,6 @@ impl CommandBlock {
|
||||
if command.is_empty() {
|
||||
command_entity.success_count.store(0, Ordering::Release);
|
||||
} else {
|
||||
if command == "Searge" && command_entity.track_output.load(Ordering::Relaxed) {
|
||||
let mut last_output = command_entity.last_output.lock().await;
|
||||
*last_output = "#itzlipofutzli".to_string();
|
||||
return;
|
||||
}
|
||||
|
||||
server
|
||||
.command_dispatcher
|
||||
.read()
|
||||
|
||||
@ -193,32 +193,36 @@ impl FromStr for TargetSelector {
|
||||
|
||||
fn from_str(arg: &str) -> Result<Self, Self::Err> {
|
||||
if arg.starts_with('@') {
|
||||
let body: Vec<_> = arg.splitn(2, '[').collect();
|
||||
let r#type = match body[0] {
|
||||
let (type_str, arguments) = arg
|
||||
.find('[')
|
||||
.map_or((arg, None), |idx| (&arg[..idx], Some(&arg[idx + 1..])));
|
||||
|
||||
let selector_type = match type_str {
|
||||
"@a" => EntitySelectorType::AllPlayers,
|
||||
"@e" => EntitySelectorType::AllEntities,
|
||||
"@s" => EntitySelectorType::Source,
|
||||
"@p" => EntitySelectorType::NearestPlayer,
|
||||
"@r" => EntitySelectorType::RandomPlayer,
|
||||
"@n" => EntitySelectorType::NearestEntity,
|
||||
_ => return Err(format!("Invalid target selector type {}", body[0])),
|
||||
_ => return Err(format!("Invalid target selector type {type_str}")),
|
||||
};
|
||||
let mut selector = Self::new(r#type);
|
||||
if body.len() < 2 {
|
||||
// No conditions specified, return the selector with default conditions
|
||||
return Ok(selector);
|
||||
}
|
||||
// parse conditions
|
||||
if body[1].as_bytes()[body[1].len() - 1] != b']' {
|
||||
return Err("Target selector must end with ]".to_string());
|
||||
}
|
||||
let conditions: Vec<_> = body[1][..body[1].len() - 1]
|
||||
|
||||
let mut selector = Self::new(selector_type);
|
||||
|
||||
if let Some(args_content) = arguments {
|
||||
let trimmed_args = args_content
|
||||
.strip_suffix(']')
|
||||
.ok_or_else(|| "Target selector must end with ]".to_string())?;
|
||||
|
||||
for s in trimmed_args
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.collect();
|
||||
for s in conditions {
|
||||
.filter(|s| !s.is_empty())
|
||||
{
|
||||
selector.conditions.push(EntityFilter::from_str(s)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(selector)
|
||||
} else if let Ok(uuid) = Uuid::parse_str(arg) {
|
||||
Ok(Self::new(EntitySelectorType::Uuid(uuid)))
|
||||
|
||||
@ -121,7 +121,7 @@ impl CommandExecutor for Executor {
|
||||
|
||||
// Check if player can hear the sound based on volume and distance
|
||||
let player_pos = target.living_entity.entity.pos.load();
|
||||
let distance = player_pos.squared_distance_to_vec(pos);
|
||||
let distance = player_pos.squared_distance_to_vec(&pos);
|
||||
let max_distance: f64 = (16.0 * volume).into(); // 16 blocks is base distance at volume 1.0
|
||||
|
||||
if distance <= max_distance || min_volume > 0.0 {
|
||||
|
||||
@ -43,7 +43,7 @@ impl CommandExecutor for ListExecutor {
|
||||
};
|
||||
let mut message = TextComponent::text(message_text);
|
||||
|
||||
for (i, metadata) in plugins.clone().into_iter().enumerate() {
|
||||
for (i, metadata) in plugins.iter().enumerate() {
|
||||
let fmt = if i == plugins.len() - 1 {
|
||||
metadata.name.to_string()
|
||||
} else {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use crate::command::tree::{CommandTree, Node, NodeType};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
|
||||
trait IsVisible {
|
||||
@ -52,65 +51,48 @@ fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec<usize> {
|
||||
|
||||
impl Display for CommandTree {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_char('/')?;
|
||||
f.write_str(&self.names[0])?;
|
||||
write!(f, "/{}", self.names[0])?;
|
||||
|
||||
let mut todo = VecDeque::<&[usize]>::with_capacity(self.children.len());
|
||||
todo.push_back(&self.children);
|
||||
let current_children = &self.children[..];
|
||||
|
||||
loop {
|
||||
let Some(children) = todo.pop_front() else {
|
||||
while !current_children.is_empty() {
|
||||
let flattened = flatten_require_nodes(&self.nodes, current_children);
|
||||
|
||||
let mut visible_iter = flattened
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&i| self.nodes[i].is_visible());
|
||||
|
||||
let Some(first_idx) = visible_iter.next() else {
|
||||
break;
|
||||
};
|
||||
|
||||
let flattened_children = flatten_require_nodes(&self.nodes, children);
|
||||
let visible_children = flattened_children
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&i| self.nodes[i].is_visible())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if visible_children.is_empty() {
|
||||
break;
|
||||
}
|
||||
let second_idx = visible_iter.next();
|
||||
|
||||
f.write_char(' ')?;
|
||||
|
||||
let is_optional = flattened_children
|
||||
let is_optional = flattened
|
||||
.iter()
|
||||
.map(|&i| &self.nodes[i].node_type)
|
||||
.any(|node| matches!(node, NodeType::ExecuteLeaf { .. }));
|
||||
.any(|&i| matches!(self.nodes[i].node_type, NodeType::ExecuteLeaf { .. }));
|
||||
|
||||
if is_optional {
|
||||
f.write_char('[')?;
|
||||
}
|
||||
|
||||
match visible_children.as_slice() {
|
||||
[] => unreachable!(),
|
||||
[i] => {
|
||||
let node = &self.nodes[*i];
|
||||
|
||||
node.fmt(f)?;
|
||||
|
||||
todo.push_back(&node.children);
|
||||
}
|
||||
_ => {
|
||||
// todo: handle cases where one of these nodes has visible children
|
||||
if let Some(second_idx) = second_idx {
|
||||
f.write_char('(')?;
|
||||
self.nodes[first_idx].fmt(f)?;
|
||||
|
||||
let mut iter = visible_children.iter().map(|&i| &self.nodes[i]);
|
||||
write!(f, " | ")?;
|
||||
self.nodes[second_idx].fmt(f)?;
|
||||
|
||||
if let Some(node) = iter.next() {
|
||||
node.fmt(f)?;
|
||||
for idx in visible_iter {
|
||||
write!(f, " | ")?;
|
||||
self.nodes[idx].fmt(f)?;
|
||||
}
|
||||
|
||||
for node in iter {
|
||||
f.write_str(" | ")?;
|
||||
node.fmt(f)?;
|
||||
}
|
||||
|
||||
f.write_char(')')?;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if is_optional {
|
||||
|
||||
@ -36,7 +36,7 @@ impl Goal for CreeperIgniteGoal {
|
||||
.get_entity()
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(target.get_entity().pos.load());
|
||||
.squared_distance_to_vec(&target.get_entity().pos.load());
|
||||
return dist_sq < 9.0;
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ impl Goal for CreeperIgniteGoal {
|
||||
.get_entity()
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(target.get_entity().pos.load());
|
||||
.squared_distance_to_vec(&target.get_entity().pos.load());
|
||||
|
||||
if dist_sq > 49.0 {
|
||||
self.creeper.set_fuse_speed(-1).await;
|
||||
|
||||
@ -119,7 +119,7 @@ impl Goal for LookAtEntityGoal {
|
||||
}
|
||||
let mob_pos = mob_entity.living_entity.entity.pos.load();
|
||||
let target_pos = target.get_entity().pos.load();
|
||||
if mob_pos.squared_distance_to_vec(target_pos) as f32 > (self.range * self.range) {
|
||||
if mob_pos.squared_distance_to_vec(&target_pos) as f32 > (self.range * self.range) {
|
||||
return false;
|
||||
}
|
||||
return self.look_time > 0;
|
||||
|
||||
@ -88,7 +88,7 @@ impl Goal for MeleeAttackGoal {
|
||||
|
||||
if mob
|
||||
.get_mob_entity()
|
||||
.is_in_position_target_range_pos(target.get_entity().block_pos.load())
|
||||
.is_in_position_target_range_pos(&target.get_entity().block_pos.load())
|
||||
{
|
||||
// This is sync based on the assumed Player methods
|
||||
target
|
||||
|
||||
@ -87,7 +87,7 @@ impl<M: MoveToTargetPos> MoveToTargetPosGoal<M> {
|
||||
|
||||
if mob
|
||||
.get_mob_entity()
|
||||
.is_in_position_target_range_pos(block_pos_mut)
|
||||
.is_in_position_target_range_pos(&block_pos_mut)
|
||||
&& can_target
|
||||
{
|
||||
self.target_pos = block_pos_mut;
|
||||
@ -177,7 +177,7 @@ impl<M: MoveToTargetPos> Goal for MoveToTargetPosGoal<M> {
|
||||
};
|
||||
let desired_distance = move_to_target_pos.get_desired_distance_to_target();
|
||||
|
||||
if block_pos.squared_distance_to_vec(mob.get_entity().pos.load())
|
||||
if block_pos.squared_distance_to_vec(&mob.get_entity().pos.load())
|
||||
< desired_distance * desired_distance
|
||||
{
|
||||
self.reached = true;
|
||||
|
||||
@ -49,8 +49,7 @@ impl Navigator {
|
||||
let state = world
|
||||
.get_block_state(&BlockPos(potential_pos.to_i32()))
|
||||
.await;
|
||||
let shapes = state.get_block_collision_shapes();
|
||||
if !shapes.is_empty() {
|
||||
if !state.collision_shapes.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -100,6 +99,6 @@ impl Node {
|
||||
/// Returns an `f64`; higher means more expensive.
|
||||
#[must_use]
|
||||
pub fn get_expense(&self, end: Vector3<f64>) -> f64 {
|
||||
self.location.squared_distance_to_vec(end).sqrt()
|
||||
self.location.squared_distance_to_vec(&end).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,12 +87,9 @@ impl ItemEntity {
|
||||
async fn try_merge(&self) {
|
||||
let bounding_box = self.entity.bounding_box.load().expand(0.5, 0.0, 0.5);
|
||||
|
||||
let items: Vec<_> = self
|
||||
.entity
|
||||
.world
|
||||
.entities
|
||||
.read()
|
||||
.await
|
||||
let entities_guard = self.entity.world.entities.read().await;
|
||||
|
||||
let items = entities_guard
|
||||
.values()
|
||||
.filter_map(|entity: &Arc<dyn EntityBase>| {
|
||||
entity.clone().get_item_entity().filter(|item| {
|
||||
@ -100,8 +97,7 @@ impl ItemEntity {
|
||||
&& !item.never_despawn.load(Ordering::Relaxed)
|
||||
&& item.entity.bounding_box.load().intersects(&bounding_box)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
|
||||
for item in items {
|
||||
if item.can_merge().await {
|
||||
|
||||
@ -322,7 +322,7 @@ impl LivingEntity {
|
||||
f64::from(pos.0.y) + 0.5,
|
||||
f64::from(pos.0.z) + 0.5,
|
||||
);
|
||||
let distance = entity_pos.squared_distance_to_vec(block_center);
|
||||
let distance = entity_pos.squared_distance_to_vec(&block_center);
|
||||
|
||||
return distance.sqrt()
|
||||
<= Self::FALL_DAMAGE_SAFE_DISTANCE * Self::FALL_DAMAGE_SAFE_DISTANCE;
|
||||
|
||||
@ -57,10 +57,10 @@ impl MobEntity {
|
||||
}
|
||||
}
|
||||
pub fn is_in_position_target_range(&self) -> bool {
|
||||
self.is_in_position_target_range_pos(self.living_entity.entity.block_pos.load())
|
||||
self.is_in_position_target_range_pos(&self.living_entity.entity.block_pos.load())
|
||||
}
|
||||
|
||||
pub fn is_in_position_target_range_pos(&self, block_pos: BlockPos) -> bool {
|
||||
pub fn is_in_position_target_range_pos(&self, block_pos: &BlockPos) -> bool {
|
||||
let position_target_range = self.position_target_range.load(Relaxed);
|
||||
if position_target_range == -1 {
|
||||
true
|
||||
|
||||
@ -984,7 +984,7 @@ impl Entity {
|
||||
}
|
||||
|
||||
let mut fluid_velo =
|
||||
self.world.get_fluid_velocity(pos, &fluid, &state).await;
|
||||
self.world.get_fluid_velocity(pos, fluid, state).await;
|
||||
|
||||
if fluid_height[i] < 0.4 {
|
||||
fluid_velo = fluid_velo * fluid_height[i];
|
||||
@ -1006,7 +1006,7 @@ impl Entity {
|
||||
for (_, fluid) in fluids {
|
||||
self.world
|
||||
.block_registry
|
||||
.on_entity_collision_fluid(&fluid, caller.as_ref())
|
||||
.on_entity_collision_fluid(fluid, caller.as_ref())
|
||||
.await;
|
||||
}
|
||||
|
||||
@ -1701,8 +1701,7 @@ impl Entity {
|
||||
let (block, state) = world.get_block_and_state(&pos).await;
|
||||
let block_outlines = state.get_block_outline_shapes();
|
||||
|
||||
if let Some(outlines) = block_outlines {
|
||||
if outlines.is_empty() {
|
||||
if state.outline_shapes.is_empty() {
|
||||
world
|
||||
.block_registry
|
||||
.on_entity_collision(block, world, entity, &pos, state, server)
|
||||
@ -1714,7 +1713,7 @@ impl Entity {
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
for outline in outlines {
|
||||
for outline in block_outlines {
|
||||
let outline_aabb = outline.at_pos(pos);
|
||||
if outline_aabb.intersects(&aabb) {
|
||||
world
|
||||
@ -1729,17 +1728,6 @@ impl Entity {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
world
|
||||
.block_registry
|
||||
.on_entity_collision(block, world, entity, &pos, state, server)
|
||||
.await;
|
||||
let fluid = world.get_fluid(&pos).await;
|
||||
world
|
||||
.block_registry
|
||||
.on_entity_collision_fluid(fluid, entity)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1019,15 +1019,6 @@ impl JavaClient {
|
||||
return Err(ChatError::InvalidPublicKey);
|
||||
}
|
||||
|
||||
// Verify signature with RSA-SHA1
|
||||
let mojang_verifying_keys = server
|
||||
.mojang_public_keys
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.map(|key| VerifyingKey::<Sha1>::new(key.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let key_signature = RsaPkcs1v15Signature::try_from(session.key_signature.as_ref())
|
||||
.map_err(|_| ChatError::InvalidPublicKey)?;
|
||||
|
||||
@ -1036,11 +1027,16 @@ impl JavaClient {
|
||||
signable.extend_from_slice(&session.expires_at.to_be_bytes());
|
||||
signable.extend_from_slice(&session.public_key);
|
||||
|
||||
let public_keys_guard = server.mojang_public_keys.lock().await;
|
||||
|
||||
// Verify signature with RSA-SHA1
|
||||
let is_valid = public_keys_guard.iter().any(|key| {
|
||||
let verifying_key = VerifyingKey::<Sha1>::new(key.clone());
|
||||
verifying_key.verify(&signable, &key_signature).is_ok()
|
||||
});
|
||||
|
||||
// Verify that the signable is valid for any one of Mojang's public keys
|
||||
if !mojang_verifying_keys
|
||||
.iter()
|
||||
.any(|key| key.verify(&signable, &key_signature).is_ok())
|
||||
{
|
||||
if !is_valid {
|
||||
return Err(ChatError::InvalidPublicKey);
|
||||
}
|
||||
|
||||
@ -2063,10 +2059,10 @@ impl JavaClient {
|
||||
.await;
|
||||
|
||||
// Check if there is a player in the way of the block being placed
|
||||
let shapes = BlockState::from_id(new_state).get_block_collision_shapes();
|
||||
let state = BlockState::from_id(new_state);
|
||||
for player in world.get_nearby_players(location.0.to_f64(), 3.0).await {
|
||||
let player_box = player.1.living_entity.entity.bounding_box.load();
|
||||
for shape in &shapes {
|
||||
for shape in state.get_block_collision_shapes() {
|
||||
if shape.at_pos(final_block_pos).intersects(&player_box) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use std::{net::IpAddr, net::SocketAddr};
|
||||
|
||||
use pumpkin_protocol::Property;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -35,30 +34,30 @@ pub async fn bungeecord_login(
|
||||
server_address: &str,
|
||||
name: String,
|
||||
) -> Result<(IpAddr, GameProfile), BungeeCordError> {
|
||||
let data = server_address.split('\0').take(4).collect::<Vec<_>>();
|
||||
let mut parts = server_address.split('\0');
|
||||
|
||||
// The IP address of the player; only given if `ip_forward` on bungee is true.
|
||||
let ip = match data.get(1) {
|
||||
Some(ip) => ip
|
||||
// Skip the first part (the actual server address/host)
|
||||
let _host = parts.next();
|
||||
|
||||
let ip = match parts.next() {
|
||||
Some(ip_str) if !ip_str.is_empty() => ip_str
|
||||
.parse()
|
||||
.map_err(|_| BungeeCordError::FailedParseAddress)?,
|
||||
None => client_address.lock().await.ip(),
|
||||
_ => client_address.lock().await.ip(),
|
||||
};
|
||||
|
||||
// The UUID of the player; only given if `ip_forward` on bungee is true.
|
||||
let id = match data.get(2) {
|
||||
Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?,
|
||||
None => offline_uuid(name.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?,
|
||||
let id = match parts.next() {
|
||||
Some(uuid_str) if !uuid_str.is_empty() => uuid_str
|
||||
.parse()
|
||||
.map_err(|_| BungeeCordError::FailedParseUUID)?,
|
||||
_ => offline_uuid(&name).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?,
|
||||
};
|
||||
|
||||
// Read properties and get textures.
|
||||
// Properties of the player's game profile are only given if `ip_forward` and `online_mode`
|
||||
// on bungee are both `true`.
|
||||
let properties: Vec<Property> = match data.get(3) {
|
||||
Some(properties) => {
|
||||
serde_json::from_str(properties).map_err(|_| BungeeCordError::FailedParseProperties)?
|
||||
let properties = match parts.next() {
|
||||
Some(json_str) if !json_str.is_empty() => {
|
||||
serde_json::from_str(json_str).map_err(|_| BungeeCordError::FailedParseProperties)?
|
||||
}
|
||||
None => vec![],
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
Ok((
|
||||
|
||||
@ -26,8 +26,7 @@ impl KeyStore {
|
||||
let public_key_der = public_key
|
||||
.to_public_key_der()
|
||||
.expect("Failed to encode public key to SPKI DER")
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
.into_vec()
|
||||
.into_boxed_slice();
|
||||
|
||||
log::debug!("Created RSA keys, took {}ms", instant.elapsed().as_millis());
|
||||
|
||||
@ -600,9 +600,7 @@ impl Server {
|
||||
let mut players = Vec::<Arc<Player>>::new();
|
||||
|
||||
for world in self.worlds.read().await.iter() {
|
||||
for player in world.players.read().await.values() {
|
||||
players.push(player.clone());
|
||||
}
|
||||
players.extend(world.players.read().await.values().cloned());
|
||||
}
|
||||
|
||||
players
|
||||
@ -926,8 +924,8 @@ impl Server {
|
||||
};
|
||||
let mut entities = iter.collect::<Vec<_>>();
|
||||
entities.sort_by(|a, b| {
|
||||
let a_distance = a.get_entity().pos.load().squared_distance_to_vec(center);
|
||||
let b_distance = b.get_entity().pos.load().squared_distance_to_vec(center);
|
||||
let a_distance = a.get_entity().pos.load().squared_distance_to_vec(¢er);
|
||||
let b_distance = b.get_entity().pos.load().squared_distance_to_vec(¢er);
|
||||
if target_selector.get_sort() == Some(EntityFilterSort::Nearest) {
|
||||
a_distance
|
||||
.partial_cmp(&b_distance)
|
||||
|
||||
@ -114,10 +114,9 @@ impl CustomBossbars {
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
|
||||
let online_players: Vec<&Arc<Player>> = players
|
||||
let online_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
|
||||
if bossbar.visible {
|
||||
for player in online_players {
|
||||
@ -170,10 +169,9 @@ impl CustomBossbars {
|
||||
}
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
let matching_players: Vec<&Arc<Player>> = players
|
||||
let matching_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
for player in matching_players {
|
||||
player
|
||||
.update_bossbar_health(&bossbar.bossbar_data.uuid, bossbar.bossbar_data.health)
|
||||
@ -206,10 +204,9 @@ impl CustomBossbars {
|
||||
bossbar.visible = new_visibility;
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
let online_players: Vec<&Arc<Player>> = players
|
||||
let online_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
|
||||
for player in online_players {
|
||||
if bossbar.visible {
|
||||
@ -245,10 +242,9 @@ impl CustomBossbars {
|
||||
}
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
let matching_players: Vec<&Arc<Player>> = players
|
||||
let matching_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
for player in matching_players {
|
||||
player
|
||||
.update_bossbar_title(
|
||||
@ -284,10 +280,9 @@ impl CustomBossbars {
|
||||
}
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
let matching_players: Vec<&Arc<Player>> = players
|
||||
let matching_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
for player in matching_players {
|
||||
player
|
||||
.update_bossbar_style(
|
||||
@ -324,10 +319,9 @@ impl CustomBossbars {
|
||||
}
|
||||
|
||||
let players: Vec<Arc<Player>> = server.get_all_players().await;
|
||||
let matching_players: Vec<&Arc<Player>> = players
|
||||
let matching_players = players
|
||||
.iter()
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id))
|
||||
.collect();
|
||||
.filter(|player| bossbar.player.contains(&player.gameprofile.id));
|
||||
for player in matching_players {
|
||||
player
|
||||
.update_bossbar_style(
|
||||
|
||||
@ -799,7 +799,7 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_fluid_collisions(self: &Arc<Self>, bounding_box: BoundingBox) -> Vec<Fluid> {
|
||||
pub async fn get_fluid_collisions(self: &Arc<Self>, bounding_box: BoundingBox) -> Vec<&Fluid> {
|
||||
let mut collisions = Vec::new();
|
||||
|
||||
let min = bounding_box.min_block_pos();
|
||||
@ -854,14 +854,10 @@ impl World {
|
||||
}
|
||||
|
||||
// FlowableFluid.getVelocity()
|
||||
|
||||
pub async fn get_fluid_velocity(
|
||||
&self,
|
||||
|
||||
pos0: BlockPos,
|
||||
|
||||
fluid0: &Fluid,
|
||||
|
||||
state0: &FluidState,
|
||||
) -> Vector3<f64> {
|
||||
let mut velo = Vector3::default();
|
||||
@ -900,9 +896,7 @@ impl World {
|
||||
}
|
||||
|
||||
//let state = fluid.get_state(block_state_id);
|
||||
let state = fluid.states[0].clone();
|
||||
|
||||
amplitude = f64::from(state0.height - state.height);
|
||||
amplitude = f64::from(state0.height - fluid.states[0].height);
|
||||
}
|
||||
|
||||
if amplitude == 0.0 {
|
||||
@ -990,7 +984,7 @@ impl World {
|
||||
}
|
||||
|
||||
let mut inside = false;
|
||||
'shapes: for shape in state.get_block_outline_shapes().unwrap() {
|
||||
'shapes: for shape in state.get_block_outline_shapes() {
|
||||
let outline_shape = shape.at_pos(pos);
|
||||
|
||||
if outline_shape.intersects(bounding_box) {
|
||||
@ -1012,30 +1006,32 @@ impl World {
|
||||
pos: BlockPos,
|
||||
state: &BlockState,
|
||||
use_collision_shape: bool,
|
||||
mut using_collision_shape: F,
|
||||
mut on_collision: F,
|
||||
) -> bool
|
||||
where
|
||||
F: FnMut(&BoundingBox),
|
||||
{
|
||||
if state.is_air() || !state.is_solid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut shapes = state
|
||||
.get_block_collision_shapes()
|
||||
.map(|shape| shape.at_pos(pos));
|
||||
|
||||
if use_collision_shape {
|
||||
let mut collided = false;
|
||||
|
||||
if !state.is_air() && state.is_solid() && !state.collision_shapes.is_empty() {
|
||||
for shape in state.get_block_collision_shapes() {
|
||||
let collision_shape = shape.at_pos(pos);
|
||||
|
||||
for collision_shape in shapes {
|
||||
if collision_shape.intersects(bounding_box) {
|
||||
collided = true;
|
||||
|
||||
if !use_collision_shape {
|
||||
break;
|
||||
}
|
||||
|
||||
using_collision_shape(&collision_shape.to_bounding_box());
|
||||
// Convert to BB and trigger the callback
|
||||
on_collision(&collision_shape.to_bounding_box());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collided
|
||||
} else {
|
||||
shapes.any(|s| s.intersects(bounding_box))
|
||||
}
|
||||
}
|
||||
|
||||
// For adjusting movement
|
||||
@ -1847,7 +1843,7 @@ impl World {
|
||||
};
|
||||
let sound = IdOr::<SoundEvent>::Id(Sound::EntityGenericExplode as u16);
|
||||
for player in self.players.read().await.values() {
|
||||
if player.position().squared_distance_to_vec(position) > 4096.0 {
|
||||
if player.position().squared_distance_to_vec(&position) > 4096.0 {
|
||||
continue;
|
||||
}
|
||||
player
|
||||
@ -2214,7 +2210,7 @@ impl World {
|
||||
.iter()
|
||||
.filter_map(|(id, player)| {
|
||||
let player_pos = player.living_entity.entity.pos.load();
|
||||
(player_pos.squared_distance_to_vec(pos) <= radius_squared)
|
||||
(player_pos.squared_distance_to_vec(&pos) <= radius_squared)
|
||||
.then(|| (*id, player.clone()))
|
||||
})
|
||||
.collect()
|
||||
@ -2233,7 +2229,7 @@ impl World {
|
||||
.iter()
|
||||
.filter_map(|(id, entity)| {
|
||||
let entity_pos = entity.get_entity().pos.load();
|
||||
(entity_pos.squared_distance_to_vec(pos) <= radius_squared)
|
||||
(entity_pos.squared_distance_to_vec(&pos) <= radius_squared)
|
||||
.then(|| (*id, entity.clone()))
|
||||
})
|
||||
.collect()
|
||||
@ -2248,13 +2244,13 @@ impl World {
|
||||
.entity
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(pos)
|
||||
.squared_distance_to_vec(&pos)
|
||||
.partial_cmp(
|
||||
&b.1.living_entity
|
||||
.entity
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(pos),
|
||||
.squared_distance_to_vec(&pos),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
@ -2301,8 +2297,8 @@ impl World {
|
||||
a.1.get_entity()
|
||||
.pos
|
||||
.load()
|
||||
.squared_distance_to_vec(pos)
|
||||
.partial_cmp(&b.1.get_entity().pos.load().squared_distance_to_vec(pos))
|
||||
.squared_distance_to_vec(&pos)
|
||||
.partial_cmp(&b.1.get_entity().pos.load().squared_distance_to_vec(&pos))
|
||||
.unwrap()
|
||||
})
|
||||
.map(|p| p.1.clone())
|
||||
@ -3000,7 +2996,10 @@ impl World {
|
||||
(block, fluid)
|
||||
}
|
||||
|
||||
pub async fn get_fluid_and_fluid_state(&self, position: &BlockPos) -> (Fluid, FluidState) {
|
||||
pub async fn get_fluid_and_fluid_state(
|
||||
&self,
|
||||
position: &BlockPos,
|
||||
) -> (&'static Fluid, &'static FluidState) {
|
||||
let id = self.get_block_state_id(position).await;
|
||||
|
||||
let Some(fluid) = Fluid::from_state_id(id) else {
|
||||
@ -3009,9 +3008,8 @@ impl World {
|
||||
for (name, value) in properties.to_props() {
|
||||
if name == "waterlogged" {
|
||||
if value == "true" {
|
||||
let fluid = Fluid::FLOWING_WATER;
|
||||
let state = fluid.states[0].clone();
|
||||
return (fluid, state);
|
||||
let state = &Fluid::FLOWING_WATER.states[0];
|
||||
return (&Fluid::FLOWING_WATER, state);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -3019,16 +3017,14 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
let fluid = Fluid::EMPTY;
|
||||
let state = fluid.states[0].clone();
|
||||
|
||||
return (fluid, state);
|
||||
let state = &Fluid::EMPTY.states[0];
|
||||
return (&Fluid::EMPTY, state);
|
||||
};
|
||||
|
||||
//let state = fluid.get_state(id);
|
||||
let state = fluid.states[0].clone();
|
||||
let state = &fluid.states[0];
|
||||
|
||||
(fluid.clone(), state)
|
||||
(fluid, state)
|
||||
}
|
||||
|
||||
pub async fn get_block_state_id(&self, position: &BlockPos) -> BlockStateId {
|
||||
@ -3277,15 +3273,13 @@ impl World {
|
||||
) -> (bool, Option<BlockDirection>) {
|
||||
let state = self.get_block_state(block_pos).await;
|
||||
|
||||
let Some(bounding_boxes) = state.get_block_outline_shapes() else {
|
||||
return (false, None);
|
||||
};
|
||||
let bounding_boxes = state.get_block_outline_shapes();
|
||||
|
||||
if bounding_boxes.is_empty() {
|
||||
if state.outline_shapes.is_empty() {
|
||||
return (true, None);
|
||||
}
|
||||
|
||||
for shape in &bounding_boxes {
|
||||
for shape in bounding_boxes {
|
||||
let world_min = shape.min.add(&block_pos.0.to_f64());
|
||||
let world_max = shape.max.add(&block_pos.0.to_f64());
|
||||
|
||||
|
||||
@ -474,7 +474,7 @@ pub fn get_nearest_player(pos: &Vector3<f64>, player_positions: &[Vector3<f64>])
|
||||
let mut min_dst_sq = f64::MAX;
|
||||
|
||||
for player_pos in player_positions {
|
||||
let cur_dst_sq = player_pos.squared_distance_to_vec(*pos);
|
||||
let cur_dst_sq = player_pos.squared_distance_to_vec(pos);
|
||||
if cur_dst_sq < min_dst_sq {
|
||||
min_dst_sq = cur_dst_sq;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user