use crate::block::blocks::amethyst::{AmethystBlock, BuddingAmethystBlock}; use crate::block::blocks::anvil::AnvilBlock; use crate::block::blocks::banners::BannerBlock; use crate::block::blocks::barrel::BarrelBlock; use crate::block::blocks::barrier::BarrierBlock; use crate::block::blocks::beacon::BeaconBlock; use crate::block::blocks::bed::BedBlock; use crate::block::blocks::beehive::BeehiveBlock; use crate::block::blocks::brewing_stand::BrewingStandBlock; use crate::block::blocks::brushable_block::BrushableBlock; use crate::block::blocks::bubble_column::BubbleColumnBlock; use crate::block::blocks::cake::CakeBlock; use crate::block::blocks::campfire::CampfireBlock; use crate::block::blocks::candle_cakes::CandleCakeBlock; use crate::block::blocks::candles::CandleBlock; use crate::block::blocks::carpet::{CarpetBlock, MossCarpetBlock, PaleMossCarpetBlock}; use crate::block::blocks::cartography_table::CartographyTableBlock; use crate::block::blocks::carved_pumpkin::CarvedPumpkinBlock; use crate::block::blocks::cauldron::CauldronBlock; use crate::block::blocks::chests::{ChestBlock, CopperChestBlock, TrappedChestBlock}; use crate::block::blocks::chiseled_bookshelf::ChiseledBookshelfBlock; use crate::block::blocks::command::CommandBlock; use crate::block::blocks::composter::ComposterBlock; use crate::block::blocks::conduit::ConduitBlock; use crate::block::blocks::coral::coral_block::CoralBlock; use crate::block::blocks::coral::coral_fan::CoralFanBlock; use crate::block::blocks::coral::coral_plant::CoralPlantBlock; use crate::block::blocks::creaking_heart::CreakingHeartBlock; use crate::block::blocks::decorated_pot::DecoratedPotBlock; use crate::block::blocks::dirt_path::DirtPathBlock; use crate::block::blocks::doors::DoorBlock; use crate::block::blocks::dripstone::DripstoneBlock; use crate::block::blocks::end_gateway::EndGatewayBlock; use crate::block::blocks::end_portal::EndPortalBlock; use crate::block::blocks::end_portal_frame::EndPortalFrameBlock; use crate::block::blocks::falling::FallingBlock; use crate::block::blocks::farmland::FarmlandBlock; use crate::block::blocks::fence_gates::FenceGateBlock; use crate::block::blocks::fences::FenceBlock; use crate::block::blocks::fire::fire::FireBlock; use crate::block::blocks::fire::soul_fire::SoulFireBlock; use crate::block::blocks::fletching_table::FletchingTableBlock; use crate::block::blocks::flower_pots::FlowerPotBlock; use crate::block::blocks::furnace::FurnaceBlock; use crate::block::blocks::glass_panes::GlassPaneBlock; use crate::block::blocks::glazed_terracotta::GlazedTerracottaBlock; use crate::block::blocks::grass_block::GrassBlock; use crate::block::blocks::grindstone::GrindstoneBlock; use crate::block::blocks::hay::HayBlock; use crate::block::blocks::ice::{FrostedIceBlock, IceBlock}; use crate::block::blocks::infested::InfestedBlock; use crate::block::blocks::iron_bars::IronBarsBlock; use crate::block::blocks::jigsaw::JigsawBlock; // use crate::block::blocks::leaves::LeavesBlock; use crate::block::blocks::logs::LogBlock; use crate::block::blocks::loom::LoomBlock; use crate::block::blocks::magma::MagmaBlock; use crate::block::blocks::mangrove_roots::MangroveRootsBlock; use crate::block::blocks::nether_portal::NetherPortalBlock; use crate::block::blocks::note::NoteBlock; use crate::block::blocks::nylium::NyliumBlock; use crate::block::blocks::piston::piston::PistonBlock; use crate::block::blocks::piston::piston_extension::PistonExtensionBlock; use crate::block::blocks::piston::piston_head::PistonHeadBlock; use crate::block::blocks::plant::bamboo::BambooBlock; use crate::block::blocks::plant::bamboo_sapling::BambooSaplingBlock; use crate::block::blocks::plant::big_dripleaf::BigDripleafBlock; use crate::block::blocks::plant::big_dripleaf_stem::BigDripleafStemBlock; use crate::block::blocks::plant::bush::BushBlock; use crate::block::blocks::plant::cactus::CactusBlock; use crate::block::blocks::plant::cactus_flower::CactusFlowerBlock; use crate::block::blocks::plant::chorus_flower::ChorusFlowerBlock; use crate::block::blocks::plant::chorus_plant::ChorusPlantBlock; use crate::block::blocks::plant::cocoa::CocoaBlock; use crate::block::blocks::plant::crop::beetroot::BeetrootBlock; use crate::block::blocks::plant::crop::carrot::CarrotBlock; use crate::block::blocks::plant::crop::nether_wart::NetherWartBlock; use crate::block::blocks::plant::crop::potatoes::PotatoBlock; use crate::block::blocks::plant::crop::sweet_berry_bush::SweetBerryBushBlock; use crate::block::blocks::plant::crop::torch_flower::TorchFlowerBlock; use crate::block::blocks::plant::crop::wheat::WheatBlock; use crate::block::blocks::plant::dry_vegetation::DryVegetationBlock; use crate::block::blocks::plant::eyeblossom::EyeblossomBlock; use crate::block::blocks::plant::flower::FlowerBlock; use crate::block::blocks::plant::flowerbed::FlowerbedBlock; use crate::block::blocks::plant::fungus::FungusBlock; use crate::block::blocks::plant::kelp::KelpBlock; use crate::block::blocks::plant::leaf_litter::LeafLitterBlock; use crate::block::blocks::plant::lily_pad::LilyPadBlock; use crate::block::blocks::plant::mangrove_propagule::MangrovePropaguleBlock; use crate::block::blocks::plant::mushroom_plant::MushroomPlantBlock; use crate::block::blocks::plant::nether_sprouts::NetherSproutsBlock; use crate::block::blocks::plant::roots::RootsBlock; use crate::block::blocks::plant::sapling::SaplingBlock; use crate::block::blocks::plant::sea_pickles::SeaPickleBlock; use crate::block::blocks::plant::seagrass::SeaGrassBlock; use crate::block::blocks::plant::short_plant::ShortPlantBlock; use crate::block::blocks::plant::small_dripleaf::SmallDripleafBlock; use crate::block::blocks::plant::spore_blossom::SporeBlossomBlock; use crate::block::blocks::plant::sugar_cane::SugarCaneBlock; use crate::block::blocks::plant::tall_plant::TallPlantBlock; use crate::block::blocks::plant::tall_seagrass::TallSeaGrassBlock; use crate::block::blocks::plant::twisting_vines::TwistingVinesBlock; use crate::block::blocks::plant::weeping_vines::WeepingVinesBlock; use crate::block::blocks::plant::wither_rose::WitherRoseBlock; use crate::block::blocks::powder_snow::PowderSnowBlock; use crate::block::blocks::pumpkin::PumpkinBlock; use crate::block::blocks::redstone::bell::BellBlock; use crate::block::blocks::redstone::buttons::ButtonBlock; use crate::block::blocks::redstone::comparator::ComparatorBlock; use crate::block::blocks::redstone::copper_bulb::CopperBulbBlock; use crate::block::blocks::redstone::crafter::CrafterBlock; use crate::block::blocks::redstone::daylight_detector::DaylightDetectorBlock; use crate::block::blocks::redstone::dispenser::DispenserBlock; use crate::block::blocks::redstone::dropper::DropperBlock; use crate::block::blocks::redstone::lever::LeverBlock; use crate::block::blocks::redstone::lightning_rod::LightningRodBlock; use crate::block::blocks::redstone::observer::ObserverBlock; use crate::block::blocks::redstone::pressure_plate::plate::PressurePlateBlock; use crate::block::blocks::redstone::pressure_plate::weighted::WeightedPressurePlateBlock; use crate::block::blocks::redstone::rails::activator_rail::ActivatorRailBlock; use crate::block::blocks::redstone::rails::detector_rail::DetectorRailBlock; use crate::block::blocks::redstone::rails::powered_rail::PoweredRailBlock; use crate::block::blocks::redstone::rails::rail::RailBlock; use crate::block::blocks::redstone::redstone_block::RedstoneBlock; use crate::block::blocks::redstone::redstone_lamp::RedstoneLamp; use crate::block::blocks::redstone::redstone_ore::RedstoneOreBlock; use crate::block::blocks::redstone::redstone_torch::RedstoneTorchBlock; use crate::block::blocks::redstone::redstone_wire::RedstoneWireBlock; use crate::block::blocks::redstone::repeater::RepeaterBlock; use crate::block::blocks::redstone::sculk_sensor::SculkSensorBlock; use crate::block::blocks::redstone::target_block::TargetBlock; use crate::block::blocks::redstone::tripwire::TripwireBlock; use crate::block::blocks::redstone::tripwire_hook::TripwireHookBlock; use crate::block::blocks::sculk::sculk_catalyst::SculkCatalystBlock; use crate::block::blocks::sculk::sculk_shrieker::SculkShriekerBlock; use crate::block::blocks::sculk::sculk_vein::SculkVeinBlock; use crate::block::blocks::shelf::ShelfBlock; use crate::block::blocks::signs::SignBlock; use crate::block::blocks::slabs::SlabBlock; use crate::block::blocks::slime::SlimeBlock; use crate::block::blocks::smithing_table::SmithingTableBlock; use crate::block::blocks::sniffer_egg::SnifferEggBlock; use crate::block::blocks::snow::LayeredSnowBlock; use crate::block::blocks::spawner::SpawnerBlock; use crate::block::blocks::sponge::{SpongeBlock, WetSpongeBlock}; use crate::block::blocks::stairs::StairBlock; use crate::block::blocks::structure_block::StructureBlock; use crate::block::blocks::tnt::TNTBlock; use crate::block::blocks::torches::TorchBlock; use crate::block::blocks::trapdoor::TrapDoorBlock; use crate::block::blocks::trial_spawner::TrialSpawnerBlock; use crate::block::blocks::turtle_egg::TurtleEggBlock; use crate::block::blocks::vault::VaultBlock; use crate::block::blocks::vine::VineBlock; use crate::block::blocks::walls::WallBlock; use crate::block::blocks::wither_skull::WitherSkeletonSkullBlock; use crate::block::fluid::lava::FlowingLava; use crate::block::fluid::water::FlowingWater; use crate::block::{ BlockBehaviour, BlockHitResult, BlockMetadata, BonemealArgs, FluidMetadata, GetInsideCollisionShapeArgs, OnEntityCollisionArgs, OnEntityStepArgs, OnLandedUponArgs, OnProjectileHitArgs, PathComputationType, UpdateEntityMovementAfterFallOnArgs, stop_vertical_movement_after_fall, }; use crate::entity::EntityBase; use crate::entity::player::Player; use crate::server::Server; use crate::world::World; use pumpkin_data::BlockStateId; use pumpkin_data::block_rotation::{Mirror, Rotation}; use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::fluid::Fluid; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::{Block, BlockDirection, BlockId, BlockState}; use pumpkin_protocol::java::server::play::SUseItemOn; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::world::{BlockAccessor, BlockFlags}; use rustc_hash::FxHashMap; use std::sync::Arc; use super::BlockIsReplacing; use super::blocks::plant::crop::gourds::attached_stem::AttachedStemBlock; use super::blocks::plant::crop::gourds::stem::StemBlock; use super::fluid::FluidBehaviour; use super::{ BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, EmitsRedstonePowerArgs, ExplodeArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, OnSyncedBlockEventArgs, PlacedArgs, PlayerPlacedArgs, PrepareArgs, UseWithItemArgs, }; use crate::block::blocks::blast_furnace::BlastFurnaceBlock; use crate::block::blocks::chain::ChainBlock; use crate::block::blocks::cobweb::CobwebBlock; use crate::block::blocks::crafting_table::CraftingTableBlock; use crate::block::blocks::dragon_egg::DragonEggBlock; use crate::block::blocks::enchanting_table::EnchantingTableBlock; use crate::block::blocks::end_rod::EndRodBlock; use crate::block::blocks::ender_chest::EnderChestBlock; use crate::block::blocks::hopper::HopperBlock; use crate::block::blocks::jukebox::JukeboxBlock; use crate::block::blocks::ladder::LadderBlock; use crate::block::blocks::lanterns::LanternBlock; use crate::block::blocks::lectern::LecternBlock; use crate::block::blocks::respawn_anchor::RespawnAnchorBlock; use crate::block::blocks::rooted_dirt::RootedDirtBlock; use crate::block::blocks::shulker_box::ShulkerBoxBlock; use crate::block::blocks::skull_block::SkullBlock; use crate::block::blocks::smoker::SmokerBlock; use crate::block::blocks::stonecutter::StonecutterBlock; use crate::block::blocks::weathering_copper::{ WeatheringCopperBlock, WeatheringCopperDoorBlock, WeatheringCopperGrateBlock, WeatheringCopperSlabBlock, WeatheringCopperStairBlock, WeatheringCopperTrapDoorBlock, }; #[must_use] #[expect(clippy::too_many_lines)] pub fn default_registry() -> Arc { let mut manager = BlockRegistry::default(); // Blocks manager.register(AnvilBlock); manager.register(BeaconBlock); manager.register(BedBlock); manager.register(SaplingBlock); manager.register(MangrovePropaguleBlock); manager.register(CactusBlock); manager.register(ChorusFlowerBlock); manager.register(ChorusPlantBlock); manager.register(CocoaBlock); manager.register(CarpetBlock); manager.register(CarvedPumpkinBlock); manager.register(WitherSkeletonSkullBlock); manager.register(CampfireBlock); manager.register(MossCarpetBlock); manager.register(PaleMossCarpetBlock); manager.register(ChestBlock); manager.register(TrappedChestBlock); manager.register(CopperChestBlock); manager.register(EnderChestBlock); manager.register(CraftingTableBlock); manager.register(EnchantingTableBlock); manager.register(DirtPathBlock); manager.register(DoorBlock); manager.register(FarmlandBlock); manager.register(FenceGateBlock); manager.register(FenceBlock); manager.register(FlowerPotBlock); manager.register(FurnaceBlock); manager.register(BrewingStandBlock); manager.register(BlastFurnaceBlock); manager.register(SmokerBlock); manager.register(GlassPaneBlock); manager.register(GlazedTerracottaBlock); manager.register(HayBlock); manager.register(GrindstoneBlock); manager.register(IceBlock); manager.register(FrostedIceBlock); manager.register(IronBarsBlock); manager.register(InfestedBlock); manager.register(JukeboxBlock); manager.register(LogBlock); // FIX despawn // manager.register(LeavesBlock); manager.register(BambooBlock); manager.register(BambooSaplingBlock); manager.register(BannerBlock); manager.register(SignBlock); manager.register(SlabBlock); manager.register(SlimeBlock); manager.register(StairBlock); manager.register(StonecutterBlock); manager.register(LoomBlock); manager.register(CartographyTableBlock); manager.register(SmithingTableBlock); manager.register(FletchingTableBlock); manager.register(StructureBlock); manager.register(ShortPlantBlock); manager.register(DryVegetationBlock); manager.register(LilyPadBlock); manager.register(SugarCaneBlock); manager.register(VineBlock); manager.register(TNTBlock); manager.register(TrialSpawnerBlock); manager.register(VaultBlock); manager.register(DecoratedPotBlock); manager.register(CreakingHeartBlock); manager.register(SnifferEggBlock); manager.register(TurtleEggBlock); manager.register(BrushableBlock); manager.register(BushBlock); manager.register(FlowerBlock); manager.register(EyeblossomBlock); manager.register(PotatoBlock); manager.register(BeetrootBlock); manager.register(TorchFlowerBlock); manager.register(CarrotBlock); manager.register(SweetBerryBushBlock); manager.register(SeaGrassBlock); manager.register(TallSeaGrassBlock); manager.register(KelpBlock); manager.register(NetherWartBlock); manager.register(WheatBlock); manager.register(TorchBlock); manager.register(TrapDoorBlock); manager.register(MushroomPlantBlock); manager.register(FlowerbedBlock); manager.register(LeafLitterBlock); manager.register(WallBlock); manager.register(RootsBlock); manager.register(NetherPortalBlock); manager.register(TallPlantBlock); manager.register(NoteBlock); manager.register(PowderSnowBlock); manager.register(SpongeBlock); manager.register(PumpkinBlock); manager.register(WetSpongeBlock); // Weathering copper blocks manager.register(WeatheringCopperBlock); manager.register(WeatheringCopperDoorBlock); manager.register(WeatheringCopperGrateBlock); manager.register(WeatheringCopperSlabBlock); manager.register(WeatheringCopperStairBlock); manager.register(WeatheringCopperTrapDoorBlock); manager.register(CommandBlock); manager.register(JigsawBlock); manager.register(ComposterBlock); manager.register(CauldronBlock); manager.register(BeehiveBlock); manager.register(PressurePlateBlock); manager.register(WeightedPressurePlateBlock); manager.register(EndGatewayBlock); manager.register(EndPortalBlock); manager.register(SpawnerBlock); manager.register(EndPortalFrameBlock); manager.register(RespawnAnchorBlock); manager.register(CandleBlock); manager.register(SeaPickleBlock); manager.register(CakeBlock); manager.register(CandleCakeBlock); manager.register(SkullBlock); manager.register(ChiseledBookshelfBlock); manager.register(ShelfBlock); manager.register(LecternBlock); manager.register(DragonEggBlock); manager.register(StemBlock); manager.register(AttachedStemBlock); manager.register(ChainBlock); manager.register(LanternBlock); manager.register(EndRodBlock); manager.register(BarrierBlock); manager.register(MangroveRootsBlock); manager.register(LayeredSnowBlock); manager.register(CobwebBlock); manager.register(WitherRoseBlock); manager.register(FungusBlock); manager.register(NetherSproutsBlock); manager.register(SporeBlossomBlock); manager.register(ConduitBlock); manager.register(DripstoneBlock); manager.register(TwistingVinesBlock); manager.register(WeepingVinesBlock); manager.register(CactusFlowerBlock); manager.register(SmallDripleafBlock); manager.register(BigDripleafStemBlock); manager.register(BigDripleafBlock); manager.register(CoralFanBlock); manager.register(CoralPlantBlock); manager.register(CoralBlock); manager.register(AmethystBlock); manager.register(BuddingAmethystBlock); manager.register(GrassBlock); manager.register(RootedDirtBlock); manager.register(NyliumBlock); manager.register(BubbleColumnBlock); manager.register(FallingBlock); // Fire manager.register(SoulFireBlock); manager.register(FireBlock); manager.register(MagmaBlock); // Redstone manager.register(ButtonBlock); manager.register(BellBlock); manager.register(LeverBlock); manager.register(LightningRodBlock); manager.register(SculkSensorBlock); manager.register(SculkVeinBlock); manager.register(SculkCatalystBlock); manager.register(SculkShriekerBlock); manager.register(ObserverBlock); manager.register(TripwireBlock); manager.register(TripwireHookBlock); // Piston manager.register(PistonBlock); manager.register(PistonExtensionBlock); manager.register(PistonHeadBlock); manager.register(RedstoneBlock); manager.register(RedstoneLamp); manager.register(RedstoneOreBlock); manager.register(CopperBulbBlock); manager.register(RedstoneTorchBlock); manager.register(RedstoneWireBlock); manager.register(RepeaterBlock); manager.register(ComparatorBlock); manager.register(CrafterBlock); manager.register(TargetBlock); manager.register(BarrelBlock); manager.register(HopperBlock); manager.register(ShulkerBoxBlock); manager.register(DropperBlock); manager.register(DispenserBlock); manager.register(LadderBlock); manager.register(DaylightDetectorBlock); // Rails manager.register(RailBlock); manager.register(ActivatorRailBlock); manager.register(DetectorRailBlock); manager.register(PoweredRailBlock); // Fluids manager.register_fluid(FlowingWater); manager.register_fluid(FlowingLava); Arc::new(manager) } // ActionResult.java #[derive(PartialEq, Eq)] pub enum BlockActionResult { /// Action was successful | Same as SUCCESS in vanilla Success, /// Action was successful and we should swing the hand for the server | Same as `SUCCESS_SERVER` in vanilla SuccessServer, /// Block other actions from being executed | Same as CONSUME in vanilla Consume, /// Allow other actions to be executed, but indicate it failed | Same as FAIL in vanilla Fail, /// Allow other actions to be executed | Same as PASS in vanilla Pass, /// Use default action for the block: `normal_use` | Same as `PASS_TO_DEFAULT_BLOCK_ACTION` in vanilla PassToDefaultBlockAction, } impl BlockActionResult { #[must_use] pub const fn consumes_action(&self) -> bool { matches!(self, Self::Consume | Self::Success | Self::SuccessServer) } } pub struct BlockRegistry { block_indices: [u8; pumpkin_data::BlockId::COUNT as usize], behaviours: Vec>, fluids: FxHashMap>, } impl Default for BlockRegistry { fn default() -> Self { Self { block_indices: [0xFF; pumpkin_data::BlockId::COUNT as usize], behaviours: Vec::new(), fluids: FxHashMap::default(), } } } #[derive(Debug)] pub enum BlockPlacingError { InvalidGamemode, BlockOutOfWorld, } impl BlockRegistry { pub fn bone_meal( &self, block: &Block, world: &Arc, position: &BlockPos, state_id: BlockStateId, ) -> bool { let Some(behaviour) = self.get_pumpkin_block(block.id) else { return false; }; let args = BonemealArgs { world, block, position, state_id, }; if !behaviour.is_valid_bonemeal_target(args) { return false; } if behaviour.is_bonemeal_success(args) { behaviour.perform_bonemeal(args); } true } fn entity_blocks_block_placement(entity: &dyn EntityBase) -> bool { let base_entity = entity.get_entity(); if base_entity.is_removed() || base_entity .no_physics .load(std::sync::atomic::Ordering::Relaxed) || entity.is_spectator() { return false; } if entity.get_living_entity().is_some() { return true; } let entity_type = base_entity.entity_type; let resource_name = entity_type.resource_name; entity_type == &pumpkin_data::entity::EntityType::END_CRYSTAL || entity_type == &pumpkin_data::entity::EntityType::FALLING_BLOCK || entity_type == &pumpkin_data::entity::EntityType::TNT || resource_name.ends_with("_minecart") || resource_name.ends_with("_boat") || resource_name.ends_with("_raft") } fn has_blocking_entity_in_box(world: &World, placed_box: &BoundingBox) -> bool { let players = world.players.load(); if players.iter().any(|player| { Self::entity_blocks_block_placement(player.as_ref()) && player .get_entity() .bounding_box .load() .intersects(placed_box) }) { return true; } world.entities.load().iter().any(|entity| { Self::entity_blocks_block_placement(entity.as_ref()) && entity .get_entity() .bounding_box .load() .intersects(placed_box) }) } #[expect(clippy::too_many_lines)] pub fn place_block( &self, player: &Arc, placed_block: &'static Block, server: &Arc, use_item_on: &SUseItemOn, location: BlockPos, face: BlockDirection, ) -> Result, BlockPlacingError> { let entity = &player.get_entity(); match player.gamemode.load() { pumpkin_util::GameMode::Spectator | pumpkin_util::GameMode::Adventure => { return Err(BlockPlacingError::InvalidGamemode); } _ => {} } let clicked_block_pos = BlockPos(location.0); let world = entity.world.load_full(); if location.0.y + face.to_offset().y < world.get_bottom_y() { return Err(BlockPlacingError::BlockOutOfWorld); } if location.0.y + face.to_offset().y > world.get_top_y() { player.send_system_message_raw( &pumpkin_util::text::TextComponent::translate_cross( pumpkin_data::translation::java::BUILD_TOOHIGH, pumpkin_data::translation::bedrock::BUILD_TOOHIGH, vec![pumpkin_util::text::TextComponent::text( (world.get_top_y()).to_string(), )], ) .color_named(pumpkin_util::text::color::NamedColor::Red), true, ); return Err(BlockPlacingError::BlockOutOfWorld); } let (clicked_block, clicked_block_state) = world.get_block_and_state(&clicked_block_pos); let replace_clicked_block = if clicked_block == placed_block { self.can_update_at( &world, clicked_block, clicked_block_state.id, &clicked_block_pos, face, use_item_on, player, ) .then_some(BlockIsReplacing::Itself(clicked_block_state.id)) } else if clicked_block_state.replaceable() { if clicked_block == &Block::WATER { use pumpkin_data::block_properties::WaterLikeProperties; let water_props = WaterLikeProperties::from_state_id(clicked_block_state.id); Some(BlockIsReplacing::Water(water_props.level)) } else { Some(BlockIsReplacing::Other) } } else { None }; let (final_block_pos, final_face, replacing) = if let Some(replacing) = replace_clicked_block { (clicked_block_pos, face.opposite(), replacing) } else { let block_pos = BlockPos(location.0 + face.to_offset()); let (previous_block, previous_block_state) = world.get_block_and_state(&block_pos); let replace_previous_block = if previous_block == placed_block { self.can_update_at( &world, previous_block, previous_block_state.id, &block_pos, face.opposite(), use_item_on, player, ) .then_some(BlockIsReplacing::Itself(previous_block_state.id)) } else { previous_block_state.replaceable().then(|| { if previous_block == &Block::WATER { use pumpkin_data::block_properties::WaterLikeProperties; let water_props = WaterLikeProperties::from_state_id(previous_block_state.id); BlockIsReplacing::Water(water_props.level) } else { BlockIsReplacing::None } }) }; match replace_previous_block { Some(replacing) => (block_pos, face.opposite(), replacing), None => { return Ok(None); } } }; if !self.can_place_at( Some(server), Some(&*world), &*world, Some(player), placed_block, placed_block.default_state, &final_block_pos, Some(final_face), Some(use_item_on), ) { return Ok(None); } let new_state = self.on_place( server, &world, player, placed_block, &final_block_pos, final_face, replacing, use_item_on, ); // Mirror vanilla obstruction checks: only entities that block building should prevent // placement. (e.g. arrows/xp orbs/displays/markers should not) let state = BlockState::from_id(new_state); let mut buildable = true; for shape in state.get_block_collision_shapes_at(&final_block_pos) { let placed_box = shape.at_pos(final_block_pos); if Self::has_blocking_entity_in_box(world.as_ref(), &placed_box) { buildable = false; break; } } let mut can_build_event = crate::plugin::block::block_can_build::BlockCanBuildEvent { block_to_build: placed_block, buildable, player: player.clone(), block: clicked_block, cancelled: false, }; server .plugin_manager .fire_blocking(server, &mut can_build_event); if can_build_event.cancelled || !can_build_event.buildable { return Ok(None); } let mut event = crate::plugin::block::block_place::BlockPlaceEvent::new( player.clone(), placed_block, clicked_block, final_block_pos, true, ); server.plugin_manager.fire_blocking(server, &mut event); if event.cancelled { return Ok(None); } let _replaced_id = world.set_block_state(&final_block_pos, new_state, BlockFlags::NOTIFY_ALL); self.player_placed( &world, placed_block, new_state, &final_block_pos, face, player, ); player.trigger_advancement( crate::entity::player::advancement::trigger::AdvancementTrigger::PlacedBlock { block_id: format!("minecraft:{}", placed_block.name), }, ); Ok(Some((final_block_pos, new_state))) } #[allow(clippy::expect_used)] pub fn register(&mut self, block: T) { let ids = T::ids(); let idx = u8::try_from(self.behaviours.len()) .expect("Too many block behaviours for u8 index table"); self.behaviours.push(Arc::new(block)); for i in ids { self.block_indices[i.as_u16() as usize] = idx; } } pub fn register_fluid(&mut self, fluid: T) { let ids = T::ids(); let val = Arc::new(fluid); self.fluids.reserve(ids.len()); for i in ids { self.fluids.insert(i, val.clone()); } } pub fn on_synced_block_event( &self, block: &Block, world: &Arc, position: &BlockPos, r#type: u8, data: u8, ) -> bool { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.on_synced_block_event(OnSyncedBlockEventArgs { world, block, position, r#type, data, }); } false } pub fn on_entity_collision( &self, block: &Block, world: &Arc, entity: &dyn EntityBase, position: &BlockPos, state: &BlockState, server: &Server, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.on_entity_collision(OnEntityCollisionArgs { server, world, block, state, position, entity, }); } } pub fn on_entity_step( &self, block: &Block, world: &Arc, entity: &dyn EntityBase, position: &BlockPos, state: &BlockState, below_supporting_block: bool, ) { if let Some(pumpkin_block) = self.get_pumpkin_block(block.id) { pumpkin_block.on_entity_step(OnEntityStepArgs { world, block, state, position, entity, below_supporting_block, }); } } #[allow(clippy::too_many_arguments)] pub fn on_projectile_hit( &self, block: &Block, world: &Arc, projectile: &dyn EntityBase, position: &BlockPos, state: &BlockState, hit_pos: &Vector3, server: &Server, ) { if let Some(pumpkin_block) = self.get_pumpkin_block(block.id) { pumpkin_block.on_projectile_hit(OnProjectileHitArgs { server, world, block, state, position, projectile, hit_pos, }); } } pub fn on_entity_collision_fluid(&self, fluid: &Fluid, entity: &dyn EntityBase) { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { pumpkin_fluid.on_entity_collision(entity); } } pub fn on_use( &self, block: &Block, player: &Arc, position: &BlockPos, hit: &BlockHitResult<'_>, server: &Server, world: &Arc, ) -> BlockActionResult { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.normal_use(NormalUseArgs { server, world, block, position, player, hit, }); } BlockActionResult::Pass } pub fn explode(&self, block: &Block, world: &Arc, position: &BlockPos) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.explode(ExplodeArgs { world, block, position, }); } } #[expect(clippy::too_many_arguments)] pub fn use_with_item( &self, block: &Block, player: &Arc, position: &BlockPos, hit: &BlockHitResult<'_>, item_stack: &mut ItemStack, equipment_slot: &EquipmentSlot, server: &Server, world: &Arc, ) -> BlockActionResult { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.use_with_item(UseWithItemArgs { server, world, block, position, player, hit, item_stack, equipment_slot, }); } BlockActionResult::Pass } pub fn use_with_item_fluid( &self, fluid: &Fluid, player: &Arc, position: BlockPos, item: &Item, server: &Server, world: &Arc, ) -> BlockActionResult { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { return pumpkin_fluid.use_with_item(fluid, player, position, item, server, world); } BlockActionResult::Pass } #[expect(clippy::too_many_arguments)] pub fn can_place_at( &self, server: Option<&Server>, world: Option<&World>, block_accessor: &dyn BlockAccessor, player: Option<&Player>, block: &Block, state: &BlockState, position: &BlockPos, direction: Option, use_item_on: Option<&SUseItemOn>, ) -> bool { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.can_place_at(CanPlaceAtArgs { server, world, block_accessor, block, state, position, direction, player, use_item_on, }); } true } #[expect(clippy::too_many_arguments)] pub 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.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.can_update_at(CanUpdateAtArgs { world, block, state_id, position, direction, player, use_item_on, }); } false } #[expect(clippy::too_many_arguments)] pub 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.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.on_place(OnPlaceArgs { server, world, block, position, direction, player, replacing, use_item_on, }); } block.default_state.id } pub fn player_placed( &self, world: &Arc, block: &Block, state_id: BlockStateId, position: &BlockPos, direction: BlockDirection, player: &Player, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.player_placed(PlayerPlacedArgs { world, block, state_id, position, direction, player, }); } } pub fn on_placed( &self, world: &Arc, block: &Block, state_id: BlockStateId, position: &BlockPos, old_state_id: BlockStateId, notify: bool, ) { let state = world.get_block_state(position); if state.block_entity_type != u16::MAX && world.get_block_entity(position).is_none() && let Some(entity) = crate::block::entities::create_block_entity(state.block_entity_type, *position) { world.add_block_entity(entity); } let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.placed(PlacedArgs { world, block, state_id, old_state_id, position, notify, }); } } pub fn on_placed_fluid( &self, world: &Arc, fluid: &Fluid, state_id: BlockStateId, position: &BlockPos, old_state_id: BlockStateId, notify: bool, ) { let pumpkin_fluid = self.get_pumpkin_fluid(fluid.id); if let Some(pumpkin_fluid) = pumpkin_fluid { pumpkin_fluid.placed(world, fluid, state_id, position, old_state_id, notify); } } pub fn on_landed_upon( &self, block: &Block, world: &Arc, fall_distance: f32, entity: &dyn EntityBase, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.on_landed_upon(OnLandedUponArgs { world, fall_distance, entity, }); } } pub fn update_entity_movement_after_fall_on(&self, block: &Block, entity: &dyn EntityBase) { if let Some(pumpkin_block) = self.get_pumpkin_block(block.id) { pumpkin_block.update_entity_movement_after_fall_on( UpdateEntityMovementAfterFallOnArgs { entity }, ); } else { stop_vertical_movement_after_fall(entity); } } pub fn broken( &self, world: &Arc, block: &Block, player: &Arc, position: &BlockPos, server: &Server, state: &BlockState, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.broken(BrokenArgs { block, player, position, server, world, state, }); } } pub fn on_state_replaced( &self, world: &Arc, block: &Block, position: &BlockPos, old_state_id: BlockStateId, moved: bool, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.on_state_replaced(OnStateReplacedArgs { world, block, old_state_id, position, moved, }); } } /// Updates state of all neighbors of the block pub fn post_process_state( &self, world: &Arc, position: &BlockPos, block: &Block, flags: BlockFlags, ) { let state_id = world.get_block_state_id(position); for direction in BlockDirection::all() { let neighbor_pos = position.offset(direction.to_offset()); let neighbor_state_id = world.get_block_state_id(&neighbor_pos); let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { let new_state = pumpkin_block.get_state_for_neighbor_update(GetStateForNeighborUpdateArgs { world, block, state_id, position, direction: direction.opposite(), neighbor_position: &neighbor_pos, neighbor_state_id, }); world.set_block_state(&neighbor_pos, new_state, flags); } } } pub fn prepare( &self, world: &Arc, position: &BlockPos, block: &Block, state_id: BlockStateId, flags: BlockFlags, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.prepare(PrepareArgs { world, block, state_id, position, flags, }); } } #[expect(clippy::too_many_arguments)] pub fn get_state_for_neighbor_update( &self, world: &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.id); 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, }); } state_id } pub fn update_neighbors(&self, world: &Arc, position: &BlockPos, flags: BlockFlags) { for direction in BlockDirection::abstract_block_update_order() { let pos = position.offset(direction.to_offset()); world.replace_with_state_for_neighbor_update(&pos, direction.opposite(), flags); } } pub fn on_neighbor_update( &self, world: &Arc, block: &Block, position: &BlockPos, source_block: &Block, notify: bool, ) { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.on_neighbor_update(OnNeighborUpdateArgs { world, block, position, source_block, notify, }); } } #[inline] #[must_use] pub fn get_pumpkin_block(&self, block: BlockId) -> Option<&Arc> { let idx = self.block_indices[block.as_u16() as usize]; if idx == 0xFF { None } else { self.behaviours.get(idx as usize) } } #[must_use] pub fn get_pumpkin_fluid(&self, fluid_id: u16) -> Option<&Arc> { self.fluids.get(&fluid_id).or_else(|| { // Still fluids share behavior with their flowing counterpart match fluid_id { 2 => self.fluids.get(&1), 4 => self.fluids.get(&3), _ => None, } }) } #[must_use] pub fn emits_redstone_power( &self, block: &Block, state: &BlockState, direction: BlockDirection, ) -> bool { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.emits_redstone_power(EmitsRedstonePowerArgs { block, state, direction, }); } false } pub 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.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.get_weak_redstone_power(GetRedstonePowerArgs { world, block, state, position, direction, }); } 0 } pub 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.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.get_strong_redstone_power(GetRedstonePowerArgs { world, block, state, position, direction, }); } 0 } pub fn get_inside_collision_shape( &self, block: &Block, world: &World, state: &BlockState, position: &BlockPos, ) -> BoundingBox { let pumpkin_block = self.get_pumpkin_block(block.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.get_inside_collision_shape(GetInsideCollisionShapeArgs { world, block, state, position, }); } BoundingBox::full_block() } #[must_use] pub fn mirror( &self, block: &Block, state_id: BlockStateId, mirror: Mirror, ) -> &'static BlockState { self.get_pumpkin_block(block.id).map_or_else( || block.mirror(state_id, mirror), |pumpkin_block| pumpkin_block.mirror(block, state_id, mirror), ) } #[must_use] pub fn rotate( &self, block: &Block, state_id: BlockStateId, rotation: Rotation, ) -> &'static BlockState { self.get_pumpkin_block(block.id).map_or_else( || block.rotate(state_id, rotation), |pumpkin_block| pumpkin_block.rotate(block, state_id, rotation), ) } #[must_use] pub fn is_pathfindable( &self, block: &Block, state: &BlockState, computation_type: PathComputationType, ) -> bool { self.get_pumpkin_block(block.id).map_or_else( || match computation_type { PathComputationType::Water => { state.is_waterlogged() || Fluid::from_state_id(state.id) .is_some_and(|f| f.has_tag(&tag::Fluid::MINECRAFT_WATER)) } PathComputationType::Land | PathComputationType::Air => !state.is_full_cube(), }, |pumpkin_block| pumpkin_block.is_pathfindable(state, computation_type), ) } }