Pumpkin/crates/pumpkin/src/block/registry.rs
Alexander Medvedev fdb6da2b0d fix: anvil
2026-08-31 16:47:23 +02:00

1376 lines
47 KiB
Rust

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, 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::{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<BlockRegistry> {
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)
}
}
#[derive(Default)]
pub struct BlockRegistry {
blocks: FxHashMap<BlockId, Arc<dyn BlockBehaviour>>,
fluids: FxHashMap<u16, Arc<dyn FluidBehaviour>>,
}
#[derive(Debug)]
pub enum BlockPlacingError {
InvalidGamemode,
BlockOutOfWorld,
}
impl BlockRegistry {
pub fn bone_meal(
&self,
block: &Block,
world: &Arc<World>,
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<Player>,
placed_block: &'static Block,
server: &Arc<Server>,
use_item_on: &SUseItemOn,
location: BlockPos,
face: BlockDirection,
) -> Result<Option<(BlockPos, BlockStateId)>, 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::{BlockProperties, WaterLikeProperties};
let water_props =
WaterLikeProperties::from_state_id(clicked_block_state.id, clicked_block);
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::{
BlockProperties, WaterLikeProperties,
};
let water_props = WaterLikeProperties::from_state_id(
previous_block_state.id,
previous_block,
);
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)))
}
pub fn register<T: BlockBehaviour + BlockMetadata + 'static>(&mut self, block: T) {
let ids = T::ids();
let val = Arc::new(block);
self.blocks.reserve(ids.len());
for i in ids {
self.blocks.insert(i, val.clone());
}
}
pub fn register_fluid<T: FluidBehaviour + FluidMetadata + 'static>(&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<World>,
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<World>,
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<World>,
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<World>,
projectile: &dyn EntityBase,
position: &BlockPos,
state: &BlockState,
hit_pos: &Vector3<f64>,
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<Player>,
position: &BlockPos,
hit: &BlockHitResult<'_>,
server: &Server,
world: &Arc<World>,
) -> 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<World>, 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<Player>,
position: &BlockPos,
hit: &BlockHitResult<'_>,
item_stack: &mut ItemStack,
equipment_slot: &EquipmentSlot,
server: &Server,
world: &Arc<World>,
) -> 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<Player>,
position: BlockPos,
item: &Item,
server: &Server,
world: &Arc<World>,
) -> 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<BlockDirection>,
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<World>,
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<World>,
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<World>,
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<World>,
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<World>,
block: &Block,
player: &Arc<Player>,
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<World>,
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<World>,
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<World>,
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<World>, 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<World>,
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,
});
}
}
#[must_use]
pub fn get_pumpkin_block(&self, block: BlockId) -> Option<&Arc<dyn BlockBehaviour>> {
self.blocks.get(&block)
}
#[must_use]
pub fn get_pumpkin_fluid(&self, fluid_id: u16) -> Option<&Arc<dyn FluidBehaviour>> {
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),
)
}
}