Implement Piston 1/2

Not fully done
This commit is contained in:
Alexander Medvedev 2025-05-12 19:42:55 +02:00
parent 3a7f68d049
commit 1edbfa7289
65 changed files with 1202 additions and 134 deletions

24
Cargo.lock generated
View File

@ -323,18 +323,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.37"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.37"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
dependencies = [
"anstyle",
"clap_lex",
@ -1365,12 +1365,12 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libloading"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.53.0",
]
[[package]]
@ -1415,9 +1415,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lru"
version = "0.13.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198"
dependencies = [
"hashbrown 0.15.3",
]
@ -2022,8 +2022,10 @@ name = "pumpkin-world"
version = "0.1.0-dev+1.21.5"
dependencies = [
"async-trait",
"bitflags 2.9.0",
"bytes",
"criterion",
"crossbeam",
"dashmap",
"derive-getters",
"enum_dispatch",
@ -2757,9 +2759,9 @@ checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964"
[[package]]
name = "tempfile"
version = "3.19.1"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"fastrand",
"getrandom 0.3.3",

View File

@ -60,4 +60,4 @@ serde_json = "1.0"
async-trait = "0.1"
tempfile = "3.19.1"
tempfile = "3.20.0"

View File

@ -357,12 +357,35 @@ pub struct BlockState {
pub state_flags: u8,
pub instrument: String, // TODO: make this an enum
pub luminance: u8,
pub piston_behavior: PistonBehavior,
pub hardness: f32,
pub collision_shapes: Vec<u16>,
pub opacity: Option<u8>,
pub block_entity_type: Option<u16>,
}
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PistonBehavior {
Normal,
Destroy,
Block,
Ignore,
PushOnly,
}
impl PistonBehavior {
fn to_tokens(&self) -> TokenStream {
match self {
PistonBehavior::Normal => quote! { PistonBehavior::Normal },
PistonBehavior::Destroy => quote! { PistonBehavior::Destroy },
PistonBehavior::Block => quote! { PistonBehavior::Block },
PistonBehavior::Ignore => quote! { PistonBehavior::Ignore },
PistonBehavior::PushOnly => quote! { PistonBehavior::PushOnly },
}
}
}
#[derive(Deserialize, Clone, Debug)]
pub struct BlockStateRef {
pub id: u16,
@ -397,6 +420,7 @@ impl BlockState {
.collision_shapes
.iter()
.map(|shape_id| LitInt::new(&shape_id.to_string(), Span::call_site()));
let piston_behavior = &self.piston_behavior.to_tokens();
tokens.extend(quote! {
BlockState {
@ -404,6 +428,7 @@ impl BlockState {
state_flags: #state_flags,
instrument: #instrument,
luminance: #luminance,
piston_behavior: #piston_behavior,
hardness: #hardness,
collision_shapes: &[#(#collision_shapes),*],
opacity: #opacity,
@ -1270,6 +1295,7 @@ pub(crate) fn build() -> TokenStream {
quote! {
use crate::{BlockState, BlockStateRef, Block, CollisionShape};
use crate::block_state::PistonBehavior;
use pumpkin_util::math::int_provider::{UniformIntProvider, IntProvider, NormalIntProvider};
use pumpkin_util::loot_table::*;
use pumpkin_util::math::experience::Experience;

View File

@ -4,6 +4,7 @@ pub struct BlockState {
pub state_flags: u8,
pub instrument: &'static str,
pub luminance: u8,
pub piston_behavior: PistonBehavior,
pub hardness: f32,
pub collision_shapes: &'static [u16],
/// u8::MAX is used as None
@ -12,6 +13,15 @@ pub struct BlockState {
pub block_entity_type: u16,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PistonBehavior {
Normal,
Destroy,
Block,
Ignore,
PushOnly,
}
// Add your methods here
impl BlockState {
pub const fn is_air(&self) -> bool {

View File

@ -122,7 +122,7 @@ pub mod noise_router {
include!(concat!(env!("OUT_DIR"), "/noise_router.rs"));
}
mod block_state;
pub mod block_state;
mod blocks;
mod collision_shape;

View File

@ -116,6 +116,7 @@ impl ClientPacket for CChunkData<'_> {
// TODO: block entities
write.write_var_int(&VarInt(self.0.block_entities.len() as i32))?;
for block_entity in self.0.block_entities.values() {
let block_entity = &block_entity.1;
let chunk_data_nbt = block_entity.chunk_data_nbt();
let pos = block_entity.get_position();
let block_entity_id = block_entity.get_id();

View File

@ -22,9 +22,11 @@ thiserror.workspace = true
serde.workspace = true
serde_json.workspace = true
log.workspace = true
crossbeam.workspace = true
num-derive = "0.4"
sha2 = "0.10"
bitflags = "2.9.0"
dashmap = "6.1"
@ -42,13 +44,13 @@ enum_dispatch = "0.3"
derive-getters = "0.5.0"
thread_local = "1.1.8"
lru = "0.13.0"
lru = "0.14.0"
tokio-util = { version = "0.7.15", features = ["rt"] }
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
temp-dir = "0.1.14"
temp-dir = "0.1.16"
# Print log info inside tests when needed
env_logger = "0.11.8"
# Allows us to modify the config

View File

@ -5,16 +5,18 @@ use barrel::BarrelBlockEntity;
use bed::BedBlockEntity;
use chest::ChestBlockEntity;
use comparator::ComparatorBlockEntity;
use piston::PistonBlockEntity;
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use sign::SignBlockEntity;
use crate::inventory::Inventory;
use crate::{inventory::Inventory, world::SimpleWorld};
pub mod barrel;
pub mod bed;
pub mod chest;
pub mod comparator;
pub mod piston;
pub mod sign;
//TODO: We need a mark_dirty for chests
@ -24,6 +26,7 @@ pub trait BlockEntity: Send + Sync {
fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self
where
Self: Sized;
async fn tick(&self, _world: &Arc<dyn SimpleWorld>) {}
fn identifier(&self) -> &'static str;
fn get_position(&self) -> BlockPos;
async fn write_internal(&self, nbt: &mut NbtCompound) {
@ -73,6 +76,9 @@ pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option<Arc<dyn BlockEntity>>
BarrelBlockEntity::ID => Some(Arc::new(block_entity_from_generic::<BarrelBlockEntity>(
nbt,
))),
PistonBlockEntity::ID => Some(Arc::new(block_entity_from_generic::<PistonBlockEntity>(
nbt,
))),
_ => None,
}
}

View File

@ -0,0 +1,153 @@
use std::sync::Arc;
use async_trait::async_trait;
use crossbeam::atomic::AtomicCell;
use pumpkin_data::{
Block, BlockState,
block_properties::{get_block_by_state_id, get_state_by_state_id},
};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_util::math::position::BlockPos;
use crate::{
block::BlockDirection,
world::{BlockFlags, SimpleWorld},
};
use super::BlockEntity;
pub struct PistonBlockEntity {
pub position: BlockPos,
pub pushed_block_state: BlockState,
pub facing: BlockDirection,
pub current_progress: AtomicCell<f32>,
pub last_progress: AtomicCell<f32>,
pub extending: bool,
pub source: bool,
}
impl PistonBlockEntity {
pub const ID: &'static str = "minecraft:piston";
pub async fn finish(&self, world: Arc<dyn SimpleWorld>) {
if self.last_progress.load() < 1.0 {
let pos = self.position;
world.remove_block_entity(&pos).await;
if world.get_block(&pos).await.unwrap() == Block::MOVING_PISTON {
let state = if self.source {
Block::AIR.default_state_id
} else {
self.pushed_block_state.id
};
world
.clone()
.set_block_state(&pos, state, BlockFlags::NOTIFY_ALL)
.await;
world
.update_neighbor(&pos, &get_block_by_state_id(state).unwrap())
.await;
}
}
}
}
const FACING: &str = "facing";
const LAST_PROGRESS: &str = "progress";
const EXTENDING: &str = "extending";
const SOURCE: &str = "source";
#[async_trait]
impl BlockEntity for PistonBlockEntity {
fn identifier(&self) -> &'static str {
Self::ID
}
fn get_position(&self) -> BlockPos {
self.position
}
async fn tick(&self, world: &Arc<dyn SimpleWorld>) {
let current_progress = self.current_progress.load();
self.last_progress.store(current_progress);
if current_progress >= 1.0 {
let pos = self.position;
world.remove_block_entity(&pos).await;
if world.get_block(&pos).await.unwrap() == Block::MOVING_PISTON {
if self.pushed_block_state.is_air() {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::FORCE_STATE | BlockFlags::MOVED,
)
.await;
} else {
world
.clone()
.set_block_state(
&pos,
self.pushed_block_state.id,
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
)
.await;
world
.clone()
.update_neighbor(
&pos,
&get_block_by_state_id(self.pushed_block_state.id).unwrap(),
)
.await;
}
}
}
self.current_progress.store(current_progress + 0.5);
if current_progress + 0.5 >= 1.0 {
self.current_progress.store(1.0);
}
}
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
where
Self: Sized,
{
// TODO
let pushed_block_state = get_state_by_state_id(Block::AIR.default_state_id).unwrap();
let facing = nbt.get_byte(FACING).unwrap_or(0);
let last_progress = nbt.get_float(LAST_PROGRESS).unwrap_or(0.0);
let extending = nbt.get_bool(EXTENDING).unwrap_or(false);
let source = nbt.get_bool(SOURCE).unwrap_or(false);
Self {
pushed_block_state,
position,
facing: BlockDirection::from_index(facing as u8).unwrap_or(BlockDirection::Down),
current_progress: last_progress.into(),
last_progress: last_progress.into(),
extending,
source,
}
}
async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
// TODO: pushed_block_state
nbt.put_byte(FACING, self.facing.to_index() as i8);
nbt.put_float(LAST_PROGRESS, self.last_progress.load());
nbt.put_bool(EXTENDING, self.extending);
nbt.put_bool(SOURCE, self.source);
}
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
let mut nbt = NbtCompound::new();
// TODO: pushed_block_state
nbt.put_byte(FACING, self.facing.to_index() as i8);
nbt.put_float(LAST_PROGRESS, self.last_progress.load());
nbt.put_bool(EXTENDING, self.extending);
nbt.put_bool(SOURCE, self.source);
// TODO: duplicated code :c
Some(nbt)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

View File

@ -44,6 +44,29 @@ pub struct BlockStateCodec {
}
impl BlockDirection {
pub fn to_index(&self) -> u8 {
match self {
BlockDirection::Down => 0,
BlockDirection::Up => 1,
BlockDirection::North => 2,
BlockDirection::South => 3,
BlockDirection::West => 4,
BlockDirection::East => 5,
}
}
pub fn from_index(index: u8) -> Option<Self> {
match index {
0 => Some(Self::Down),
1 => Some(Self::Up),
2 => Some(Self::North),
3 => Some(Self::South),
4 => Some(Self::West),
5 => Some(Self::East),
_ => None,
}
}
pub fn to_offset(&self) -> Vector3<i32> {
match self {
BlockDirection::Down => (0, -1, 0),

View File

@ -885,7 +885,7 @@ pub async fn chunk_to_bytes(chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSeri
block_entities: join_all(chunk_data.block_entities.values().map(
|block_entity| async move {
let mut nbt = NbtCompound::new();
block_entity.write_internal(&mut nbt).await;
block_entity.1.write_internal(&mut nbt).await;
nbt
},
))

View File

@ -8,7 +8,7 @@ use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use serde::{Deserialize, Serialize};
use super::{
ChunkData, ChunkHeightmaps, ChunkLightEngine, ChunkParsingError, ChunkSections, ScheduledTick,
ChunkData, ChunkHeightmaps, ChunkLight, ChunkParsingError, ChunkSections, ScheduledTick,
SubChunk, TickPriority,
palette::{BiomePalette, BlockPalette},
};
@ -82,7 +82,7 @@ impl ChunkData {
)));
}
let light_engine = ChunkLightEngine {
let light_engine = ChunkLight {
block_light: (0..chunk_data.sections.len() + 2)
.map(|index| {
chunk_data
@ -167,7 +167,7 @@ impl ChunkData {
for nbt in chunk_data.block_entities {
let block_entity = block_entity_from_nbt(&nbt);
if let Some(block_entity) = block_entity {
block_entities.insert(block_entity.get_position(), block_entity);
block_entities.insert(block_entity.get_position(), (nbt, block_entity));
}
}
block_entities

View File

@ -297,7 +297,7 @@ where
let mut serializer = chunk_serializer.write().await;
for chunk_lock in chunk_locks {
let mut chunk = chunk_lock.write().await;
let chunk_is_dirty = chunk.dirty || chunk.block_entities.values().any(|block_entity| block_entity.is_dirty());
let chunk_is_dirty = chunk.dirty || chunk.block_entities.values().any(|block_entity| block_entity.1.is_dirty());
// Edge case: this chunk is loaded while we were saving, mark it as cleaned since we are
// updating what we will write here
chunk.dirty = false;

View File

@ -1,5 +1,6 @@
use crate::block::entities::BlockEntity;
use palette::{BiomePalette, BlockPalette};
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::nbt_long_array;
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use serde::{Deserialize, Serialize};
@ -115,8 +116,8 @@ pub struct ChunkData {
pub position: Vector2<i32>,
pub block_ticks: Vec<ScheduledTick>,
pub fluid_ticks: Vec<ScheduledTick>,
pub block_entities: HashMap<BlockPos, Arc<dyn BlockEntity>>,
pub light_engine: ChunkLightEngine,
pub block_entities: HashMap<BlockPos, (NbtCompound, Arc<dyn BlockEntity>)>,
pub light_engine: ChunkLight,
pub dirty: bool,
}
@ -166,7 +167,7 @@ pub struct SubChunk {
}
#[derive(Debug, Default)]
pub struct ChunkLightEngine {
pub struct ChunkLight {
pub sky_light: Box<[LightContainer]>,
pub block_light: Box<[LightContainer]>,
}

View File

@ -6,7 +6,7 @@ use super::{
noise_router::proto_noise_router::ProtoNoiseRouters,
settings::{GENERATION_SETTINGS, GeneratorSetting},
};
use crate::chunk::ChunkLightEngine;
use crate::chunk::ChunkLight;
use crate::chunk::format::LightContainer;
use crate::{
chunk::{
@ -81,7 +81,7 @@ impl WorldGenerator for VanillaGenerator {
}
ChunkData {
light_engine: ChunkLightEngine {
light_engine: ChunkLight {
sky_light: (0..sections.sections.len() + 2)
.map(|_| LightContainer::new_filled(15))
.collect(),

View File

@ -13,6 +13,7 @@ pub mod inventory;
pub mod item;
pub mod level;
mod lock;
pub mod world;
pub mod world_info;
pub type BlockId = u16;

View File

@ -0,0 +1,62 @@
use std::sync::Arc;
use crate::block::BlockDirection;
use async_trait::async_trait;
use bitflags::bitflags;
use pumpkin_util::math::position::BlockPos;
use thiserror::Error;
use crate::BlockStateId;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockFlags: u32 {
const NOTIFY_NEIGHBORS = 0b000_0000_0001;
const NOTIFY_LISTENERS = 0b000_0000_0010;
const NOTIFY_ALL = 0b000_0000_0011;
const FORCE_STATE = 0b000_0000_0100;
const SKIP_DROPS = 0b000_0000_1000;
const MOVED = 0b000_0001_0000;
const SKIP_REDSTONE_WIRE_STATE_REPLACEMENT = 0b000_0010_0000;
const SKIP_BLOCK_ENTITY_REPLACED_CALLBACK = 0b000_0100_0000;
const SKIP_BLOCK_ADDED_CALLBACK = 0b000_1000_0000;
}
}
#[derive(Debug, Error)]
pub enum GetBlockError {
InvalidBlockId,
BlockOutOfWorldBounds,
}
impl std::fmt::Display for GetBlockError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
#[async_trait]
pub trait SimpleWorld: Send + Sync {
async fn set_block_state(
self: Arc<Self>,
position: &BlockPos,
block_state_id: BlockStateId,
flags: BlockFlags,
) -> BlockStateId;
async fn get_block(&self, position: &BlockPos) -> Result<pumpkin_data::Block, GetBlockError>;
async fn update_neighbor(
self: Arc<Self>,
neighbor_block_pos: &BlockPos,
source_block: &pumpkin_data::Block,
);
async fn update_neighbors(
self: Arc<Self>,
block_pos: &BlockPos,
except: Option<BlockDirection>,
);
async fn remove_block_entity(&self, block_pos: &BlockPos);
}

View File

@ -57,7 +57,7 @@ impl PumpkinBlock for BarrelBlock {
world: &Arc<World>,
) {
if let Some(block_entity) = world.get_block_entity(&location).await {
if let Some(inventory) = block_entity.get_inventory() {
if let Some(inventory) = block_entity.1.get_inventory() {
player
.open_handled_screen(&BarrelScreenFactory(inventory))
.await;
@ -75,7 +75,7 @@ impl PumpkinBlock for BarrelBlock {
world: &Arc<World>,
) -> BlockActionResult {
if let Some(block_entity) = world.get_block_entity(&location).await {
if let Some(inventory) = block_entity.get_inventory() {
if let Some(inventory) = block_entity.1.get_inventory() {
player
.open_handled_screen(&BarrelScreenFactory(inventory))
.await;

View File

@ -9,12 +9,12 @@ use pumpkin_util::GameMode;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::entity::player::Player;
use crate::world::BlockFlags;
use pumpkin_protocol::server::play::SUseItemOn;
use crate::server::Server;

View File

@ -12,11 +12,11 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
#[pumpkin_block("minecraft:cactus")]

View File

@ -13,10 +13,11 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::block::entities::chest::ChestBlockEntity;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockIsReplacing;
use crate::entity::EntityBase;
use crate::world::{BlockFlags, World};
use crate::world::World;
use crate::{
block::{pumpkin_block::PumpkinBlock, registry::BlockActionResult},
entity::player::Player,

View File

@ -4,7 +4,6 @@ use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::Block;
@ -14,6 +13,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:dirt_path")]
pub struct DirtPathBlock;

View File

@ -14,6 +14,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::block::HorizontalFacingExt;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
@ -21,7 +22,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::world::BlockFlags;
use pumpkin_data::item::Item;
use pumpkin_protocol::server::play::SUseItemOn;

View File

@ -4,7 +4,6 @@ use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::Block;
@ -14,6 +13,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:farmland")]
pub struct FarmLandBlock;

View File

@ -11,11 +11,11 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::block::registry::BlockActionResult;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use pumpkin_data::item::Item;

View File

@ -4,7 +4,7 @@ use crate::block::pumpkin_block::PumpkinBlock;
use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::{BlockFlags, World};
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::item::Item;
use pumpkin_data::world::WorldEvent;
@ -15,6 +15,7 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block;
use pumpkin_registry::SYNCED_REGISTRIES;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:jukebox")]
pub struct JukeboxBlock;

View File

@ -16,6 +16,7 @@ pub mod jukebox;
pub mod logs;
pub mod nether_portal;
pub mod note;
pub mod piston;
pub mod pumpkin;
pub mod redstone;
pub mod signs;

View File

@ -18,11 +18,9 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use crate::{
block::pumpkin_block::PumpkinBlock,
world::{BlockFlags, World},
};
use crate::{block::pumpkin_block::PumpkinBlock, world::World};
use super::redstone::block_receives_redstone_power;

View File

@ -0,0 +1,238 @@
use piston::PistonBlock;
use pumpkin_data::Block;
use pumpkin_data::block_state::PistonBehavior;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use crate::world::World;
#[expect(clippy::module_inception)]
pub(crate) mod piston;
pub(crate) mod piston_extension;
pub(crate) mod piston_head;
const MAX_MOVABLE_BLOCKS: usize = 12;
pub struct PistonHandler<'a> {
world: &'a World,
pos_from: BlockPos,
retracted: bool,
pos_to: BlockPos,
motion_direction: BlockDirection,
pub moved_blocks: Vec<BlockPos>,
pub broken_blocks: Vec<BlockPos>,
_piston_direction: BlockDirection,
}
impl<'a> PistonHandler<'a> {
pub fn new(world: &'a World, pos: BlockPos, dir: BlockDirection, retracted: bool) -> Self {
let motion_direction;
let pos_to = if retracted {
motion_direction = dir;
pos.offset_dir(dir.to_offset(), 1)
} else {
motion_direction = dir.opposite();
pos.offset_dir(dir.to_offset(), 2)
};
PistonHandler {
world,
pos_from: pos,
_piston_direction: dir,
retracted,
motion_direction,
pos_to,
moved_blocks: Vec::new(),
broken_blocks: Vec::new(),
}
}
pub async fn calculate_push(&mut self) -> bool {
self.moved_blocks.clear();
self.broken_blocks.clear();
let (block, block_state) = self
.world
.get_block_and_block_state(&self.pos_to)
.await
.unwrap();
if !PistonBlock::is_movable(
&block,
&block_state,
// self.world,
// self.pos_to,
// self.motion_direction,
// false,
// self.piston_direction,
) {
if self.retracted && block_state.piston_behavior == PistonBehavior::Destroy {
self.broken_blocks.push(self.pos_to);
return true;
}
return false;
}
if !self.try_move(self.pos_to, self.motion_direction).await {
return false;
}
for block_pos in self.moved_blocks.clone() {
let block = self.world.get_block(&block_pos).await.unwrap();
if Self::is_block_sticky(&block)
&& !self.try_move_adjacent_block(&block, block_pos).await
{
return false;
}
}
true
}
fn is_block_sticky(block: &Block) -> bool {
block == &Block::SLIME_BLOCK || block == &Block::HONEY_BLOCK
}
fn is_adjacent_block_stuck(state: &Block, adjacent_state: &Block) -> bool {
if state == &Block::HONEY_BLOCK && adjacent_state == &Block::SLIME_BLOCK {
return false;
}
if state == &Block::SLIME_BLOCK && adjacent_state == &Block::HONEY_BLOCK {
return false;
}
Self::is_block_sticky(state) || Self::is_block_sticky(adjacent_state)
}
async fn try_move(&mut self, pos: BlockPos, _dir: BlockDirection) -> bool {
let (mut block, mut block_state) =
self.world.get_block_and_block_state(&pos).await.unwrap();
if block_state.is_air() {
return true;
}
if !PistonBlock::is_movable(
&block,
&block_state,
// self.world,
// pos,
// self.motion_direction,
// false,
// dir,
) {
return true;
}
if pos == self.pos_from {
return true;
}
if self.moved_blocks.contains(&pos) {
return true;
}
let mut i = 1;
if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS {
return false;
}
while Self::is_block_sticky(&block) {
let block_pos = pos.offset_dir(self.motion_direction.opposite().to_offset(), i as i32);
let block2 = block;
(block, block_state) = self
.world
.get_block_and_block_state(&block_pos)
.await
.unwrap();
if block_state.is_air()
|| !Self::is_adjacent_block_stuck(&block2, &block)
|| !PistonBlock::is_movable(
&block,
&block_state,
// self.world,
// block_pos,
// self.motion_direction,
// false,
// self.motion_direction.opposite(),
)
|| block_pos == self.pos_from
{
break;
}
i += 1;
if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS {
return false;
}
}
let mut j = 0;
for k in (0..i).rev() {
self.moved_blocks
.push(pos.offset_dir(self.motion_direction.opposite().to_offset(), k as i32));
j += 1;
}
let mut k = 1;
loop {
let block_pos2 = pos.offset_dir(self.motion_direction.to_offset(), k);
if let Some(l) = self.moved_blocks.iter().position(|&p| p == block_pos2) {
self.set_moved_blocks(j, l);
for m in 0..=(l + j) {
let block_pos3 = self.moved_blocks[m];
let block = self.world.get_block(&block_pos3).await.unwrap();
if Self::is_block_sticky(&block)
&& !Box::pin(self.try_move_adjacent_block(&block, block_pos3)).await
{
return false;
}
}
return true;
}
(block, block_state) = self
.world
.get_block_and_block_state(&block_pos2)
.await
.unwrap();
if block_state.is_air() {
return true;
}
if !PistonBlock::is_movable(
&block,
&block_state,
// self.world,
// block_pos2,
// self.motion_direction,
// true,
// self.motion_direction,
) || block_pos2 == self.pos_from
{
return false;
}
if block_state.piston_behavior == PistonBehavior::Destroy {
self.broken_blocks.push(block_pos2);
return true;
}
if self.moved_blocks.len() >= MAX_MOVABLE_BLOCKS {
return false;
}
self.moved_blocks.push(block_pos2);
j += 1;
k += 1;
}
}
fn set_moved_blocks(&mut self, from: usize, to: usize) {
let mut list = Vec::new();
let mut list2 = Vec::new();
let mut list3 = Vec::new();
list.extend_from_slice(&self.moved_blocks[0..to]);
list2.extend_from_slice(&self.moved_blocks[self.moved_blocks.len() - from..]);
list3.extend_from_slice(&self.moved_blocks[to..self.moved_blocks.len() - from]);
self.moved_blocks.clear();
self.moved_blocks.extend(list);
self.moved_blocks.extend(list2);
self.moved_blocks.extend(list3);
}
async fn try_move_adjacent_block(&mut self, block: &Block, pos: BlockPos) -> bool {
for direction in BlockDirection::all() {
if direction.to_axis() == self.motion_direction.to_axis() {
continue;
}
let block_pos = pos.offset(direction.to_offset());
let block_state2 = self.world.get_block(&block_pos).await.unwrap();
if Self::is_adjacent_block_stuck(&block_state2, block)
&& !self.try_move(block_pos, direction).await
{
return false;
}
}
true
}
}

View File

@ -0,0 +1,462 @@
use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockState,
block_properties::{
BlockProperties, MovingPistonLikeProperties, PistonHeadLikeProperties, PistonType,
get_block_by_state_id, get_state_by_state_id,
},
};
use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
BlockStateId,
block::{
BlockDirection, FacingExt,
entities::{BlockEntity, piston::PistonBlockEntity},
},
world::BlockFlags,
};
use crate::{
block::{
BlockIsReplacing,
blocks::redstone::is_emitting_redstone_power,
pumpkin_block::{BlockMetadata, PumpkinBlock},
},
entity::player::Player,
server::Server,
world::World,
};
use super::{PistonHandler, piston_extension::MovingPistonProps};
pub(crate) type PistonProps = pumpkin_data::block_properties::StickyPistonLikeProperties;
pub struct PistonBlock;
impl BlockMetadata for PistonBlock {
fn namespace(&self) -> &'static str {
"minecraft"
}
fn ids(&self) -> &'static [&'static str] {
&[Block::PISTON.name, Block::STICKY_PISTON.name]
}
}
impl PistonBlock {
pub fn is_movable(block: &Block, state: &BlockState) -> bool {
// TODO: more checks
if state.is_air() {
return true;
}
// Vanilla hardcoded them aswell
if block == &Block::OBSIDIAN
|| block == &Block::CRYING_OBSIDIAN
|| block == &Block::RESPAWN_ANCHOR
|| block == &Block::REINFORCED_DEEPSLATE
{
return false;
}
true
}
}
#[async_trait]
impl PumpkinBlock for PistonBlock {
async fn on_place(
&self,
_server: &Server,
_world: &World,
player: &Player,
block: &Block,
_block_pos: &BlockPos,
_face: BlockDirection,
_replacing: BlockIsReplacing,
_use_item_on: &SUseItemOn,
) -> BlockStateId {
let mut props = PistonProps::default(block);
props.extended = false;
props.facing = player.living_entity.entity.get_facing().opposite();
props.to_state_id(block)
}
async fn placed(
&self,
world: &Arc<World>,
block: &Block,
state_id: BlockStateId,
pos: &BlockPos,
old_state_id: BlockStateId,
_notify: bool,
) {
if old_state_id == state_id {
return;
}
try_move(world, block, pos).await;
}
async fn on_neighbor_update(
&self,
world: &Arc<World>,
block: &Block,
block_pos: &BlockPos,
_source_block: &Block,
_notify: bool,
) {
try_move(world, block, block_pos).await;
}
async fn on_synced_block_event(
&self,
block: &Block,
world: &Arc<World>,
pos: &BlockPos,
r#type: u8,
_data: u8,
) -> bool {
let state = world.get_block_state(pos).await.unwrap();
let mut props = PistonProps::from_state_id(state.id, block);
let dir = props.facing.to_block_direction();
// I don't think this is optimal ?
let sticky = block == &Block::STICKY_PISTON;
let should_extend = should_extend(world, block, &state, pos, dir).await;
if should_extend && (r#type == 1 || r#type == 2) {
props.extended = true;
world
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
.await;
return false;
}
// This may prevents when something happens in the one tick before this function got called
if !should_extend && r#type == 0 {
return false;
}
if r#type == 0 {
if !move_piston(world, dir, pos, true, sticky).await {
return false;
}
props.extended = true;
world
.set_block_state(
pos,
props.to_state_id(block),
BlockFlags::NOTIFY_ALL | BlockFlags::MOVED,
)
.await;
return true;
}
let extended_pos = pos.offset(dir.to_offset());
if let Some((block_entity_nbt, _block_entity)) = world.get_block_entity(&extended_pos).await
{
PistonBlockEntity::from_nbt(&block_entity_nbt, extended_pos)
.finish(world.clone())
.await;
}
let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON);
props.facing = dir.to_facing();
props.r#type = if sticky {
PistonType::Sticky
} else {
PistonType::Normal
};
let state = props.to_state_id(&Block::MOVING_PISTON);
world
.set_block_state(pos, state, BlockFlags::FORCE_STATE)
.await;
world
.add_block_entity(Arc::new(PistonBlockEntity {
position: *pos,
facing: dir.to_facing().to_block_direction(),
pushed_block_state: get_state_by_state_id(state).unwrap(),
current_progress: 0.0.into(),
last_progress: 0.0.into(),
extending: false,
source: true,
}))
.await;
world.update_neighbors(pos, None).await;
if sticky {
} else {
world
.set_block_state(
&extended_pos,
Block::AIR.default_state_id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
return true;
}
}
async fn should_extend(
world: &Arc<World>,
block: &Block,
state: &BlockState,
block_pos: &BlockPos,
piston_dir: BlockDirection,
) -> bool {
// Pistons can't be powered from the same direction as they are facing
for dir in BlockDirection::all() {
if dir == piston_dir
|| !is_emitting_redstone_power(
block,
state,
world,
&block_pos.offset(dir.to_offset()),
dir,
)
.await
{
continue;
}
return true;
}
if is_emitting_redstone_power(block, state, world, block_pos, BlockDirection::Down).await {
return true;
}
for dir in BlockDirection::all() {
if dir == BlockDirection::Down
|| !is_emitting_redstone_power(
block,
state,
world,
&block_pos.up().offset(dir.to_offset()),
dir,
)
.await
{
continue;
}
return true;
}
false
}
async fn try_move(world: &Arc<World>, block: &Block, block_pos: &BlockPos) {
let state = world.get_block_state(block_pos).await.unwrap();
let props = PistonProps::from_state_id(state.id, block);
let dir = props.facing.to_block_direction();
let should_extent = should_extend(world, block, &state, block_pos, dir).await;
if should_extent && !props.extended {
if PistonHandler::new(world, *block_pos, dir, true)
.calculate_push()
.await
{
world
.add_synced_block_event(*block_pos, 0, dir.to_index() as u8)
.await;
}
} else if !should_extent && props.extended {
let new_pos = block_pos.offset_dir(dir.to_offset(), 2);
let (new_block, new_state) = world.get_block_and_block_state(&new_pos).await.unwrap();
let mut r#type = 1;
if new_block == Block::MOVING_PISTON {
let new_props = MovingPistonProps::from_state_id(new_state.id, &new_block);
// TODO: check more things
if new_props.facing == props.facing {
r#type = 2;
}
}
world
.add_synced_block_event(*block_pos, r#type, dir.to_index() as u8)
.await;
}
}
#[expect(clippy::too_many_lines)]
async fn move_piston(
world: &Arc<World>,
dir: BlockDirection,
block_pos: &BlockPos,
extend: bool,
sticky: bool,
) -> bool {
let extended_pos = block_pos.offset(dir.to_offset());
if !extend && world.get_block(&extended_pos).await.unwrap() == Block::PISTON_HEAD {
world
.set_block_state(
&extended_pos,
Block::AIR.default_state_id,
BlockFlags::FORCE_STATE,
)
.await;
}
let mut handler = PistonHandler::new(world, *block_pos, dir, extend);
if !handler.calculate_push().await {
return false;
}
let mut moved_blocks_map: HashMap<BlockPos, BlockState> = HashMap::new();
let moved_blocks: Vec<BlockPos> = handler.moved_blocks;
dbg!(&moved_blocks);
let mut moved_block_states: Vec<BlockState> = Vec::new();
for &block_pos in &moved_blocks {
let block_state = world.get_block_state(&block_pos).await.unwrap();
dbg!(block_state.id);
moved_block_states.push(block_state.clone());
moved_blocks_map.insert(block_pos, block_state);
}
let broken_blocks: Vec<BlockPos> = handler.broken_blocks;
dbg!(&broken_blocks);
let mut affected_block_states: Vec<BlockState> =
Vec::with_capacity(moved_blocks.len() + broken_blocks.len());
let move_direction = if extend { dir } else { dir.opposite() };
for &broken_block_pos in broken_blocks.iter().rev() {
let block_state = world.get_block_state(&broken_block_pos).await.unwrap();
world
.break_block(&broken_block_pos, None, BlockFlags::empty())
.await;
affected_block_states.push(block_state);
}
for (index, &moved_block_pos) in moved_blocks.iter().rev().enumerate() {
let block_state = world.get_block_state(&moved_block_pos).await.unwrap();
let target_pos = moved_block_pos.offset(move_direction.to_offset());
moved_blocks_map.remove(&target_pos);
let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON);
props.facing = dir.to_facing();
let state = props.to_state_id(&Block::MOVING_PISTON);
world
.set_block_state(&target_pos, state, BlockFlags::MOVED)
.await;
if let Some(moved_state) = moved_block_states.get(index) {
world
.add_block_entity(Arc::new(PistonBlockEntity {
position: extended_pos,
facing: dir.to_facing().to_block_direction(),
pushed_block_state: moved_state.clone(),
current_progress: 0.0.into(),
last_progress: 0.0.into(),
extending: extend,
source: false,
}))
.await;
}
affected_block_states.push(block_state);
}
if extend {
let pistion_type = if sticky {
PistonType::Sticky
} else {
PistonType::Normal
};
let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON);
props.facing = dir.to_facing();
props.r#type = pistion_type;
moved_blocks_map.remove(&extended_pos);
world
.set_block_state(
&extended_pos,
props.to_state_id(&Block::MOVING_PISTON),
BlockFlags::MOVED,
)
.await;
let mut props = PistonHeadLikeProperties::default(&Block::PISTON_HEAD);
props.facing = dir.to_facing();
props.r#type = pistion_type;
world
.add_block_entity(Arc::new(PistonBlockEntity {
position: extended_pos,
facing: dir.to_facing().to_block_direction(),
pushed_block_state: get_state_by_state_id(props.to_state_id(&Block::PISTON_HEAD))
.unwrap(),
current_progress: 0.0.into(),
last_progress: 0.0.into(),
extending: true,
source: true,
}))
.await;
}
let air_state = Block::AIR.default_state_id;
for &pos in moved_blocks_map.keys() {
world
.set_block_state(
&pos,
air_state,
BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE | BlockFlags::MOVED,
)
.await;
}
for (pos, state) in &moved_blocks_map {
// state.prepare(world, pos, BlockFlags::NOTIFY_LISTENERS);
world
.block_registry
.prepare(
world,
pos,
&get_block_by_state_id(state.id).unwrap(),
state.id,
BlockFlags::NOTIFY_LISTENERS,
)
.await;
world.update_neighbors(pos, None).await;
world
.block_registry
.prepare(
world,
pos,
&Block::AIR,
air_state,
BlockFlags::NOTIFY_LISTENERS,
)
.await;
}
for (i, &broken_block_pos) in broken_blocks.iter().rev().enumerate() {
if let Some(block_state) = affected_block_states.get(i).cloned() {
world
.block_registry
.on_state_replaced(
world,
&get_block_by_state_id(block_state.id).unwrap(),
broken_block_pos,
block_state.id, // ?
false,
)
.await;
world
.block_registry
.prepare(
world,
&broken_block_pos,
&get_block_by_state_id(block_state.id).unwrap(),
block_state.id,
BlockFlags::NOTIFY_LISTENERS,
)
.await;
world.update_neighbors(&broken_block_pos, None).await;
}
}
for &moved_block_pos in moved_blocks.iter().rev() {
world.update_neighbors(&moved_block_pos, None).await;
}
// world.update_neighbors(&extended_pos, None).await;
true
}

View File

@ -0,0 +1,46 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::{Block, BlockState};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{block::FacingExt, world::BlockFlags};
use crate::entity::player::Player;
use crate::{
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
world::World,
};
use super::piston::{PistonBlock, PistonProps};
pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPistonLikeProperties;
use crate::server::Server;
#[pumpkin_block("minecraft:moving_piston")]
pub struct PistonExtensionBlock;
#[async_trait]
impl PumpkinBlock for PistonExtensionBlock {
async fn broken(
&self,
_block: &Block,
_player: &Arc<Player>,
location: BlockPos,
_server: &Server,
world: Arc<World>,
state: BlockState,
) {
let props = MovingPistonProps::from_state_id(state.id, &Block::MOVING_PISTON);
let pos = location.offset(props.facing.opposite().to_block_direction().to_offset());
let (new_block, new_state) = world.get_block_and_block_state(&pos).await.unwrap();
if PistonBlock::ids(&PistonBlock).contains(&new_block.name) {
let props = PistonProps::from_state_id(new_state.id, &new_block);
if props.extended {
// TODO: use player
world.break_block(&pos, None, BlockFlags::SKIP_DROPS).await;
}
}
}
}

View File

@ -0,0 +1,10 @@
use async_trait::async_trait;
use pumpkin_macros::pumpkin_block;
use crate::block::pumpkin_block::PumpkinBlock;
#[pumpkin_block("minecraft:piston_head")]
pub struct PistonHeadBlock;
#[async_trait]
impl PumpkinBlock for PistonHeadBlock {}

View File

@ -1,6 +1,6 @@
use crate::entity::item::ItemEntity;
use crate::server::Server;
use crate::world::{BlockFlags, World};
use crate::world::World;
use crate::{block::registry::BlockActionResult, entity::player::Player};
use async_trait::async_trait;
use pumpkin_data::Block;
@ -9,6 +9,7 @@ use pumpkin_data::item::Item;
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
#[pumpkin_block("minecraft:pumpkin")]
pub struct PumpkinBlock;

View File

@ -13,6 +13,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
type ButtonLikeProperties = pumpkin_data::block_properties::LeverLikeProperties;
@ -22,7 +23,6 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
async fn click_button(world: &Arc<World>, block_pos: &BlockPos) {

View File

@ -1,6 +1,5 @@
use std::sync::Arc;
use crate::world::BlockFlags;
use crate::{block::BlockIsReplacing, entity::player::Player};
use async_trait::async_trait;
use pumpkin_data::{
@ -11,8 +10,8 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block;
use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::{BlockDirection, HorizontalFacingExt};
use pumpkin_world::{BlockStateId, world::BlockFlags};
use crate::{
block::{pumpkin_block::PumpkinBlock, registry::BlockActionResult},

View File

@ -43,20 +43,30 @@ pub async fn update_wire_neighbors(world: &Arc<World>, pos: &BlockPos) {
}
}
pub async fn is_emitting_redstone_power(
block: &Block,
state: &BlockState,
world: &World,
pos: &BlockPos,
facing: BlockDirection,
) -> bool {
get_redstone_power(block, state, world, pos, facing).await > 0
}
pub async fn get_redstone_power(
block: &Block,
state: &BlockState,
world: &World,
pos: BlockPos,
pos: &BlockPos,
facing: BlockDirection,
) -> u8 {
if state.is_solid() {
return std::cmp::max(
get_max_strong_power(world, &pos, true).await,
get_weak_power(block, state, world, &pos, facing, true).await,
get_max_strong_power(world, pos, true).await,
get_weak_power(block, state, world, pos, facing, true).await,
);
}
get_weak_power(block, state, world, &pos, facing, true).await
get_weak_power(block, state, world, pos, facing, true).await
}
async fn get_redstone_power_no_dust(
@ -160,7 +170,7 @@ pub async fn block_receives_redstone_power(world: &World, pos: &BlockPos) -> boo
.get_block_and_block_state(&neighbor_pos)
.await
.unwrap();
if get_redstone_power(&block, &state, world, neighbor_pos, face).await > 0 {
if is_emitting_redstone_power(&block, &state, world, pos, face).await {
return true;
}
}
@ -174,7 +184,7 @@ pub fn is_diode(block: &Block) -> bool {
pub async fn diode_get_input_strength(world: &World, pos: &BlockPos, facing: BlockDirection) -> u8 {
let input_pos = pos.offset(facing.to_offset());
let (input_block, input_state) = world.get_block_and_block_state(&input_pos).await.unwrap();
let power: u8 = get_redstone_power(&input_block, &input_state, world, input_pos, facing).await;
let power: u8 = get_redstone_power(&input_block, &input_state, world, &input_pos, facing).await;
if power == 0 && input_state.is_solid() {
return get_max_weak_power(world, &input_pos, true).await;
}

View File

@ -13,13 +13,10 @@ use pumpkin_world::{
BlockStateId,
block::{BlockDirection, FacingExt},
chunk::TickPriority,
world::BlockFlags,
};
use crate::{
block::pumpkin_block::PumpkinBlock,
server::Server,
world::{BlockFlags, World},
};
use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World};
#[pumpkin_block("minecraft:observer")]
pub struct ObserverBlock;

View File

@ -5,12 +5,12 @@ use async_trait::async_trait;
use pumpkin_data::{Block, block_properties::BlockProperties};
use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, block::BlockDirection};
use pumpkin_world::{BlockStateId, block::BlockDirection, world::BlockFlags};
use crate::{
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
server::Server,
world::{BlockFlags, World},
world::World,
};
use super::block_receives_redstone_power;

View File

@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use super::RailProperties;

View File

@ -5,9 +5,9 @@ use pumpkin_data::{
block_properties::{HorizontalFacing, RailShape, StraightRailShape},
};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::{BlockStateId, world::BlockFlags};
use crate::world::{BlockFlags, World};
use crate::world::World;
use super::{HorizontalFacingRailExt, Rail, RailElevation, RailProperties, StraightRailShapeExt};

View File

@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use super::RailProperties;

View File

@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use super::RailProperties;

View File

@ -7,13 +7,13 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use super::StraightRailShapeExt;

View File

@ -6,13 +6,9 @@ use pumpkin_data::{Block, block_properties::BlockProperties};
use pumpkin_macros::pumpkin_block;
use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, block::BlockDirection, chunk::TickPriority};
use pumpkin_world::{BlockStateId, block::BlockDirection, chunk::TickPriority, world::BlockFlags};
use crate::{
block::pumpkin_block::PumpkinBlock,
server::Server,
world::{BlockFlags, World},
};
use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World};
use super::block_receives_redstone_power;

View File

@ -12,13 +12,13 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::block::HorizontalFacingExt;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
type RWallTorchProps = pumpkin_data::block_properties::FurnaceLikeProperties;
type RTorchProps = pumpkin_data::block_properties::RedstoneOreLikeProperties;
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
use super::get_redstone_power;
@ -209,7 +209,7 @@ impl PumpkinBlock for RedstoneTorchBlock {
pub async fn should_be_lit(world: &World, pos: &BlockPos, face: BlockDirection) -> bool {
let other_pos = pos.offset(face.to_offset());
let (block, state) = world.get_block_and_block_state(&other_pos).await.unwrap();
get_redstone_power(&block, &state, world, other_pos, face).await == 0
get_redstone_power(&block, &state, world, &other_pos, face).await == 0
}
pub async fn update_neighbors(world: &Arc<World>, pos: &BlockPos) {

View File

@ -13,11 +13,11 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::{BlockDirection, HorizontalFacingExt};
use pumpkin_world::world::BlockFlags;
use crate::block::BlockIsReplacing;
use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::world::BlockFlags;
use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World};
use super::turbo::RedstoneWireTurbo;

View File

@ -9,14 +9,17 @@ use pumpkin_data::{
use pumpkin_macros::pumpkin_block;
use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::{BlockDirection, HorizontalFacingExt};
use pumpkin_world::{BlockStateId, chunk::TickPriority};
use pumpkin_world::{
block::{BlockDirection, HorizontalFacingExt},
world::BlockFlags,
};
use crate::{
block::{BlockIsReplacing, pumpkin_block::PumpkinBlock, registry::BlockActionResult},
entity::player::Player,
server::Server,
world::{BlockFlags, World},
world::World,
};
use super::{diode_get_input_strength, get_weak_power, is_diode};

View File

@ -7,11 +7,11 @@ use pumpkin_data::{
block_properties::{BlockProperties, EnumVariants, Integer0To15, RedstoneWireLikeProperties},
};
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use pumpkin_world::block::BlockDirection;
use pumpkin_world::{block::BlockDirection, world::BlockFlags};
use rustc_hash::FxHashMap;
use std::sync::Arc;
use crate::world::{BlockFlags, World};
use crate::world::World;
use super::get_redstone_power_no_dust;

View File

@ -11,11 +11,11 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use crate::block::BlockIsReplacing;
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::world::BlockFlags;
use crate::world::World;
use crate::{entity::player::Player, server::Server};

View File

@ -13,11 +13,11 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::chunk::TickPriority;
use pumpkin_world::world::BlockFlags;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::BlockFlags;
use crate::world::World;
#[pumpkin_block("minecraft:sugar_cane")]

View File

@ -5,7 +5,7 @@ use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::entity::tnt::TNTEntity;
use crate::server::Server;
use crate::world::{BlockFlags, World};
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::entity::EntityType;
@ -15,6 +15,7 @@ use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::BlockStateId;
use pumpkin_world::world::BlockFlags;
use rand::Rng;
use super::redstone::block_receives_redstone_power;

View File

@ -7,9 +7,9 @@ use pumpkin_data::{
fluid::{EnumVariants, Falling, Fluid, FluidProperties, Level},
};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockId, BlockStateId, block::BlockDirection};
use pumpkin_world::{BlockId, BlockStateId, block::BlockDirection, world::BlockFlags};
use crate::world::{BlockFlags, World};
use crate::world::World;
type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties;
#[derive(Clone)]

View File

@ -13,6 +13,7 @@ use blocks::iron_bars::IronBarsBlock;
use blocks::logs::LogBlock;
use blocks::nether_portal::NetherPortalBlock;
use blocks::note::NoteBlock;
use blocks::piston::piston_extension::PistonExtensionBlock;
use blocks::pumpkin::PumpkinBlock;
use blocks::redstone::buttons::ButtonBlock;
use blocks::redstone::observer::ObserverBlock;
@ -101,7 +102,11 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(ButtonBlock);
manager.register(LeverBlock);
manager.register(ObserverBlock);
// Piston
manager.register(PistonBlock);
manager.register(PistonExtensionBlock);
manager.register(RedstoneBlock);
manager.register(RedstoneLamp);
manager.register(RedstoneTorchBlock);

View File

@ -1,7 +1,7 @@
use crate::block::registry::BlockActionResult;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::{BlockFlags, World};
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::item::Item;
use pumpkin_data::{Block, BlockState};
@ -9,6 +9,7 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
use super::BlockIsReplacing;

View File

@ -1,7 +1,7 @@
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::{BlockFlags, World};
use crate::world::World;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::item::Item;
use pumpkin_data::{Block, BlockState};
@ -9,6 +9,7 @@ use pumpkin_protocol::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use std::collections::HashMap;
use std::sync::Arc;

View File

@ -4,12 +4,12 @@ use crate::command::args::{ConsumedArgs, FindArg};
use crate::command::tree::CommandTree;
use crate::command::tree::builder::{argument, literal};
use crate::command::{CommandError, CommandExecutor, CommandSender};
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
use pumpkin_world::world::BlockFlags;
const NAMES: [&str; 1] = ["fill"];

View File

@ -1,5 +1,6 @@
use async_trait::async_trait;
use pumpkin_util::text::TextComponent;
use pumpkin_world::world::BlockFlags;
use crate::command::args::block::BlockArgumentConsumer;
use crate::command::args::position_block::BlockPosArgumentConsumer;
@ -7,7 +8,6 @@ use crate::command::args::{ConsumedArgs, FindArg};
use crate::command::tree::CommandTree;
use crate::command::tree::builder::{argument, literal};
use crate::command::{CommandError, CommandExecutor, CommandSender};
use crate::world::BlockFlags;
const NAMES: [&str; 1] = ["setblock"];

View File

@ -1,7 +1,6 @@
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::block_properties::BlockProperties;
@ -10,6 +9,7 @@ use pumpkin_data::item::Item;
use pumpkin_data::tag::Tagable;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
pub struct AxeItem;

View File

@ -8,9 +8,10 @@ use pumpkin_data::item::Item;
use pumpkin_util::GameMode;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::world::BlockFlags;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::world::{BlockFlags, World};
use crate::world::World;
pub struct EmptyBucketItem;
pub struct FilledBucketItem;

View File

@ -3,12 +3,12 @@ use pumpkin_data::Block;
use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
use crate::block::blocks::fire::FireBlockBase;
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
pub struct FlintAndSteelItem;

View File

@ -2,7 +2,6 @@ use crate::entity::item::ItemEntity;
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::entity::EntityType;
@ -11,6 +10,7 @@ use pumpkin_data::tag::Tagable;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
use std::sync::Arc;
pub struct HoeItem;

View File

@ -1,7 +1,6 @@
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::block_properties::BlockProperties;
@ -10,6 +9,7 @@ use pumpkin_data::item::Item;
use pumpkin_data::tag::Tagable;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
pub struct HoneyCombItem;

View File

@ -1,13 +1,13 @@
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::item::Item;
use pumpkin_data::tag::Tagable;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::BlockDirection;
use pumpkin_world::world::BlockFlags;
pub struct ShovelItem;

View File

@ -3,6 +3,7 @@ use pumpkin_data::item::Item;
use pumpkin_inventory::InventoryError;
use pumpkin_inventory::equipment_slot::EquipmentSlot;
use pumpkin_inventory::screen_handler::ScreenHandler;
use pumpkin_world::world::BlockFlags;
use rsa::pkcs1v15::{Signature as RsaPkcs1v15Signature, VerifyingKey};
use rsa::signature::Verifier;
use sha1::Sha1;
@ -21,7 +22,7 @@ use crate::plugin::player::player_chat::PlayerChatEvent;
use crate::plugin::player::player_command_send::PlayerCommandSendEvent;
use crate::plugin::player::player_move::PlayerMoveEvent;
use crate::server::seasonal_events;
use crate::world::{BlockFlags, World};
use crate::world::World;
use crate::{
command::CommandSender,
entity::player::{ChatMode, Hand, Player},

View File

@ -1,4 +1,3 @@
use std::hash::Hash;
use std::{
collections::HashMap,
sync::{Arc, atomic::Ordering},
@ -23,7 +22,7 @@ use crate::{
},
server::Server,
};
use bitflags::bitflags;
use async_trait::async_trait;
use border::Worldborder;
use bytes::{BufMut, Bytes};
use explosion::Explosion;
@ -42,7 +41,7 @@ use pumpkin_data::{
world::{RAW, WorldEvent},
};
use pumpkin_macros::send_cancellable;
use pumpkin_nbt::to_bytes_unnamed;
use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed};
use pumpkin_protocol::client::play::{
CBlockEvent, CRemoveMobEffect, CSetEntityMetadata, MetaDataType, Metadata,
};
@ -68,19 +67,20 @@ use pumpkin_registry::DimensionType;
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use pumpkin_util::math::{position::chunk_section_from_pos, vector2::Vector2};
use pumpkin_util::text::{TextComponent, color::NamedColor};
use pumpkin_world::entity::entity_data_flags::{
DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION,
};
use pumpkin_world::world::BlockFlags;
use pumpkin_world::{
BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity,
level::SyncChunk,
};
use pumpkin_world::{block::BlockDirection, chunk::ChunkData};
use pumpkin_world::{chunk::TickPriority, level::Level};
use pumpkin_world::{
entity::entity_data_flags::{DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION},
world::GetBlockError,
};
use rand::{Rng, thread_rng};
use scoreboard::Scoreboard;
use serde::Serialize;
use thiserror::Error;
use time::LevelTime;
use tokio::sync::{RwLock, mpsc};
use tokio::{
@ -96,33 +96,6 @@ pub mod weather;
use weather::Weather;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockFlags: u32 {
const NOTIFY_NEIGHBORS = 0b000_0000_0001;
const NOTIFY_LISTENERS = 0b000_0000_0010;
const NOTIFY_ALL = 0b000_0000_0011;
const FORCE_STATE = 0b000_0000_0100;
const SKIP_DROPS = 0b000_0000_1000;
const MOVED = 0b000_0001_0000;
const SKIP_REDSTONE_WIRE_STATE_REPLACEMENT = 0b000_0010_0000;
const SKIP_BLOCK_ENTITY_REPLACED_CALLBACK = 0b000_0100_0000;
const SKIP_BLOCK_ADDED_CALLBACK = 0b000_1000_0000;
}
}
#[derive(Debug, Error)]
pub enum GetBlockError {
InvalidBlockId,
BlockOutOfWorldBounds,
}
impl std::fmt::Display for GetBlockError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl PumpkinError for GetBlockError {
fn is_kick(&self) -> bool {
false
@ -226,10 +199,7 @@ impl World {
async fn flush_synced_block_events(self: &Arc<Self>) {
let mut queue = self.synced_block_event_queue.lock().await;
let events: Vec<BlockEvent> = queue.drain(..).collect();
// lets drop it here, allows us to add new things
drop(queue);
for event in events {
for event in queue.drain(..) {
let block = self.get_block(&event.pos).await.unwrap(); // TODO
if !self
.block_registry
@ -1731,7 +1701,10 @@ impl World {
}
}
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<(NbtCompound, Arc<dyn BlockEntity>)> {
let chunk = self.get_chunk(block_pos).await;
let chunk: tokio::sync::RwLockReadGuard<ChunkData> = chunk.read().await;
@ -1744,9 +1717,9 @@ impl World {
let mut chunk: tokio::sync::RwLockWriteGuard<ChunkData> = chunk.write().await;
let block_entity_nbt = block_entity.chunk_data_nbt();
if let Some(nbt) = block_entity_nbt {
if let Some(nbt) = &block_entity_nbt {
let mut bytes = Vec::new();
to_bytes_unnamed(&nbt, &mut bytes).unwrap();
to_bytes_unnamed(nbt, &mut bytes).unwrap();
self.broadcast_packet_all(&CBlockEntityData::new(
block_entity.get_position(),
VarInt(block_entity.get_id() as i32),
@ -1755,7 +1728,10 @@ impl World {
.await;
}
chunk.block_entities.insert(block_pos, block_entity);
chunk.block_entities.insert(
block_pos,
(block_entity_nbt.unwrap_or_default(), block_entity),
);
chunk.dirty = true;
}
@ -1868,3 +1844,35 @@ impl World {
(None, None)
}
}
#[async_trait]
impl pumpkin_world::world::SimpleWorld for World {
async fn set_block_state(
self: Arc<Self>,
position: &BlockPos,
block_state_id: BlockStateId,
flags: BlockFlags,
) -> BlockStateId {
Self::set_block_state(&self, position, block_state_id, flags).await
}
async fn get_block(&self, position: &BlockPos) -> Result<pumpkin_data::Block, GetBlockError> {
Self::get_block(self, position).await
}
async fn update_neighbor(self: Arc<Self>, neighbor_block_pos: &BlockPos, source_block: &Block) {
Self::update_neighbor(&self, neighbor_block_pos, source_block).await;
}
async fn update_neighbors(
self: Arc<Self>,
block_pos: &BlockPos,
except: Option<BlockDirection>,
) {
Self::update_neighbors(&self, block_pos, except).await;
}
async fn remove_block_entity(&self, block_pos: &BlockPos) {
self.remove_block_entity(block_pos).await;
}
}