refactor: move block entities from pumpkin-world -> pumpkin
This commit is contained in:
parent
2586d46380
commit
5c90387b02
425
Cargo.lock
generated
425
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||||
|
|
||||||
|
|||||||
136
pumpkin-inventory/src/beacon_screen_handler.rs
Normal file
136
pumpkin-inventory/src/beacon_screen_handler.rs
Normal 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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::*;
|
||||||
|
|||||||
12
pumpkin-protocol/src/java/server/play/set_beacon.rs
Normal file
12
pumpkin-protocol/src/java/server/play/set_beacon.rs
Normal 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>,
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|||||||
11
pumpkin-world/src/block/entities.rs
Normal file
11
pumpkin-world/src/block/entities.rs
Normal 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;
|
||||||
|
}
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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 {})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()),
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
69
pumpkin/src/block/blocks/beacon.rs
Normal file
69
pumpkin/src/block/blocks/beacon.rs
Normal 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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) =
|
||||||
|
|||||||
@ -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")]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>);
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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};
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -97,3 +97,5 @@ pub mod tnt;
|
|||||||
|
|
||||||
// Misc / abstract
|
// Misc / abstract
|
||||||
pub mod abstract_wall_mounting;
|
pub mod abstract_wall_mounting;
|
||||||
|
|
||||||
|
pub mod beacon;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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::{
|
||||||
|
|||||||
@ -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};
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
345
pumpkin/src/block/entities/beacon.rs
Normal file
345
pumpkin/src/block/entities/beacon.rs
Normal 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();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
@ -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,
|
||||||
@ -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();
|
||||||
@ -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(
|
||||||
@ -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 {
|
||||||
@ -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;
|
||||||
@ -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;
|
||||||
@ -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
|
||||||
@ -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,
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
||||||
@ -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);
|
||||||
@ -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,
|
||||||
@ -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;
|
||||||
207
pumpkin/src/block/entities/mod.rs
Normal file
207
pumpkin/src/block/entities/mod.rs
Normal 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)
|
||||||
|
}
|
||||||
@ -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);
|
||||||
@ -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
|
||||||
@ -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();
|
||||||
@ -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,
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
83
pumpkin/src/block/viewer.rs
Normal file
83
pumpkin/src/block/viewer.rs
Normal 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 {})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.";
|
||||||
|
|||||||
@ -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"];
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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},
|
||||||
|
|||||||
@ -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},
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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},
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user