From e9958f5d64b5d86675865e879c988491d6020123 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 12 Aug 2026 20:28:44 +0200 Subject: [PATCH] feat(block): implement decorated pot block logic and fix deadlock --- .../pumpkin/src/block/blocks/decorated_pot.rs | 131 ++++++++++++++++++ crates/pumpkin/src/block/blocks/mod.rs | 1 + .../src/block/entities/decorated_pot.rs | 42 ++++++ crates/pumpkin/src/block/registry.rs | 2 + crates/pumpkin/src/world/mod.rs | 8 +- 5 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 crates/pumpkin/src/block/blocks/decorated_pot.rs diff --git a/crates/pumpkin/src/block/blocks/decorated_pot.rs b/crates/pumpkin/src/block/blocks/decorated_pot.rs new file mode 100644 index 000000000..696e7cb53 --- /dev/null +++ b/crates/pumpkin/src/block/blocks/decorated_pot.rs @@ -0,0 +1,131 @@ +use std::sync::Arc; + +use pumpkin_data::block_properties::{BlockProperties, DecoratedPotLikeProperties}; +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; +use pumpkin_data::sound::{Sound, SoundCategory}; +use pumpkin_data::BlockStateId; +use pumpkin_macros::pumpkin_block; + +use crate::block::entities::decorated_pot::DecoratedPotBlockEntity; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, BlockFuture, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + PlacedArgs, UseWithItemArgs, +}; + +#[pumpkin_block("minecraft:decorated_pot")] +pub struct DecoratedPotBlock; + +impl BlockBehaviour for DecoratedPotBlock { + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = DecoratedPotLikeProperties::from_state_id( + args.block.default_state.id, + args.block, + ); + props.facing = args + .player + .living_entity + .entity + .get_horizontal_facing() + .opposite(); + props.to_state_id(args.block) + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let entity = DecoratedPotBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + }) + } + + fn use_with_item<'a>( + &'a self, + args: UseWithItemArgs<'a>, + ) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + if args.item_stack.item_count == 0 { + return self + .normal_use(NormalUseArgs { + server: args.server, + world: args.world, + block: args.block, + position: args.position, + player: args.player, + hit: args.hit, + }) + .await; + } + + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = + block_entity.as_any().downcast_ref::() + { + if pot_entity.try_insert_item(args.item_stack, 1).await { + args.world.play_sound( + Sound::BlockDecoratedPotInsert, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } else { + args.world.play_sound( + Sound::BlockDecoratedPotInsertFail, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } + return BlockActionResult::Success; + } + + BlockActionResult::Pass + }) + } + + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + args.world.play_sound( + Sound::BlockDecoratedPotInsertFail, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + BlockActionResult::Success + }) + } + + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = + block_entity.as_any().downcast_ref::() + && let Some(contained) = pot_entity.take_item().await + { + args.world.drop_stack(args.position, contained).await; + } + + args.world.play_sound( + Sound::BlockDecoratedPotShatter, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world.drop_stack(args.position, ItemStack::new(4, &Item::BRICK)).await; + }) + } + + fn get_comparator_output<'a>( + &'a self, + args: GetComparatorOutputArgs<'a>, + ) -> BlockFuture<'a, Option> { + Box::pin(async move { + if let Some(block_entity) = args.world.get_block_entity(args.position) + && let Some(pot_entity) = + block_entity.as_any().downcast_ref::() + { + Some(pot_entity.get_comparator_output().await) + } else { + Some(0) + } + }) + } +} diff --git a/crates/pumpkin/src/block/blocks/mod.rs b/crates/pumpkin/src/block/blocks/mod.rs index 34e7a64fc..de3ff8698 100644 --- a/crates/pumpkin/src/block/blocks/mod.rs +++ b/crates/pumpkin/src/block/blocks/mod.rs @@ -110,5 +110,6 @@ pub mod coral; pub mod abstract_wall_mounting; pub mod beacon; +pub mod decorated_pot; pub mod trial_spawner; pub mod vault; diff --git a/crates/pumpkin/src/block/entities/decorated_pot.rs b/crates/pumpkin/src/block/entities/decorated_pot.rs index 4905c4d66..04ad6c689 100644 --- a/crates/pumpkin/src/block/entities/decorated_pot.rs +++ b/crates/pumpkin/src/block/entities/decorated_pot.rs @@ -76,6 +76,7 @@ impl BlockEntity for DecoratedPotBlockEntity { impl DecoratedPotBlockEntity { pub const ID: &'static str = "minecraft:decorated_pot"; + #[must_use] pub const fn new(position: BlockPos) -> Self { Self { @@ -84,4 +85,45 @@ impl DecoratedPotBlockEntity { item: Mutex::const_new(None), } } + + pub async fn get_item(&self) -> Option { + self.item.lock().await.clone() + } + + pub async fn take_item(&self) -> Option { + self.item.lock().await.take() + } + + pub async fn try_insert_item(&self, stack: &mut ItemStack, count: u8) -> bool { + let mut item_guard = self.item.lock().await; + if let Some(existing) = item_guard.as_mut() { + if existing.item.id == stack.item.id { + let add = count.min(64 - existing.item_count); + if add > 0 { + existing.item_count += add; + stack.item_count -= add; + return true; + } + } + false + } else { + let insert_count = count.min(stack.item_count); + let mut inserted = stack.clone(); + inserted.item_count = insert_count; + *item_guard = Some(inserted); + stack.item_count -= insert_count; + true + } + } + + pub async fn get_comparator_output(&self) -> u8 { + self.item.lock().await.as_ref().map_or(0, |item| { + if item.item_count == 0 { + 0 + } else { + let max_count = 64f32; + 1 + ((item.item_count as f32 / max_count) * 14.0).floor() as u8 + } + }) + } } diff --git a/crates/pumpkin/src/block/registry.rs b/crates/pumpkin/src/block/registry.rs index 18ef89412..af10a00e3 100644 --- a/crates/pumpkin/src/block/registry.rs +++ b/crates/pumpkin/src/block/registry.rs @@ -23,6 +23,7 @@ use crate::block::blocks::conduit::ConduitBlock; use crate::block::blocks::coral::coral_block::CoralBlock; use crate::block::blocks::coral::coral_fan::CoralFanBlock; use crate::block::blocks::coral::coral_plant::CoralPlantBlock; +use crate::block::blocks::decorated_pot::DecoratedPotBlock; use crate::block::blocks::dirt_path::DirtPathBlock; use crate::block::blocks::doors::DoorBlock; use crate::block::blocks::dripstone::DripstoneBlock; @@ -252,6 +253,7 @@ pub fn default_registry() -> Arc { manager.register(TNTBlock); manager.register(TrialSpawnerBlock); manager.register(VaultBlock); + manager.register(DecoratedPotBlock); manager.register(BushBlock); manager.register(FlowerBlock); manager.register(PotatoBlock); diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index ae78ac689..b31d84b5a 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -5291,10 +5291,12 @@ impl World { pub fn get_block_entity(&self, block_pos: &BlockPos) -> Option> { let chunk_pos = block_pos.chunk_position(); - if let Some(chunk_block_entities) = self.block_entities.get(&chunk_pos) - && let Some(entity) = chunk_block_entities.get(block_pos) + if let Some(entity) = self + .block_entities + .get(&chunk_pos) + .and_then(|m| m.get(block_pos).cloned()) { - return Some(entity.clone()); + return Some(entity); } let nbt = self