refactor: move block entities from pumpkin-world -> pumpkin

This commit is contained in:
Alexander Medvedev 2026-05-07 19:35:17 +02:00
parent 2586d46380
commit 5c90387b02
87 changed files with 1514 additions and 1180 deletions

425
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -179,7 +179,7 @@ ureq = "3.3.0"
notify = "8.2.0" notify = "8.2.0"
wasmtime = "44.0" wasmtime = "44.0"
wasmtime-wasi = "44.0" wasmtime-wasi = { version = "44.0", default-features = false, features = ["p2"] }
wit-bindgen = "0.57" wit-bindgen = "0.57"
wasmparser = "0.248" wasmparser = "0.248"

View File

@ -0,0 +1,136 @@
use std::{any::Any, sync::Arc};
use pumpkin_data::{item_stack::ItemStack, screen::WindowType};
use pumpkin_world::inventory::Inventory;
use crate::{
player::player_inventory::PlayerInventory,
screen_handler::{
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
ScreenHandlerFuture,
},
slot::NormalSlot,
};
/// Creates a beacon container screen handler.
///
/// Beacons feature a single payment slot and a specialized UI for selecting status effects.
pub async fn create_beacon_handler(
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
inventory: Arc<dyn Inventory>,
) -> BeaconScreenHandler {
BeaconScreenHandler::new(sync_id, player_inventory, inventory).await
}
/// Screen handler specifically for Beacon blocks.
pub struct BeaconScreenHandler {
/// The beacon's inventory (contains exactly 1 slot for payment).
pub inventory: Arc<dyn Inventory>,
/// Core screen handler behavior (slots, sync ID, listeners).
behaviour: ScreenHandlerBehaviour,
}
impl BeaconScreenHandler {
/// Creates a new beacon screen handler.
async fn new(
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
inventory: Arc<dyn Inventory>,
) -> Self {
let mut handler = Self {
inventory: inventory.clone(),
behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Beacon)),
};
inventory.on_open().await;
// Add the single payment slot for the beacon (slot 0)
handler.add_slot(Arc::new(NormalSlot::new(handler.inventory.clone(), 0)));
// Add the player's inventory slots (27 slots + 9 hotbar)
let player_inventory_arc: Arc<dyn Inventory> = player_inventory.clone();
handler.add_player_slots(&player_inventory_arc);
handler
}
}
impl ScreenHandler for BeaconScreenHandler {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn get_behaviour(&self) -> &ScreenHandlerBehaviour {
&self.behaviour
}
fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour {
&mut self.behaviour
}
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move {
self.default_on_closed(player).await;
self.inventory.on_close().await;
})
}
/// Quick move logic specifically for the beacon UI.
///
/// - From beacon payment slot (0): Move to player inventory
/// - From player inventory (1+): Move to beacon payment slot
fn quick_move<'a>(
&'a mut self,
_player: &'a dyn InventoryPlayer,
slot_index: i32,
) -> ItemStackFuture<'a> {
Box::pin(async move {
let mut stack_left = ItemStack::EMPTY.clone();
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if slot.has_stack().await {
let slot_stack_lock = slot.get_stack().await;
let slot_stack_guard = slot_stack_lock.lock().await;
stack_left = slot_stack_guard.clone();
drop(slot_stack_guard);
let mut slot_stack_mut = slot_stack_lock.lock().await;
if slot_index == 0 {
// Move from the single beacon slot to the player inventory (slots 1 to end)
if !self
.insert_item(
&mut slot_stack_mut,
1,
self.get_behaviour().slots.len() as i32,
true,
)
.await
{
return ItemStack::EMPTY.clone();
}
} else {
// Move from player inventory into the beacon payment slot (slot 0)
if !self.insert_item(&mut slot_stack_mut, 0, 1, false).await {
return ItemStack::EMPTY.clone();
}
}
if slot_stack_mut.is_empty() {
drop(slot_stack_mut);
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
drop(slot_stack_mut);
slot.mark_dirty().await;
}
}
stack_left
})
}
}

View File

@ -20,7 +20,7 @@ use std::{any::Any, pin::Pin, sync::Arc};
use pumpkin_data::{fuels::is_fuel, item_stack::ItemStack, screen::WindowType}; use pumpkin_data::{fuels::is_fuel, item_stack::ItemStack, screen::WindowType};
use pumpkin_world::{ use pumpkin_world::{
block::entities::{PropertyDelegate, furnace_like_block_entity::ExperienceContainer}, block::entities::{ExperienceContainer, PropertyDelegate},
inventory::Inventory, inventory::Inventory,
}; };

View File

@ -9,9 +9,7 @@
use std::sync::{Arc, atomic::AtomicU8}; use std::sync::{Arc, atomic::AtomicU8};
use pumpkin_data::{fuels::is_fuel, item::Item}; use pumpkin_data::{fuels::is_fuel, item::Item};
use pumpkin_world::{ use pumpkin_world::{block::entities::ExperienceContainer, inventory::Inventory};
block::entities::furnace_like_block_entity::ExperienceContainer, inventory::Inventory,
};
use tracing::debug; use tracing::debug;

View File

@ -31,6 +31,7 @@
//! [`SyncHandler`]: sync_handler::SyncHandler //! [`SyncHandler`]: sync_handler::SyncHandler
pub mod anvil; pub mod anvil;
pub mod beacon_screen_handler;
pub mod brewing; pub mod brewing;
pub mod container_click; pub mod container_click;
pub mod crafting; pub mod crafting;

View File

@ -5,7 +5,6 @@ use pumpkin_data::block_state_remap::remap_block_state_for_version;
use pumpkin_data::packet::CURRENT_MC_VERSION; use pumpkin_data::packet::CURRENT_MC_VERSION;
use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT; use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_nbt::END_ID;
use pumpkin_util::math::position::get_local_cord; use pumpkin_util::math::position::get_local_cord;
use pumpkin_util::version::MinecraftVersion; use pumpkin_util::version::MinecraftVersion;
use pumpkin_world::chunk::format::LightContainer; use pumpkin_world::chunk::format::LightContainer;
@ -195,23 +194,26 @@ impl ClientPacket for CChunkData<'_> {
let block_entities = self let block_entities = self
.0 .0
.block_entities .pending_block_entities
.lock() .lock()
.map_err(|_| WritingError::Message("block_entities lock poisoned".into()))?; .map_err(|_| WritingError::Message("block_entities lock poisoned".into()))?;
write.write_var_int(&VarInt(block_entities.len() as i32))?; write.write_var_int(&VarInt(block_entities.len() as i32))?;
for block_entity in block_entities.values() { for (pos, nbt) in block_entities.iter() {
let pos = block_entity.get_position();
let local_xz = ((get_local_cord(pos.0.x) & 0xF) << 4) | (get_local_cord(pos.0.z) & 0xF); let local_xz = ((get_local_cord(pos.0.x) & 0xF) << 4) | (get_local_cord(pos.0.z) & 0xF);
write.write_u8(local_xz as u8)?; write.write_u8(local_xz as u8)?;
write.write_i16_be(pos.0.y as i16)?; write.write_i16_be(pos.0.y as i16)?;
write.write_var_int(&VarInt(block_entity.get_id() as i32))?;
if let Some(nbt) = block_entity.chunk_data_nbt() { let id = nbt.get_string("id").map_or(0, |id_str| {
write.write_nbt(nbt.into())?; let name = id_str.split(':').next_back().unwrap_or(id_str);
} else { pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
write.write_u8(END_ID)?; .iter()
} .position(|&n| n == name)
.unwrap_or(0)
});
write.write_var_int(&VarInt(id as i32))?;
write.write_nbt(nbt.clone().into())?;
} }
{ {

View File

@ -34,6 +34,7 @@ mod recipe_book_change_settings;
mod recipe_book_seen_recipe; mod recipe_book_seen_recipe;
mod rename_item; mod rename_item;
mod select_trade; mod select_trade;
mod set_beacon;
mod set_command_block; mod set_command_block;
mod set_creative_slot; mod set_creative_slot;
mod set_held_item; mod set_held_item;
@ -78,6 +79,7 @@ pub use recipe_book_change_settings::*;
pub use recipe_book_seen_recipe::*; pub use recipe_book_seen_recipe::*;
pub use rename_item::*; pub use rename_item::*;
pub use select_trade::*; pub use select_trade::*;
pub use set_beacon::*;
pub use set_command_block::*; pub use set_command_block::*;
pub use set_creative_slot::*; pub use set_creative_slot::*;
pub use set_held_item::*; pub use set_held_item::*;

View File

@ -0,0 +1,12 @@
use pumpkin_data::packet::serverbound::PLAY_SET_BEACON;
use pumpkin_macros::java_packet;
use serde::Deserialize;
use crate::codec::var_int::VarInt;
#[derive(Deserialize)]
#[java_packet(PLAY_SET_BEACON)]
pub struct SSetBeacon {
pub primary_effect: Option<VarInt>,
pub secondary_effect: Option<VarInt>,
}

View File

@ -94,6 +94,49 @@ impl BoundingBox {
} }
} }
/// Expands this bounding box towards a specific direction.
///
/// If a provided value is negative, it extends the minimum boundary along that axis.
/// If a provided value is positive, it extends the maximum boundary along that axis.
///
/// # Arguments
/// * `x` – Amount to expand towards on the X axis.
/// * `y` – Amount to expand towards on the Y axis.
/// * `z` – Amount to expand towards on the Z axis.
#[must_use]
pub fn expand_towards(&self, x: f64, y: f64, z: f64) -> Self {
let mut min_x = self.min.x;
let mut min_y = self.min.y;
let mut min_z = self.min.z;
let mut max_x = self.max.x;
let mut max_y = self.max.y;
let mut max_z = self.max.z;
if x < 0.0 {
min_x += x;
} else if x > 0.0 {
max_x += x;
}
if y < 0.0 {
min_y += y;
} else if y > 0.0 {
max_y += y;
}
if z < 0.0 {
min_z += z;
} else if z > 0.0 {
max_z += z;
}
Self {
min: Vector3::new(min_x, min_y, min_z),
max: Vector3::new(max_x, max_y, max_z),
}
}
/// Expands this box uniformly along all axes. /// Expands this box uniformly along all axes.
/// ///
/// # Arguments /// # Arguments

View File

@ -0,0 +1,11 @@
pub trait PropertyDelegate: Sync + Send {
fn get_property(&self, _index: i32) -> i32;
fn set_property(&self, _index: i32, _value: i32);
fn get_properties_size(&self) -> i32;
}
/// Trait for extracting smelting experience from cooking block entities.
pub trait ExperienceContainer: Send + Sync {
/// Extract and reset accumulated experience, returning the total as an integer
fn extract_experience(&self) -> i32;
}

View File

@ -1,209 +0,0 @@
use std::pin::Pin;
use std::{any::Any, sync::Arc};
use barrel::BarrelBlockEntity;
use bed::BedBlockEntity;
use brewing_stand::BrewingStandBlockEntity;
use chest::ChestBlockEntity;
use comparator::ComparatorBlockEntity;
use daylight_detector::DaylightDetectorBlockEntity;
use end_portal::EndPortalBlockEntity;
use furnace::FurnaceBlockEntity;
use furnace_like_block_entity::ExperienceContainer;
use piston::PistonBlockEntity;
use pumpkin_data::{Block, block_properties::BLOCK_ENTITY_TYPES};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use sign::SignBlockEntity;
use trapped_chest::TrappedChestBlockEntity;
use crate::block::entities::bell::BellBlockEntity;
use crate::block::entities::blasting_furnace::BlastingFurnaceBlockEntity;
use crate::block::entities::command_block::CommandBlockEntity;
use crate::block::entities::ender_chest::EnderChestBlockEntity;
use crate::block::entities::hopper::HopperBlockEntity;
use crate::block::entities::jukebox::JukeboxBlockEntity;
use crate::block::entities::lectern::LecternBlockEntity;
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity;
use crate::block::entities::shulker_box::ShulkerBoxBlockEntity;
use crate::block::entities::smoker::SmokerBlockEntity;
use crate::{
BlockStateId, block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity,
block::entities::dropper::DropperBlockEntity, inventory::Inventory, world::SimpleWorld,
};
pub mod barrel;
pub mod bed;
pub mod bell;
pub mod blasting_furnace;
pub mod brewing_stand;
pub mod chest;
pub mod chest_like_block_entity;
pub mod chiseled_bookshelf;
pub mod command_block;
pub mod comparator;
pub mod daylight_detector;
pub mod dropper;
pub mod end_portal;
pub mod ender_chest;
pub mod furnace;
pub mod furnace_like_block_entity;
pub mod hopper;
pub mod jukebox;
pub mod lectern;
pub mod mob_spawner;
pub mod piston;
pub mod shulker_box;
pub mod sign;
pub mod smoker;
pub mod trapped_chest;
//TODO: We need a mark_dirty for chests
pub trait BlockEntity: Any + Send + Sync {
fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
where
Self: Sized;
fn tick<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async {})
}
fn resource_location(&self) -> &'static str;
fn get_position(&self) -> BlockPos;
fn write_internal<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put_string("id", self.resource_location().to_string());
let position = self.get_position();
nbt.put_int("x", position.0.x);
nbt.put_int("y", position.0.y);
nbt.put_int("z", position.0.z);
self.write_nbt(nbt).await;
})
}
fn get_id(&self) -> u32 {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
.iter()
.position(|block_entity_name| {
*block_entity_name == self.resource_location().split(':').next_back().unwrap()
})
.unwrap() as u32
}
/// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData)
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
None
}
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
None
}
fn set_block_state(&mut self, _block_state: BlockStateId) {}
fn on_block_replaced<'a>(
self: Arc<Self>,
world: Arc<dyn SimpleWorld>,
position: BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
Self: 'a,
{
Box::pin(async move {
if let Some(inventory) = self.get_inventory() {
// Assuming scatter_inventory is an async method on SimpleWorld
world.scatter_inventory(&position, &inventory).await;
}
})
}
fn is_dirty(&self) -> bool {
false
}
fn clear_dirty(&self) {
// Default implementation does nothing
// Override in implementations that have a dirty flag
}
fn as_any(&self) -> &dyn Any;
fn to_property_delegate(self: Arc<Self>) -> Option<Arc<dyn PropertyDelegate>> {
None
}
fn to_experience_container(self: Arc<Self>) -> Option<Arc<dyn ExperienceContainer>> {
None
}
}
#[must_use]
pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T {
let x = nbt.get_int("x").unwrap();
let y = nbt.get_int("y").unwrap();
let z = nbt.get_int("z").unwrap();
T::from_nbt(nbt, BlockPos::new(x, y, z))
}
#[must_use]
pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>> {
Some(match nbt.get_string("id").unwrap() {
ChestBlockEntity::ID => Arc::new(block_entity_from_generic::<ChestBlockEntity>(nbt)),
TrappedChestBlockEntity::ID => {
Arc::new(block_entity_from_generic::<TrappedChestBlockEntity>(nbt))
}
EnderChestBlockEntity::ID => {
Arc::new(block_entity_from_generic::<EnderChestBlockEntity>(nbt))
}
JukeboxBlockEntity::ID => Arc::new(block_entity_from_generic::<JukeboxBlockEntity>(nbt)),
SignBlockEntity::ID => Arc::new(block_entity_from_generic::<SignBlockEntity>(nbt)),
BedBlockEntity::ID => Arc::new(block_entity_from_generic::<BedBlockEntity>(nbt)),
ComparatorBlockEntity::ID => {
Arc::new(block_entity_from_generic::<ComparatorBlockEntity>(nbt))
}
BarrelBlockEntity::ID => Arc::new(block_entity_from_generic::<BarrelBlockEntity>(nbt)),
HopperBlockEntity::ID => Arc::new(block_entity_from_generic::<HopperBlockEntity>(nbt)),
MobSpawnerBlockEntity::ID => {
Arc::new(block_entity_from_generic::<MobSpawnerBlockEntity>(nbt))
}
DropperBlockEntity::ID => Arc::new(block_entity_from_generic::<DropperBlockEntity>(nbt)),
ShulkerBoxBlockEntity::ID => {
Arc::new(block_entity_from_generic::<ShulkerBoxBlockEntity>(nbt))
}
PistonBlockEntity::ID => Arc::new(block_entity_from_generic::<PistonBlockEntity>(nbt)),
EndPortalBlockEntity::ID => {
Arc::new(block_entity_from_generic::<EndPortalBlockEntity>(nbt))
}
ChiseledBookshelfBlockEntity::ID => Arc::new(block_entity_from_generic::<
ChiseledBookshelfBlockEntity,
>(nbt)),
FurnaceBlockEntity::ID => Arc::new(block_entity_from_generic::<FurnaceBlockEntity>(nbt)),
CommandBlockEntity::ID => Arc::new(block_entity_from_generic::<CommandBlockEntity>(nbt)),
BlastingFurnaceBlockEntity::ID => {
Arc::new(block_entity_from_generic::<BlastingFurnaceBlockEntity>(nbt))
}
SmokerBlockEntity::ID => Arc::new(block_entity_from_generic::<SmokerBlockEntity>(nbt)),
DaylightDetectorBlockEntity::ID => Arc::new(block_entity_from_generic::<
DaylightDetectorBlockEntity,
>(nbt)),
BrewingStandBlockEntity::ID => {
Arc::new(block_entity_from_generic::<BrewingStandBlockEntity>(nbt))
}
BellBlockEntity::ID => Arc::new(block_entity_from_generic::<BellBlockEntity>(nbt)),
LecternBlockEntity::ID => Arc::new(block_entity_from_generic::<LecternBlockEntity>(nbt)),
_ => return None,
})
}
#[must_use]
pub fn has_block_block_entity(block: &Block) -> bool {
BLOCK_ENTITY_TYPES.contains(&block.name)
}
pub trait PropertyDelegate: Sync + Send {
fn get_property(&self, _index: i32) -> i32;
fn set_property(&self, _index: i32, _value: i32);
fn get_properties_size(&self) -> i32;
}

