feat: Implement all missing block entites
This commit is contained in:
parent
9eab7581fd
commit
3c6557d6db
@ -179,7 +179,7 @@ tracing-subscriber = { version = "0.3.23", default-features = false, features =
|
||||
] }
|
||||
slotmap = { version = "1.1", default-features = false, features = ["std"] }
|
||||
temp-dir = { version = "0.2.0", default-features = false }
|
||||
thread_local = { version = "1.1.9", default-features = false }
|
||||
thread_local = { version = "1.1.10", default-features = false }
|
||||
time = { version = "0.3", default-features = false, features = ["std"] }
|
||||
arc-swap = { version = "1.9", default-features = false }
|
||||
tokio-util = { version = "0.7.18", default-features = false }
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
PlacedArgs,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use pumpkin_data::BlockStateId;
|
||||
@ -9,10 +10,20 @@ use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
||||
|
||||
use crate::block::entities::banner::BannerBlockEntity;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[pumpkin_block_from_tag("minecraft:banners")]
|
||||
pub struct BannerBlock;
|
||||
|
||||
impl BlockBehaviour for BannerBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = BannerBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||
Box::pin(async move {
|
||||
let mut props = WhiteBannerLikeProperties::default(args.block);
|
||||
|
||||
@ -9,18 +9,27 @@ use pumpkin_data::{
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
|
||||
use crate::block::entities::campfire::CampfireBlockEntity;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, BlockFuture, BlockIsReplacing, GetStateForNeighborUpdateArgs,
|
||||
OnEntityCollisionArgs, OnPlaceArgs,
|
||||
OnEntityCollisionArgs, OnPlaceArgs, PlacedArgs,
|
||||
},
|
||||
entity::EntityBase,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[pumpkin_block_from_tag("minecraft:campfires")]
|
||||
pub struct CampfireBlock;
|
||||
|
||||
impl BlockBehaviour for CampfireBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = CampfireBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: cooking food on campfire (CampfireBlockEntity)
|
||||
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||
use crate::block::entities::conduit::ConduitBlockEntity;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs};
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::BlockProperties;
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[pumpkin_block("minecraft:conduit")]
|
||||
pub struct ConduitBlock;
|
||||
@ -16,4 +18,11 @@ impl BlockBehaviour for ConduitBlock {
|
||||
props.to_state_id(args.block)
|
||||
})
|
||||
}
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = ConduitBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::entities::enchanting_table::EnchantingTableBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
|
||||
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs, PlacedArgs};
|
||||
use pumpkin_data::{Block, BlockStateId, translation};
|
||||
use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
@ -18,6 +19,13 @@ use tokio::sync::Mutex;
|
||||
pub struct EnchantingTableBlock;
|
||||
|
||||
impl BlockBehaviour for EnchantingTableBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = EnchantingTableBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
let mut bookshelf_count = 0;
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||
use crate::block::entities::dispenser::DispenserBlockEntity;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs};
|
||||
use crate::entity::EntityBase;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::{BlockProperties, DispenserLikeProperties};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[pumpkin_block("minecraft:dispenser")]
|
||||
pub struct DispenserBlock;
|
||||
@ -15,4 +17,12 @@ impl BlockBehaviour for DispenserBlock {
|
||||
props.to_state_id(args.block)
|
||||
})
|
||||
}
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let dispenser_block_entity = DispenserBlockEntity::new(*args.position);
|
||||
args.world
|
||||
.add_block_entity(Arc::new(dispenser_block_entity));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,10 @@ use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::{AcaciaShelfLikeProperties, BlockProperties};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs};
|
||||
use crate::block::entities::shelf::ShelfBlockEntity;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, OnPlaceArgs, PlacedArgs};
|
||||
use crate::entity::EntityBase;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[pumpkin_block_from_tag("minecraft:wooden_shelves")]
|
||||
pub struct ShelfBlock;
|
||||
@ -19,4 +21,11 @@ impl BlockBehaviour for ShelfBlock {
|
||||
properties.to_state_id(args.block)
|
||||
})
|
||||
}
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = ShelfBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::block::entities::hanging_sign::HangingSignBlockEntity;
|
||||
use crate::block::entities::sign::SignBlockEntity;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockDirection;
|
||||
@ -308,8 +309,13 @@ impl BlockBehaviour for SignBlock {
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
if args.block.name.contains("hanging") {
|
||||
args.world
|
||||
.add_block_entity(Arc::new(HangingSignBlockEntity::empty(*args.position)));
|
||||
} else {
|
||||
args.world
|
||||
.add_block_entity(Arc::new(SignBlockEntity::empty(*args.position)));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,18 @@ impl BlockMetadata for SkullBlock {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::block::PlacedArgs;
|
||||
use crate::block::entities::skull::SkullBlockEntity;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl BlockBehaviour for SkullBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = SkullBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
})
|
||||
}
|
||||
|
||||
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||
Box::pin(async move {
|
||||
let mut props = SkeletonSkullLikeProperties::default(args.block);
|
||||
|
||||
@ -19,6 +19,9 @@ impl BlockBehaviour for WitherSkeletonSkullBlock {
|
||||
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = crate::block::entities::skull::SkullBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(std::sync::Arc::new(entity));
|
||||
|
||||
let world = args.world;
|
||||
let pos = args.position;
|
||||
|
||||
|
||||
65
pumpkin/src/block/entities/banner.rs
Normal file
65
pumpkin/src/block/entities/banner.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct BannerBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub custom_name: Mutex<Option<String>>,
|
||||
pub patterns: Mutex<Option<Vec<NbtTag>>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for BannerBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let custom_name = nbt.get_string("CustomName").map(ToString::to_string);
|
||||
let patterns = nbt.get_list("patterns").map(<[_]>::to_vec);
|
||||
Self {
|
||||
position,
|
||||
custom_name: Mutex::new(custom_name),
|
||||
patterns: Mutex::new(patterns),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(name) = self.custom_name.lock().await.as_ref() {
|
||||
nbt.put_string("CustomName", name.clone());
|
||||
}
|
||||
if let Some(pats) = self.patterns.lock().await.as_ref() {
|
||||
nbt.put_list("patterns", pats.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BannerBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:banner";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
custom_name: Mutex::const_new(None),
|
||||
patterns: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
75
pumpkin/src/block/entities/beehive.rs
Normal file
75
pumpkin/src/block/entities/beehive.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct BeehiveBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub bees: Mutex<Option<Vec<NbtTag>>>,
|
||||
pub flower_pos: Mutex<Option<BlockPos>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for BeehiveBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let bees = nbt.get_list("Bees").map(<[_]>::to_vec);
|
||||
let flower_pos = nbt.get_compound("FlowerPos").map(|c| {
|
||||
BlockPos::new(
|
||||
c.get_int("X").unwrap_or(0),
|
||||
c.get_int("Y").unwrap_or(0),
|
||||
c.get_int("Z").unwrap_or(0),
|
||||
)
|
||||
});
|
||||
Self {
|
||||
position,
|
||||
bees: Mutex::new(bees),
|
||||
flower_pos: Mutex::new(flower_pos),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(b) = self.bees.lock().await.as_ref() {
|
||||
nbt.put_list("Bees", b.clone());
|
||||
}
|
||||
if let Some(fp) = self.flower_pos.lock().await.as_ref() {
|
||||
let mut fp_nbt = NbtCompound::new();
|
||||
fp_nbt.put_int("X", fp.0.x);
|
||||
fp_nbt.put_int("Y", fp.0.y);
|
||||
fp_nbt.put_int("Z", fp.0.z);
|
||||
nbt.put_compound("FlowerPos", fp_nbt);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BeehiveBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:beehive";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
bees: Mutex::const_new(None),
|
||||
flower_pos: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
72
pumpkin/src/block/entities/brushable_block.rs
Normal file
72
pumpkin/src/block/entities/brushable_block.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct BrushableBlockBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub item: Mutex<Option<ItemStack>>,
|
||||
pub hits: Mutex<i32>,
|
||||
pub direction: Mutex<u8>,
|
||||
}
|
||||
|
||||
impl BlockEntity for BrushableBlockBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let item = nbt
|
||||
.get_compound("item")
|
||||
.and_then(ItemStack::read_item_stack);
|
||||
let hits = nbt.get_int("hits").unwrap_or(0);
|
||||
let direction = nbt.get_byte("direction").unwrap_or(0) as u8;
|
||||
Self {
|
||||
position,
|
||||
item: Mutex::new(item),
|
||||
hits: Mutex::new(hits),
|
||||
direction: Mutex::new(direction),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(it) = self.item.lock().await.as_ref() {
|
||||
let mut it_nbt = NbtCompound::new();
|
||||
it.write_item_stack(&mut it_nbt);
|
||||
nbt.put_compound("item", it_nbt);
|
||||
}
|
||||
nbt.put_int("hits", *self.hits.lock().await);
|
||||
nbt.put_byte("direction", *self.direction.lock().await as i8);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BrushableBlockBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:brushable_block";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
item: Mutex::const_new(None),
|
||||
hits: Mutex::const_new(0),
|
||||
direction: Mutex::const_new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
58
pumpkin/src/block/entities/calibrated_sculk_sensor.rs
Normal file
58
pumpkin/src/block/entities/calibrated_sculk_sensor.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct CalibratedSculkSensorBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub last_vibration_frequency: Mutex<i32>,
|
||||
}
|
||||
|
||||
impl BlockEntity for CalibratedSculkSensorBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let last_vibration_frequency = nbt.get_int("last_vibration_frequency").unwrap_or(0);
|
||||
Self {
|
||||
position,
|
||||
last_vibration_frequency: Mutex::new(last_vibration_frequency),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_int(
|
||||
"last_vibration_frequency",
|
||||
*self.last_vibration_frequency.lock().await,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CalibratedSculkSensorBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:calibrated_sculk_sensor";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
last_vibration_frequency: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
111
pumpkin/src/block/entities/campfire.rs
Normal file
111
pumpkin/src/block/entities/campfire.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct CampfireBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub items: [Arc<Mutex<ItemStack>>; 4],
|
||||
pub cooking_times: [tokio::sync::Mutex<i32>; 4],
|
||||
pub cooking_total_times: [tokio::sync::Mutex<i32>; 4],
|
||||
}
|
||||
|
||||
impl BlockEntity for CampfireBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut items = std::array::from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone())));
|
||||
if let Some(list) = nbt.get_list("Items") {
|
||||
for tag in list {
|
||||
if let Some(compound) = tag.extract_compound() {
|
||||
let slot = compound.get_byte("Slot").unwrap_or(0) as usize;
|
||||
if slot < 4
|
||||
&& let Some(stack) = ItemStack::read_item_stack(compound)
|
||||
{
|
||||
items[slot] = Arc::new(Mutex::new(stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut cooking_times = [Mutex::new(0), Mutex::new(0), Mutex::new(0), Mutex::new(0)];
|
||||
if let Some(arr) = nbt.get_int_array("CookingTimes") {
|
||||
for (i, &val) in arr.iter().enumerate().take(4) {
|
||||
cooking_times[i] = Mutex::new(val);
|
||||
}
|
||||
}
|
||||
let mut cooking_total_times = [Mutex::new(0), Mutex::new(0), Mutex::new(0), Mutex::new(0)];
|
||||
if let Some(arr) = nbt.get_int_array("CookingTotalTimes") {
|
||||
for (i, &val) in arr.iter().enumerate().take(4) {
|
||||
cooking_total_times[i] = Mutex::new(val);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
position,
|
||||
items,
|
||||
cooking_times,
|
||||
cooking_total_times,
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
let mut list = Vec::new();
|
||||
for (i, item_mutex) in self.items.iter().enumerate() {
|
||||
let stack = item_mutex.lock().await;
|
||||
if !stack.is_empty() {
|
||||
let mut item_nbt = NbtCompound::new();
|
||||
item_nbt.put_byte("Slot", i as i8);
|
||||
stack.write_item_stack(&mut item_nbt);
|
||||
list.push(NbtTag::Compound(item_nbt));
|
||||
}
|
||||
}
|
||||
nbt.put_list("Items", list);
|
||||
|
||||
let mut times = Vec::new();
|
||||
for m in &self.cooking_times {
|
||||
times.push(*m.lock().await);
|
||||
}
|
||||
nbt.put("CookingTimes", NbtTag::IntArray(times));
|
||||
|
||||
let mut total_times = Vec::new();
|
||||
for m in &self.cooking_total_times {
|
||||
total_times.push(*m.lock().await);
|
||||
}
|
||||
nbt.put("CookingTotalTimes", NbtTag::IntArray(total_times));
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CampfireBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:campfire";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
items: std::array::from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
cooking_times: [Mutex::new(0), Mutex::new(0), Mutex::new(0), Mutex::new(0)],
|
||||
cooking_total_times: [Mutex::new(0), Mutex::new(0), Mutex::new(0), Mutex::new(0)],
|
||||
}
|
||||
}
|
||||
}
|
||||
63
pumpkin/src/block/entities/conduit.rs
Normal file
63
pumpkin/src/block/entities/conduit.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct ConduitBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub active: Mutex<bool>,
|
||||
pub target: Mutex<Option<NbtTag>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for ConduitBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let active = nbt.get_bool("Active").unwrap_or(false);
|
||||
let target = nbt.get("Target").cloned();
|
||||
Self {
|
||||
position,
|
||||
active: Mutex::new(active),
|
||||
target: Mutex::new(target),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_bool("Active", *self.active.lock().await);
|
||||
if let Some(tgt) = self.target.lock().await.as_ref() {
|
||||
nbt.put("Target", tgt.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ConduitBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:conduit";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
active: Mutex::new(false),
|
||||
target: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
46
pumpkin/src/block/entities/copper_golem_statue.rs
Normal file
46
pumpkin/src/block/entities/copper_golem_statue.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::BlockEntity;
|
||||
|
||||
pub struct CopperGolemStatueBlockEntity {
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
impl BlockEntity for CopperGolemStatueBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(_nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self { position }
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
_nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CopperGolemStatueBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:copper_golem_statue";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
160
pumpkin/src/block/entities/crafter.rs
Normal file
160
pumpkin/src/block/entities/crafter.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use crate::block::entities::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
|
||||
use std::any::Any;
|
||||
use std::array::from_fn;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct CrafterBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub items: [Arc<Mutex<ItemStack>>; Self::INVENTORY_SIZE],
|
||||
pub crafting_ticks_remaining: AtomicI32,
|
||||
pub triggered: AtomicBool,
|
||||
pub dirty: AtomicBool,
|
||||
}
|
||||
|
||||
impl BlockEntity for CrafterBlockEntity {
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.write_inventory_nbt(nbt, true).await;
|
||||
nbt.put_int(
|
||||
"crafting_ticks_remaining",
|
||||
self.crafting_ticks_remaining.load(Ordering::Relaxed),
|
||||
);
|
||||
nbt.put_bool("triggered", self.triggered.load(Ordering::Relaxed));
|
||||
})
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let crafter = Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
crafting_ticks_remaining: AtomicI32::new(
|
||||
nbt.get_int("crafting_ticks_remaining").unwrap_or(0),
|
||||
),
|
||||
triggered: AtomicBool::new(nbt.get_bool("triggered").unwrap_or(false)),
|
||||
dirty: AtomicBool::new(false),
|
||||
};
|
||||
|
||||
crafter.read_data(nbt, &crafter.items);
|
||||
|
||||
crafter
|
||||
}
|
||||
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||
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 CrafterBlockEntity {
|
||||
pub const INVENTORY_SIZE: usize = 9;
|
||||
pub const ID: &'static str = "minecraft:crafter";
|
||||
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
crafting_ticks_remaining: AtomicI32::new(0),
|
||||
triggered: AtomicBool::new(false),
|
||||
dirty: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inventory for CrafterBlockEntity {
|
||||
fn size(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> InventoryFuture<'_, bool> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
if !slot.lock().await.is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
|
||||
Box::pin(async move { self.items[slot].clone() })
|
||||
}
|
||||
|
||||
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
||||
Box::pin(async move {
|
||||
let mut removed = ItemStack::EMPTY.clone();
|
||||
let mut guard = self.items[slot].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 res = split_stack(&self.items, slot, amount).await;
|
||||
self.mark_dirty();
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
*self.items[slot].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 CrafterBlockEntity {
|
||||
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||
}
|
||||
self.mark_dirty();
|
||||
})
|
||||
}
|
||||
}
|
||||
46
pumpkin/src/block/entities/creaking_heart.rs
Normal file
46
pumpkin/src/block/entities/creaking_heart.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::BlockEntity;
|
||||
|
||||
pub struct CreakingHeartBlockEntity {
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
impl BlockEntity for CreakingHeartBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(_nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self { position }
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
_nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CreakingHeartBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:creaking_heart";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
70
pumpkin/src/block/entities/decorated_pot.rs
Normal file
70
pumpkin/src/block/entities/decorated_pot.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct DecoratedPotBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub sherds: Mutex<Option<Vec<NbtTag>>>,
|
||||
pub item: Mutex<Option<ItemStack>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for DecoratedPotBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let sherds = nbt.get_list("sherds").map(<[_]>::to_vec);
|
||||
let item = nbt
|
||||
.get_compound("item")
|
||||
.and_then(ItemStack::read_item_stack);
|
||||
Self {
|
||||
position,
|
||||
sherds: Mutex::new(sherds),
|
||||
item: Mutex::new(item),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(sh) = self.sherds.lock().await.as_ref() {
|
||||
nbt.put_list("sherds", sh.clone());
|
||||
}
|
||||
if let Some(it) = self.item.lock().await.as_ref() {
|
||||
let mut it_nbt = NbtCompound::new();
|
||||
it.write_item_stack(&mut it_nbt);
|
||||
nbt.put_compound("item", it_nbt);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl DecoratedPotBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:decorated_pot";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
sherds: Mutex::const_new(None),
|
||||
item: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
160
pumpkin/src/block/entities/dispenser.rs
Normal file
160
pumpkin/src/block/entities/dispenser.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use crate::block::entities::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
|
||||
use rand::{RngExt, rng};
|
||||
use std::any::Any;
|
||||
use std::array::from_fn;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
||||
pub struct DispenserBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub items: [Arc<Mutex<ItemStack>>; Self::INVENTORY_SIZE],
|
||||
pub dirty: AtomicBool,
|
||||
}
|
||||
|
||||
impl BlockEntity for DispenserBlockEntity {
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
self.write_inventory_nbt(nbt, true)
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let dispenser = Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
dirty: AtomicBool::new(false),
|
||||
};
|
||||
|
||||
dispenser.read_data(nbt, &dispenser.items);
|
||||
|
||||
dispenser
|
||||
}
|
||||
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||
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 DispenserBlockEntity {
|
||||
pub const INVENTORY_SIZE: usize = 9;
|
||||
pub const ID: &'static str = "minecraft:dispenser";
|
||||
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
dirty: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
pub async fn get_random_slot(&self) -> Option<MutexGuard<'_, ItemStack>> {
|
||||
let mut ret = None;
|
||||
let mut j = 1;
|
||||
for i in &self.items {
|
||||
let item = i.lock().await;
|
||||
if !item.is_empty() {
|
||||
if rng().random_range(0..j) == 0 {
|
||||
ret = Some(item);
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Inventory for DispenserBlockEntity {
|
||||
fn size(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> InventoryFuture<'_, bool> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
if !slot.lock().await.is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
|
||||
Box::pin(async move { self.items[slot].clone() })
|
||||
}
|
||||
|
||||
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
||||
Box::pin(async move {
|
||||
let mut removed = ItemStack::EMPTY.clone();
|
||||
let mut guard = self.items[slot].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 res = split_stack(&self.items, slot, amount).await;
|
||||
self.mark_dirty();
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
*self.items[slot].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 DispenserBlockEntity {
|
||||
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||
}
|
||||
self.mark_dirty();
|
||||
})
|
||||
}
|
||||
}
|
||||
57
pumpkin/src/block/entities/enchanting_table.rs
Normal file
57
pumpkin/src/block/entities/enchanting_table.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct EnchantingTableBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub custom_name: Mutex<Option<String>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for EnchantingTableBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let custom_name = nbt.get_string("CustomName").map(ToString::to_string);
|
||||
Self {
|
||||
position,
|
||||
custom_name: Mutex::new(custom_name),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(name) = self.custom_name.lock().await.as_ref() {
|
||||
nbt.put_string("CustomName", name.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl EnchantingTableBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:enchanting_table";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
custom_name: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
77
pumpkin/src/block/entities/end_gateway.rs
Normal file
77
pumpkin/src/block/entities/end_gateway.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct EndGatewayBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub age: Mutex<i64>,
|
||||
pub exact_teleport: Mutex<bool>,
|
||||
pub exit_portal: Mutex<Option<BlockPos>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for EndGatewayBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let age = nbt.get_long("Age").unwrap_or(0);
|
||||
let exact_teleport = nbt.get_bool("ExactTeleport").unwrap_or(false);
|
||||
let exit_portal = nbt.get_compound("ExitPortal").map(|c| {
|
||||
BlockPos::new(
|
||||
c.get_int("X").unwrap_or(0),
|
||||
c.get_int("Y").unwrap_or(0),
|
||||
c.get_int("Z").unwrap_or(0),
|
||||
)
|
||||
});
|
||||
Self {
|
||||
position,
|
||||
age: Mutex::new(age),
|
||||
exact_teleport: Mutex::new(exact_teleport),
|
||||
exit_portal: Mutex::new(exit_portal),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_long("Age", *self.age.lock().await);
|
||||
nbt.put_bool("ExactTeleport", *self.exact_teleport.lock().await);
|
||||
if let Some(exit) = self.exit_portal.lock().await.as_ref() {
|
||||
let mut exit_nbt = NbtCompound::new();
|
||||
exit_nbt.put_int("X", exit.0.x);
|
||||
exit_nbt.put_int("Y", exit.0.y);
|
||||
exit_nbt.put_int("Z", exit.0.z);
|
||||
nbt.put_compound("ExitPortal", exit_nbt);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl EndGatewayBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:end_gateway";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
age: Mutex::new(0),
|
||||
exact_teleport: Mutex::new(false),
|
||||
exit_portal: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
110
pumpkin/src/block/entities/hanging_sign.rs
Normal file
110
pumpkin/src/block/entities/hanging_sign.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
use super::BlockEntity;
|
||||
use crate::block::entities::sign::Text;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct HangingSignBlockEntity {
|
||||
pub front_text: Text,
|
||||
pub back_text: Text,
|
||||
pub is_waxed: AtomicBool,
|
||||
position: BlockPos,
|
||||
pub currently_editing_player: Arc<Mutex<Option<uuid::Uuid>>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for HangingSignBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let front_text = nbt
|
||||
.get("front_text")
|
||||
.cloned()
|
||||
.map(Text::from)
|
||||
.unwrap_or_default();
|
||||
let back_text = nbt
|
||||
.get("back_text")
|
||||
.cloned()
|
||||
.map(Text::from)
|
||||
.unwrap_or_default();
|
||||
let is_waxed = nbt.get_bool("is_waxed").unwrap_or(false);
|
||||
Self {
|
||||
position,
|
||||
front_text,
|
||||
back_text,
|
||||
is_waxed: AtomicBool::new(is_waxed),
|
||||
currently_editing_player: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put("front_text", self.front_text.clone());
|
||||
nbt.put("back_text", self.back_text.clone());
|
||||
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
||||
})
|
||||
}
|
||||
|
||||
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
||||
let mut nbt = NbtCompound::new();
|
||||
nbt.put("front_text", self.front_text.clone());
|
||||
nbt.put("back_text", self.back_text.clone());
|
||||
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
||||
Some(nbt)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HangingSignBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:hanging_sign";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos, is_front: bool, messages: [Box<str>; 4]) -> Self {
|
||||
Self {
|
||||
position,
|
||||
is_waxed: AtomicBool::new(false),
|
||||
front_text: if is_front {
|
||||
Text::new(messages.clone())
|
||||
} else {
|
||||
Text::default()
|
||||
},
|
||||
back_text: if is_front {
|
||||
Text::default()
|
||||
} else {
|
||||
Text::new(messages)
|
||||
},
|
||||
currently_editing_player: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn empty(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
is_waxed: AtomicBool::new(false),
|
||||
front_text: Text::default(),
|
||||
back_text: Text::default(),
|
||||
currently_editing_player: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,32 @@ pub mod sign;
|
||||
pub mod smoker;
|
||||
pub mod trapped_chest;
|
||||
|
||||
pub mod banner;
|
||||
pub mod beehive;
|
||||
pub mod brushable_block;
|
||||
pub mod calibrated_sculk_sensor;
|
||||
pub mod campfire;
|
||||
pub mod conduit;
|
||||
pub mod copper_golem_statue;
|
||||
pub mod crafter;
|
||||
pub mod creaking_heart;
|
||||
pub mod decorated_pot;
|
||||
pub mod dispenser;
|
||||
pub mod enchanting_table;
|
||||
pub mod end_gateway;
|
||||
pub mod hanging_sign;
|
||||
pub mod potent_sulfur;
|
||||
pub mod sculk_catalyst;
|
||||
pub mod sculk_sensor;
|
||||
pub mod sculk_shrieker;
|
||||
pub mod shelf;
|
||||
pub mod skull;
|
||||
pub mod structure_block;
|
||||
pub mod test_block;
|
||||
pub mod test_instance_block;
|
||||
pub mod trial_spawner;
|
||||
pub mod vault;
|
||||
|
||||
pub use furnace_like_block_entity::ExperienceContainer;
|
||||
pub use pumpkin_world::block::entities::PropertyDelegate;
|
||||
|
||||
@ -148,6 +174,7 @@ pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>> {
|
||||
let id = nbt.get_string("id")?;
|
||||
let x = nbt.get_int("x")?;
|
||||
@ -222,6 +249,75 @@ pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>>
|
||||
lectern::LecternBlockEntity::ID => {
|
||||
Some(Arc::new(lectern::LecternBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
dispenser::DispenserBlockEntity::ID => Some(Arc::new(
|
||||
dispenser::DispenserBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
hanging_sign::HangingSignBlockEntity::ID => Some(Arc::new(
|
||||
hanging_sign::HangingSignBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
creaking_heart::CreakingHeartBlockEntity::ID => Some(Arc::new(
|
||||
creaking_heart::CreakingHeartBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
enchanting_table::EnchantingTableBlockEntity::ID => Some(Arc::new(
|
||||
enchanting_table::EnchantingTableBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
skull::SkullBlockEntity::ID => Some(Arc::new(skull::SkullBlockEntity::from_nbt(nbt, pos))),
|
||||
banner::BannerBlockEntity::ID => {
|
||||
Some(Arc::new(banner::BannerBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
structure_block::StructureBlockBlockEntity::ID => Some(Arc::new(
|
||||
structure_block::StructureBlockBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
end_gateway::EndGatewayBlockEntity::ID => Some(Arc::new(
|
||||
end_gateway::EndGatewayBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
conduit::ConduitBlockEntity::ID => {
|
||||
Some(Arc::new(conduit::ConduitBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
campfire::CampfireBlockEntity::ID => {
|
||||
Some(Arc::new(campfire::CampfireBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
beehive::BeehiveBlockEntity::ID => {
|
||||
Some(Arc::new(beehive::BeehiveBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
sculk_sensor::SculkSensorBlockEntity::ID => Some(Arc::new(
|
||||
sculk_sensor::SculkSensorBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity::ID => Some(Arc::new(
|
||||
calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
sculk_catalyst::SculkCatalystBlockEntity::ID => Some(Arc::new(
|
||||
sculk_catalyst::SculkCatalystBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
sculk_shrieker::SculkShriekerBlockEntity::ID => Some(Arc::new(
|
||||
sculk_shrieker::SculkShriekerBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
shelf::ShelfBlockEntity::ID => Some(Arc::new(shelf::ShelfBlockEntity::from_nbt(nbt, pos))),
|
||||
brushable_block::BrushableBlockBlockEntity::ID => Some(Arc::new(
|
||||
brushable_block::BrushableBlockBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
decorated_pot::DecoratedPotBlockEntity::ID => Some(Arc::new(
|
||||
decorated_pot::DecoratedPotBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
crafter::CrafterBlockEntity::ID => {
|
||||
Some(Arc::new(crafter::CrafterBlockEntity::from_nbt(nbt, pos)))
|
||||
}
|
||||
trial_spawner::TrialSpawnerBlockEntity::ID => Some(Arc::new(
|
||||
trial_spawner::TrialSpawnerBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
vault::VaultBlockEntity::ID => Some(Arc::new(vault::VaultBlockEntity::from_nbt(nbt, pos))),
|
||||
test_block::TestBlockBlockEntity::ID => Some(Arc::new(
|
||||
test_block::TestBlockBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
test_instance_block::TestInstanceBlockBlockEntity::ID => Some(Arc::new(
|
||||
test_instance_block::TestInstanceBlockBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
copper_golem_statue::CopperGolemStatueBlockEntity::ID => Some(Arc::new(
|
||||
copper_golem_statue::CopperGolemStatueBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
potent_sulfur::PotentSulfurBlockEntity::ID => Some(Arc::new(
|
||||
potent_sulfur::PotentSulfurBlockEntity::from_nbt(nbt, pos),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -230,3 +326,112 @@ pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>>
|
||||
pub fn has_block_block_entity(block: &Block) -> bool {
|
||||
BLOCK_ENTITY_TYPES.contains(&block.name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn create_block_entity(
|
||||
block_entity_type_id: u16,
|
||||
position: BlockPos,
|
||||
) -> Option<Arc<dyn BlockEntity>> {
|
||||
use pumpkin_data::block_properties::FacingHopper;
|
||||
if block_entity_type_id == u16::MAX {
|
||||
return None;
|
||||
}
|
||||
let name = BLOCK_ENTITY_TYPES.get(block_entity_type_id as usize)?;
|
||||
match *name {
|
||||
"furnace" => Some(Arc::new(furnace::FurnaceBlockEntity::new(position))),
|
||||
"chest" => Some(Arc::new(chest::ChestBlockEntity::new(position))),
|
||||
"trapped_chest" => Some(Arc::new(trapped_chest::TrappedChestBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"ender_chest" => Some(Arc::new(ender_chest::EnderChestBlockEntity::new(position))),
|
||||
"jukebox" => Some(Arc::new(jukebox::JukeboxBlockEntity::new(position))),
|
||||
"dispenser" => Some(Arc::new(dispenser::DispenserBlockEntity::new(position))),
|
||||
"dropper" => Some(Arc::new(dropper::DropperBlockEntity::new(position))),
|
||||
"sign" => Some(Arc::new(sign::SignBlockEntity::empty(position))),
|
||||
"hanging_sign" => Some(Arc::new(hanging_sign::HangingSignBlockEntity::empty(
|
||||
position,
|
||||
))),
|
||||
"mob_spawner" => Some(Arc::new(mob_spawner::MobSpawnerBlockEntity::new(
|
||||
position, None,
|
||||
))),
|
||||
"creaking_heart" => Some(Arc::new(creaking_heart::CreakingHeartBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"brewing_stand" => Some(Arc::new(brewing_stand::BrewingStandBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"enchanting_table" => Some(Arc::new(enchanting_table::EnchantingTableBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"end_portal" => Some(Arc::new(end_portal::EndPortalBlockEntity::new(position))),
|
||||
"beacon" => Some(Arc::new(beacon::BeaconBlockEntity::new(position))),
|
||||
"skull" => Some(Arc::new(skull::SkullBlockEntity::new(position))),
|
||||
"daylight_detector" => Some(Arc::new(
|
||||
daylight_detector::DaylightDetectorBlockEntity::new(position),
|
||||
)),
|
||||
"hopper" => Some(Arc::new(hopper::HopperBlockEntity::new(
|
||||
position,
|
||||
FacingHopper::Down,
|
||||
))),
|
||||
"comparator" => Some(Arc::new(comparator::ComparatorBlockEntity::new(position))),
|
||||
"banner" => Some(Arc::new(banner::BannerBlockEntity::new(position))),
|
||||
"structure_block" => Some(Arc::new(structure_block::StructureBlockBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"end_gateway" => Some(Arc::new(end_gateway::EndGatewayBlockEntity::new(position))),
|
||||
"command_block" => Some(Arc::new(command_block::CommandBlockEntity::new(
|
||||
position, true, false,
|
||||
))),
|
||||
"shulker_box" => Some(Arc::new(shulker_box::ShulkerBoxBlockEntity::new(position))),
|
||||
"conduit" => Some(Arc::new(conduit::ConduitBlockEntity::new(position))),
|
||||
"barrel" => Some(Arc::new(barrel::BarrelBlockEntity::new(position))),
|
||||
"smoker" => Some(Arc::new(smoker::SmokerBlockEntity::new(position))),
|
||||
"blast_furnace" => Some(Arc::new(blasting_furnace::BlastingFurnaceBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"lectern" => Some(Arc::new(lectern::LecternBlockEntity::new(position))),
|
||||
"bell" => Some(Arc::new(bell::BellBlockEntity::new(position))),
|
||||
"jigsaw" => Some(Arc::new(jigsaw_block::JigsawBlockEntity::new(position))),
|
||||
"campfire" => Some(Arc::new(campfire::CampfireBlockEntity::new(position))),
|
||||
"beehive" => Some(Arc::new(beehive::BeehiveBlockEntity::new(position))),
|
||||
"sculk_sensor" => Some(Arc::new(sculk_sensor::SculkSensorBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"calibrated_sculk_sensor" => Some(Arc::new(
|
||||
calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity::new(position),
|
||||
)),
|
||||
"sculk_catalyst" => Some(Arc::new(sculk_catalyst::SculkCatalystBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"sculk_shrieker" => Some(Arc::new(sculk_shrieker::SculkShriekerBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"chiseled_bookshelf" => Some(Arc::new(
|
||||
chiseled_bookshelf::ChiseledBookshelfBlockEntity::new(position),
|
||||
)),
|
||||
"shelf" => Some(Arc::new(shelf::ShelfBlockEntity::new(position))),
|
||||
"brushable_block" => Some(Arc::new(brushable_block::BrushableBlockBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"decorated_pot" => Some(Arc::new(decorated_pot::DecoratedPotBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"crafter" => Some(Arc::new(crafter::CrafterBlockEntity::new(position))),
|
||||
"trial_spawner" => Some(Arc::new(trial_spawner::TrialSpawnerBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
"vault" => Some(Arc::new(vault::VaultBlockEntity::new(position))),
|
||||
"test_block" => Some(Arc::new(test_block::TestBlockBlockEntity::new(position))),
|
||||
"test_instance_block" => Some(Arc::new(
|
||||
test_instance_block::TestInstanceBlockBlockEntity::new(position),
|
||||
)),
|
||||
"copper_golem_statue" => Some(Arc::new(
|
||||
copper_golem_statue::CopperGolemStatueBlockEntity::new(position),
|
||||
)),
|
||||
"potent_sulfur" => Some(Arc::new(potent_sulfur::PotentSulfurBlockEntity::new(
|
||||
position,
|
||||
))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
46
pumpkin/src/block/entities/potent_sulfur.rs
Normal file
46
pumpkin/src/block/entities/potent_sulfur.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::BlockEntity;
|
||||
|
||||
pub struct PotentSulfurBlockEntity {
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
impl BlockEntity for PotentSulfurBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(_nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self { position }
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
_nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PotentSulfurBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:potent_sulfur";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
55
pumpkin/src/block/entities/sculk_catalyst.rs
Normal file
55
pumpkin/src/block/entities/sculk_catalyst.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct SculkCatalystBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub decay_delay: Mutex<i32>,
|
||||
}
|
||||
|
||||
impl BlockEntity for SculkCatalystBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let decay_delay = nbt.get_int("decay_delay").unwrap_or(0);
|
||||
Self {
|
||||
position,
|
||||
decay_delay: Mutex::new(decay_delay),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_int("decay_delay", *self.decay_delay.lock().await);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SculkCatalystBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:sculk_catalyst";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
decay_delay: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
58
pumpkin/src/block/entities/sculk_sensor.rs
Normal file
58
pumpkin/src/block/entities/sculk_sensor.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct SculkSensorBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub last_vibration_frequency: Mutex<i32>,
|
||||
}
|
||||
|
||||
impl BlockEntity for SculkSensorBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let last_vibration_frequency = nbt.get_int("last_vibration_frequency").unwrap_or(0);
|
||||
Self {
|
||||
position,
|
||||
last_vibration_frequency: Mutex::new(last_vibration_frequency),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_int(
|
||||
"last_vibration_frequency",
|
||||
*self.last_vibration_frequency.lock().await,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SculkSensorBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:sculk_sensor";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
last_vibration_frequency: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pumpkin/src/block/entities/sculk_shrieker.rs
Normal file
55
pumpkin/src/block/entities/sculk_shrieker.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct SculkShriekerBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub warning_level: Mutex<i32>,
|
||||
}
|
||||
|
||||
impl BlockEntity for SculkShriekerBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let warning_level = nbt.get_int("warning_level").unwrap_or(0);
|
||||
Self {
|
||||
position,
|
||||
warning_level: Mutex::new(warning_level),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_int("warning_level", *self.warning_level.lock().await);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SculkShriekerBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:sculk_shrieker";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
warning_level: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
145
pumpkin/src/block/entities/shelf.rs
Normal file
145
pumpkin/src/block/entities/shelf.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use crate::block::entities::BlockEntity;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
|
||||
use std::any::Any;
|
||||
use std::array::from_fn;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct ShelfBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub items: [Arc<Mutex<ItemStack>>; Self::INVENTORY_SIZE],
|
||||
pub dirty: AtomicBool,
|
||||
}
|
||||
|
||||
impl BlockEntity for ShelfBlockEntity {
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
self.write_inventory_nbt(nbt, true)
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let shelf = Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
dirty: AtomicBool::new(false),
|
||||
};
|
||||
|
||||
shelf.read_data(nbt, &shelf.items);
|
||||
|
||||
shelf
|
||||
}
|
||||
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||
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 ShelfBlockEntity {
|
||||
pub const INVENTORY_SIZE: usize = 9;
|
||||
pub const ID: &'static str = "minecraft:shelf";
|
||||
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
dirty: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inventory for ShelfBlockEntity {
|
||||
fn size(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> InventoryFuture<'_, bool> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
if !slot.lock().await.is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
|
||||
Box::pin(async move { self.items[slot].clone() })
|
||||
}
|
||||
|
||||
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
|
||||
Box::pin(async move {
|
||||
let mut removed = ItemStack::EMPTY.clone();
|
||||
let mut guard = self.items[slot].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 res = split_stack(&self.items, slot, amount).await;
|
||||
self.mark_dirty();
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
*self.items[slot].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 ShelfBlockEntity {
|
||||
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
|
||||
Box::pin(async move {
|
||||
for slot in &self.items {
|
||||
*slot.lock().await = ItemStack::EMPTY.clone();
|
||||
}
|
||||
self.mark_dirty();
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -196,7 +196,8 @@ impl From<NbtTag> for Text {
|
||||
}
|
||||
|
||||
impl Text {
|
||||
fn new(messages: [Box<str>; 4]) -> Self {
|
||||
#[must_use]
|
||||
pub fn new(messages: [Box<str>; 4]) -> Self {
|
||||
Self {
|
||||
has_glowing_text: AtomicBool::new(false),
|
||||
color: AtomicI8::new(DyeColor::default() as i8),
|
||||
|
||||
64
pumpkin/src/block/entities/skull.rs
Normal file
64
pumpkin/src/block/entities/skull.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct SkullBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub note_block_sound: Mutex<Option<String>>,
|
||||
pub profile: Mutex<Option<NbtCompound>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for SkullBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let note_block_sound = nbt.get_string("note_block_sound").map(ToString::to_string);
|
||||
let profile = nbt.get_compound("profile").cloned();
|
||||
Self {
|
||||
position,
|
||||
note_block_sound: Mutex::new(note_block_sound),
|
||||
profile: Mutex::new(profile),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(sound) = self.note_block_sound.lock().await.as_ref() {
|
||||
nbt.put_string("note_block_sound", sound.clone());
|
||||
}
|
||||
if let Some(prof) = self.profile.lock().await.as_ref() {
|
||||
nbt.put_compound("profile", prof.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SkullBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:skull";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
note_block_sound: Mutex::const_new(None),
|
||||
profile: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
118
pumpkin/src/block/entities/structure_block.rs
Normal file
118
pumpkin/src/block/entities/structure_block.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct StructureBlockBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub name: Mutex<String>,
|
||||
pub author: Mutex<String>,
|
||||
pub metadata: Mutex<String>,
|
||||
pub pos_x: Mutex<i32>,
|
||||
pub pos_y: Mutex<i32>,
|
||||
pub pos_z: Mutex<i32>,
|
||||
pub size_x: Mutex<i32>,
|
||||
pub size_y: Mutex<i32>,
|
||||
pub size_z: Mutex<i32>,
|
||||
pub rotation: Mutex<String>,
|
||||
pub mirror: Mutex<String>,
|
||||
pub mode: Mutex<String>,
|
||||
pub ignore_entities: Mutex<bool>,
|
||||
pub show_air: Mutex<bool>,
|
||||
pub show_bounding_box: Mutex<bool>,
|
||||
pub integrity: Mutex<f32>,
|
||||
pub seed: Mutex<i64>,
|
||||
}
|
||||
|
||||
impl BlockEntity for StructureBlockBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
position,
|
||||
name: Mutex::new(nbt.get_string("name").unwrap_or("").to_string()),
|
||||
author: Mutex::new(nbt.get_string("author").unwrap_or("").to_string()),
|
||||
metadata: Mutex::new(nbt.get_string("metadata").unwrap_or("").to_string()),
|
||||
pos_x: Mutex::new(nbt.get_int("posX").unwrap_or(0)),
|
||||
pos_y: Mutex::new(nbt.get_int("posY").unwrap_or(0)),
|
||||
pos_z: Mutex::new(nbt.get_int("posZ").unwrap_or(0)),
|
||||
size_x: Mutex::new(nbt.get_int("sizeX").unwrap_or(0)),
|
||||
size_y: Mutex::new(nbt.get_int("sizeY").unwrap_or(0)),
|
||||
size_z: Mutex::new(nbt.get_int("sizeZ").unwrap_or(0)),
|
||||
rotation: Mutex::new(nbt.get_string("rotation").unwrap_or("NONE").to_string()),
|
||||
mirror: Mutex::new(nbt.get_string("mirror").unwrap_or("NONE").to_string()),
|
||||
mode: Mutex::new(nbt.get_string("mode").unwrap_or("DATA").to_string()),
|
||||
ignore_entities: Mutex::new(nbt.get_bool("ignoreEntities").unwrap_or(true)),
|
||||
show_air: Mutex::new(nbt.get_bool("showAir").unwrap_or(false)),
|
||||
show_bounding_box: Mutex::new(nbt.get_bool("showBoundingBox").unwrap_or(true)),
|
||||
integrity: Mutex::new(nbt.get_float("integrity").unwrap_or(1.0)),
|
||||
seed: Mutex::new(nbt.get_long("seed").unwrap_or(0)),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
nbt.put_string("name", self.name.lock().await.clone());
|
||||
nbt.put_string("author", self.author.lock().await.clone());
|
||||
nbt.put_string("metadata", self.metadata.lock().await.clone());
|
||||
nbt.put_int("posX", *self.pos_x.lock().await);
|
||||
nbt.put_int("posY", *self.pos_y.lock().await);
|
||||
nbt.put_int("posZ", *self.pos_z.lock().await);
|
||||
nbt.put_int("sizeX", *self.size_x.lock().await);
|
||||
nbt.put_int("sizeY", *self.size_y.lock().await);
|
||||
nbt.put_int("sizeZ", *self.size_z.lock().await);
|
||||
nbt.put_string("rotation", self.rotation.lock().await.clone());
|
||||
nbt.put_string("mirror", self.mirror.lock().await.clone());
|
||||
nbt.put_string("mode", self.mode.lock().await.clone());
|
||||
nbt.put_bool("ignoreEntities", *self.ignore_entities.lock().await);
|
||||
nbt.put_bool("showAir", *self.show_air.lock().await);
|
||||
nbt.put_bool("showBoundingBox", *self.show_bounding_box.lock().await);
|
||||
nbt.put_float("integrity", *self.integrity.lock().await);
|
||||
nbt.put_long("seed", *self.seed.lock().await);
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl StructureBlockBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:structure_block";
|
||||
#[must_use]
|
||||
pub fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
name: Mutex::new(String::new()),
|
||||
author: Mutex::new(String::new()),
|
||||
metadata: Mutex::new(String::new()),
|
||||
pos_x: Mutex::new(0),
|
||||
pos_y: Mutex::new(0),
|
||||
pos_z: Mutex::new(0),
|
||||
size_x: Mutex::new(0),
|
||||
size_y: Mutex::new(0),
|
||||
size_z: Mutex::new(0),
|
||||
rotation: Mutex::new("NONE".to_string()),
|
||||
mirror: Mutex::new("NONE".to_string()),
|
||||
mode: Mutex::new("DATA".to_string()),
|
||||
ignore_entities: Mutex::new(true),
|
||||
show_air: Mutex::new(false),
|
||||
show_bounding_box: Mutex::new(true),
|
||||
integrity: Mutex::new(1.0),
|
||||
seed: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
46
pumpkin/src/block/entities/test_block.rs
Normal file
46
pumpkin/src/block/entities/test_block.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::BlockEntity;
|
||||
|
||||
pub struct TestBlockBlockEntity {
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
impl BlockEntity for TestBlockBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(_nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self { position }
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
_nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TestBlockBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:test_block";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
46
pumpkin/src/block/entities/test_instance_block.rs
Normal file
46
pumpkin/src/block/entities/test_instance_block.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use super::BlockEntity;
|
||||
|
||||
pub struct TestInstanceBlockBlockEntity {
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
impl BlockEntity for TestInstanceBlockBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(_nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self { position }
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
_nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async {})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TestInstanceBlockBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:test_instance_block";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
68
pumpkin/src/block/entities/trial_spawner.rs
Normal file
68
pumpkin/src/block/entities/trial_spawner.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct TrialSpawnerBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub normal_config: Mutex<Option<NbtCompound>>,
|
||||
pub ominous_config: Mutex<Option<NbtCompound>>,
|
||||
pub spawner_data: Mutex<Option<NbtCompound>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for TrialSpawnerBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
position,
|
||||
normal_config: Mutex::new(nbt.get_compound("normal_config").cloned()),
|
||||
ominous_config: Mutex::new(nbt.get_compound("ominous_config").cloned()),
|
||||
spawner_data: Mutex::new(nbt.get_compound("spawner_data").cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(cfg) = self.normal_config.lock().await.as_ref() {
|
||||
nbt.put_compound("normal_config", cfg.clone());
|
||||
}
|
||||
if let Some(cfg) = self.ominous_config.lock().await.as_ref() {
|
||||
nbt.put_compound("ominous_config", cfg.clone());
|
||||
}
|
||||
if let Some(data) = self.spawner_data.lock().await.as_ref() {
|
||||
nbt.put_compound("spawner_data", data.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TrialSpawnerBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:trial_spawner";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
normal_config: Mutex::const_new(None),
|
||||
ominous_config: Mutex::const_new(None),
|
||||
spawner_data: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
62
pumpkin/src/block/entities/vault.rs
Normal file
62
pumpkin/src/block/entities/vault.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use super::BlockEntity;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub struct VaultBlockEntity {
|
||||
pub position: BlockPos,
|
||||
pub config: Mutex<Option<NbtCompound>>,
|
||||
pub server_data: Mutex<Option<NbtCompound>>,
|
||||
}
|
||||
|
||||
impl BlockEntity for VaultBlockEntity {
|
||||
fn resource_location(&self) -> &'static str {
|
||||
Self::ID
|
||||
}
|
||||
|
||||
fn get_position(&self) -> BlockPos {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
position,
|
||||
config: Mutex::new(nbt.get_compound("config").cloned()),
|
||||
server_data: Mutex::new(nbt.get_compound("server_data").cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_nbt<'a>(
|
||||
&'a self,
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(cfg) = self.config.lock().await.as_ref() {
|
||||
nbt.put_compound("config", cfg.clone());
|
||||
}
|
||||
if let Some(data) = self.server_data.lock().await.as_ref() {
|
||||
nbt.put_compound("server_data", data.clone());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultBlockEntity {
|
||||
pub const ID: &'static str = "minecraft:vault";
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
Self {
|
||||
position,
|
||||
config: Mutex::const_new(None),
|
||||
server_data: Mutex::const_new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -919,6 +919,15 @@ impl BlockRegistry {
|
||||
old_state_id: BlockStateId,
|
||||
notify: bool,
|
||||
) {
|
||||
let state = world.get_block_state(position);
|
||||
if state.block_entity_type != u16::MAX
|
||||
&& world.get_block_entity(position).is_none()
|
||||
&& let Some(entity) =
|
||||
crate::block::entities::create_block_entity(state.block_entity_type, *position)
|
||||
{
|
||||
world.add_block_entity(entity);
|
||||
}
|
||||
|
||||
let pumpkin_block = self.get_pumpkin_block(block.id);
|
||||
if let Some(pumpkin_block) = pumpkin_block {
|
||||
pumpkin_block
|
||||
|
||||
Loading…
Reference in New Issue
Block a user