diff --git a/Cargo.lock b/Cargo.lock index 896e1a552..1bb1a5ec5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,9 +263,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cast" @@ -275,9 +275,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.54" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ "find-msvc-tools", "shlex", @@ -932,9 +932,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" @@ -3269,9 +3269,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.1.4" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" dependencies = [ "base64 0.22.1", "cookie_store", @@ -3898,6 +3898,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" diff --git a/Cargo.toml b/Cargo.toml index c2958c865..2036b4249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,4 +167,4 @@ time = "0.3" arc-swap = "1.8" tokio-util = "0.7.18" toml = "0.9" -ureq = "3.1.4" +ureq = "3.2.0" diff --git a/Dockerfile b/Dockerfile index 9e519d1e1..68f81fba3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1-alpine3.22 AS builder +FROM rust:1-alpine3.23 AS builder ENV RUSTFLAGS="-C target-feature=-crt-static" RUN apk add --no-cache musl-dev \ # Required for git-version diff --git a/pumpkin-data/build/fluid.rs b/pumpkin-data/build/fluid.rs index 698ab18dd..0afb9ed99 100644 --- a/pumpkin-data/build/fluid.rs +++ b/pumpkin-data/build/fluid.rs @@ -777,7 +777,7 @@ pub(crate) fn build() -> TokenStream { impl FromResourceLocation for &'static Fluid { fn from_resource_location(resource_location: &ResourceLocation) -> Option { - Fluid::from_registry_key(resource_location.strip_prefix(resource_location).unwrap_or(resource_location)) + Fluid::from_registry_key(resource_location.strip_prefix("minecraft:").unwrap_or(resource_location)) } } diff --git a/pumpkin-data/build/packet.rs b/pumpkin-data/build/packet.rs index ecf240246..9fa49d854 100644 --- a/pumpkin-data/build/packet.rs +++ b/pumpkin-data/build/packet.rs @@ -11,7 +11,6 @@ pub struct Packets { } pub(crate) fn build() -> TokenStream { - // 1. Track the JSON assets for rebuilds println!("cargo:rerun-if-changed=../assets/packet/1_21_7_packets.json"); println!("cargo:rerun-if-changed=../assets/packet/1_21_11_packets.json"); diff --git a/pumpkin-data/build/tracked_data.rs b/pumpkin-data/build/tracked_data.rs index 058e16d9a..f9d08f78c 100644 --- a/pumpkin-data/build/tracked_data.rs +++ b/pumpkin-data/build/tracked_data.rs @@ -3,7 +3,6 @@ use quote::{format_ident, quote}; use std::{collections::BTreeMap, fs}; pub(crate) fn build() -> TokenStream { - // 1. Track files println!("cargo:rerun-if-changed=../assets/tracked_data/1_21_7_tracked_data.json"); println!("cargo:rerun-if-changed=../assets/tracked_data/1_21_11_tracked_data.json"); diff --git a/pumpkin-data/src/blocks.rs b/pumpkin-data/src/blocks.rs index 8e0a6d708..29309ac23 100644 --- a/pumpkin-data/src/blocks.rs +++ b/pumpkin-data/src/blocks.rs @@ -99,7 +99,7 @@ impl FromResourceLocation for &'static Block { fn from_resource_location(resource_location: &ResourceLocation) -> Option { Block::from_registry_key( resource_location - .strip_prefix(resource_location) + .strip_prefix("minecraft:") .unwrap_or(resource_location), ) } diff --git a/pumpkin-protocol/src/bedrock/client/level_chunk.rs b/pumpkin-protocol/src/bedrock/client/level_chunk.rs index ccd5c1fd5..d7d36900e 100644 --- a/pumpkin-protocol/src/bedrock/client/level_chunk.rs +++ b/pumpkin-protocol/src/bedrock/client/level_chunk.rs @@ -26,32 +26,25 @@ impl PacketWrite for CLevelChunk<'_> { VarInt(self.chunk.z).write(writer)?; VarInt(self.dimension).write(writer)?; - let sub_chunk_count = self.chunk.section.sections.lock().unwrap().len() as u32; - assert_eq!(sub_chunk_count, 24); + let sub_chunk_count = self.chunk.section.count as u32; + debug_assert_eq!(sub_chunk_count, 24); VarUInt(sub_chunk_count).write(writer)?; self.cache_enabled.write(writer)?; let mut chunk_data = Vec::new(); let data_write = &mut chunk_data; - let min_y = (self.chunk.section.min_y >> 4) as i8; + let block_sections = self.chunk.section.block_sections.read().unwrap(); + let min_y_section = (self.chunk.section.min_y >> 4) as i8; - // Blocks - for (i, sub_chunk) in self - .chunk - .section - .sections - .lock() - .unwrap() - .iter() - .enumerate() - { - // Version 9 - // [version:byte][num_storages:byte][sub_chunk_index:byte][block storage1]...[blockStorageN] - let y = i as i8 + min_y; + for (i, block_palette) in block_sections.iter().enumerate() { + // Version 9: [version:byte][num_storages:byte][sub_chunk_index:byte] + let y = (i as i8) + min_y_section; let num_storages = 1; - data_write.write_all(&[VERSION, num_storages, y as _])?; - let network_repr = sub_chunk.block_states.convert_be_network(); + data_write.write_all(&[VERSION, num_storages, y as u8])?; + + let network_repr = block_palette.convert_be_network(); + (network_repr.bits_per_entry << 1 | 1).write(data_write)?; for data in network_repr.packed_data { @@ -72,14 +65,14 @@ impl PacketWrite for CLevelChunk<'_> { } } - // Biomes - for i in 0..sub_chunk_count { + for i in 0..self.chunk.section.count { let num_storages = 1; - let y = i as i8 + min_y; - data_write.write_all(&[VERSION, num_storages, y as _])?; + let y = (i as i8) + min_y_section; + data_write.write_all(&[VERSION, num_storages, y as u8])?; for _ in 0..num_storages { 1u8.write(data_write)?; + // TODO VarInt(0).write(data_write)?; } } diff --git a/pumpkin-protocol/src/java/client/play/chunk_data.rs b/pumpkin-protocol/src/java/client/play/chunk_data.rs index 6cdecd767..2baec1655 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_data.rs @@ -50,15 +50,17 @@ impl ClientPacket for CChunkData<'_> { { let mut blocks_and_biomes_buf = Vec::new(); - for section in self.0.section.sections.lock().unwrap().iter() { - // Block count - let non_empty_block_count = section.block_states.non_air_block_count() as i16; + let block_sections = self.0.section.block_sections.read().unwrap(); + let biome_sections = self.0.section.biome_sections.read().unwrap(); + + for (block_palette, biome_palette) in block_sections.iter().zip(biome_sections.iter()) { + let non_empty_block_count = block_palette.non_air_block_count() as i16; blocks_and_biomes_buf.write_i16_be(non_empty_block_count)?; - // This is a bit messy, but we dont have access to VarInt in pumpkin-world - let network_repr = section.block_states.convert_network(); - blocks_and_biomes_buf.write_u8(network_repr.bits_per_entry)?; - match network_repr.palette { + let block_network = block_palette.convert_network(); + blocks_and_biomes_buf.write_u8(block_network.bits_per_entry)?; + + match block_network.palette { NetworkPalette::Single(registry_id) => { blocks_and_biomes_buf.write_var_int(®istry_id.into())?; } @@ -78,13 +80,14 @@ impl ClientPacket for CChunkData<'_> { NetworkPalette::Direct => {} } - for packed in network_repr.packed_data { + for packed in block_network.packed_data { blocks_and_biomes_buf.write_i64_be(packed)?; } - let network_repr = section.biomes.convert_network(); - blocks_and_biomes_buf.write_u8(network_repr.bits_per_entry)?; - match network_repr.palette { + let biome_network = biome_palette.convert_network(); + blocks_and_biomes_buf.write_u8(biome_network.bits_per_entry)?; + + match biome_network.palette { NetworkPalette::Single(registry_id) => { blocks_and_biomes_buf.write_var_int(®istry_id.into())?; } @@ -104,12 +107,11 @@ impl ClientPacket for CChunkData<'_> { NetworkPalette::Direct => {} } - // NOTE: Not updated in wiki; i64 array length is now determined by the bits per entry - //data_buf.write_var_int(&network_repr.packed_data.len().into())?; - for packed in network_repr.packed_data { + for packed in biome_network.packed_data { blocks_and_biomes_buf.write_i64_be(packed)?; } } + write.write_var_int(&blocks_and_biomes_buf.len().try_into().map_err(|_| { WritingError::Message(format!( "{} is not representable as a VarInt!", diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index 7ea2994f1..fb8d31a93 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -2,7 +2,10 @@ use std::{ io::Cursor, path::PathBuf, pin::Pin, - sync::atomic::{AtomicBool, Ordering}, + sync::{ + RwLock, + atomic::{AtomicBool, Ordering}, + }, }; use bytes::Bytes; @@ -28,7 +31,7 @@ use pumpkin_util::math::vector2::Vector2; use serde::{Deserialize, Serialize}; use super::{ - ChunkData, ChunkHeightmaps, ChunkLight, ChunkParsingError, ChunkSections, SubChunk, + ChunkData, ChunkHeightmaps, ChunkLight, ChunkParsingError, ChunkSections, palette::{BiomePalette, BlockPalette}, }; use crate::block::BlockStateCodec; @@ -123,51 +126,57 @@ impl ChunkData { position.x, position.y, chunk_data.x_pos, chunk_data.z_pos, ))); } - let (block_lights, sky_lights, sub_chunks) = chunk_data + let (block_lights, sky_lights, block_palettes, biome_palettes) = chunk_data .sections .into_iter() .map(|section| { + // Map light data to the LightContainer enum let block_light = section .block_light - .map(LightContainer::new) - .unwrap_or_default(); + .map(LightContainer::Full) + .unwrap_or(LightContainer::Empty(0)); // Standard default let sky_light = section .sky_light - .map(LightContainer::new) + .map(LightContainer::Full) + .unwrap_or(LightContainer::Empty(15)); // Sky is usually bright + + // Convert NBT to Palettes + let block_palette = section + .block_states + .map(BlockPalette::from_disk_nbt) + .unwrap_or_default(); + let biome_palette = section + .biomes + .map(BiomePalette::from_disk_nbt) .unwrap_or_default(); - let sub_chunk = SubChunk { - block_states: section - .block_states - .map(BlockPalette::from_disk_nbt) - .unwrap_or_default(), - biomes: section - .biomes - .map(BiomePalette::from_disk_nbt) - .unwrap_or_default(), - }; - - (block_light, sky_light, sub_chunk) + (block_light, sky_light, block_palette, biome_palette) }) .fold( - (Vec::new(), Vec::new(), Vec::new()), - |(mut bl, mut sl, mut sc), (block_l, sky_l, sub_c)| { - bl.push(block_l); - sl.push(sky_l); - sc.push(sub_c); - (bl, sl, sc) + (Vec::new(), Vec::new(), Vec::new(), Vec::new()), + |(mut bl, mut sl, mut bp, mut bip), (b_l, s_l, b_p, bi_p)| { + bl.push(b_l); + sl.push(s_l); + bp.push(b_p); + bip.push(bi_p); + (bl, sl, bp, bip) }, ); - // 2. Assemble the final structs using the collected vectors. + // 2. Assemble the LightEngine let light_engine = ChunkLight { block_light: block_lights.into_boxed_slice(), sky_light: sky_lights.into_boxed_slice(), }; - let min_y = section_coords::section_to_block(chunk_data.min_y_section); - let section = - ChunkSections::new(std::sync::Mutex::new(sub_chunks.into_boxed_slice()), min_y); + // 3. Assemble the ChunkSections using your specific struct fields + let min_y = section_coords::section_to_block(chunk_data.min_y_section); + let section = ChunkSections { + count: block_palettes.len(), + block_sections: RwLock::new(block_palettes.into_boxed_slice()), + biome_sections: RwLock::new(biome_palettes.into_boxed_slice()), + min_y, + }; Ok(Self { section, heightmap: std::sync::Mutex::new(chunk_data.heightmaps), @@ -193,42 +202,38 @@ impl ChunkData { } async fn internal_to_bytes(&self) -> Result { - // 1. Process sections (Standard Mutex) - // We do this in a block or just let the guard drop naturally - // before the first .await - let sections: Vec<_> = { - let sections_guard = self.section.sections.lock().unwrap(); - sections_guard - .iter() - .enumerate() - .map(|(i, section)| ChunkSectionNBT { - y: (i as i8) + section_coords::block_to_section(self.section.min_y) as i8, - block_states: Some(section.block_states.to_disk_nbt()), - biomes: Some(section.biomes.to_disk_nbt()), - block_light: match self.light_engine.block_light[i].clone() { - LightContainer::Empty(_) => None, - LightContainer::Full(data) => Some(data), - }, - sky_light: match self.light_engine.sky_light[i].clone() { - LightContainer::Empty(_) => None, - LightContainer::Full(data) => Some(data), - }, + let sections: Vec = { + let block_lock = self.section.block_sections.read().unwrap(); + let biome_lock = self.section.biome_sections.read().unwrap(); + let min_section_y = (self.section.min_y >> 4) as i8; + + (0..self.section.count) + .map(|i| { + ChunkSectionNBT { + y: i as i8 + min_section_y, + // Convert the palettes to their NBT disk representation + block_states: Some(block_lock[i].to_disk_nbt()), + biomes: Some(biome_lock[i].to_disk_nbt()), + block_light: match self.light_engine.block_light.get(i) { + Some(LightContainer::Full(data)) => Some(data.clone()), + _ => None, + }, + sky_light: match self.light_engine.sky_light.get(i) { + Some(LightContainer::Full(data)) => Some(data.clone()), + _ => None, + }, + } }) .collect() }; - // 2. Clone heightmaps (Standard Mutex) let heightmaps = self.heightmap.lock().unwrap().clone(); - // 3. Prepare block entities (CRITICAL: Must drop lock before .await) let entities_to_serialize = { - // We clone the Arcs out of the map while holding the lock let entities_guard = self.block_entities.lock().unwrap(); entities_guard.values().cloned().collect::>() }; - // entities_guard drops here automatically! - // 4. Now we can safely .await because no MutexGuards are held let block_entities_nbt = join_all(entities_to_serialize.into_iter().map( |block_entity| async move { let mut nbt = NbtCompound::new(); diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 46e63e75d..8f3e21c35 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -15,8 +15,8 @@ use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::fmt::Write; use std::ops::{BitAnd, BitOr}; -use std::sync::Arc; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, RwLock}; use thiserror::Error; use tokio::sync::Mutex; @@ -102,7 +102,9 @@ pub struct ChunkEntityData { /// A chunk can be: /// - Subchunks: 24 separate subchunks are stored. pub struct ChunkSections { - pub sections: std::sync::Mutex>, + pub count: usize, + pub block_sections: RwLock>, + pub biome_sections: RwLock>, pub min_y: i32, } @@ -112,8 +114,8 @@ impl ChunkSections { pub fn dump_blocks(&self) -> Vec { // TODO: this is not optimal, we could use rust iters let mut dump = Vec::new(); - for section in self.sections.lock().unwrap().iter() { - section.block_states.for_each(|raw_id| { + for section in self.block_sections.read().unwrap().iter() { + section.for_each(|raw_id| { dump.push(raw_id); }); } @@ -125,8 +127,8 @@ impl ChunkSections { pub fn dump_biomes(&self) -> Vec { // TODO: this is not optimal, we could use rust iters let mut dump = Vec::new(); - for section in self.sections.lock().unwrap().iter() { - section.biomes.for_each(|raw_id| { + for section in self.biome_sections.read().unwrap().iter() { + section.for_each(|raw_id| { dump.push(raw_id); }); } @@ -134,12 +136,6 @@ impl ChunkSections { } } -#[derive(Default, Clone)] -pub struct SubChunk { - pub block_states: BlockPalette, - pub biomes: BiomePalette, -} - #[derive(Default, Clone)] pub struct ChunkLight { pub sky_light: Box<[LightContainer]>, @@ -292,8 +288,16 @@ impl Default for ChunkHeightmaps { impl ChunkSections { #[must_use] - pub const fn new(sections: std::sync::Mutex>, min_y: i32) -> Self { - Self { sections, min_y } + pub fn new(num_sections: usize, min_y: i32) -> Self { + let block_sections = vec![BlockPalette::default(); num_sections].into_boxed_slice(); + let biome_sections = vec![BiomePalette::default(); num_sections].into_boxed_slice(); + + Self { + count: num_sections, + block_sections: RwLock::new(block_sections), + biome_sections: RwLock::new(biome_sections), + min_y, + } } #[must_use] @@ -362,11 +366,11 @@ impl ChunkSections { let section_index = relative_y / BlockPalette::SIZE; let relative_y = relative_y % BlockPalette::SIZE; - self.sections - .lock() + self.block_sections + .read() .unwrap() .get(section_index) - .map(|section| section.block_states.get(relative_x, relative_y, relative_z)) + .map(|section| section.get(relative_x, relative_y, relative_z)) } /// Sets the given block in the chunk, returning the old block state ID @@ -399,10 +403,8 @@ impl ChunkSections { let section_index = relative_y / BlockPalette::SIZE; let relative_y = relative_y % BlockPalette::SIZE; - if let Some(section) = self.sections.lock().unwrap().get_mut(section_index) { - return section - .block_states - .set(relative_x, relative_y, relative_z, block_state_id); + if let Some(section) = self.block_sections.write().unwrap().get_mut(section_index) { + return section.set(relative_x, relative_y, relative_z, block_state_id); } 0 } @@ -419,10 +421,8 @@ impl ChunkSections { let section_index = relative_y / BiomePalette::SIZE; let relative_y = relative_y % BiomePalette::SIZE; - if let Some(section) = self.sections.lock().unwrap().get_mut(section_index) { - section - .biomes - .set(relative_x, relative_y, relative_z, biome_id); + if let Some(section) = self.biome_sections.write().unwrap().get_mut(section_index) { + section.set(relative_x, relative_y, relative_z, biome_id); } } @@ -436,11 +436,11 @@ impl ChunkSections { ) -> Option { debug_assert!(scale_x < BiomePalette::SIZE); debug_assert!(scale_z < BiomePalette::SIZE); - self.sections - .lock() + self.biome_sections + .read() .unwrap() .get(index) - .map(|section| section.biomes.get(scale_x, scale_y, scale_z)) + .map(|section| section.get(scale_x, scale_y, scale_z)) } #[must_use] @@ -582,13 +582,15 @@ impl ChunkData { #[must_use] pub fn get_highest_non_empty_subchunk(&self) -> usize { - let sections = self.section.sections.lock().unwrap(); - sections + self.section + .block_sections + .read() + .unwrap() .iter() .enumerate() .rev() - .position(|(_, sub)| !sub.block_states.has_only_air()) - .map_or(0, |p| sections.len() - 1 - p) + .find(|(_, sub)| !sub.has_only_air()) + .map_or(0, |(idx, _)| idx) } } diff --git a/pumpkin-world/src/chunk_system.rs b/pumpkin-world/src/chunk_system.rs index 3bbb03c9c..ec05f498a 100644 --- a/pumpkin-world/src/chunk_system.rs +++ b/pumpkin-world/src/chunk_system.rs @@ -8,7 +8,7 @@ TODO use crate::block::RawBlockState; use crate::chunk::io::LoadedData::Loaded; -use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections, SubChunk}; +use crate::chunk::{ChunkData, ChunkHeightmapType, ChunkLight, ChunkSections}; use crate::generation::biome_coords; use pumpkin_data::block_properties::is_air; use pumpkin_data::chunk_gen_settings::GenerationSettings; @@ -787,10 +787,7 @@ impl Chunk { let proto_chunk = self.get_proto_chunk(); let total_sections = dimension.height as usize / 16; - let sections = ChunkSections::new( - Mutex::new(vec![SubChunk::default(); total_sections].into_boxed_slice()), - dimension.min_y, - ); + let sections = ChunkSections::new(total_sections, dimension.min_y); let proto_biome_height = biome_coords::from_block(proto_chunk.height()); let biome_min_y = biome_coords::from_block(dimension.min_y); @@ -799,13 +796,18 @@ impl Chunk { let section_index = y_offset as usize / 4; let relative_y = y_offset as usize % 4; - if let Some(section) = sections.sections.lock().unwrap().get_mut(section_index) { + if let Some(section) = sections + .biome_sections + .write() + .unwrap() + .get_mut(section_index) + { let absolute_biome_y = biome_min_y + y_offset as i32; for z in 0..4 { for x in 0..4 { let biome = proto_chunk.get_biome_id(x as i32, absolute_biome_y, z as i32); - section.biomes.set(x, relative_y, z, biome); + section.set(x, relative_y, z, biome); } } } @@ -817,18 +819,23 @@ impl Chunk { let section_index = (y_offset as usize) / 16; let relative_y = (y_offset as usize) % 16; - if let Some(section) = sections.sections.lock().unwrap().get_mut(section_index) { + if let Some(section) = sections + .block_sections + .write() + .unwrap() + .get_mut(section_index) + { for z in 0..16 { for x in 0..16 { let block = proto_chunk.get_block_state_raw(x as i32, y_offset as i32, z as i32); - section.block_states.set(x, relative_y, z, block); + section.set(x, relative_y, z, block); } } } } - let len = sections.sections.lock().unwrap().len(); + let len = sections.count; let mut chunk = ChunkData { light_engine: ChunkLight { sky_light: (0..len) diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index f6d0c9545..1990bd63c 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -229,18 +229,20 @@ impl ProtoChunk { let section_data = &chunk_data.section; let heightmap_data = chunk_data.heightmap.lock().unwrap(); - for (section_y, section) in section_data.sections.lock().unwrap().iter().enumerate() { - let section_base_y = section_y as i32 * 16; + let block_sections_guard = section_data.block_sections.read().unwrap(); + let biome_sections_guard = section_data.biome_sections.read().unwrap(); + + for (section_idx, block_palette) in block_sections_guard.iter().enumerate() { + let section_base_y = section_idx as i32 * 16; if section_base_y >= proto_chunk.height() as i32 { continue; } - // --- Block States --- for x in 0..16 { for y in 0..16 { for z in 0..16 { - let block_state_id = section.block_states.get(x, y, z); + let block_state_id = block_palette.get(x, y, z); let block_state = BlockState::from_id(block_state_id); let absolute_y = section_base_y + y as i32 + section_data.min_y; @@ -249,26 +251,29 @@ impl ProtoChunk { } } - // --- Biomes --- - for x in 0..4 { - for y in 0..4 { - for z in 0..4 { - let biome_id = section.biomes.get(x, y, z); - let relative_y_block = section_base_y + (y as i32 * 4); - let biome_y_idx = biome_coords::from_block(relative_y_block); - let index = proto_chunk.local_biome_pos_to_biome_index( - x as i32, - biome_y_idx, - z as i32, - ); + if let Some(biome_palette) = biome_sections_guard.get(section_idx) { + for x in 0..4 { + for y in 0..4 { + for z in 0..4 { + let biome_id = biome_palette.get(x, y, z); - proto_chunk.flat_biome_map[index] = biome_id; + let biome_y_idx = (section_idx * 4) + y; + + let index = proto_chunk.local_biome_pos_to_biome_index( + x as i32, + biome_y_idx as i32, + z as i32, + ); + + proto_chunk.flat_biome_map[index] = biome_id; + } } } } } + drop(block_sections_guard); + drop(biome_sections_guard); - // --- Heightmaps --- for z in 0..16 { for x in 0..16 { let index = ((z << 4) + x) as usize; diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index d0e12d213..a3bb95413 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -441,7 +441,7 @@ impl Level { for chunk in self.loaded_chunks.iter() { let chunk_x_base = chunk.x * 16; let chunk_z_base = chunk.z * 16; - let section_count = chunk.section.sections.lock().unwrap().len(); + let section_count = chunk.section.count; ticks .block_entities diff --git a/pumpkin/src/block/blocks/carved_pumpkin.rs b/pumpkin/src/block/blocks/carved_pumpkin.rs index f93bd9dae..a8494bec0 100644 --- a/pumpkin/src/block/blocks/carved_pumpkin.rs +++ b/pumpkin/src/block/blocks/carved_pumpkin.rs @@ -65,7 +65,7 @@ impl BlockBehaviour for CarvedPumpkinBlock { down_pos.down().to_centered_f64(), &EntityType::SNOW_GOLEM, ); - let golem = SnowGolemEntity::make(entity).await; + let golem = SnowGolemEntity::new(entity).await; args.world.spawn_entity(golem).await; return; } @@ -103,7 +103,7 @@ impl BlockBehaviour for CarvedPumpkinBlock { down_pos.down().to_centered_f64(), &EntityType::IRON_GOLEM, ); - let golem = IronGolemEntity::make(entity).await; + let golem = IronGolemEntity::new(entity).await; args.world.spawn_entity(golem).await; return; } diff --git a/pumpkin/src/block/blocks/infested.rs b/pumpkin/src/block/blocks/infested.rs new file mode 100644 index 000000000..2269db3ef --- /dev/null +++ b/pumpkin/src/block/blocks/infested.rs @@ -0,0 +1,30 @@ +use std::sync::Arc; + +use pumpkin_data::entity::EntityType; +use pumpkin_macros::pumpkin_block_from_tag; +use pumpkin_util::GameMode; + +use crate::block::BrokenArgs; +use crate::block::{BlockBehaviour, BlockFuture}; +use crate::entity::Entity; + +#[pumpkin_block_from_tag("c:cobblestones/infested")] +pub struct InfestedBlock; + +impl BlockBehaviour for InfestedBlock { + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async { + // TODO: ugly fix, use onStacksDropped + if args.player.gamemode.load() == GameMode::Creative { + return; + } + let entity = Entity::new( + args.world.clone(), + args.position.0.to_f64(), + &EntityType::SILVERFISH, + ); + + args.world.spawn_entity(Arc::new(entity)).await; + }) + } +} diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 858f69d50..5bdcb483e 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -30,6 +30,7 @@ pub mod glass_panes; pub mod glazed_terracotta; pub mod grindstone; pub mod hay; +pub mod infested; pub mod iron_bars; pub mod jukebox; pub mod ladder; diff --git a/pumpkin/src/block/blocks/wither_skull.rs b/pumpkin/src/block/blocks/wither_skull.rs index a29139d7f..450c00d7a 100644 --- a/pumpkin/src/block/blocks/wither_skull.rs +++ b/pumpkin/src/block/blocks/wither_skull.rs @@ -88,7 +88,7 @@ impl BlockBehaviour for WitherSkeletonSkullBlock { top_middle.to_centered_f64(), &EntityType::WITHER, ); - let wither = WitherEntity::make(entity).await; + let wither = WitherEntity::new(entity).await; world.spawn_entity(wither).await; return; } diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index ad65e475f..74dc79ae1 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -22,6 +22,7 @@ use crate::block::blocks::furnace::FurnaceBlock; use crate::block::blocks::glass_panes::GlassPaneBlock; use crate::block::blocks::grindstone::GrindstoneBlock; use crate::block::blocks::hay::HayBlock; +use crate::block::blocks::infested::InfestedBlock; use crate::block::blocks::iron_bars::IronBarsBlock; use crate::block::blocks::logs::LogBlock; use crate::block::blocks::mangrove_roots::MangroveRootsBlock; @@ -179,6 +180,7 @@ pub fn default_registry() -> Arc { manager.register(HayBlock); manager.register(GrindstoneBlock); manager.register(IronBarsBlock); + manager.register(InfestedBlock); manager.register(JukeboxBlock); manager.register(LogBlock); manager.register(BambooBlock); diff --git a/pumpkin/src/command/args/bounded_num.rs b/pumpkin/src/command/args/bounded_num.rs index fceafa33b..169ce919c 100644 --- a/pumpkin/src/command/args/bounded_num.rs +++ b/pumpkin/src/command/args/bounded_num.rs @@ -31,10 +31,8 @@ where _server: &'a Server, args: &'b mut RawArgs<'a>, ) -> ConsumeResult<'a> { - // 1. Perform the synchronous mutable operation (pop) outside the Future. let s_opt: Option<&'a str> = args.pop(); - // 2. Process the entire argument and check bounds synchronously. let result: Option> = s_opt // Replace args.pop()?.parse::().ok()? .and_then(|s| s.parse::().ok()) @@ -57,7 +55,6 @@ where Arg::Num(Ok(x.to_number())) }); - // 3. Return a Future that immediately resolves to the calculated result. Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/commands/pumpkin.rs b/pumpkin/src/command/commands/pumpkin.rs index ea717d0c5..8965ef5ac 100644 --- a/pumpkin/src/command/commands/pumpkin.rs +++ b/pumpkin/src/command/commands/pumpkin.rs @@ -83,10 +83,16 @@ impl CommandExecutor for Executor { .collect::>() .join(", "); let locale = sender.get_locale(); + let profile = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; let version_string = format!( - "{} (Commit: {}) - {} Contributors", + "{} (Commit: {}/{}) - {} Contributors", CARGO_PKG_VERSION, GIT_HASH, + profile, contributors.len() ); sender diff --git a/pumpkin/src/entity/boss/wither.rs b/pumpkin/src/entity/boss/wither.rs index 01bf35d8f..b708c7320 100644 --- a/pumpkin/src/entity/boss/wither.rs +++ b/pumpkin/src/entity/boss/wither.rs @@ -13,7 +13,7 @@ pub struct WitherEntity { } impl WitherEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let wither = Self { mob_entity }; let mob_arc = Arc::new(wither); diff --git a/pumpkin/src/entity/mob/creeper.rs b/pumpkin/src/entity/mob/creeper.rs index d68bab0ff..acdf347ea 100644 --- a/pumpkin/src/entity/mob/creeper.rs +++ b/pumpkin/src/entity/mob/creeper.rs @@ -21,7 +21,7 @@ pub struct CreeperEntity { } impl CreeperEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let entity = Self { mob_entity, diff --git a/pumpkin/src/entity/mob/drowned.rs b/pumpkin/src/entity/mob/drowned.rs index ab64c298c..aacd45b72 100644 --- a/pumpkin/src/entity/mob/drowned.rs +++ b/pumpkin/src/entity/mob/drowned.rs @@ -13,8 +13,8 @@ pub struct DrownedEntity { } impl DrownedEntity { - pub async fn make(entity: Entity) -> Arc { - let entity = ZombieEntity::make(entity).await; + pub async fn new(entity: Entity) -> Arc { + let entity = ZombieEntity::new(entity).await; let zombie = Self { entity }; let mob_arc = Arc::new(zombie); diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index 15de65ccb..a7b4370c3 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -19,6 +19,7 @@ use tokio::sync::Mutex; pub mod creeper; pub mod drowned; +pub mod silverfish; pub mod skeleton; pub mod zombie; pub mod zombie_villager; diff --git a/pumpkin/src/entity/mob/silverfish.rs b/pumpkin/src/entity/mob/silverfish.rs new file mode 100644 index 000000000..629f4e5e7 --- /dev/null +++ b/pumpkin/src/entity/mob/silverfish.rs @@ -0,0 +1,41 @@ +use std::sync::Arc; + +use pumpkin_data::entity::EntityType; + +use crate::entity::{ + Entity, NBTStorage, + ai::goal::active_target::ActiveTargetGoal, + mob::{Mob, MobEntity}, +}; + +pub struct SilverfishEntity { + entity: Arc, +} + +impl SilverfishEntity { + pub async fn new(entity: Entity) -> Arc { + let entity = Arc::new(MobEntity::new(entity)); + let zombie = Self { entity }; + let mob_arc = Arc::new(zombie); + + { + let mut target_selector = mob_arc.entity.target_selector.lock().await; + + // TODO + target_selector.add_goal( + 2, + ActiveTargetGoal::with_default(&mob_arc.entity, &EntityType::PLAYER, true), + ); + }; + + mob_arc + } +} + +impl NBTStorage for SilverfishEntity {} + +impl Mob for SilverfishEntity { + fn get_mob_entity(&self) -> &MobEntity { + &self.entity + } +} diff --git a/pumpkin/src/entity/mob/zombie.rs b/pumpkin/src/entity/mob/zombie.rs index 31bb102e5..fb145867f 100644 --- a/pumpkin/src/entity/mob/zombie.rs +++ b/pumpkin/src/entity/mob/zombie.rs @@ -24,7 +24,7 @@ pub struct ZombieEntity { } impl ZombieEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let zombie = Self { mob_entity }; let mob_arc = Arc::new(zombie); diff --git a/pumpkin/src/entity/mob/zombie_villager.rs b/pumpkin/src/entity/mob/zombie_villager.rs index 7c698174d..3746cdcbf 100644 --- a/pumpkin/src/entity/mob/zombie_villager.rs +++ b/pumpkin/src/entity/mob/zombie_villager.rs @@ -8,8 +8,8 @@ pub struct ZombieVillagerEntity { } impl ZombieVillagerEntity { - pub async fn make(entity: Entity) -> Arc { - let mob_entity = ZombieEntity::make(entity).await; + pub async fn new(entity: Entity) -> Arc { + let mob_entity = ZombieEntity::new(entity).await; let zombie = Self { mob_entity }; Arc::new(zombie) } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 1d4867401..4e08ad6e0 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -978,7 +978,6 @@ impl Entity { async fn update_fluid_state(&self, caller: &Arc) { let is_pushed = caller.is_pushed_by_fluids(); - let mut fluids = BTreeMap::new(); let water_push = Vector3::default(); diff --git a/pumpkin/src/entity/passive/iron_golem.rs b/pumpkin/src/entity/passive/iron_golem.rs index 2b1389e8e..380a41166 100644 --- a/pumpkin/src/entity/passive/iron_golem.rs +++ b/pumpkin/src/entity/passive/iron_golem.rs @@ -13,7 +13,7 @@ pub struct IronGolemEntity { } impl IronGolemEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let iron_golem = Self { mob_entity }; let mob_arc = Arc::new(iron_golem); diff --git a/pumpkin/src/entity/passive/snow_golem.rs b/pumpkin/src/entity/passive/snow_golem.rs index 34e005733..de21b9d81 100644 --- a/pumpkin/src/entity/passive/snow_golem.rs +++ b/pumpkin/src/entity/passive/snow_golem.rs @@ -13,7 +13,7 @@ pub struct SnowGolemEntity { } impl SnowGolemEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let wolf = Self { mob_entity }; let mob_arc = Arc::new(wolf); diff --git a/pumpkin/src/entity/passive/wolf.rs b/pumpkin/src/entity/passive/wolf.rs index 121ac849b..8714b9998 100644 --- a/pumpkin/src/entity/passive/wolf.rs +++ b/pumpkin/src/entity/passive/wolf.rs @@ -13,7 +13,7 @@ pub struct WolfEntity { } impl WolfEntity { - pub async fn make(entity: Entity) -> Arc { + pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let wolf = Self { mob_entity }; let mob_arc = Arc::new(wolf); diff --git a/pumpkin/src/entity/type.rs b/pumpkin/src/entity/type.rs index 509064d52..b6f674656 100644 --- a/pumpkin/src/entity/type.rs +++ b/pumpkin/src/entity/type.rs @@ -13,8 +13,8 @@ use crate::{ }, living::LivingEntity, mob::{ - creeper::CreeperEntity, drowned::DrownedEntity, zombie::ZombieEntity, - zombie_villager::ZombieVillagerEntity, + creeper::CreeperEntity, drowned::DrownedEntity, silverfish::SilverfishEntity, + zombie::ZombieEntity, zombie_villager::ZombieVillagerEntity, }, passive::{iron_golem::IronGolemEntity, snow_golem::SnowGolemEntity, wolf::WolfEntity}, }, @@ -30,17 +30,18 @@ pub async fn from_type( let entity = Entity::from_uuid(uuid, world.clone(), position, entity_type); let mob: Arc = match entity_type.id { - id if id == EntityType::ZOMBIE.id => ZombieEntity::make(entity).await, - id if id == EntityType::DROWNED.id => DrownedEntity::make(entity).await, - id if id == EntityType::ZOMBIE_VILLAGER.id => ZombieVillagerEntity::make(entity).await, - id if id == EntityType::CREEPER.id => CreeperEntity::make(entity).await, - id if id == EntityType::SNOW_GOLEM.id => SnowGolemEntity::make(entity).await, - id if id == EntityType::IRON_GOLEM.id => IronGolemEntity::make(entity).await, - id if id == EntityType::WOLF.id => WolfEntity::make(entity).await, - id if id == EntityType::WITHER.id => WitherEntity::make(entity).await, + id if id == EntityType::ZOMBIE.id => ZombieEntity::new(entity).await, + id if id == EntityType::DROWNED.id => DrownedEntity::new(entity).await, + id if id == EntityType::ZOMBIE_VILLAGER.id => ZombieVillagerEntity::new(entity).await, + id if id == EntityType::CREEPER.id => CreeperEntity::new(entity).await, + id if id == EntityType::SNOW_GOLEM.id => SnowGolemEntity::new(entity).await, + id if id == EntityType::IRON_GOLEM.id => IronGolemEntity::new(entity).await, + id if id == EntityType::WOLF.id => WolfEntity::new(entity).await, + id if id == EntityType::WITHER.id => WitherEntity::new(entity).await, id if id == EntityType::ARMOR_STAND.id => Arc::new(ArmorStandEntity::new(entity)), id if id == EntityType::PAINTING.id => Arc::new(PaintingEntity::new(entity)), id if id == EntityType::END_CRYSTAL.id => Arc::new(EndCrystalEntity::new(entity)), + id if id == EntityType::SILVERFISH.id => SilverfishEntity::new(entity).await, // Fallback Entity _ => { if entity_type.max_health.is_some() { diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index dcdc59fed..7858facde 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -2186,7 +2186,7 @@ impl World { for (uuid, entity_nbt) in chunk.data.lock().await.iter() { let Some(id) = entity_nbt.get_string("id") else { - log::warn!("Entity has no ID"); + log::debug!("Entity has no ID"); continue; }; let Some(entity_type) = @@ -2777,12 +2777,12 @@ impl World { return block_state_id; } chunk.mark_dirty(true); + drop(chunk); self.unsent_block_changes .lock() .await .insert(*position, block_state_id); - drop(chunk); let old_block = Block::from_state_id(replaced_block_state_id); let new_block = Block::from_state_id(block_state_id); @@ -3337,8 +3337,15 @@ impl World { pub async fn remove_block_entity(&self, block_pos: &BlockPos) { let chunk = self.level.get_chunk(block_pos.chunk_position()).await; - chunk.block_entities.lock().unwrap().remove(block_pos); - chunk.mark_dirty(true); + if chunk + .block_entities + .lock() + .unwrap() + .remove(block_pos) + .is_some() + { + chunk.mark_dirty(true); + } } pub async fn update_block_entity(&self, block_entity: &Arc) {