use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; use std::{ any::Any, pin::Pin, sync::{ Arc, atomic::{AtomicBool, Ordering}, }, }; use tokio::sync::Mutex; use crate::block::entities::BlockEntity; use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; pub struct LecternBlockEntity { pub position: BlockPos, pub book: Arc>, pub dirty: AtomicBool, } impl BlockEntity for LecternBlockEntity { fn resource_location(&self) -> &'static str { Self::ID } fn get_position(&self) -> BlockPos { self.position } fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self where Self: Sized, { let book = nbt .get_compound("Book") .and_then(ItemStack::read_item_stack) .map_or_else( || Arc::new(Mutex::new(ItemStack::EMPTY.clone())), |stack| Arc::new(Mutex::new(stack)), ); Self { position, book, dirty: AtomicBool::new(false), } } fn write_nbt<'a>( &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { let book = self.book.lock().await; if !book.is_empty() { let mut book_nbt = NbtCompound::default(); book.write_item_stack(&mut book_nbt); nbt.put_compound("Book", book_nbt); } }) } fn get_inventory(self: Arc) -> Option> { Some(self) } fn is_dirty(&self) -> bool { self.dirty.load(Ordering::Relaxed) } fn clear_dirty(&self) { self.dirty.store(false, Ordering::Relaxed); } fn as_any(&self) -> &dyn std::any::Any { self } } impl LecternBlockEntity { pub const ID: &'static str = "minecraft:lectern"; #[must_use] pub fn new(position: BlockPos) -> Self { Self { position, book: Arc::new(Mutex::new(ItemStack::EMPTY.clone())), dirty: AtomicBool::new(false), } } } impl Inventory for LecternBlockEntity { fn size(&self) -> usize { 1 } fn is_empty(&self) -> InventoryFuture<'_, bool> { Box::pin(async move { self.book.lock().await.is_empty() }) } fn get_stack(&self, _slot: usize) -> InventoryFuture<'_, Arc>> { Box::pin(async move { self.book.clone() }) } fn remove_stack(&self, _slot: usize) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.book.lock().await; std::mem::swap(&mut removed, &mut *guard); self.mark_dirty(); removed }) } fn remove_stack_specific(&self, _slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut stack = self.book.lock().await; if stack.is_empty() { return ItemStack::EMPTY.clone(); } let res = stack.split(amount); self.mark_dirty(); res }) } fn set_stack(&self, _slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { Box::pin(async move { *self.book.lock().await = stack; self.mark_dirty(); }) } fn mark_dirty(&self) { self.dirty.store(true, Ordering::Relaxed); } fn as_any(&self) -> &dyn Any { self } } impl Clearable for LecternBlockEntity { fn clear(&self) -> Pin + Send + '_>> { Box::pin(async move { *self.book.lock().await = ItemStack::EMPTY.clone(); self.mark_dirty(); }) } }