Pumpkin/crates/pumpkin/src/block/blocks/hopper.rs
2026-09-02 18:10:50 +02:00

143 lines
5.1 KiB
Rust

use std::sync::Arc;
use std::sync::Mutex;
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::{
GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, OnNeighborUpdateArgs, OnPlaceArgs,
PathComputationType, PlacedArgs,
};
use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
};
use crate::world::World;
use crate::block::entities::hopper::HopperBlockEntity;
use pumpkin_data::block_properties::FacingHopper;
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, translation};
use pumpkin_inventory::generic_container_screen_handler::create_hopper;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::text::TextComponent;
use pumpkin_world::inventory::Inventory;
use pumpkin_world::world::BlockFlags;
struct HopperBlockScreenFactory(Arc<dyn Inventory>);
impl ScreenHandlerFactory for HopperBlockScreenFactory {
fn create_screen_handler(
&self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
player: &dyn InventoryPlayer,
) -> Option<SharedScreenHandler> {
let concrete_handler = create_hopper(sync_id, player_inventory, self.0.clone(), player);
let concrete_arc = Arc::new(Mutex::new(concrete_handler));
Some(concrete_arc as SharedScreenHandler)
}
fn get_display_name(&self) -> TextComponent {
pumpkin_macros::translate_cross!(
translation::java::CONTAINER_HOPPER,
translation::bedrock::CONTAINER_HOPPER
)
}
}
#[pumpkin_block("minecraft:hopper")]
pub struct HopperBlock;
type HopperLikeProperties = pumpkin_data::block_properties::HopperLikeProperties;
impl BlockBehaviour for HopperBlock {
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs {
server: args.server,
world: args.world,
block: args.block,
position: args.position,
player: args.player,
}) {
args.player.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
pumpkin_data::statistic::CustomStatistic::InspectHopper as i32,
1,
);
args.player
.open_handled_screen(factory.as_ref(), Some(*args.position));
}
BlockActionResult::Success
}
fn get_screen_handler_factory(
&self,
args: GetScreenHandlerFactoryArgs<'_>,
) -> Option<Box<dyn ScreenHandlerFactory>> {
let block_entity = args.world.get_block_entity(args.position)?;
let inventory = block_entity.get_inventory()?;
Some(Box::new(HopperBlockScreenFactory(inventory)))
}
fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = HopperLikeProperties::default(args.block);
props.facing = match args.direction {
BlockDirection::North => FacingHopper::North,
BlockDirection::East => FacingHopper::East,
BlockDirection::South => FacingHopper::South,
BlockDirection::West => FacingHopper::West,
BlockDirection::Up | BlockDirection::Down => FacingHopper::Down,
};
props.enabled = true;
props.to_state_id(args.block)
}
fn placed(&self, args: PlacedArgs<'_>) {
let props = HopperLikeProperties::from_state_id(args.state_id);
let hopper_block_entity = HopperBlockEntity::new(*args.position, props.facing);
args.world.add_block_entity(Arc::new(hopper_block_entity));
if Block::from_state_id(args.old_state_id) != Block::from_state_id(args.state_id) {
check_powered_state(args.world, args.position, args.state_id, args.block);
}
}
fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
check_powered_state(
args.world,
args.position,
args.world.get_block_state_id(args.position),
args.block,
);
}
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
if let Some(block_entity) = args.world.get_block_entity(args.position)
&& let Some(inventory) = block_entity.get_inventory()
{
Some(crate::block::calculate_comparator_output(
inventory.as_ref(),
))
} else {
None
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn check_powered_state(world: &Arc<World>, pos: &BlockPos, state_id: BlockStateId, block: &Block) {
let signal = !block_receives_redstone_power(world, pos);
let mut state = HopperLikeProperties::from_state_id(state_id);
if signal != state.enabled {
state.enabled = signal;
world.set_block_state(pos, state.to_state_id(block), BlockFlags::NOTIFY_LISTENERS);
}
}