Pumpkin/pumpkin/src/block/registry.rs
2025-07-11 14:23:14 +02:00

631 lines
18 KiB
Rust

use crate::block::pumpkin_block::{
BlockHitResult, BlockMetadata, OnEntityCollisionArgs, PumpkinBlock,
};
use crate::entity::EntityBase;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::block_properties;
use pumpkin_data::fluid;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::item::Item;
use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_protocol::java::server::play::SUseItemOn;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::{BlockAccessor, BlockFlags, BlockRegistryExt};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use super::BlockIsReplacing;
use super::pumpkin_block::{
BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, EmitsRedstonePowerArgs, ExplodeArgs,
GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs,
OnPlaceArgs, OnStateReplacedArgs, OnSyncedBlockEventArgs, PlacedArgs, PlayerPlacedArgs,
PrepareArgs, UseWithItemArgs,
};
use super::pumpkin_fluid::PumpkinFluid;
// ActionResult.java
pub enum BlockActionResult {
/// Action was successful and we should swing the hand | Same as SUCCESS in vanilla
Success,
/// Block other actions from being executed and we should swing the hand | Same as CONSUME in vanilla
Consume,
/// Block other actions from being executed | Same as FAIL in vanilla
Fail,
/// Allow other actions to be executed | Same as PASS in vanilla
Continue,
/// Use default action for the block | Same as `PASS_TO_DEFAULT_BLOCK_ACTION` in vanilla
PassToDefault,
}
#[derive(Default)]
pub struct BlockRegistry {
blocks: HashMap<&'static Block, Arc<dyn PumpkinBlock>>,
fluids: HashMap<&'static Fluid, Arc<dyn PumpkinFluid>>,
}
#[async_trait]
impl BlockRegistryExt for BlockRegistry {
async fn can_place_at(
&self,
block: &pumpkin_data::Block,
block_accessor: &dyn BlockAccessor,
block_pos: &BlockPos,
face: BlockDirection,
) -> bool {
self.can_place_at(
None,
None,
block_accessor,
None,
block,
block_pos,
face,
None,
)
.await
}
}
impl BlockRegistry {
pub fn register<T: PumpkinBlock + BlockMetadata + 'static>(&mut self, block: T) {
let names = block.names();
let val = Arc::new(block);
self.blocks.reserve(names.len());
for i in names {
self.blocks.insert(
block_properties::get_block(i.as_str()).unwrap(),
val.clone(),
);
}
}
pub fn register_fluid<T: PumpkinFluid + BlockMetadata + 'static>(&mut self, fluid: T) {
let names = fluid.names();
let val = Arc::new(fluid);
self.fluids.reserve(names.len());
for i in names {
self.fluids
.insert(fluid::get_fluid(i.as_str()).unwrap(), val.clone());
}
}
pub async fn on_synced_block_event(
&self,
block: &Block,
world: &Arc<World>,
position: &BlockPos,
r#type: u8,
data: u8,
) -> bool {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.on_synced_block_event(OnSyncedBlockEventArgs {
world,
block,
position,
r#type,
data,
})
.await;
}
false
}
pub async fn on_entity_collision(
&self,
block: &Block,
world: &Arc<World>,
entity: &dyn EntityBase,
position: &BlockPos,
state: &BlockState,
server: &Server,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.on_entity_collision(OnEntityCollisionArgs {
server,
world,
block,
state,
position,
entity,
})
.await;
}
}
pub async fn on_entity_collision_fluid(&self, fluid: &Fluid, entity: &dyn EntityBase) {
let pumpkin_fluid = self.get_pumpkin_fluid(fluid);
if let Some(pumpkin_fluid) = pumpkin_fluid {
pumpkin_fluid.on_entity_collision(entity).await;
}
}
pub async fn on_use(
&self,
block: &Block,
player: &Player,
position: &BlockPos,
hit: &BlockHitResult<'_>,
server: &Server,
world: &Arc<World>,
) -> BlockActionResult {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.normal_use(NormalUseArgs {
server,
world,
block,
position,
player,
hit,
})
.await;
}
BlockActionResult::Continue
}
pub async fn explode(&self, block: &Block, world: &Arc<World>, position: &BlockPos) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.explode(ExplodeArgs {
world,
block,
position,
})
.await;
}
}
#[allow(clippy::too_many_arguments)]
pub async fn use_with_item(
&self,
block: &Block,
player: &Player,
position: &BlockPos,
hit: &BlockHitResult<'_>,
item_stack: &Arc<Mutex<ItemStack>>,
server: &Server,
world: &Arc<World>,
) -> BlockActionResult {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.use_with_item(UseWithItemArgs {
server,
world,
block,
position,
player,
hit,
item_stack,
})
.await;
}
BlockActionResult::Continue
}
pub async fn use_with_item_fluid(
&self,
fluid: &Fluid,
player: &Player,
position: BlockPos,
item: &Item,
server: &Server,
world: &Arc<World>,
) -> BlockActionResult {
let pumpkin_fluid = self.get_pumpkin_fluid(fluid);
if let Some(pumpkin_fluid) = pumpkin_fluid {
return pumpkin_fluid
.use_with_item(fluid, player, position, item, server, world)
.await;
}
BlockActionResult::Continue
}
#[allow(clippy::too_many_arguments)]
pub async fn can_place_at(
&self,
server: Option<&Server>,
world: Option<&World>,
block_accessor: &dyn BlockAccessor,
player: Option<&Player>,
block: &Block,
position: &BlockPos,
direction: BlockDirection,
use_item_on: Option<&SUseItemOn>,
) -> bool {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.can_place_at(CanPlaceAtArgs {
server,
world,
block_accessor,
block,
position,
direction,
player,
use_item_on,
})
.await;
}
true
}
#[allow(clippy::too_many_arguments)]
pub async fn can_update_at(
&self,
world: &World,
block: &Block,
state_id: BlockStateId,
position: &BlockPos,
direction: BlockDirection,
use_item_on: &SUseItemOn,
player: &Player,
) -> bool {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.can_update_at(CanUpdateAtArgs {
world,
block,
state_id,
position,
direction,
player,
use_item_on,
})
.await;
}
false
}
#[allow(clippy::too_many_arguments)]
pub async fn on_place(
&self,
server: &Server,
world: &World,
player: &Player,
block: &Block,
position: &BlockPos,
direction: BlockDirection,
replacing: BlockIsReplacing,
use_item_on: &SUseItemOn,
) -> BlockStateId {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.on_place(OnPlaceArgs {
server,
world,
block,
position,
direction,
player,
replacing,
use_item_on,
})
.await;
}
block.default_state.id
}
pub async fn player_placed(
&self,
world: &Arc<World>,
block: &Block,
state_id: u16,
position: &BlockPos,
direction: BlockDirection,
player: &Player,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.player_placed(PlayerPlacedArgs {
world,
block,
state_id,
position,
direction,
player,
})
.await;
}
}
pub async fn on_placed(
&self,
world: &Arc<World>,
block: &Block,
state_id: BlockStateId,
position: &BlockPos,
old_state_id: BlockStateId,
notify: bool,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.placed(PlacedArgs {
world,
block,
state_id,
old_state_id,
position,
notify,
})
.await;
}
}
pub async fn on_placed_fluid(
&self,
world: &Arc<World>,
fluid: &Fluid,
state_id: BlockStateId,
position: &BlockPos,
old_state_id: BlockStateId,
notify: bool,
) {
let pumpkin_fluid = self.get_pumpkin_fluid(fluid);
if let Some(pumpkin_fluid) = pumpkin_fluid {
pumpkin_fluid
.placed(world, fluid, state_id, position, old_state_id, notify)
.await;
}
}
pub async fn broken(
&self,
world: &Arc<World>,
block: &Block,
player: &Arc<Player>,
position: &BlockPos,
server: &Server,
state: &BlockState,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.broken(BrokenArgs {
block,
player,
position,
server,
world,
state,
})
.await;
}
}
pub async fn on_state_replaced(
&self,
world: &Arc<World>,
block: &Block,
position: &BlockPos,
old_state_id: BlockStateId,
moved: bool,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.on_state_replaced(OnStateReplacedArgs {
world,
block,
old_state_id,
position,
moved,
})
.await;
}
}
/// Updates state of all neighbors of the block
pub async fn post_process_state(
&self,
world: &Arc<World>,
position: &BlockPos,
block: &Block,
flags: BlockFlags,
) {
let state = world.get_block_state(position).await;
for direction in BlockDirection::all() {
let neighbor_pos = position.offset(direction.to_offset());
let neighbor_state = world.get_block_state(&neighbor_pos).await;
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
let new_state = pumpkin_block
.get_state_for_neighbor_update(GetStateForNeighborUpdateArgs {
world,
block,
state_id: state.id,
position,
direction: direction.opposite(),
neighbor_position: &neighbor_pos,
neighbor_state_id: neighbor_state.id,
})
.await;
world.set_block_state(&neighbor_pos, new_state, flags).await;
}
}
}
pub async fn prepare(
&self,
world: &Arc<World>,
position: &BlockPos,
block: &Block,
state_id: BlockStateId,
flags: BlockFlags,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.prepare(PrepareArgs {
world,
block,
state_id,
position,
flags,
})
.await;
}
}
#[allow(clippy::too_many_arguments)]
pub async fn get_state_for_neighbor_update(
&self,
world: &Arc<World>,
block: &Block,
state_id: BlockStateId,
position: &BlockPos,
direction: BlockDirection,
neighbor_location: &BlockPos,
neighbor_state_id: BlockStateId,
) -> BlockStateId {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.get_state_for_neighbor_update(GetStateForNeighborUpdateArgs {
world,
block,
state_id,
position,
direction,
neighbor_position: neighbor_location,
neighbor_state_id,
})
.await;
}
state_id
}
pub async fn update_neighbors(
&self,
world: &Arc<World>,
position: &BlockPos,
_block: &Block,
flags: BlockFlags,
) {
for direction in BlockDirection::abstract_block_update_order() {
let pos = position.offset(direction.to_offset());
Box::pin(world.replace_with_state_for_neighbor_update(
&pos,
direction.opposite(),
flags,
))
.await;
}
}
pub async fn on_neighbor_update(
&self,
world: &Arc<World>,
block: &Block,
position: &BlockPos,
source_block: &Block,
notify: bool,
) {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.on_neighbor_update(OnNeighborUpdateArgs {
world,
block,
position,
source_block,
notify,
})
.await;
}
}
#[must_use]
pub fn get_pumpkin_block(&self, block: &Block) -> Option<&Arc<dyn PumpkinBlock>> {
self.blocks.get(block)
}
#[must_use]
pub fn get_pumpkin_fluid(&self, fluid: &Fluid) -> Option<&Arc<dyn PumpkinFluid>> {
self.fluids.get(fluid)
}
pub async fn emits_redstone_power(
&self,
block: &Block,
state: &BlockState,
direction: BlockDirection,
) -> bool {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.emits_redstone_power(EmitsRedstonePowerArgs {
block,
state,
direction,
})
.await;
}
false
}
pub async fn get_weak_redstone_power(
&self,
block: &Block,
world: &World,
position: &BlockPos,
state: &BlockState,
direction: BlockDirection,
) -> u8 {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.get_weak_redstone_power(GetRedstonePowerArgs {
world,
block,
state,
position,
direction,
})
.await;
}
0
}
pub async fn get_strong_redstone_power(
&self,
block: &Block,
world: &World,
position: &BlockPos,
state: &BlockState,
direction: BlockDirection,
) -> u8 {
let pumpkin_block = self.get_pumpkin_block(block);
if let Some(pumpkin_block) = pumpkin_block {
return pumpkin_block
.get_strong_redstone_power(GetRedstonePowerArgs {
world,
block,
state,
position,
direction,
})
.await;
}
0
}
}