View File

@ -1,19 +1,9 @@
use std::{ use std::sync::atomic::{AtomicU16, Ordering};
pin::Pin,
sync::{
Arc,
atomic::{AtomicU16, Ordering},
},
};
use pumpkin_util::math::position::BlockPos;
use crate::{block::entities::BlockEntity, world::SimpleWorld};
#[derive(Debug)] #[derive(Debug)]
pub struct ViewerCountTracker { pub struct ViewerCountTracker {
old: AtomicU16, pub old: AtomicU16,
current: AtomicU16, pub current: AtomicU16,
} }
impl Default for ViewerCountTracker { impl Default for ViewerCountTracker {
@ -43,67 +33,4 @@ impl ViewerCountTracker {
pub fn get_viewer_count(&self) -> u16 { pub fn get_viewer_count(&self) -> u16 {
self.current.load(Ordering::Relaxed) self.current.load(Ordering::Relaxed)
} }
pub async fn update_viewer_count<T>(
&self,
entity: &T,
world: &Arc<dyn SimpleWorld>,
position: &BlockPos,
) where
T: BlockEntity + ViewerCountListener + 'static,
{
let current = self.current.load(Ordering::Relaxed);
let old = self.old.swap(current, Ordering::Relaxed);
if old != current {
match (old, current) {
(n, 0) if n > 0 => {
entity.on_container_close(world, position).await;
// TODO: world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, pos);
// TODO: this.maxBlockInteractionRange = 0.0;
}
(0, n) if n > 0 => {
entity.on_container_open(world, position).await;
// TODO: world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, pos);
// TODO: scheduleBlockTick(world, pos, state);
}
_ => {} // Ignore
}
entity
.on_viewer_count_update(world, position, old, current)
.await;
}
// TODO: Requires players
}
}
pub type ViewerFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait ViewerCountListener: Send + Sync {
fn on_container_open<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_container_close<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_viewer_count_update<'a>(
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
_position: &'a BlockPos,
_old: u16,
_new: u16,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
} }

View File

@ -8,7 +8,6 @@ use std::{
}; };
use bytes::Bytes; use bytes::Bytes;
use futures::future::join_all;
use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid}; use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid};
use pumpkin_nbt::{compound::NbtCompound, nbt_long_array}; use pumpkin_nbt::{compound::NbtCompound, nbt_long_array};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -16,7 +15,6 @@ use tokio::sync::Mutex;
use tracing::debug; use tracing::debug;
use crate::{ use crate::{
block::entities::block_entity_from_nbt,
chunk::{ chunk::{
ChunkEntityData, ChunkReadingError, ChunkSerializingError, ChunkEntityData, ChunkReadingError, ChunkSerializingError,
format::anvil::{SingleChunkDataSerializer, WORLD_DATA_VERSION}, format::anvil::{SingleChunkDataSerializer, WORLD_DATA_VERSION},
@ -26,6 +24,7 @@ use crate::{
level::LevelFolder, level::LevelFolder,
tick::{ScheduledTick, scheduler::ChunkTickScheduler}, tick::{ScheduledTick, scheduler::ChunkTickScheduler},
}; };
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector2::Vector2;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -67,36 +66,11 @@ impl Dirtiable for ChunkData {
#[inline] #[inline]
fn mark_dirty(&self, flag: bool) { fn mark_dirty(&self, flag: bool) {
self.dirty.store(flag, Ordering::Relaxed); self.dirty.store(flag, Ordering::Relaxed);
if flag {
return;
}
// When marking chunk as clean, also clear all block entity dirty flags
if let Ok(block_entities) = self.block_entities.lock() {
for block_entity in block_entities.values() {
block_entity.clear_dirty();
}
}
} }
#[inline] #[inline]
fn is_dirty(&self) -> bool { fn is_dirty(&self) -> bool {
// Check if chunk itself is dirty self.dirty.load(Ordering::Relaxed)
if self.dirty.load(Ordering::Relaxed) {
return true;
}
// Also check if any block entities are dirty (e.g., inventory changes)
if let Ok(block_entities) = self.block_entities.lock() {
for block_entity in block_entities.values() {
if block_entity.is_dirty() {
return true;
}
}
}
false
} }
} }
@ -180,12 +154,14 @@ impl ChunkData {
dirty: AtomicBool::new(false), dirty: AtomicBool::new(false),
block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks), block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks),
fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks), fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks),
block_entities: { pending_block_entities: {
let mut block_entities = FxHashMap::default(); let mut block_entities = FxHashMap::default();
for nbt in chunk_data.block_entities { for nbt in chunk_data.block_entities {
let block_entity = block_entity_from_nbt(&nbt); if let Some(x) = nbt.get_int("x")
if let Some(block_entity) = block_entity { && let Some(y) = nbt.get_int("y")
block_entities.insert(block_entity.get_position(), block_entity); && let Some(z) = nbt.get_int("z")
{
block_entities.insert(BlockPos::new(x, y, z), nbt);
} }
} }
std::sync::Mutex::new(block_entities) std::sync::Mutex::new(block_entities)
@ -202,20 +178,11 @@ impl ChunkData {
.light_populated .light_populated
.load(std::sync::atomic::Ordering::Relaxed); .load(std::sync::atomic::Ordering::Relaxed);
let entities_to_serialize = { let block_entities_nbt = {
let entities_guard = self.block_entities.lock().unwrap(); let entities_guard = self.pending_block_entities.lock().unwrap();
entities_guard.values().cloned().collect::<Vec<_>>() entities_guard.values().cloned().collect::<Vec<_>>()
}; };
let block_entities_nbt = join_all(entities_to_serialize.into_iter().map(
|block_entity| async move {
let mut nbt = NbtCompound::new();
block_entity.write_internal(&mut nbt).await;
nbt
},
))
.await;
fn extract_light_ref(light: Option<&LightContainer>) -> Option<&[u8]> { fn extract_light_ref(light: Option<&LightContainer>) -> Option<&[u8]> {
match light { match light {
Some(LightContainer::Full(data)) => Some(data.as_ref()), Some(LightContainer::Full(data)) => Some(data.as_ref()),

View File

@ -1,5 +1,4 @@
use crate::BlockStateId; use crate::BlockStateId;
use crate::block::entities::BlockEntity;
use crate::chunk::format::LightContainer; use crate::chunk::format::LightContainer;
use crate::tick::scheduler::ChunkTickScheduler; use crate::tick::scheduler::ChunkTickScheduler;
use palette::{BiomePalette, BlockPalette, has_random_ticking_fluid}; use palette::{BiomePalette, BlockPalette, has_random_ticking_fluid};
@ -8,12 +7,13 @@ use pumpkin_data::chunk::ChunkStatus;
use pumpkin_data::fluid::Fluid; use pumpkin_data::fluid::Fluid;
use pumpkin_data::tag::Block::MINECRAFT_LEAVES; use pumpkin_data::tag::Block::MINECRAFT_LEAVES;
use pumpkin_data::{Block, BlockState}; use pumpkin_data::{Block, BlockState};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::nbt_long_array; use pumpkin_nbt::nbt_long_array;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::RwLock; use std::sync::RwLock;
use std::sync::{Arc, atomic::AtomicBool}; use std::sync::atomic::AtomicBool;
use thiserror::Error; use thiserror::Error;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -76,7 +76,7 @@ pub struct ChunkData {
pub z: i32, pub z: i32,
pub block_ticks: ChunkTickScheduler<&'static Block>, pub block_ticks: ChunkTickScheduler<&'static Block>,
pub fluid_ticks: ChunkTickScheduler<&'static Fluid>, pub fluid_ticks: ChunkTickScheduler<&'static Fluid>,
pub block_entities: std::sync::Mutex<FxHashMap<BlockPos, Arc<dyn BlockEntity>>>, pub pending_block_entities: std::sync::Mutex<FxHashMap<BlockPos, NbtCompound>>,
pub light_engine: std::sync::Mutex<ChunkLight>, pub light_engine: std::sync::Mutex<ChunkLight>,
pub light_populated: AtomicBool, pub light_populated: AtomicBool,
pub status: ChunkStatus, pub status: ChunkStatus,

View File

@ -1,4 +1,3 @@
use crate::block::entities::block_entity_from_nbt;
use crate::chunk::{ChunkData, ChunkLight, ChunkSections}; use crate::chunk::{ChunkData, ChunkLight, ChunkSections};
use crate::generation::biome_coords; use crate::generation::biome_coords;
use pumpkin_config::lighting::LightingEngineConfig; use pumpkin_config::lighting::LightingEngineConfig;
@ -225,7 +224,7 @@ impl Chunk {
z: 0, z: 0,
block_ticks: Default::default(), block_ticks: Default::default(),
fluid_ticks: Default::default(), fluid_ticks: Default::default(),
block_entities: Default::default(), pending_block_entities: Default::default(),
light_engine: Mutex::new(ChunkLight::default()), light_engine: Mutex::new(ChunkLight::default()),
light_populated: AtomicBool::new(false), light_populated: AtomicBool::new(false),
status: ChunkStatus::Empty, status: ChunkStatus::Empty,
@ -298,11 +297,14 @@ impl Chunk {
&& *lighting_config == LightingEngineConfig::Default; && *lighting_config == LightingEngineConfig::Default;
// Convert pending block entities from structure generation to actual block entities // Convert pending block entities from structure generation to actual block entities
let mut block_entities = FxHashMap::default(); let mut pending_block_entities = FxHashMap::default();
for nbt in proto_chunk.pending_block_entities { for nbt in proto_chunk.pending_block_entities {
if let Some(block_entity) = block_entity_from_nbt(&nbt) { if let Some(x) = nbt.get_int("x")
let pos = block_entity.get_position(); && let Some(y) = nbt.get_int("y")
block_entities.insert(pos, block_entity); && let Some(z) = nbt.get_int("z")
{
pending_block_entities
.insert(pumpkin_util::math::position::BlockPos::new(x, y, z), nbt);
} }
} }
@ -316,7 +318,7 @@ impl Chunk {
dirty: AtomicBool::new(true), dirty: AtomicBool::new(true),
block_ticks: Default::default(), block_ticks: Default::default(),
fluid_ticks: Default::default(), fluid_ticks: Default::default(),
block_entities: Mutex::new(block_entities), pending_block_entities: Mutex::new(pending_block_entities),
status: proto_chunk.stage.into(), status: proto_chunk.stage.into(),
blending_data: proto_chunk.blending_data.clone(), blending_data: proto_chunk.blending_data.clone(),
}; };

View File

@ -1,13 +1,11 @@
use pumpkin_data::{Block, BlockDirection, entity::EntityType}; use pumpkin_data::{Block, BlockDirection};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::{ use pumpkin_util::{
math::{position::BlockPos, vector3::Vector3}, math::{position::BlockPos, vector3::Vector3},
random::{RandomGenerator, RandomImpl}, random::{RandomGenerator, RandomImpl},
}; };
use crate::{ use crate::generation::proto_chunk::GenerationCache;
block::entities::mob_spawner::MobSpawnerBlockEntity, generation::proto_chunk::GenerationCache,
};
/// The three mob types that can appear in a dungeon spawner. /// The three mob types that can appear in a dungeon spawner.
/// ///
@ -148,9 +146,19 @@ impl DungeonFeature {
// TODO: set spawner entity type // TODO: set spawner entity type
let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize]; let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize];
chunk.set_block_state(&pos.0, Block::SPAWNER.default_state); chunk.set_block_state(&pos.0, Block::SPAWNER.default_state);
let spawner_block_entity = MobSpawnerBlockEntity::new(pos, EntityType::from_name(mob));
let mut entity_nbt = NbtCompound::new(); let mut entity_nbt = NbtCompound::new();
spawner_block_entity.write_nbt(&mut entity_nbt); entity_nbt.put_string("id", "minecraft:mob_spawner".to_string());
entity_nbt.put_int("x", pos.0.x);
entity_nbt.put_int("y", pos.0.y);
entity_nbt.put_int("z", pos.0.z);
let mut spawn_entry = NbtCompound::new();
let mut entity_nbt_inner = NbtCompound::new();
entity_nbt_inner.put_string("id", mob.to_string());
spawn_entry.put_compound("entity", entity_nbt_inner);
entity_nbt.put_compound("SpawnData", spawn_entry);
chunk.add_block_entity(&pos.0, entity_nbt); chunk.add_block_entity(&pos.0, entity_nbt);
true true

View File

@ -1,18 +1,12 @@
use pumpkin_data::{ use pumpkin_data::{
Block, Block,
block_properties::{BlockProperties, OakFenceLikeProperties}, block_properties::{BlockProperties, OakFenceLikeProperties},
entity::EntityType,
}; };
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::{ use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
BlockDirection,
math::{block_box::BlockBox, position::BlockPos},
random::RandomGenerator,
};
use crate::{ use crate::{
ProtoChunk, ProtoChunk,
block::entities::mob_spawner::MobSpawnerBlockEntity,
generation::structure::{ generation::structure::{
piece::StructurePieceType, piece::StructurePieceType,
structures::{ structures::{
@ -147,10 +141,18 @@ impl StructurePieceBase for BridgePlatformPiece {
spawner_pos.z, spawner_pos.z,
Block::SPAWNER.default_state, Block::SPAWNER.default_state,
); );
let spawner_block_entity =
MobSpawnerBlockEntity::new(BlockPos(spawner_pos), Some(&EntityType::BLAZE));
let mut entity_nbt = NbtCompound::new(); let mut entity_nbt = NbtCompound::new();
spawner_block_entity.write_nbt(&mut entity_nbt); entity_nbt.put_string("id", "minecraft:mob_spawner".to_string());
entity_nbt.put_int("x", spawner_pos.x);
entity_nbt.put_int("y", spawner_pos.y);
entity_nbt.put_int("z", spawner_pos.z);
let mut spawn_entry = NbtCompound::new();
let mut entity_nbt_inner = NbtCompound::new();
entity_nbt_inner.put_string("id", "minecraft:blaze".to_string());
spawn_entry.put_compound("entity", entity_nbt_inner);
entity_nbt.put_compound("SpawnData", spawn_entry);
chunk.add_block_entity(entity_nbt); chunk.add_block_entity(entity_nbt);
} }
} }

View File

@ -4,18 +4,16 @@ use pumpkin_data::{
BlockProperties, EndPortalFrameLikeProperties, HorizontalFacing, OakFenceLikeProperties, BlockProperties, EndPortalFrameLikeProperties, HorizontalFacing, OakFenceLikeProperties,
OakStairsLikeProperties, OakStairsLikeProperties,
}, },
entity::EntityType,
}; };
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::{ use pumpkin_util::{
BlockDirection, BlockDirection,
math::{block_box::BlockBox, position::BlockPos}, math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl}, random::{RandomGenerator, RandomImpl},
}; };
use crate::{ use crate::{
ProtoChunk, ProtoChunk,
block::entities::mob_spawner::MobSpawnerBlockEntity,
generation::structure::{ generation::structure::{
piece::StructurePieceType, piece::StructurePieceType,
structures::{ structures::{
@ -358,10 +356,18 @@ impl StructurePieceBase for PortalRoomPiece {
self.spawner_placed = true; self.spawner_placed = true;
let spawner = Block::SPAWNER.default_state; let spawner = Block::SPAWNER.default_state;
inner.add_block(chunk, spawner, 5, 3, 6, &box_limit); inner.add_block(chunk, spawner, 5, 3, 6, &box_limit);
let spawner_block_entity =
MobSpawnerBlockEntity::new(BlockPos(pos), Some(&EntityType::SILVERFISH));
let mut entity_nbt = NbtCompound::new(); let mut entity_nbt = NbtCompound::new();
spawner_block_entity.write_nbt(&mut entity_nbt); entity_nbt.put_string("id", "minecraft:mob_spawner".to_string());
entity_nbt.put_int("x", pos.x);
entity_nbt.put_int("y", pos.y);
entity_nbt.put_int("z", pos.z);
let mut spawn_entry = NbtCompound::new();
let mut entity_nbt_inner = NbtCompound::new();
entity_nbt_inner.put_string("id", "minecraft:silverfish".to_string());
spawn_entry.put_compound("entity", entity_nbt_inner);
entity_nbt.put_compound("SpawnData", spawn_entry);
chunk.add_block_entity(entity_nbt); chunk.add_block_entity(entity_nbt);
} }
} }

View File

@ -5,7 +5,7 @@ use crate::generation::generator::VanillaGenerator;
use crate::lighting::DynamicLightEngine; use crate::lighting::DynamicLightEngine;
use crate::{ use crate::{
BlockStateId, BlockStateId,
block::{RawBlockState, entities::BlockEntity}, block::RawBlockState,
chunk::{ chunk::{
ChunkData, ChunkEntityData, ChunkReadingError, ChunkData, ChunkEntityData, ChunkReadingError,
format::anvil::AnvilChunkFile, format::anvil::AnvilChunkFile,
@ -110,7 +110,6 @@ pub struct TickData {
pub block_ticks: Vec<OrderedTick<&'static Block>>, pub block_ticks: Vec<OrderedTick<&'static Block>>,
pub fluid_ticks: Vec<OrderedTick<&'static Fluid>>, pub fluid_ticks: Vec<OrderedTick<&'static Fluid>>,
pub random_ticks: Vec<RandomTickSample>, pub random_ticks: Vec<RandomTickSample>,
pub block_entities: Vec<Arc<dyn BlockEntity>>,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -446,7 +445,6 @@ impl Level {
block_ticks: Vec::new(), block_ticks: Vec::new(),
fluid_ticks: Vec::new(), fluid_ticks: Vec::new(),
random_ticks: Vec::with_capacity(active_chunks.len() * 3), random_ticks: Vec::with_capacity(active_chunks.len() * 3),
block_entities: Vec::new(),
}; };
// 1. Process active chunks (random ticks, block entities) // 1. Process active chunks (random ticks, block entities)
@ -457,10 +455,6 @@ impl Level {
let chunk_z_base = chunk.z * 16; let chunk_z_base = chunk.z * 16;
let section_count = chunk.section.count; let section_count = chunk.section.count;
ticks
.block_entities
.extend(chunk.block_entities.lock().unwrap().values().cloned());
// Use the bitmask to skip sections // Use the bitmask to skip sections
let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed); let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed);
if mask != 0 { if mask != 0 {

View File

@ -1,17 +1,9 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc;
use crate::block::entities::BlockEntity; use crate::BlockStateId;
use crate::{BlockStateId, inventory::Inventory, level::Level};
use bitflags::bitflags; use bitflags::bitflags;
use pumpkin_data::dimension::Dimension; use pumpkin_data::{Block, BlockState};
use pumpkin_data::entity::EntityType;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_util::math::boundingbox::BoundingBox;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use thiserror::Error; use thiserror::Error;
bitflags! { bitflags! {
@ -63,97 +55,6 @@ impl std::fmt::Display for GetBlockError {
pub type WorldFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; pub type WorldFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait SimpleWorld: BlockAccessor + Send + Sync {
fn set_block_state(
self: Arc<Self>,
position: &BlockPos,
block_state_id: BlockStateId,
flags: BlockFlags,
) -> WorldFuture<'_, BlockStateId>;
fn update_neighbor<'a>(
self: Arc<Self>,
neighbor_block_pos: &'a BlockPos,
source_block: &'a pumpkin_data::Block,
) -> WorldFuture<'a, ()>;
fn update_neighbors(
self: Arc<Self>,
block_pos: &BlockPos,
except: Option<BlockDirection>,
) -> WorldFuture<'_, ()>;
fn is_space_empty(&self, bounding_box: BoundingBox) -> WorldFuture<'_, bool>;
fn spawn_from_type(
self: Arc<Self>,
entity_type: &'static EntityType,
position: Vector3<f64>,
) -> WorldFuture<'static, ()>;
fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) -> WorldFuture<'_, ()>;
fn sync_world_event(
&self,
world_event: WorldEvent,
position: BlockPos,
data: i32,
) -> WorldFuture<'_, ()>;
fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()>;
fn get_block_entity<'a>(
&'a self,
block_pos: &'a BlockPos,
) -> WorldFuture<'a, Option<Arc<dyn BlockEntity>>>;
fn get_world_age(&self) -> WorldFuture<'_, i64>;
fn get_time_of_day(&self) -> WorldFuture<'_, i64>;
fn get_level(&self) -> WorldFuture<'_, &Arc<Level>>;
fn get_dimension(&self) -> WorldFuture<'_, &Dimension>;
fn play_sound<'a>(
&'a self,
sound: Sound,
category: SoundCategory,
position: &'a Vector3<f64>,
) -> WorldFuture<'a, ()>;
fn play_sound_fine<'a>(
&'a self,
sound: Sound,
category: SoundCategory,
position: &'a Vector3<f64>,
volume: f32,
pitch: f32,
) -> WorldFuture<'a, ()>;
/* ItemScatterer */
fn scatter_inventory<'a>(
self: Arc<Self>,
position: &'a BlockPos,
inventory: &'a Arc<dyn Inventory>,
) -> WorldFuture<'a, ()>;
/// Spawn experience orbs at the given position with the specified amount
fn spawn_experience_orbs(
self: Arc<Self>,
position: Vector3<f64>,
amount: u32,
) -> WorldFuture<'static, ()>;
/// `Block.updateFromNeighbourShapes`: updates a block state by calling
/// `get_state_for_neighbor_update` on itself for each of the 6 directions.
fn update_from_neighbor_shapes(
self: Arc<Self>,
block_state_id: BlockStateId,
position: &BlockPos,
) -> WorldFuture<'_, BlockStateId>;
}
pub trait BlockRegistryExt: Send + Sync { pub trait BlockRegistryExt: Send + Sync {
fn can_place_at( fn can_place_at(
&self, &self,

View File

@ -92,7 +92,7 @@ rustc-hash.workspace = true
tokio-util = { workspace = true, features = ["rt"] } tokio-util = { workspace = true, features = ["rt"] }
# System details & Rustc version # System details & Rustc version
sysinfo = "0.38" sysinfo = "0.39"
rustc_version_runtime = "0.3" rustc_version_runtime = "0.3"
flate2.workspace = true flate2.workspace = true

View File

@ -6,6 +6,7 @@ use crate::block::{
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
}; };
use crate::block::entities::barrel::BarrelBlockEntity;
use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties}; use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties};
use pumpkin_data::translation; use pumpkin_data::translation;
use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3;
@ -16,7 +17,6 @@ use pumpkin_inventory::screen_handler::{
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::barrel::BarrelBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use tokio::sync::Mutex; use tokio::sync::Mutex;

View File

@ -0,0 +1,69 @@
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::block::entities::BlockEntity;
use pumpkin_data::translation;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent;
use pumpkin_world::inventory::Inventory;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
// Create the factory just like ChestScreenFactory
struct BeaconScreenFactory(Arc<dyn Inventory>);
impl ScreenHandlerFactory for BeaconScreenFactory {
fn create_screen_handler<'a>(
&'a self,
sync_id: u8,
player_inventory: &'a Arc<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
Box::pin(async move {
// Assumes create_beacon_handler exists in your generic_container_screen_handler equivalent
use pumpkin_inventory::beacon_screen_handler::create_beacon_handler;
let concrete_handler =
create_beacon_handler(sync_id, player_inventory, self.0.clone()).await;
let concrete_arc = Arc::new(Mutex::new(concrete_handler));
Some(concrete_arc as SharedScreenHandler)
})
}
fn get_display_name(&self) -> TextComponent {
TextComponent::translate_cross(
translation::java::CONTAINER_BEACON,
translation::bedrock::CONTAINER_BEACON,
&[],
)
}
}
#[pumpkin_block("minecraft:beacon")]
pub struct BeaconBlock;
impl BlockBehaviour for BeaconBlock {
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let block_entity = args.world.get_block_entity(args.position).await;
// Extract the inventory from the entity
let Some(inventory) = block_entity.and_then(BlockEntity::get_inventory) else {
return BlockActionResult::Fail;
};
// Open the screen using the factory
args.player
.open_handled_screen(&BeaconScreenFactory(inventory), Some(*args.position))
.await;
BlockActionResult::Success
})
}
}

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::bed::BedBlockEntity;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::block_properties::BedPart; use pumpkin_data::block_properties::BedPart;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
@ -11,7 +12,6 @@ use pumpkin_util::GameMode;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::bed::BedBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::BlockFuture; use crate::block::BlockFuture;

View File

@ -1,5 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::{
PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity,
furnace_like_block_entity::ExperienceContainer,
};
use pumpkin_data::{ use pumpkin_data::{
block_properties::{BlockProperties, FurnaceLikeProperties}, block_properties::{BlockProperties, FurnaceLikeProperties},
screen::WindowType, screen::WindowType,
@ -12,14 +16,7 @@ use pumpkin_inventory::{
}; };
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::{ use pumpkin_world::{BlockStateId, inventory::Inventory};
BlockStateId,
block::entities::{
PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity,
furnace_like_block_entity::ExperienceContainer,
},
inventory::Inventory,
};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{

View File

@ -6,17 +6,17 @@ use crate::block::{
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
}; };
use crate::block::entities::brewing_stand::BrewingStandBlockEntity;
use pumpkin_data::translation; use pumpkin_data::translation;
use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{BoxFuture, ScreenHandlerFactory, SharedScreenHandler}; use pumpkin_inventory::screen_handler::{BoxFuture, ScreenHandlerFactory, SharedScreenHandler};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::block::entities::brewing_stand::BrewingStandBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
struct BrewingScreenFactory( struct BrewingScreenFactory(
Arc<dyn Inventory>, Arc<dyn Inventory>,
Arc<dyn pumpkin_world::block::entities::PropertyDelegate>, Arc<dyn crate::block::entities::PropertyDelegate>,
); );
impl ScreenHandlerFactory for BrewingScreenFactory { impl ScreenHandlerFactory for BrewingScreenFactory {

View File

@ -1,5 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::BlockEntity;
use crate::block::entities::chest::ChestBlockEntity;
use futures::future::join; use futures::future::join;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, ChestLikeProperties, ChestType, HorizontalFacing, BlockProperties, ChestLikeProperties, ChestType, HorizontalFacing,
@ -16,8 +18,6 @@ use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::BlockEntity;
use pumpkin_world::block::entities::chest::ChestBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -445,7 +445,7 @@ impl BlockBehaviour for TrappedChestBlock {
} }
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
use pumpkin_world::block::entities::trapped_chest::TrappedChestBlockEntity; use crate::block::entities::trapped_chest::TrappedChestBlockEntity;
Box::pin(placed_chest_impl(args, TrappedChestBlockEntity::new)) Box::pin(placed_chest_impl(args, TrappedChestBlockEntity::new))
} }
@ -469,7 +469,7 @@ impl BlockBehaviour for TrappedChestBlock {
args: GetRedstonePowerArgs<'a>, args: GetRedstonePowerArgs<'a>,
) -> BlockFuture<'a, u8> { ) -> BlockFuture<'a, u8> {
Box::pin(async move { Box::pin(async move {
use pumpkin_world::block::entities::trapped_chest::TrappedChestBlockEntity; use crate::block::entities::trapped_chest::TrappedChestBlockEntity;
// Get viewer count from this chest // Get viewer count from this chest
let viewer_count = if let Some(block_entity) = let viewer_count = if let Some(block_entity) =

View File

@ -2,6 +2,7 @@ use std::sync::{Arc, atomic::Ordering};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use crate::block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity;
use crate::{ use crate::{
block::{ block::{
BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs,
@ -20,10 +21,7 @@ use pumpkin_data::{
}; };
use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_inventory::screen_handler::InventoryPlayer;
use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use pumpkin_world::{ use pumpkin_world::{BlockStateId, inventory::Inventory};
BlockStateId, block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity,
inventory::Inventory,
};
use tokio::sync::Mutex; use tokio::sync::Mutex;
#[pumpkin_block("minecraft:chiseled_bookshelf")] #[pumpkin_block("minecraft:chiseled_bookshelf")]

View File

@ -1,6 +1,7 @@
use std::sync::{Arc, atomic::Ordering}; use std::sync::{Arc, atomic::Ordering};
use super::redstone::block_receives_redstone_power; use super::redstone::block_receives_redstone_power;
use crate::block::entities::{BlockEntity, command_block::CommandBlockEntity};
use crate::command::CommandSender; use crate::command::CommandSender;
use crate::{ use crate::{
block::{ block::{
@ -16,11 +17,7 @@ use pumpkin_data::{
block_properties::{BlockProperties, CommandBlockLikeProperties, Facing}, block_properties::{BlockProperties, CommandBlockLikeProperties, Facing},
}; };
use pumpkin_util::{GameMode, PermissionLvl, math::position::BlockPos}; use pumpkin_util::{GameMode, PermissionLvl, math::position::BlockPos};
use pumpkin_world::{ use pumpkin_world::{BlockStateId, tick::TickPriority};
BlockStateId,
block::entities::{BlockEntity, command_block::CommandBlockEntity},
tick::TickPriority,
};
use tracing::warn; use tracing::warn;
pub struct CommandBlock; pub struct CommandBlock;

View File

@ -4,9 +4,9 @@ use crate::block::BlockBehaviour;
use crate::block::BlockFuture; use crate::block::BlockFuture;
use crate::block::OnEntityCollisionArgs; use crate::block::OnEntityCollisionArgs;
use crate::block::PlacedArgs; use crate::block::PlacedArgs;
use crate::block::entities::end_portal::EndPortalBlockEntity;
use pumpkin_data::dimension::Dimension; use pumpkin_data::dimension::Dimension;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity;
#[pumpkin_block("minecraft:end_portal")] #[pumpkin_block("minecraft:end_portal")]
pub struct EndPortalBlock; pub struct EndPortalBlock;

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::ender_chest::EnderChestBlockEntity;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
registry::BlockActionResult, registry::BlockActionResult,
@ -15,9 +16,7 @@ use pumpkin_inventory::{
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::{ use pumpkin_world::{BlockStateId, inventory::Inventory};
BlockStateId, block::entities::ender_chest::EnderChestBlockEntity, inventory::Inventory,
};
use tokio::sync::Mutex; use tokio::sync::Mutex;
struct EnderChestScreenFactory(Arc<dyn Inventory>); struct EnderChestScreenFactory(Arc<dyn Inventory>);

View File

@ -1,5 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::{
PropertyDelegate, furnace::FurnaceBlockEntity, furnace_like_block_entity::ExperienceContainer,
};
use pumpkin_data::{ use pumpkin_data::{
block_properties::{BlockProperties, FurnaceLikeProperties}, block_properties::{BlockProperties, FurnaceLikeProperties},
screen::WindowType, screen::WindowType,
@ -12,14 +15,7 @@ use pumpkin_inventory::{
}; };
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::{ use pumpkin_world::{BlockStateId, inventory::Inventory};
BlockStateId,
block::entities::{
PropertyDelegate, furnace::FurnaceBlockEntity,
furnace_like_block_entity::ExperienceContainer,
},
inventory::Inventory,
};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{

View File

@ -8,6 +8,7 @@ use crate::block::{
}; };
use crate::world::World; use crate::world::World;
use crate::block::entities::hopper::HopperBlockEntity;
use pumpkin_data::block_properties::{BlockProperties, FacingHopper}; use pumpkin_data::block_properties::{BlockProperties, FacingHopper};
use pumpkin_data::{Block, BlockDirection, translation}; use pumpkin_data::{Block, BlockDirection, translation};
use pumpkin_inventory::generic_container_screen_handler::create_hopper; use pumpkin_inventory::generic_container_screen_handler::create_hopper;
@ -19,7 +20,6 @@ use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::hopper::HopperBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use tokio::sync::Mutex; use tokio::sync::Mutex;

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::jukebox::JukeboxBlockEntity;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
@ -19,7 +20,6 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::block::entities::jukebox::JukeboxBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use rand::{RngExt, rng}; use rand::{RngExt, rng};

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::lectern::LecternBlockEntity;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
@ -15,7 +16,6 @@ use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::lectern::LecternBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;

View File

@ -97,3 +97,5 @@ pub mod tnt;
// Misc / abstract // Misc / abstract
pub mod abstract_wall_mounting; pub mod abstract_wall_mounting;
pub mod beacon;

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::{has_block_block_entity, piston::PistonBlockEntity};
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, BlockState, FacingExt, Block, BlockDirection, BlockState, FacingExt,
block_properties::{ block_properties::{
@ -9,11 +10,7 @@ use pumpkin_data::{
sound::{Sound, SoundCategory}, sound::{Sound, SoundCategory},
}; };
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{ use pumpkin_world::{BlockStateId, world::BlockFlags};
BlockStateId,
block::entities::{has_block_block_entity, piston::PistonBlockEntity},
world::BlockFlags,
};
use rand::RngExt; use rand::RngExt;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;

View File

@ -1,6 +1,7 @@
use crate::Arc; use crate::Arc;
use crate::block::blocks::abstract_wall_mounting::WallMountedBlock; use crate::block::blocks::abstract_wall_mounting::WallMountedBlock;
use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::entities::bell::BellBlockEntity;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockFuture, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, BlockBehaviour, BlockFuture, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs,
@ -18,7 +19,6 @@ use pumpkin_data::{HorizontalFacingExt, tag};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::bell::BellBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
async fn ring_bell( async fn ring_bell(

View File

@ -1,5 +1,6 @@
use std::sync::{Arc, atomic::Ordering}; use std::sync::{Arc, atomic::Ordering};
use crate::block::entities::comparator::ComparatorBlockEntity;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, BlockState, Block, BlockDirection, BlockState,
block_properties::{ block_properties::{
@ -9,10 +10,7 @@ use pumpkin_data::{
}; };
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
use pumpkin_world::{ use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags};
BlockStateId, block::entities::comparator::ComparatorBlockEntity, tick::TickPriority,
world::BlockFlags,
};
use crate::{ use crate::{
block::{ block::{

View File

@ -1,9 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::daylight_detector::DaylightDetectorBlockEntity;
use pumpkin_data::{Block, block_properties::BlockProperties}; use pumpkin_data::{Block, block_properties::BlockProperties};
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::entities::daylight_detector::DaylightDetectorBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use crate::block::{ use crate::block::{

View File

@ -7,6 +7,8 @@ use crate::block::{
use crate::entity::Entity; use crate::entity::Entity;
use crate::entity::item::ItemEntity; use crate::entity::item::ItemEntity;
use crate::block::entities::dropper::DropperBlockEntity;
use crate::block::entities::hopper::HopperBlockEntity;
use pumpkin_data::block_properties::{BlockProperties, Facing}; use pumpkin_data::block_properties::{BlockProperties, Facing};
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_data::world::WorldEvent; use pumpkin_data::world::WorldEvent;
@ -20,8 +22,6 @@ use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::dropper::DropperBlockEntity;
use pumpkin_world::block::entities::hopper::HopperBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use pumpkin_world::tick::TickPriority; use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;

View File

@ -6,6 +6,7 @@ use crate::block::{
{BlockBehaviour, NormalUseArgs}, {BlockBehaviour, NormalUseArgs},
}; };
use crate::block::entities::shulker_box::ShulkerBoxBlockEntity;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::tag::{self}; use pumpkin_data::tag::{self};
use pumpkin_data::translation; use pumpkin_data::translation;
@ -16,7 +17,6 @@ use pumpkin_inventory::screen_handler::{
}; };
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::shulker_box::ShulkerBoxBlockEntity;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use tokio::sync::Mutex; use tokio::sync::Mutex;

View File

@ -1,6 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use crate::block::entities::sign::SignBlockEntity;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::block_properties::EnumVariants; use pumpkin_data::block_properties::EnumVariants;
@ -10,7 +11,6 @@ use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::BlockStateId; use pumpkin_world::BlockStateId;
use pumpkin_world::block::entities::sign::SignBlockEntity;
use uuid::Uuid; use uuid::Uuid;
use crate::block::BlockBehaviour; use crate::block::BlockBehaviour;

View File

@ -1,5 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::{
PropertyDelegate, furnace_like_block_entity::ExperienceContainer, smoker::SmokerBlockEntity,
};
use pumpkin_data::{ use pumpkin_data::{
block_properties::{BlockProperties, FurnaceLikeProperties}, block_properties::{BlockProperties, FurnaceLikeProperties},
screen::WindowType, screen::WindowType,
@ -12,13 +15,7 @@ use pumpkin_inventory::{
}; };
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_world::{ use pumpkin_world::{BlockStateId, inventory::Inventory};
BlockStateId,
block::entities::{
PropertyDelegate, furnace_like_block_entity::ExperienceContainer, smoker::SmokerBlockEntity,
},
inventory::Inventory,
};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity;
use pumpkin_macros::pumpkin_block; use pumpkin_macros::pumpkin_block;
use pumpkin_world::block::entities::mob_spawner::MobSpawnerBlockEntity;
use crate::block::{BlockBehaviour, BlockFuture, PlacedArgs}; use crate::block::{BlockBehaviour, BlockFuture, PlacedArgs};

View File

@ -17,12 +17,14 @@ use std::{
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; use crate::block::viewer::{
use crate::inventory::InventoryFuture; ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture,
use crate::inventory::{ };
use crate::world::{BlockFlags, World};
use pumpkin_world::inventory::InventoryFuture;
use pumpkin_world::inventory::{
split_stack, {Clearable, Inventory}, split_stack, {Clearable, Inventory},
}; };
use crate::world::{BlockFlags, SimpleWorld};
use super::BlockEntity; use super::BlockEntity;
@ -67,10 +69,7 @@ impl BlockEntity for BarrelBlockEntity {
self.write_inventory_nbt(nbt, true) self.write_inventory_nbt(nbt, true)
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers
.update_viewer_count::<Self>(self, world, &self.position) .update_viewer_count::<Self>(self, world, &self.position)
@ -98,7 +97,7 @@ impl BlockEntity for BarrelBlockEntity {
impl ViewerCountListener for BarrelBlockEntity { impl ViewerCountListener for BarrelBlockEntity {
fn on_container_open<'a>( fn on_container_open<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
_position: &'a BlockPos, _position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -109,7 +108,7 @@ impl ViewerCountListener for BarrelBlockEntity {
fn on_container_close<'a>( fn on_container_close<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
_position: &'a BlockPos, _position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -133,7 +132,7 @@ impl BarrelBlockEntity {
} }
} }
async fn set_open(&self, world: &Arc<dyn SimpleWorld>, open: bool) { async fn set_open(&self, world: &Arc<World>, open: bool) {
let state = world.get_block_state(&self.position).await; let state = world.get_block_state(&self.position).await;
let mut properties = BarrelLikeProperties::from_state_id(state.id, &Block::BARREL); let mut properties = BarrelLikeProperties::from_state_id(state.id, &Block::BARREL);
@ -149,7 +148,7 @@ impl BarrelBlockEntity {
.await; .await;
} }
async fn play_sound(&self, world: &Arc<dyn SimpleWorld>, sound: Sound) { async fn play_sound(&self, world: &Arc<World>, sound: Sound) {
let mut rng = Xoroshiro::from_seed(get_seed()); let mut rng = Xoroshiro::from_seed(get_seed());
let state = world.get_block_state(&self.position).await; let state = world.get_block_state(&self.position).await;

View File

@ -0,0 +1,345 @@
use futures::Future;
use pumpkin_data::data_component_impl::IDSetContent;
use pumpkin_data::tag::Taggable;
use std::any::Any;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use pumpkin_data::effect::StatusEffect;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::boundingbox::BoundingBox;
use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex;
use crate::block::entities::BlockEntity;
use crate::world::World;
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture};
pub struct BeaconBlockEntity {
pub position: BlockPos,
pub primary_effect: AtomicI32,
pub secondary_effect: AtomicI32,
pub levels: AtomicI32,
pub dirty: AtomicBool,
pub payment: Arc<Mutex<ItemStack>>,
// Vanilla Parity Fields
pub custom_name: Mutex<Option<String>>,
pub lock_key: Mutex<Option<String>>,
pub last_check_y: AtomicI32,
}
impl BeaconBlockEntity {
pub const ID: &'static str = "minecraft:beacon";
// ContainerData Property Constants
pub const DATA_LEVELS: usize = 0;
pub const DATA_PRIMARY: usize = 1;
pub const DATA_SECONDARY: usize = 2;
pub const NUM_DATA_VALUES: usize = 3;
#[must_use]
pub fn new(position: BlockPos) -> Self {
Self {
position,
primary_effect: AtomicI32::new(-1),
secondary_effect: AtomicI32::new(-1),
levels: AtomicI32::new(0),
dirty: AtomicBool::new(false),
payment: Arc::new(Mutex::new(ItemStack::EMPTY.clone())),
custom_name: Mutex::new(None),
lock_key: Mutex::new(None),
last_check_y: AtomicI32::new(position.0.y - 1),
}
}
/// Replicates the Java `ContainerData` used to sync values to the `BeaconMenu`
pub fn get_data(&self, id: usize) -> i32 {
match id {
Self::DATA_LEVELS => self.levels.load(Ordering::Relaxed),
Self::DATA_PRIMARY => self.primary_effect.load(Ordering::Relaxed),
Self::DATA_SECONDARY => self.secondary_effect.load(Ordering::Relaxed),
_ => 0,
}
}
pub fn set_data(&self, id: usize, value: i32) {
match id {
Self::DATA_LEVELS => self.levels.store(value, Ordering::Relaxed),
Self::DATA_PRIMARY => self.primary_effect.store(value, Ordering::Relaxed),
Self::DATA_SECONDARY => self.secondary_effect.store(value, Ordering::Relaxed),
_ => {}
}
self.mark_dirty();
}
/// Replicates Java's `updateBase` logic
async fn update_base(&self, world: &Arc<World>) -> i32 {
let mut levels = 0;
let x = self.position.0.x;
let y = self.position.0.y;
let z = self.position.0.z;
for step in 1..=4 {
let ly = y - step;
if ly < world.dimension.min_y {
break;
}
let mut is_ok = true;
for lx in (x - step)..=(x + step) {
for lz in (z - step)..=(z + step) {
let pos = BlockPos::new(lx, ly, lz);
let block = world.get_block(&pos).await;
if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEACON_BASE_BLOCKS) {
is_ok = false;
break;
}
}
if !is_ok {
break;
}
}
if !is_ok {
break;
}
levels = step;
}
levels
}
/// Replicates Java's `applyEffects` bounding box mapping and duration mapping
async fn apply_effects(&self, world: &Arc<World>, levels: i32) {
if levels <= 0 {
return;
}
let primary_id = self.primary_effect.load(Ordering::Relaxed);
let secondary_id = self.secondary_effect.load(Ordering::Relaxed);
if primary_id <= 0 {
return;
}
let primary_effect = StatusEffect::from_id(primary_id as u16);
let secondary_effect = if secondary_id > 0 {
StatusEffect::from_id(secondary_id as u16)
} else {
None
};
// Vanilla: expandTowards(0.0, level.getHeight(), 0.0) -> Reaches across the entire Y axis
let range = (levels * 10 + 10) as f64;
let pos = self.position.0.to_f64();
// Use the dimension height for vanilla parity (usually 384.0 in modern versions)
let world_height = world.dimension.height as f64;
let bounding_box = BoundingBox::new(pos, pos.add_raw(1.0, 1.0, 1.0))
.expand(range, range, range)
.expand_towards(0.0, world_height, 0.0);
let players = world.get_players_at_box(&bounding_box);
let duration_ticks = (9 + levels * 2) * 20;
let base_amp = i32::from(levels >= 4 && primary_id == secondary_id);
for player in players {
if let Some(effect) = primary_effect {
player
.add_effect(pumpkin_data::potion::Effect {
effect_type: effect,
duration: duration_ticks,
amplifier: base_amp as u8,
ambient: true,
show_particles: true,
show_icon: true,
blend: false,
})
.await;
}
if levels >= 4
&& primary_id != secondary_id
&& let Some(effect) = secondary_effect
{
player
.add_effect(pumpkin_data::potion::Effect {
effect_type: effect,
duration: duration_ticks,
amplifier: 0,
ambient: true,
show_particles: true,
show_icon: true,
blend: false,
})
.await;
}
}
}
}
impl BlockEntity for BeaconBlockEntity {
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,
{
// Aligning to strict vanilla NBT tags
let primary = nbt.get_int("primary_effect").unwrap_or(-1);
let secondary = nbt.get_int("secondary_effect").unwrap_or(-1);
let levels = nbt.get_int("Levels").unwrap_or(0); // Vanilla uses capital L
let custom_name = nbt
.get_string("CustomName")
.map(std::string::ToString::to_string);
let lock_key = nbt.get_string("Lock").map(std::string::ToString::to_string);
Self {
position,
primary_effect: AtomicI32::new(primary),
secondary_effect: AtomicI32::new(secondary),
levels: AtomicI32::new(levels),
dirty: AtomicBool::new(false),
payment: Arc::new(Mutex::new(ItemStack::EMPTY.clone())),
custom_name: Mutex::new(custom_name),
lock_key: Mutex::new(lock_key),
last_check_y: AtomicI32::new(position.0.y - 1),
}
}
fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put_int(
"primary_effect",
self.primary_effect.load(Ordering::Relaxed),
);
nbt.put_int(
"secondary_effect",
self.secondary_effect.load(Ordering::Relaxed),
);
nbt.put_int("Levels", self.levels.load(Ordering::Relaxed));
if let Some(name) = &*self.custom_name.lock().await {
nbt.put_string("CustomName", name.clone());
}
if let Some(lock) = &*self.lock_key.lock().await {
nbt.put_string("Lock", lock.clone());
}
})
}
fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
// Check properties every 80 ticks matching Java
if world.get_time_of_day().await % 80 == 0 {
let levels = self.update_base(world).await;
self.levels.store(levels, Ordering::Relaxed);
// TODO: Beam Section validation (scanning upward to heightmap to check for sky visibility)
// is typically checked here before applying effects in Vanilla.
if levels > 0 {
self.apply_effects(world, levels).await;
}
}
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
Some(self as Arc<dyn Inventory>)
}
}
impl Inventory for BeaconBlockEntity {
fn size(&self) -> usize {
1
}
fn is_empty(&self) -> InventoryFuture<'_, bool> {
Box::pin(async move { self.payment.lock().await.is_empty() })
}
fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move {
if slot == 0 {
self.payment.clone()
} else {
Arc::new(Mutex::new(ItemStack::EMPTY.clone()))
}
})
}
fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot == 0 {
let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.payment.lock().await;
std::mem::swap(&mut removed, &mut *guard);
self.mark_dirty();
removed
} else {
ItemStack::EMPTY.clone()
}
})
}
fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> {
Box::pin(async move {
if slot == 0 {
let mut stack = self.payment.lock().await;
if stack.is_empty() {
return ItemStack::EMPTY.clone();
}
let res = stack.split(amount);
self.mark_dirty();
res
} else {
ItemStack::EMPTY.clone()
}
})
}
fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> {
Box::pin(async move {
if slot == 0 {
*self.payment.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 BeaconBlockEntity {
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
if let Ok(mut payment) = self.payment.try_lock() {
*payment = ItemStack::EMPTY.clone();
}
})
}
}

View File

@ -1,5 +1,5 @@
use crate::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
use crate::world::SimpleWorld; use crate::world::World;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::sound::{Sound, SoundCategory};
@ -61,10 +61,7 @@ impl BlockEntity for BellBlockEntity {
Self::new(position) Self::new(position)
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
if self.ringing.load() { if self.ringing.load() {
self.ring_ticks.fetch_add(1); self.ring_ticks.fetch_add(1);

View File

@ -1,5 +1,5 @@
use crate::inventory::Inventory;
use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack}; use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack};
use pumpkin_world::inventory::Inventory;
use std::{ use std::{
array::from_fn, array::from_fn,

View File

@ -8,7 +8,6 @@ use std::sync::{
}; };
use crate::block::entities::PropertyDelegate; use crate::block::entities::PropertyDelegate;
use crate::inventory::Inventory;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::data_component_impl::DataComponentImpl; use pumpkin_data::data_component_impl::DataComponentImpl;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
@ -19,6 +18,7 @@ use pumpkin_data::tag::{self, Taggable};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::inventory::Inventory;
use tokio::sync::Mutex; use tokio::sync::Mutex;
pub struct BrewingStandBlockEntity { pub struct BrewingStandBlockEntity {
@ -50,12 +50,11 @@ impl BrewingStandBlockEntity {
} }
/// Check if the current ingredient matches the stored ingredient /// Check if the current ingredient matches the stored ingredient
async fn ingredient_matches(&self, ingredient: &ItemStack) -> bool { fn ingredient_matches(&self, ingredient: &ItemStack) -> bool {
if let Some(stored) = *self.ingredient_item.lock().unwrap() { self.ingredient_item
!ingredient.is_empty() && ingredient.get_item().id == stored.id .lock()
} else { .unwrap()
false .is_some_and(|stored| !ingredient.is_empty() && ingredient.get_item().id == stored.id)
}
} }
/// Check if any potion slot has a valid recipe with the ingredient /// Check if any potion slot has a valid recipe with the ingredient
@ -101,7 +100,7 @@ impl BrewingStandBlockEntity {
} }
/// Perform brewing on all valid potion slots /// Perform brewing on all valid potion slots
async fn do_brew(&self, world: &Arc<dyn crate::world::SimpleWorld>, ingredient: &ItemStack) { async fn do_brew(&self, world: &Arc<crate::world::World>, ingredient: &ItemStack) {
let ingredient_id = ingredient.get_item().id; let ingredient_id = ingredient.get_item().id;
// Apply recipes to each slot // Apply recipes to each slot
@ -120,18 +119,19 @@ impl BrewingStandBlockEntity {
{ {
let new_item = recipe.to(); let new_item = recipe.to();
let potion_comp = slot.get_data_component::<pumpkin_data::data_component_impl::PotionContentsImpl>().cloned(); let potion_comp = slot.get_data_component::<pumpkin_data::data_component_impl::PotionContentsImpl>().cloned();
let new_stack = if let Some(pc) = potion_comp { let new_stack = potion_comp.map_or_else(
ItemStack::new_with_component( || ItemStack::new(slot.item_count, new_item),
slot.item_count, |pc| {
new_item, ItemStack::new_with_component(
vec![( slot.item_count,
pumpkin_data::data_component::DataComponent::PotionContents, new_item,
Some(pc.to_dyn()), vec![(
)], pumpkin_data::data_component::DataComponent::PotionContents,
) Some(pc.to_dyn()),
} else { )],
ItemStack::new(slot.item_count, new_item) )
}; },
);
new_stack_opt = Some(new_stack); new_stack_opt = Some(new_stack);
break; break;
} }
@ -203,12 +203,12 @@ impl BrewingStandBlockEntity {
} }
} }
impl crate::inventory::Inventory for BrewingStandBlockEntity { impl pumpkin_world::inventory::Inventory for BrewingStandBlockEntity {
fn size(&self) -> usize { fn size(&self) -> usize {
Self::INVENTORY_SIZE Self::INVENTORY_SIZE
} }
fn is_empty(&self) -> crate::inventory::InventoryFuture<'_, bool> { fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> {
Box::pin(async move { Box::pin(async move {
for slot in &self.items { for slot in &self.items {
if !slot.lock().await.is_empty() { if !slot.lock().await.is_empty() {
@ -222,11 +222,14 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity {
fn get_stack( fn get_stack(
&self, &self,
slot: usize, slot: usize,
) -> crate::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> { ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() }) Box::pin(async move { self.items[slot].clone() })
} }
fn remove_stack(&self, slot: usize) -> crate::inventory::InventoryFuture<'_, ItemStack> { fn remove_stack(
&self,
slot: usize,
) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone(); let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await; let mut guard = self.items[slot].lock().await;
@ -239,7 +242,7 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity {
&self, &self,
slot: usize, slot: usize,
amount: u8, amount: u8,
) -> crate::inventory::InventoryFuture<'_, ItemStack> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut guard = self.items[slot].lock().await; let mut guard = self.items[slot].lock().await;
let mut taken = ItemStack::EMPTY.clone(); let mut taken = ItemStack::EMPTY.clone();
@ -258,18 +261,18 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity {
&self, &self,
slot: usize, slot: usize,
stack: ItemStack, stack: ItemStack,
) -> crate::inventory::InventoryFuture<'_, ()> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
*self.items[slot].lock().await = stack; *self.items[slot].lock().await = stack;
self.mark_dirty(); self.mark_dirty();
}) })
} }
fn on_open(&self) -> crate::inventory::InventoryFuture<'_, ()> { fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move {}) Box::pin(async move {})
} }
fn on_close(&self) -> crate::inventory::InventoryFuture<'_, ()> { fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move {}) Box::pin(async move {})
} }
@ -307,7 +310,7 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity {
} }
} }
impl crate::inventory::Clearable for BrewingStandBlockEntity { impl pumpkin_world::inventory::Clearable for BrewingStandBlockEntity {
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> { fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move { Box::pin(async move {
for slot in &self.items { for slot in &self.items {
@ -368,15 +371,15 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
fn write_nbt<'a>( fn write_nbt<'a>(
&'a self, &'a self,
_nbt: &'a mut NbtCompound, nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
// Persist brew state // Persist brew state
_nbt.put_int("BrewTime", self.brew_time.load(Ordering::Relaxed)); nbt.put_int("BrewTime", self.brew_time.load(Ordering::Relaxed));
_nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed)); nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed));
// Save inventory contents to NBT // Save inventory contents to NBT
self.write_inventory_nbt(_nbt, true).await; self.write_inventory_nbt(nbt, true).await;
}) })
} }
@ -406,7 +409,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: &'a Arc<dyn crate::world::SimpleWorld>, world: &'a Arc<crate::world::World>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
// Refill fuel counter from fuel item if needed // Refill fuel counter from fuel item if needed
@ -442,7 +445,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
if is_done_brewing && brewable { if is_done_brewing && brewable {
// Brewing complete // Brewing complete
self.do_brew(world, &ingredient).await; self.do_brew(world, &ingredient).await;
} else if !brewable || !self.ingredient_matches(&ingredient).await { } else if !brewable || !self.ingredient_matches(&ingredient) {
// Cancel brewing // Cancel brewing
self.brew_time.store(0, Ordering::Relaxed); self.brew_time.store(0, Ordering::Relaxed);
self.mark_dirty(); self.mark_dirty();

View File

@ -18,7 +18,7 @@ macro_rules! impl_block_entity_for_chest {
where where
Self: Sized, Self: Sized,
{ {
use $crate::inventory::Inventory; use pumpkin_world::inventory::Inventory;
let chest = Self { let chest = Self {
position, position,
@ -36,7 +36,7 @@ macro_rules! impl_block_entity_for_chest {
&'a self, &'a self,
nbt: &'a mut pumpkin_nbt::compound::NbtCompound, nbt: &'a mut pumpkin_nbt::compound::NbtCompound,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> { ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> {
use $crate::inventory::Inventory; use pumpkin_world::inventory::Inventory;
// Write inventory data to NBT // Write inventory data to NBT
self.write_inventory_nbt(nbt, true) self.write_inventory_nbt(nbt, true)
@ -44,16 +44,20 @@ macro_rules! impl_block_entity_for_chest {
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: &'a Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<$crate::world::World>,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> { ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers $crate::block::viewer::ViewerCountTrackerExt::update_viewer_count::<$struct_name>(
.update_viewer_count::<Self>(self, world, &self.position) &self.viewers,
.await; self,
world,
&self.position,
)
.await;
}) })
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn $crate::inventory::Inventory>> { fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn pumpkin_world::inventory::Inventory>> {
Some(self) Some(self)
} }
@ -77,12 +81,12 @@ macro_rules! impl_block_entity_for_chest {
#[macro_export] #[macro_export]
macro_rules! impl_inventory_for_chest { macro_rules! impl_inventory_for_chest {
($struct_name:ty) => { ($struct_name:ty) => {
impl $crate::inventory::Inventory for $struct_name { impl pumpkin_world::inventory::Inventory for $struct_name {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
fn is_empty(&self) -> $crate::inventory::InventoryFuture<'_, bool> { fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> {
Box::pin(async move { Box::pin(async move {
for slot in &self.items { for slot in &self.items {
if !slot.lock().await.is_empty() { if !slot.lock().await.is_empty() {
@ -97,14 +101,14 @@ macro_rules! impl_inventory_for_chest {
fn get_stack( fn get_stack(
&self, &self,
slot: usize, slot: usize,
) -> $crate::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> { ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() }) Box::pin(async move { self.items[slot].clone() })
} }
fn remove_stack( fn remove_stack(
&self, &self,
slot: usize, slot: usize,
) -> $crate::inventory::InventoryFuture<'_, ItemStack> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone(); let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await; let mut guard = self.items[slot].lock().await;
@ -118,9 +122,10 @@ macro_rules! impl_inventory_for_chest {
&self, &self,
slot: usize, slot: usize,
amount: u8, amount: u8,
) -> $crate::inventory::InventoryFuture<'_, ItemStack> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let res = $crate::inventory::split_stack(&self.items, slot, amount).await; let res =
pumpkin_world::inventory::split_stack(&self.items, slot, amount).await;
self.mark_dirty(); self.mark_dirty();
res res
}) })
@ -130,20 +135,20 @@ macro_rules! impl_inventory_for_chest {
&self, &self,
slot: usize, slot: usize,
stack: ItemStack, stack: ItemStack,
) -> $crate::inventory::InventoryFuture<'_, ()> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
*self.items[slot].lock().await = stack; *self.items[slot].lock().await = stack;
self.mark_dirty(); self.mark_dirty();
}) })
} }
fn on_open(&self) -> $crate::inventory::InventoryFuture<'_, ()> { fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
self.viewers.open_container(); self.viewers.open_container();
}) })
} }
fn on_close(&self) -> $crate::inventory::InventoryFuture<'_, ()> { fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
self.viewers.close_container(); self.viewers.close_container();
}) })
@ -164,7 +169,7 @@ macro_rules! impl_inventory_for_chest {
#[macro_export] #[macro_export]
macro_rules! impl_clearable_for_chest { macro_rules! impl_clearable_for_chest {
($struct_name:ty) => { ($struct_name:ty) => {
impl $crate::inventory::Clearable for $struct_name { impl pumpkin_world::inventory::Clearable for $struct_name {
fn clear( fn clear(
&self, &self,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + '_>> { ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + '_>> {
@ -172,7 +177,7 @@ macro_rules! impl_clearable_for_chest {
for slot in &self.items { for slot in &self.items {
*slot.lock().await = ItemStack::EMPTY.clone(); *slot.lock().await = ItemStack::EMPTY.clone();
} }
<$struct_name as $crate::inventory::Inventory>::mark_dirty(self); <$struct_name as pumpkin_world::inventory::Inventory>::mark_dirty(self);
}) })
} }
} }
@ -189,7 +194,7 @@ macro_rules! impl_viewer_count_listener_for_chest {
impl $crate::block::viewer::ViewerCountListener for $struct_name { impl $crate::block::viewer::ViewerCountListener for $struct_name {
fn on_container_open<'a>( fn on_container_open<'a>(
&'a self, &'a self,
world: &'a Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<$crate::world::World>,
_position: &'a pumpkin_util::math::position::BlockPos, _position: &'a pumpkin_util::math::position::BlockPos,
) -> $crate::block::viewer::ViewerFuture<'a, ()> { ) -> $crate::block::viewer::ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -200,7 +205,7 @@ macro_rules! impl_viewer_count_listener_for_chest {
fn on_container_close<'a>( fn on_container_close<'a>(
&'a self, &'a self,
world: &'a Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<$crate::world::World>,
_position: &'a pumpkin_util::math::position::BlockPos, _position: &'a pumpkin_util::math::position::BlockPos,
) -> $crate::block::viewer::ViewerFuture<'a, ()> { ) -> $crate::block::viewer::ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -211,7 +216,7 @@ macro_rules! impl_viewer_count_listener_for_chest {
fn on_viewer_count_update<'a>( fn on_viewer_count_update<'a>(
&'a self, &'a self,
world: &'a Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<$crate::world::World>,
position: &'a pumpkin_util::math::position::BlockPos, position: &'a pumpkin_util::math::position::BlockPos,
old: u16, old: u16,
new: u16, new: u16,
@ -271,7 +276,7 @@ macro_rules! impl_chest_helper_methods {
async fn play_sound( async fn play_sound(
&self, &self,
world: &Arc<dyn $crate::world::SimpleWorld>, world: &Arc<$crate::world::World>,
sound: pumpkin_data::sound::Sound, sound: pumpkin_data::sound::Sound,
) { ) {
let mut rng = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed( let mut rng = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(

View File

@ -15,12 +15,12 @@ use std::{
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tracing::warn; use tracing::warn;
use crate::inventory::InventoryFuture;
use crate::{ use crate::{
block::entities::BlockEntity, block::entities::BlockEntity,
inventory::{Clearable, Inventory, split_stack}, world::{BlockFlags, World},
world::{BlockFlags, SimpleWorld},
}; };
use pumpkin_world::inventory::InventoryFuture;
use pumpkin_world::inventory::{Clearable, Inventory, split_stack};
pub struct ChiseledBookshelfBlockEntity { pub struct ChiseledBookshelfBlockEntity {
pub position: BlockPos, pub position: BlockPos,
@ -108,7 +108,7 @@ impl ChiseledBookshelfBlockEntity {
pub async fn update_state( pub async fn update_state(
&self, &self,
mut properties: ChiseledBookshelfLikeProperties, mut properties: ChiseledBookshelfLikeProperties,
world: Arc<dyn SimpleWorld>, world: Arc<World>,
slot: i8, slot: i8,
) { ) {
if slot >= 0 && slot < self.items.len() as i8 { if slot >= 0 && slot < self.items.len() as i8 {

View File

@ -5,7 +5,7 @@ use pumpkin_data::block_properties::BlockProperties;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use crate::world::{BlockFlags, SimpleWorld}; use crate::world::{BlockFlags, World};
use super::BlockEntity; use super::BlockEntity;
@ -42,12 +42,9 @@ impl BlockEntity for DaylightDetectorBlockEntity {
self self
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async { Box::pin(async {
if world.get_world_age().await % 20 == 0 && world.get_dimension().await.has_skylight { if world.get_world_age().await % 20 == 0 && world.dimension.has_skylight {
Self::update_power(world, &self.position).await; Self::update_power(world, &self.position).await;
} }
}) })
@ -62,13 +59,13 @@ impl DaylightDetectorBlockEntity {
Self { position } Self { position }
} }
pub async fn update_power<W: SimpleWorld + ?Sized>(world: &Arc<W>, block_pos: &BlockPos) { pub async fn update_power(world: &Arc<World>, block_pos: &BlockPos) {
use std::f32::consts::PI; use std::f32::consts::PI;
let (block, state) = world.get_block_and_state(block_pos).await; let (block, state) = world.get_block_and_state(block_pos).await;
let mut props = DaylightDetectorProperties::from_state_id(state.id, block); let mut props = DaylightDetectorProperties::from_state_id(state.id, block);
let level = world.get_level().await; let level = world.level.clone();
let inverted = props.inverted; let inverted = props.inverted;
@ -102,7 +99,7 @@ impl DaylightDetectorBlockEntity {
let sky_light_level = level let sky_light_level = level
.light_engine .light_engine
.get_sky_light_level(level, block_pos) .get_sky_light_level(&level, block_pos)
.await; .await;
let mut power = sky_light_level - ambient_darkness; let mut power = sky_light_level - ambient_darkness;

View File

@ -1,8 +1,8 @@
use crate::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
use rand::{RngExt, rng}; use rand::{RngExt, rng};
use std::any::Any; use std::any::Any;
use std::array::from_fn; use std::array::from_fn;

View File

@ -7,8 +7,10 @@ use std::any::Any;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; use crate::block::viewer::{
use crate::world::SimpleWorld; ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture,
};
use crate::world::World;
use super::BlockEntity; use super::BlockEntity;
@ -45,10 +47,7 @@ impl BlockEntity for EnderChestBlockEntity {
Box::pin(async {}) Box::pin(async {})
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers
.update_viewer_count::<Self>(self, world, &self.position) .update_viewer_count::<Self>(self, world, &self.position)
@ -64,7 +63,7 @@ impl BlockEntity for EnderChestBlockEntity {
impl ViewerCountListener for EnderChestBlockEntity { impl ViewerCountListener for EnderChestBlockEntity {
fn on_container_open<'a>( fn on_container_open<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
_position: &'a BlockPos, _position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -74,7 +73,7 @@ impl ViewerCountListener for EnderChestBlockEntity {
fn on_container_close<'a>( fn on_container_close<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
_position: &'a BlockPos, _position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -84,7 +83,7 @@ impl ViewerCountListener for EnderChestBlockEntity {
fn on_viewer_count_update<'a>( fn on_viewer_count_update<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
position: &'a BlockPos, position: &'a BlockPos,
_old: u16, _old: u16,
new: u16, new: u16,
@ -114,7 +113,7 @@ impl EnderChestBlockEntity {
self.viewers.clone() self.viewers.clone()
} }
async fn play_sound(&self, world: &Arc<dyn SimpleWorld>, sound: Sound) { async fn play_sound(&self, world: &Arc<World>, sound: Sound) {
let mut rng = Xoroshiro::from_seed(get_seed()); let mut rng = Xoroshiro::from_seed(get_seed());
world world

View File

@ -1,5 +1,5 @@
use crate::inventory::Inventory;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_world::inventory::Inventory;
use std::{ use std::{
array::from_fn, array::from_fn,

View File

@ -3,18 +3,11 @@ use std::sync::Arc;
use pumpkin_data::{item_stack::ItemStack, recipes::CookingRecipe}; use pumpkin_data::{item_stack::ItemStack, recipes::CookingRecipe};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::block::entities::{BlockEntity, PropertyDelegate};
block::entities::{BlockEntity, PropertyDelegate}, pub use pumpkin_world::block::entities::ExperienceContainer;
inventory::{Clearable, Inventory}, use pumpkin_world::inventory::{Clearable, Inventory};
};
/// Trait for extracting smelting experience from cooking block entities. /// Trait for extracting smelting experience from cooking block entities.
/// This is a separate dyn-compatible trait since `CookingBlockEntityBase` is not.
pub trait ExperienceContainer: Send + Sync {
/// Extract and reset accumulated experience, returning the total as an integer
fn extract_experience(&self) -> i32;
}
pub trait CookingBlockEntityBase: pub trait CookingBlockEntityBase:
Sync + Send + Inventory + PropertyDelegate + BlockEntity + Clearable Sync + Send + Inventory + PropertyDelegate + BlockEntity + Clearable
{ {
@ -130,10 +123,7 @@ macro_rules! impl_cooking_block_entity_base {
recipe: Option<&pumpkin_data::recipes::CookingRecipe>, recipe: Option<&pumpkin_data::recipes::CookingRecipe>,
max_count: u8, max_count: u8,
) -> bool { ) -> bool {
let recipe = match recipe { let Some(recipe) = recipe else { return false };
Some(cooking_recipe) => cooking_recipe,
None => return false,
};
let top_item_stack = self.items[0].lock().await; let top_item_stack = self.items[0].lock().await;
let is_top_items_empty = top_item_stack.is_empty(); let is_top_items_empty = top_item_stack.is_empty();
@ -165,11 +155,10 @@ macro_rules! impl_cooking_block_entity_base {
if let Some(recipe) = recipe { if let Some(recipe) = recipe {
if can_accept_output { if can_accept_output {
let mut side_items = self.items[2].lock().await; let mut side_items = self.items[2].lock().await;
let output_item = match pumpkin_data::item::Item::from_registry_key( let Some(output_item) = pumpkin_data::item::Item::from_registry_key(
recipe.result.id.strip_prefix("minecraft:").unwrap(), recipe.result.id.strip_prefix("minecraft:").unwrap(),
) { ) else {
Some(item) => item, return false;
None => return false,
}; };
let output_item_stack = ItemStack::new(recipe.result.count, output_item); let output_item_stack = ItemStack::new(recipe.result.count, output_item);
@ -243,7 +232,7 @@ macro_rules! impl_property_delegate_for_cooking {
#[macro_export] #[macro_export]
macro_rules! impl_clearable_for_cooking { macro_rules! impl_clearable_for_cooking {
($struct_name:ty) => { ($struct_name:ty) => {
impl $crate::inventory::Clearable for $struct_name { impl pumpkin_world::inventory::Clearable for $struct_name {
fn clear(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + '_>> { fn clear(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move { Box::pin(async move {
for slot in self.items.iter() { for slot in self.items.iter() {
@ -273,12 +262,12 @@ macro_rules! impl_experience_container_for_cooking {
#[macro_export] #[macro_export]
macro_rules! impl_inventory_for_cooking { macro_rules! impl_inventory_for_cooking {
($struct_name:ty) => { ($struct_name:ty) => {
impl $crate::inventory::Inventory for $struct_name { impl pumpkin_world::inventory::Inventory for $struct_name {
fn size(&self) -> usize { fn size(&self) -> usize {
self.items.len() self.items.len()
} }
fn is_empty(&self) -> $crate::inventory::InventoryFuture<'_, bool> { fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> {
Box::pin(async move { Box::pin(async move {
for slot in self.items.iter() { for slot in self.items.iter() {
if !slot.lock().await.is_empty() { if !slot.lock().await.is_empty() {
@ -292,14 +281,14 @@ macro_rules! impl_inventory_for_cooking {
fn get_stack( fn get_stack(
&self, &self,
slot: usize, slot: usize,
) -> $crate::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> { ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc<Mutex<ItemStack>>> {
Box::pin(async move { self.items[slot].clone() }) Box::pin(async move { self.items[slot].clone() })
} }
fn remove_stack( fn remove_stack(
&self, &self,
slot: usize, slot: usize,
) -> $crate::inventory::InventoryFuture<'_, ItemStack> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let mut removed = ItemStack::EMPTY.clone(); let mut removed = ItemStack::EMPTY.clone();
let mut guard = self.items[slot].lock().await; let mut guard = self.items[slot].lock().await;
@ -313,9 +302,10 @@ macro_rules! impl_inventory_for_cooking {
&self, &self,
slot: usize, slot: usize,
amount: u8, amount: u8,
) -> $crate::inventory::InventoryFuture<'_, ItemStack> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> {
Box::pin(async move { Box::pin(async move {
let res = $crate::inventory::split_stack(&self.items, slot, amount).await; let res =
pumpkin_world::inventory::split_stack(&self.items, slot, amount).await;
self.mark_dirty(); self.mark_dirty();
res res
}) })
@ -325,7 +315,7 @@ macro_rules! impl_inventory_for_cooking {
&self, &self,
slot: usize, slot: usize,
stack: ItemStack, stack: ItemStack,
) -> $crate::inventory::InventoryFuture<'_, ()> { ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> {
Box::pin(async move { Box::pin(async move {
let furnace_stack = self.get_stack(slot).await; let furnace_stack = self.get_stack(slot).await;
let mut furnace_stack = furnace_stack.lock().await; let mut furnace_stack = furnace_stack.lock().await;
@ -370,9 +360,10 @@ macro_rules! impl_inventory_for_cooking {
macro_rules! impl_block_entity_for_cooking { macro_rules! impl_block_entity_for_cooking {
($struct_name:ty,$recipe_kind:expr) => { ($struct_name:ty,$recipe_kind:expr) => {
impl $crate::block::entities::BlockEntity for $struct_name { impl $crate::block::entities::BlockEntity for $struct_name {
#[expect(clippy::too_many_lines)]
fn tick<'a>( fn tick<'a>(
&'a self, &'a self,
world: &'a Arc<dyn $crate::world::SimpleWorld>, world: &'a Arc<$crate::world::World>,
) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'a>> { ) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let is_burning = self.is_burning(); let is_burning = self.is_burning();
@ -586,7 +577,9 @@ macro_rules! impl_block_entity_for_cooking {
}) })
} }
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn $crate::inventory::Inventory>> { fn get_inventory(
self: Arc<Self>,
) -> Option<Arc<dyn pumpkin_world::inventory::Inventory>> {
Some(self) Some(self)
} }

View File

@ -1,7 +1,6 @@
use crate::BlockStateId;
use crate::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack}; use crate::entity::experience_orb::ExperienceOrbEntity;
use crate::world::SimpleWorld; use crate::world::World;
use pumpkin_data::block_properties::{BlockProperties, FacingHopper, HopperLikeProperties}; use pumpkin_data::block_properties::{BlockProperties, FacingHopper, HopperLikeProperties};
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
@ -10,6 +9,8 @@ use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::BlockStateId;
use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack};
use std::any::Any; use std::any::Any;
use std::array::from_fn; use std::array::from_fn;
use std::pin::Pin; use std::pin::Pin;
@ -71,10 +72,7 @@ impl BlockEntity for HopperBlockEntity {
hopper hopper
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.ticked_game_time self.ticked_game_time
.store(world.get_world_age().await, Ordering::Relaxed); .store(world.get_world_age().await, Ordering::Relaxed);
@ -134,12 +132,13 @@ impl HopperBlockEntity {
ticked_game_time: AtomicI64::new(0), ticked_game_time: AtomicI64::new(0),
} }
} }
async fn try_move_items(&self, state: &HopperLikeProperties, world: &Arc<dyn SimpleWorld>) { async fn try_move_items(&self, state: &HopperLikeProperties, world: &Arc<World>) {
if self.cooldown_time.load(Ordering::Relaxed) <= 0 && state.enabled { if self.cooldown_time.load(Ordering::Relaxed) <= 0 && state.enabled {
let mut success = false; let mut success = if self.is_empty().await {
if !self.is_empty().await { false
success = self.eject_items(world).await; } else {
} self.eject_items(world).await
};
if !self.inventory_full().await { if !self.inventory_full().await {
success |= self.suck_in_items(world).await; success |= self.suck_in_items(world).await;
} }
@ -160,7 +159,7 @@ impl HopperBlockEntity {
true true
} }
async fn suck_in_items(&self, world: &Arc<dyn SimpleWorld>) -> bool { async fn suck_in_items(&self, world: &Arc<World>) -> bool {
// TODO getEntityContainer // TODO getEntityContainer
let pos_up = &self.position.up(); let pos_up = &self.position.up();
if let Some(entity) = world.get_block_entity(pos_up).await if let Some(entity) = world.get_block_entity(pos_up).await
@ -184,7 +183,7 @@ impl HopperBlockEntity {
let xp = experience_container.extract_experience(); let xp = experience_container.extract_experience();
if xp > 0 { if xp > 0 {
let pos = self.position.to_f64(); let pos = self.position.to_f64();
world.clone().spawn_experience_orbs(pos, xp as u32).await; ExperienceOrbEntity::spawn(world, pos, xp as u32).await;
} }
} }
return true; return true;
@ -202,7 +201,7 @@ impl HopperBlockEntity {
false false
} }
async fn eject_items(&self, world: &Arc<dyn SimpleWorld>) -> bool { async fn eject_items(&self, world: &Arc<World>) -> bool {
// TODO getEntityContainer // TODO getEntityContainer
if let Some(entity) = world if let Some(entity) = world

View File

@ -10,8 +10,8 @@ use pumpkin_util::math::position::BlockPos;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
use crate::inventory::{Clearable, Inventory, InventoryFuture}; use crate::world::World;
use crate::world::SimpleWorld; use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture};
/// Matches vanilla's `JukeboxBlockEntity` /// Matches vanilla's `JukeboxBlockEntity`
pub struct JukeboxBlockEntity { pub struct JukeboxBlockEntity {
@ -77,10 +77,7 @@ impl BlockEntity for JukeboxBlockEntity {
}) })
} }
fn tick<'a>( fn tick<'a>(&'a self, _world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
_world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
// Increment ticks if we're playing // Increment ticks if we're playing
let song_length = self.song_length_ticks.load(Ordering::Relaxed); let song_length = self.song_length_ticks.load(Ordering::Relaxed);

View File

@ -11,10 +11,8 @@ use std::{
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::block::entities::BlockEntity;
block::entities::BlockEntity, use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture};
inventory::{Clearable, Inventory, InventoryFuture},
};
pub struct LecternBlockEntity { pub struct LecternBlockEntity {
pub position: BlockPos, pub position: BlockPos,
@ -38,8 +36,10 @@ impl BlockEntity for LecternBlockEntity {
let book = nbt let book = nbt
.get_compound("Book") .get_compound("Book")
.and_then(ItemStack::read_item_stack) .and_then(ItemStack::read_item_stack)
.map(|stack| Arc::new(Mutex::new(stack))) .map_or_else(
.unwrap_or_else(|| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))); || Arc::new(Mutex::new(ItemStack::EMPTY.clone())),
|stack| Arc::new(Mutex::new(stack)),
);
Self { Self {
position, position,

View File

@ -15,7 +15,7 @@ use pumpkin_util::math::{
vector3::Vector3, vector3::Vector3,
}; };
use crate::{block::entities::BlockEntity, world::SimpleWorld}; use crate::{block::entities::BlockEntity, world::World};
pub struct MobSpawnerBlockEntity { pub struct MobSpawnerBlockEntity {
pub position: BlockPos, pub position: BlockPos,
@ -69,7 +69,7 @@ impl MobSpawnerBlockEntity {
} }
impl MobSpawnerBlockEntity { impl MobSpawnerBlockEntity {
async fn update_spawns(&self, world: &Arc<dyn SimpleWorld>) { async fn update_spawns(&self, world: &Arc<World>) {
let min_delay = self.min_delay; let min_delay = self.min_delay;
let max_delay = self.max_delay; let max_delay = self.max_delay;
@ -98,10 +98,7 @@ impl BlockEntity for MobSpawnerBlockEntity {
self.position self.position
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
if let Some(entity_type) = &self.entity_type.load() { if let Some(entity_type) = &self.entity_type.load() {
if self.delay.load(Ordering::Relaxed) == -1 { if self.delay.load(Ordering::Relaxed) == -1 {
@ -140,7 +137,14 @@ impl BlockEntity for MobSpawnerBlockEntity {
{ {
continue; continue;
} }
world.clone().spawn_from_type(entity_type, spawn_pos).await; let entity = crate::entity::r#type::from_type(
entity_type,
spawn_pos,
world,
uuid::Uuid::new_v4(),
)
.await;
world.spawn_entity(entity).await;
world world
.sync_world_event(WorldEvent::ParticlesMobblockSpawn, self.position, 0) .sync_world_event(WorldEvent::ParticlesMobblockSpawn, self.position, 0)
.await; .await;

View File

@ -0,0 +1,207 @@
use std::pin::Pin;
use std::{any::Any, sync::Arc};
use pumpkin_data::{Block, block_properties::BLOCK_ENTITY_TYPES};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use crate::world::World;
use pumpkin_world::BlockStateId;
use pumpkin_world::inventory::Inventory;
pub mod barrel;
pub mod beacon;
pub mod bed;
pub mod bell;
pub mod blasting_furnace;
pub mod brewing_stand;
pub mod chest;
pub mod chest_like_block_entity;
pub mod chiseled_bookshelf;
pub mod command_block;
pub mod comparator;
pub mod daylight_detector;
pub mod dropper;
pub mod end_portal;
pub mod ender_chest;
pub mod furnace;
pub mod furnace_like_block_entity;
pub mod hopper;
pub mod jukebox;
pub mod lectern;
pub mod mob_spawner;
pub mod piston;
pub mod shulker_box;
pub mod sign;
pub mod smoker;
pub mod trapped_chest;
pub use furnace_like_block_entity::ExperienceContainer;
pub use pumpkin_world::block::entities::PropertyDelegate;
//TODO: We need a mark_dirty for chests
pub trait BlockEntity: Any + Send + Sync {
fn write_nbt<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
where
Self: Sized;
fn tick<'a>(&'a self, _world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async {})
}
fn resource_location(&self) -> &'static str;
fn get_position(&self) -> BlockPos;
fn write_internal<'a>(
&'a self,
nbt: &'a mut NbtCompound,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
nbt.put_string("id", self.resource_location().to_string());
let position = self.get_position();
nbt.put_int("x", position.0.x);
nbt.put_int("y", position.0.y);
nbt.put_int("z", position.0.z);
self.write_nbt(nbt).await;
})
}
fn get_id(&self) -> u32 {
pumpkin_data::block_properties::BLOCK_ENTITY_TYPES
.iter()
.position(|block_entity_name| {
*block_entity_name == self.resource_location().split(':').next_back().unwrap()
})
.unwrap() as u32
}
/// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData)
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
None
}
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
None
}
fn set_block_state(&mut self, _block_state: BlockStateId) {}
fn on_block_replaced<'a>(
self: Arc<Self>,
world: Arc<World>,
position: BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
Self: 'a,
{
Box::pin(async move {
if let Some(inventory) = self.get_inventory() {
// Assuming scatter_inventory is an async method on World
world.scatter_inventory(&position, &inventory).await;
}
})
}
fn is_dirty(&self) -> bool {
false
}
fn clear_dirty(&self) {
// Default implementation does nothing
// Override in implementations that have a dirty flag
}
fn as_any(&self) -> &dyn Any;
fn to_property_delegate(self: Arc<Self>) -> Option<Arc<dyn PropertyDelegate>> {
None
}
fn to_experience_container(self: Arc<Self>) -> Option<Arc<dyn ExperienceContainer>> {
None
}
}
#[must_use]
pub fn block_entity_from_generic<T: BlockEntity>(nbt: &NbtCompound) -> T {
let x = nbt.get_int("x").unwrap();
let y = nbt.get_int("y").unwrap();
let z = nbt.get_int("z").unwrap();
T::from_nbt(nbt, BlockPos::new(x, y, z))
}
#[must_use]
pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>> {
let id = nbt.get_string("id")?;
let x = nbt.get_int("x")?;
let y = nbt.get_int("y")?;
let z = nbt.get_int("z")?;
let pos = BlockPos::new(x, y, z);
match id {
barrel::BarrelBlockEntity::ID => {
Some(Arc::new(barrel::BarrelBlockEntity::from_nbt(nbt, pos)))
}
chest::ChestBlockEntity::ID => Some(Arc::new(chest::ChestBlockEntity::from_nbt(nbt, pos))),
trapped_chest::TrappedChestBlockEntity::ID => Some(Arc::new(
trapped_chest::TrappedChestBlockEntity::from_nbt(nbt, pos),
)),
ender_chest::EnderChestBlockEntity::ID => Some(Arc::new(
ender_chest::EnderChestBlockEntity::from_nbt(nbt, pos),
)),
furnace::FurnaceBlockEntity::ID => {
Some(Arc::new(furnace::FurnaceBlockEntity::from_nbt(nbt, pos)))
}
blasting_furnace::BlastingFurnaceBlockEntity::ID => Some(Arc::new(
blasting_furnace::BlastingFurnaceBlockEntity::from_nbt(nbt, pos),
)),
smoker::SmokerBlockEntity::ID => {
Some(Arc::new(smoker::SmokerBlockEntity::from_nbt(nbt, pos)))
}
brewing_stand::BrewingStandBlockEntity::ID => Some(Arc::new(
brewing_stand::BrewingStandBlockEntity::from_nbt(nbt, pos),
)),
hopper::HopperBlockEntity::ID => {
Some(Arc::new(hopper::HopperBlockEntity::from_nbt(nbt, pos)))
}
jukebox::JukeboxBlockEntity::ID => {
Some(Arc::new(jukebox::JukeboxBlockEntity::from_nbt(nbt, pos)))
}
mob_spawner::MobSpawnerBlockEntity::ID => Some(Arc::new(
mob_spawner::MobSpawnerBlockEntity::from_nbt(nbt, pos),
)),
sign::SignBlockEntity::ID => Some(Arc::new(sign::SignBlockEntity::from_nbt(nbt, pos))),
piston::PistonBlockEntity::ID => {
Some(Arc::new(piston::PistonBlockEntity::from_nbt(nbt, pos)))
}
chiseled_bookshelf::ChiseledBookshelfBlockEntity::ID => Some(Arc::new(
chiseled_bookshelf::ChiseledBookshelfBlockEntity::from_nbt(nbt, pos),
)),
dropper::DropperBlockEntity::ID => {
Some(Arc::new(dropper::DropperBlockEntity::from_nbt(nbt, pos)))
}
command_block::CommandBlockEntity::ID => Some(Arc::new(
command_block::CommandBlockEntity::from_nbt(nbt, pos),
)),
comparator::ComparatorBlockEntity::ID => Some(Arc::new(
comparator::ComparatorBlockEntity::from_nbt(nbt, pos),
)),
daylight_detector::DaylightDetectorBlockEntity::ID => Some(Arc::new(
daylight_detector::DaylightDetectorBlockEntity::from_nbt(nbt, pos),
)),
end_portal::EndPortalBlockEntity::ID => Some(Arc::new(
end_portal::EndPortalBlockEntity::from_nbt(nbt, pos),
)),
beacon::BeaconBlockEntity::ID => {
Some(Arc::new(beacon::BeaconBlockEntity::from_nbt(nbt, pos)))
}
bed::BedBlockEntity::ID => Some(Arc::new(bed::BedBlockEntity::from_nbt(nbt, pos))),
bell::BellBlockEntity::ID => Some(Arc::new(bell::BellBlockEntity::from_nbt(nbt, pos))),
shulker_box::ShulkerBoxBlockEntity::ID => Some(Arc::new(
shulker_box::ShulkerBoxBlockEntity::from_nbt(nbt, pos),
)),
lectern::LecternBlockEntity::ID => {
Some(Arc::new(lectern::LecternBlockEntity::from_nbt(nbt, pos)))
}
_ => None,
}
}
#[must_use]
pub fn has_block_block_entity(block: &Block) -> bool {
BLOCK_ENTITY_TYPES.contains(&block.name)
}

View File

@ -5,7 +5,7 @@ use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use crate::world::{BlockFlags, SimpleWorld}; use crate::world::{BlockFlags, World};
use super::BlockEntity; use super::BlockEntity;
@ -22,7 +22,7 @@ pub struct PistonBlockEntity {
impl PistonBlockEntity { impl PistonBlockEntity {
pub const ID: &'static str = "minecraft:piston"; pub const ID: &'static str = "minecraft:piston";
pub async fn finish(&self, world: Arc<dyn SimpleWorld>) { pub async fn finish(&self, world: Arc<World>) {
if self.last_progress.load() < 1.0 { if self.last_progress.load() < 1.0 {
let pos = self.position; let pos = self.position;
world.remove_block_entity(&pos).await; world.remove_block_entity(&pos).await;
@ -61,10 +61,7 @@ impl BlockEntity for PistonBlockEntity {
self.position self.position
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let current_progress = self.current_progress.load(); let current_progress = self.current_progress.load();
self.last_progress.store(current_progress); self.last_progress.store(current_progress);

View File

@ -13,12 +13,14 @@ use std::{
}; };
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; use crate::block::viewer::{
use crate::inventory::InventoryFuture; ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture,
use crate::inventory::{ };
use crate::world::World;
use pumpkin_world::inventory::InventoryFuture;
use pumpkin_world::inventory::{
split_stack, {Clearable, Inventory}, split_stack, {Clearable, Inventory},
}; };
use crate::world::SimpleWorld;
use super::BlockEntity; use super::BlockEntity;
@ -63,10 +65,7 @@ impl BlockEntity for ShulkerBoxBlockEntity {
self.write_inventory_nbt(nbt, true) self.write_inventory_nbt(nbt, true)
} }
fn tick<'a>( fn tick<'a>(&'a self, world: &'a Arc<World>) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
&'a self,
world: &'a Arc<dyn SimpleWorld>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
self.viewers self.viewers
.update_viewer_count::<Self>(self, world, &self.position) .update_viewer_count::<Self>(self, world, &self.position)
@ -76,7 +75,7 @@ impl BlockEntity for ShulkerBoxBlockEntity {
fn on_block_replaced<'a>( fn on_block_replaced<'a>(
self: Arc<Self>, self: Arc<Self>,
_world: Arc<dyn SimpleWorld>, _world: Arc<World>,
_position: BlockPos, _position: BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where where
@ -107,7 +106,7 @@ impl BlockEntity for ShulkerBoxBlockEntity {
impl ViewerCountListener for ShulkerBoxBlockEntity { impl ViewerCountListener for ShulkerBoxBlockEntity {
fn on_container_open<'a>( fn on_container_open<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
position: &'a BlockPos, position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -119,7 +118,7 @@ impl ViewerCountListener for ShulkerBoxBlockEntity {
fn on_container_close<'a>( fn on_container_close<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
position: &'a BlockPos, position: &'a BlockPos,
) -> ViewerFuture<'a, ()> { ) -> ViewerFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
@ -131,7 +130,7 @@ impl ViewerCountListener for ShulkerBoxBlockEntity {
fn on_viewer_count_update<'a>( fn on_viewer_count_update<'a>(
&'a self, &'a self,
world: &'a Arc<dyn SimpleWorld>, world: &'a Arc<World>,
position: &'a BlockPos, position: &'a BlockPos,
_old: u16, _old: u16,
new: u16, new: u16,
@ -159,7 +158,7 @@ impl ShulkerBoxBlockEntity {
} }
} }
async fn play_sound(&self, world: &Arc<dyn SimpleWorld>, position: &BlockPos, sound: Sound) { async fn play_sound(&self, world: &Arc<World>, position: &BlockPos, sound: Sound) {
let mut rng = Xoroshiro::from_seed(get_seed()); let mut rng = Xoroshiro::from_seed(get_seed());
world world

View File

@ -139,11 +139,12 @@ impl Default for Text {
Self { Self {
has_glowing_text: AtomicBool::new(false), has_glowing_text: AtomicBool::new(false),
color: AtomicI8::new(DyeColor::default() as i8), color: AtomicI8::new(DyeColor::default() as i8),
messages: Default::default(), messages: Arc::default(),
} }
} }
} }
#[allow(clippy::fallible_impl_from)]
impl From<Text> for NbtTag { impl From<Text> for NbtTag {
fn from(value: Text) -> Self { fn from(value: Text) -> Self {
let mut nbt = NbtCompound::new(); let mut nbt = NbtCompound::new();
@ -166,6 +167,7 @@ impl From<Text> for NbtTag {
} }
} }
#[allow(clippy::fallible_impl_from)]
impl From<NbtTag> for Text { impl From<NbtTag> for Text {
fn from(tag: NbtTag) -> Self { fn from(tag: NbtTag) -> Self {
let nbt = tag.extract_compound().unwrap(); let nbt = tag.extract_compound().unwrap();

View File

@ -1,5 +1,5 @@
use crate::inventory::Inventory;
use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack}; use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack};
use pumpkin_world::inventory::Inventory;
use std::{ use std::{
array::from_fn, array::from_fn,

View File

@ -13,8 +13,10 @@ use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
pub mod blocks; pub mod blocks;
pub mod entities;
pub mod fluid; pub mod fluid;
pub mod registry; pub mod registry;
pub mod viewer;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::entity::EntityBase; use crate::entity::EntityBase;

View File

@ -1,8 +1,8 @@
use crate::block::blocks::anvil::AnvilBlock; use crate::block::blocks::anvil::AnvilBlock;
use crate::block::blocks::banners::BannerBlock; use crate::block::blocks::banners::BannerBlock;
use crate::block::blocks::barrel::BarrelBlock; use crate::block::blocks::barrel::BarrelBlock;
use crate::block::blocks::barrier::BarrierBlock; use crate::block::blocks::barrier::BarrierBlock;
use crate::block::blocks::beacon::BeaconBlock;
use crate::block::blocks::bed::BedBlock; use crate::block::blocks::bed::BedBlock;
use crate::block::blocks::brewing_stand::BrewingStandBlock; use crate::block::blocks::brewing_stand::BrewingStandBlock;
use crate::block::blocks::cake::CakeBlock; use crate::block::blocks::cake::CakeBlock;
@ -179,6 +179,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
// Blocks // Blocks
manager.register(AnvilBlock); manager.register(AnvilBlock);
manager.register(BeaconBlock);
manager.register(BedBlock); manager.register(BedBlock);
manager.register(SaplingBlock); manager.register(SaplingBlock);
manager.register(CactusBlock); manager.register(CactusBlock);

View File

@ -0,0 +1,83 @@
use std::{
pin::Pin,
sync::{Arc, atomic::Ordering},
};
use pumpkin_util::math::position::BlockPos;
use crate::{block::entities::BlockEntity, world::World};
pub use pumpkin_world::block::viewer::ViewerCountTracker;
pub trait ViewerCountTrackerExt {
fn update_viewer_count<'a, T>(
&'a self,
entity: &'a T,
world: &'a Arc<World>,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
T: BlockEntity + ViewerCountListener + 'static;
}
impl ViewerCountTrackerExt for ViewerCountTracker {
fn update_viewer_count<'a, T>(
&'a self,
entity: &'a T,
world: &'a Arc<World>,
position: &'a BlockPos,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
where
T: BlockEntity + ViewerCountListener + 'static,
{
Box::pin(async move {
let current = self.current.load(Ordering::Relaxed);
let old = self.old.swap(current, Ordering::Relaxed);
if old != current {
match (old, current) {
(n, 0) if n > 0 => {
entity.on_container_close(world, position).await;
}
(0, n) if n > 0 => {
entity.on_container_open(world, position).await;
}
_ => {} // Ignore
}
entity
.on_viewer_count_update(world, position, old, current)
.await;
}
})
}
}
pub type ViewerFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait ViewerCountListener: Send + Sync {
fn on_container_open<'a>(
&'a self,
_world: &'a Arc<World>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_container_close<'a>(
&'a self,
_world: &'a Arc<World>,
_position: &'a BlockPos,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
fn on_viewer_count_update<'a>(
&'a self,
_world: &'a Arc<World>,
_position: &'a BlockPos,
_old: u16,
_new: u16,
) -> ViewerFuture<'a, ()> {
Box::pin(async {})
}
}

View File

@ -1,3 +1,4 @@
use crate::block::entities::BlockEntity;
use crate::command::{ use crate::command::{
CommandError, CommandExecutor, CommandResult, CommandSender, CommandError, CommandExecutor, CommandResult, CommandSender,
args::{ args::{
@ -7,7 +8,6 @@ use crate::command::{
tree::{CommandTree, builder::argument}, tree::{CommandTree, builder::argument},
}; };
use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
use pumpkin_world::block::entities::BlockEntity;
const NAMES: [&str; 1] = ["particle"]; const NAMES: [&str; 1] = ["particle"];
const DESCRIPTION: &str = "Spawns a Particle at position."; const DESCRIPTION: &str = "Spawns a Particle at position.";

View File

@ -13,7 +13,7 @@ use pumpkin_data::translation;
use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
use uuid::Uuid; use uuid::Uuid;
use pumpkin_world::block::entities::BlockEntity; use crate::block::entities::BlockEntity;
const NAMES: [&str; 1] = ["summon"]; const NAMES: [&str; 1] = ["summon"];

View File

@ -8,6 +8,8 @@ use crate::server::Server;
use crate::world::World; use crate::world::World;
use args::ConsumedArgs; use args::ConsumedArgs;
use crate::block::entities::BlockEntity;
use crate::block::entities::command_block::CommandBlockEntity;
use crate::command::context::command_source::CommandSource; use crate::command::context::command_source::CommandSource;
use crate::entity::EntityBase; use crate::entity::EntityBase;
use dispatcher::CommandError; use dispatcher::CommandError;
@ -21,8 +23,6 @@ use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::permission::{PermissionDefault, PermissionLvl}; use pumpkin_util::permission::{PermissionDefault, PermissionLvl};
use pumpkin_util::text::TextComponent; use pumpkin_util::text::TextComponent;
use pumpkin_util::translation::Locale; use pumpkin_util::translation::Locale;
use pumpkin_world::block::entities::BlockEntity;
use pumpkin_world::block::entities::command_block::CommandBlockEntity;
pub mod args; pub mod args;
pub mod argument_builder; pub mod argument_builder;

View File

@ -1,11 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use pumpkin_data::tag; use crate::block::entities::{
use pumpkin_util::GameMode;
use pumpkin_world::block::entities::{
BlockEntity, BlockEntity,
sign::{DyeColor, Text}, sign::{DyeColor, Text},
}; };
use pumpkin_data::tag;
use pumpkin_util::GameMode;
use crate::{ use crate::{
block::{UseWithItemArgs, registry::BlockActionResult}, block::{UseWithItemArgs, registry::BlockActionResult},

View File

@ -1,7 +1,7 @@
use std::sync::{Arc, atomic::Ordering}; use std::sync::{Arc, atomic::Ordering};
use crate::block::entities::{BlockEntity, sign::Text};
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_world::block::entities::{BlockEntity, sign::Text};
use crate::{ use crate::{
block::{UseWithItemArgs, registry::BlockActionResult}, block::{UseWithItemArgs, registry::BlockActionResult},

View File

@ -3,6 +3,8 @@ use std::sync::Arc;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use crate::block::UseWithItemArgs; use crate::block::UseWithItemArgs;
use crate::block::entities::BlockEntity;
use crate::block::entities::sign::SignBlockEntity;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::item::{ItemBehaviour, ItemMetadata}; use crate::item::{ItemBehaviour, ItemMetadata};
@ -17,8 +19,6 @@ use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::block::entities::BlockEntity;
use pumpkin_world::block::entities::sign::SignBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
pub struct HoneyCombItem; pub struct HoneyCombItem;

View File

@ -1,7 +1,7 @@
use std::sync::{Arc, atomic::Ordering}; use std::sync::{Arc, atomic::Ordering};
use crate::block::entities::{BlockEntity, sign::Text};
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_world::block::entities::{BlockEntity, sign::Text};
use crate::{ use crate::{
block::{UseWithItemArgs, registry::BlockActionResult}, block::{UseWithItemArgs, registry::BlockActionResult},

View File

@ -1,5 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity;
use crate::entity::player::Player; use crate::entity::player::Player;
use crate::entity::r#type::from_type; use crate::entity::r#type::from_type;
use crate::item::{ItemBehaviour, ItemMetadata}; use crate::item::{ItemBehaviour, ItemMetadata};
@ -10,7 +11,6 @@ use pumpkin_data::{Block, BlockDirection};
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::math::wrap_degrees; use pumpkin_util::math::wrap_degrees;
use pumpkin_world::block::entities::mob_spawner::MobSpawnerBlockEntity;
use uuid::Uuid; use uuid::Uuid;
pub struct SpawnEggItem; pub struct SpawnEggItem;

View File

@ -33,6 +33,8 @@ use crate::plugin::player::player_move::PlayerMoveEvent;
use crate::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent; use crate::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent;
use crate::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent; use crate::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent;
use crate::block::entities::command_block::CommandBlockEntity;
use crate::block::entities::sign::SignBlockEntity;
use crate::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent; use crate::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent;
use crate::server::{Server, seasonal_events}; use crate::server::{Server, seasonal_events};
use crate::world::{World, chunker}; use crate::world::{World, chunker};
@ -71,8 +73,6 @@ use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::math::{polynomial_rolling_hash, position::BlockPos, wrap_degrees}; use pumpkin_util::math::{polynomial_rolling_hash, position::BlockPos, wrap_degrees};
use pumpkin_util::text::color::NamedColor; use pumpkin_util::text::color::NamedColor;
use pumpkin_util::{GameMode, text::TextComponent}; use pumpkin_util::{GameMode, text::TextComponent};
use pumpkin_world::block::entities::command_block::CommandBlockEntity;
use pumpkin_world::block::entities::sign::SignBlockEntity;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use tokio::sync::Mutex; use tokio::sync::Mutex;

View File

@ -1,7 +1,7 @@
use pumpkin_data::BlockDirection as InternalBlockDirection; use pumpkin_data::BlockDirection as InternalBlockDirection;
use pumpkin_data::block_state::PistonBehavior; use pumpkin_data::block_state::PistonBehavior;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::{BlockFlags, SimpleWorld}; use pumpkin_world::world::BlockFlags;
use std::sync::Arc; use std::sync::Arc;
use wasmtime::component::Resource; use wasmtime::component::Resource;

View File

@ -1,3 +1,5 @@
use crate::block::entities::BlockEntity;
use dashmap::DashMap;
use pumpkin_protocol::bedrock::client::level_event::{CLevelEvent, LevelEvent}; use pumpkin_protocol::bedrock::client::level_event::{CLevelEvent, LevelEvent};
use pumpkin_protocol::codec::data_component::data_to_proto_sound; use pumpkin_protocol::codec::data_component::data_to_proto_sound;
use std::pin::Pin; use std::pin::Pin;
@ -18,9 +20,7 @@ pub mod time;
use crate::block::RandomTickArgs; use crate::block::RandomTickArgs;
use crate::world::chunker::is_within_view_distance; use crate::world::chunker::is_within_view_distance;
use crate::world::{chunker::get_view_distance, loot::LootContextParameters}; use crate::world::{chunker::get_view_distance, loot::LootContextParameters};
use crate::{ use crate::{block::BlockEvent, entity::item::ItemEntity};
block::BlockEvent, entity::experience_orb::ExperienceOrbEntity, entity::item::ItemEntity,
};
use crate::{ use crate::{
block::{ block::{
self, self,
@ -120,14 +120,13 @@ use pumpkin_util::{
random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro}, random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro},
}; };
use pumpkin_world::inventory::Clearable; use pumpkin_world::inventory::Clearable;
use pumpkin_world::world::{GetBlockError, WorldFuture}; use pumpkin_world::world::GetBlockError;
use pumpkin_world::{ use pumpkin_world::{
BlockStateId, CURRENT_BEDROCK_MC_VERSION, biome, block::entities::BlockEntity, BlockStateId, CURRENT_BEDROCK_MC_VERSION, biome, chunk::io::Dirtiable, inventory::Inventory,
chunk::io::Dirtiable, inventory::Inventory, world::SimpleWorld,
}; };
use pumpkin_world::{chunk::ChunkData, world::BlockAccessor}; use pumpkin_world::{chunk::ChunkData, world::BlockAccessor};
use pumpkin_world::{level::Level, tick::TickPriority}; use pumpkin_world::{level::Level, tick::TickPriority};
use pumpkin_world::{world::BlockFlags, world_info::LevelData}; pub use pumpkin_world::{world::BlockFlags, world_info::LevelData};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::{RngExt, rng}; use rand::{RngExt, rng};
use scoreboard::Scoreboard; use scoreboard::Scoreboard;
@ -213,6 +212,7 @@ pub struct World {
pub dragon_fight: Option<Mutex<dragon_fight::DragonFight>>, pub dragon_fight: Option<Mutex<dragon_fight::DragonFight>>,
pub spawn_state: ArcSwap<SpawnState>, pub spawn_state: ArcSwap<SpawnState>,
pub active_chunks: ArcSwap<FxHashSet<Vector2<i32>>>, pub active_chunks: ArcSwap<FxHashSet<Vector2<i32>>>,
pub block_entities: DashMap<BlockPos, Arc<dyn BlockEntity>>,
} }
impl PartialEq for World { impl PartialEq for World {
@ -260,6 +260,7 @@ impl World {
spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())), spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())),
active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())), active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())),
server, server,
block_entities: DashMap::new(),
} }
} }
@ -777,6 +778,7 @@ impl World {
.await; .await;
} }
#[expect(clippy::too_many_lines)]
pub async fn tick(self: &Arc<Self>, server: Arc<Server>) { pub async fn tick(self: &Arc<Self>, server: Arc<Server>) {
let start = tokio::time::Instant::now(); let start = tokio::time::Instant::now();
@ -853,6 +855,28 @@ impl World {
} }
let entity_elapsed = entity_start.elapsed(); let entity_elapsed = entity_start.elapsed();
let block_entity_start = tokio::time::Instant::now();
let block_entities: Vec<Arc<dyn BlockEntity>> = self
.block_entities
.iter()
.map(|e| e.value().clone())
.collect();
let block_entity_count = block_entities.len();
let mut block_entity_tasks = tokio::task::JoinSet::new();
for block_entity in block_entities {
let world_clone = self.clone();
block_entity_tasks.spawn(async move {
block_entity.tick(&world_clone).await;
});
}
while let Some(res) = block_entity_tasks.join_next().await {
if let Err(e) = res {
error!("Block entity tick panicked: {:?}", e);
}
}
let block_entity_elapsed = block_entity_start.elapsed();
//self.level.chunk_loading.lock().unwrap().send_change(); //self.level.chunk_loading.lock().unwrap().send_change();
// Tick the End dragon fight (only on THE_END worlds). // Tick the End dragon fight (only on THE_END worlds).
@ -863,13 +887,15 @@ impl World {
let total_elapsed = start.elapsed(); let total_elapsed = start.elapsed();
if total_elapsed.as_millis() > 50 { if total_elapsed.as_millis() > 50 {
debug!( debug!(
"Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?}", "Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?} | Block Entities({}): {:?}",
total_elapsed.as_millis(), total_elapsed.as_millis(),
chunk_elapsed, chunk_elapsed,
player_count, player_count,
player_elapsed, player_elapsed,
entity_count, entity_count,
entity_elapsed, entity_elapsed,
block_entity_count,
block_entity_elapsed,
); );
} }
} }
@ -974,7 +1000,6 @@ impl World {
} }
} }
#[expect(clippy::too_many_lines)]
pub async fn tick_chunks(self: &Arc<Self>) { pub async fn tick_chunks(self: &Arc<Self>) {
let active_chunks = self.active_chunks.load(); let active_chunks = self.active_chunks.load();
let tick_data = self.level.get_tick_data(&active_chunks); let tick_data = self.level.get_tick_data(&active_chunks);
@ -1031,16 +1056,6 @@ impl World {
} }
} }
let world_simple: Arc<dyn SimpleWorld> = self.clone();
let mut block_entity_tasks = JoinSet::new();
for block_entity in tick_data.block_entities {
let world_simple = world_simple.clone();
block_entity_tasks.spawn(async move {
block_entity.tick(&world_simple).await;
});
}
while block_entity_tasks.join_next().await.is_some() {}
let spawn_state = self.spawn_state.load(); let spawn_state = self.spawn_state.load();
// TODO gamerule this.spawnEnemies || this.spawnFriendlies // TODO gamerule this.spawnEnemies || this.spawnFriendlies
@ -1486,6 +1501,14 @@ impl World {
spawn_for_chunk(self, chunk_pos, chunk, spawn_state, spawn_list).await; spawn_for_chunk(self, chunk_pos, chunk, spawn_state, spawn_list).await;
} }
pub async fn get_world_age(&self) -> i64 {
self.level_time.lock().await.world_age
}
pub async fn get_time_of_day(&self) -> i64 {
self.level_time.lock().await.time_of_day
}
pub async fn set_time_of_day(&self, time: i64) { pub async fn set_time_of_day(&self, time: i64) {
let mut level_time = self.level_time.lock().await; let mut level_time = self.level_time.lock().await;
level_time.set_time(time); level_time.set_time(time);
@ -3306,8 +3329,7 @@ impl World {
&& old_block.default_state.block_entity_type != u16::MAX && old_block.default_state.block_entity_type != u16::MAX
&& let Some(entity) = self.get_block_entity(position).await && let Some(entity) = self.get_block_entity(position).await
{ {
let world: Arc<dyn SimpleWorld> = self.clone(); entity.on_block_replaced(self.clone(), *position).await;
entity.on_block_replaced(world, *position).await;
self.remove_block_entity(position).await; self.remove_block_entity(position).await;
} }
@ -3843,6 +3865,32 @@ impl World {
} }
} }
pub async fn update_from_neighbor_shapes(
self: &Arc<Self>,
state_id: BlockStateId,
pos: &BlockPos,
) -> BlockStateId {
let mut current_state_id = state_id;
let block = Block::from_state_id(state_id);
for direction in BlockDirection::all() {
let neighbor_pos = pos.offset(direction.to_offset());
let neighbor_state_id = self.get_block_state_id(&neighbor_pos).await;
current_state_id = self
.block_registry
.get_state_for_neighbor_update(
self,
block,
current_state_id,
pos,
direction,
&neighbor_pos,
neighbor_state_id,
)
.await;
}
current_state_id
}
pub async fn replace_with_state_for_neighbor_update( pub async fn replace_with_state_for_neighbor_update(
self: &Arc<Self>, self: &Arc<Self>,
block_pos: &BlockPos, block_pos: &BlockPos,
@ -3882,12 +3930,11 @@ impl World {
} }
} }
#[expect(clippy::unused_async)]
pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>> { pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option<Arc<dyn BlockEntity>> {
self.level self.block_entities
.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { .get(block_pos)
chunk.block_entities.lock().unwrap().get(block_pos).cloned() .map(|e| e.value().clone())
})
.await
} }
pub async fn add_block_entity(&self, block_entity: Arc<dyn BlockEntity>) { pub async fn add_block_entity(&self, block_entity: Arc<dyn BlockEntity>) {
@ -3909,32 +3956,22 @@ impl World {
.await; .await;
} }
self.block_entities.insert(block_pos, block_entity.clone());
self.level self.level
.get_or_fetch_chunk(chunk_pos, |chunk| { .get_or_fetch_chunk(chunk_pos, |chunk| {
chunk
.block_entities
.lock()
.unwrap()
.insert(block_pos, block_entity.clone());
chunk.mark_dirty(true); chunk.mark_dirty(true);
}) })
.await; .await;
} }
pub async fn remove_block_entity(&self, block_pos: &BlockPos) { pub async fn remove_block_entity(&self, block_pos: &BlockPos) {
self.level if self.block_entities.remove(block_pos).is_some() {
.get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { self.level
if chunk .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| {
.block_entities
.lock()
.unwrap()
.remove(block_pos)
.is_some()
{
chunk.mark_dirty(true); chunk.mark_dirty(true);
} })
}) .await;
.await; }
} }
pub async fn update_block_entity(&self, block_entity: &Arc<dyn BlockEntity>) { pub async fn update_block_entity(&self, block_entity: &Arc<dyn BlockEntity>) {
@ -4237,178 +4274,6 @@ impl World {
} }
} }
impl pumpkin_world::world::SimpleWorld for World {
fn set_block_state(
self: Arc<Self>,
position: &BlockPos,
block_state_id: BlockStateId,
flags: BlockFlags,
) -> WorldFuture<'_, BlockStateId> {
Box::pin(async move { Self::set_block_state(&self, position, block_state_id, flags).await })
}
fn update_neighbor<'a>(
self: Arc<Self>,
neighbor_block_pos: &'a BlockPos,
source_block: &'a pumpkin_data::Block,
) -> WorldFuture<'a, ()> {
Box::pin(async move {
Self::update_neighbor(&self, neighbor_block_pos, source_block).await;
})
}
fn update_neighbors(
self: Arc<Self>,
block_pos: &BlockPos,
except: Option<BlockDirection>,
) -> WorldFuture<'_, ()> {
Box::pin(async move {
Self::update_neighbors(&self, block_pos, except).await;
})
}
fn is_space_empty(&self, bounding_box: BoundingBox) -> WorldFuture<'_, bool> {
Box::pin(async move { self.is_space_empty(bounding_box).await })
}
fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) -> WorldFuture<'_, ()> {
Box::pin(async move {
self.add_synced_block_event(pos, r#type, data).await;
})
}
fn sync_world_event(
&self,
world_event: WorldEvent,
position: BlockPos,
data: i32,
) -> WorldFuture<'_, ()> {
Box::pin(async move {
self.sync_world_event(world_event, position, data).await;
})
}
fn spawn_from_type(
self: Arc<Self>,
entity_type: &'static EntityType,
position: Vector3<f64>,
) -> WorldFuture<'static, ()> {
Box::pin(async move {
let mob = from_type(entity_type, position, &self, Uuid::new_v4()).await;
self.spawn_entity(mob).await;
})
}
fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()> {
Box::pin(async move {
self.remove_block_entity(block_pos).await;
})
}
fn get_block_entity<'a>(
&'a self,
block_pos: &'a BlockPos,
) -> WorldFuture<'a, Option<Arc<dyn BlockEntity>>> {
Box::pin(async move { self.get_block_entity(block_pos).await })
}
fn get_world_age(&self) -> WorldFuture<'_, i64> {
Box::pin(async move {
// Note: MutexGuard must be released before returning the future's result.
let level_time_guard = self.level_time.lock().await;
level_time_guard.world_age
})
}
fn get_time_of_day(&self) -> WorldFuture<'_, i64> {
Box::pin(async move {
let level_time_guard = self.level_time.lock().await;
level_time_guard.query_daytime()
})
}
fn get_level(&self) -> WorldFuture<'_, &Arc<Level>> {
Box::pin(async move { &self.level })
}
fn get_dimension(&self) -> WorldFuture<'_, &Dimension> {
Box::pin(async move { &self.dimension })
}
fn play_sound<'a>(
&'a self,
sound: Sound,
category: SoundCategory,
position: &'a Vector3<f64>,
) -> WorldFuture<'a, ()> {
Box::pin(async move {
self.play_sound(sound, category, position).await;
})
}
fn play_sound_fine<'a>(
&'a self,
sound: Sound,
category: SoundCategory,
position: &'a Vector3<f64>,
volume: f32,
pitch: f32,
) -> WorldFuture<'a, ()> {
Box::pin(async move {
self.play_sound_fine(sound, category, position, volume, pitch)
.await;
})
}
fn scatter_inventory<'a>(
self: Arc<Self>,
position: &'a BlockPos,
inventory: &'a Arc<dyn Inventory>,
) -> WorldFuture<'a, ()> {
Box::pin(async move {
Self::scatter_inventory(&self, position, inventory).await;
})
}
fn spawn_experience_orbs(
self: Arc<Self>,
position: Vector3<f64>,
amount: u32,
) -> WorldFuture<'static, ()> {
Box::pin(async move {
ExperienceOrbEntity::spawn(&self, position, amount).await;
})
}
fn update_from_neighbor_shapes(
self: Arc<Self>,
block_state_id: BlockStateId,
position: &BlockPos,
) -> WorldFuture<'_, BlockStateId> {
Box::pin(async move {
let block = Block::from_state_id(block_state_id);
let mut state_id = block_state_id;
for direction in BlockDirection::update_order() {
let neighbor_pos = position.offset(direction.to_offset());
let neighbor_state_id = self.get_block_state_id(&neighbor_pos).await;
state_id = self
.block_registry
.get_state_for_neighbor_update(
&self,
block,
state_id,
position,
direction,
&neighbor_pos,
neighbor_state_id,
)
.await;
}
state_id
})
}
}
impl BlockAccessor for World { impl BlockAccessor for World {
fn get_block<'a>( fn get_block<'a>(
&'a self, &'a self,