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); impl ScreenHandlerFactory for HopperBlockScreenFactory { fn create_screen_handler( &self, sync_id: u8, player_inventory: &Arc, player: &dyn InventoryPlayer, ) -> Option { 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> { 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 { 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, 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); } }