package net.minecraft.core.dispenser; import com.mojang.logging.LogUtils; import java.util.List; import java.util.Optional; import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.BlockTags; import net.minecraft.tags.FluidTags; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.animal.armadillo.Armadillo; import net.minecraft.world.entity.animal.horse.AbstractChestedHorse; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.item.PrimedTnt; import net.minecraft.world.item.BoneMealItem; import net.minecraft.world.item.DispensibleContainerItem; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.SpawnEggItem; import net.minecraft.world.item.alchemy.PotionContents; import net.minecraft.world.item.alchemy.Potions; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.BeehiveBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.BucketPickup; import net.minecraft.world.level.block.CampfireBlock; import net.minecraft.world.level.block.CandleBlock; import net.minecraft.world.level.block.CandleCakeBlock; import net.minecraft.world.level.block.CarvedPumpkinBlock; import net.minecraft.world.level.block.DispenserBlock; import net.minecraft.world.level.block.RespawnAnchorBlock; import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.SkullBlock; import net.minecraft.world.level.block.TntBlock; import net.minecraft.world.level.block.WitherSkullBlock; import net.minecraft.world.level.block.entity.BeehiveBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.RotationSegment; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.AABB; import org.slf4j.Logger; public interface DispenseItemBehavior { Logger LOGGER = LogUtils.getLogger(); DispenseItemBehavior NOOP = (blockSource, itemStack) -> itemStack; ItemStack dispense(BlockSource blockSource, ItemStack itemStack); static void bootStrap() { DispenserBlock.registerProjectileBehavior(Items.ARROW); DispenserBlock.registerProjectileBehavior(Items.TIPPED_ARROW); DispenserBlock.registerProjectileBehavior(Items.SPECTRAL_ARROW); DispenserBlock.registerProjectileBehavior(Items.EGG); DispenserBlock.registerProjectileBehavior(Items.BLUE_EGG); DispenserBlock.registerProjectileBehavior(Items.BROWN_EGG); DispenserBlock.registerProjectileBehavior(Items.SNOWBALL); DispenserBlock.registerProjectileBehavior(Items.EXPERIENCE_BOTTLE); DispenserBlock.registerProjectileBehavior(Items.SPLASH_POTION); DispenserBlock.registerProjectileBehavior(Items.LINGERING_POTION); DispenserBlock.registerProjectileBehavior(Items.FIREWORK_ROCKET); DispenserBlock.registerProjectileBehavior(Items.FIRE_CHARGE); DispenserBlock.registerProjectileBehavior(Items.WIND_CHARGE); DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { Direction direction = blockSource.state().getValue(DispenserBlock.FACING); EntityType entityType = ((SpawnEggItem)item.getItem()).getType(blockSource.level().registryAccess(), item); try { entityType.spawn(blockSource.level(), item, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false); } catch (Exception var6) { LOGGER.error("Error while dispensing spawn egg from dispenser at {}", blockSource.pos(), var6); return ItemStack.EMPTY; } item.shrink(1); blockSource.level().gameEvent(null, GameEvent.ENTITY_PLACE, blockSource.pos()); return item; } }; for (SpawnEggItem spawnEggItem : SpawnEggItem.eggs()) { DispenserBlock.registerBehavior(spawnEggItem, defaultDispenseItemBehavior); } DispenserBlock.registerBehavior(Items.ARMOR_STAND, new DefaultDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { Direction direction = blockSource.state().getValue(DispenserBlock.FACING); BlockPos blockPos = blockSource.pos().relative(direction); ServerLevel serverLevel = blockSource.level(); Consumer consumer = EntityType.appendDefaultStackConfig(armorStandx -> armorStandx.setYRot(direction.toYRot()), serverLevel, item, null); ArmorStand armorStand = EntityType.ARMOR_STAND.spawn(serverLevel, consumer, blockPos, EntitySpawnReason.DISPENSER, false, false); if (armorStand != null) { item.shrink(1); } return item; } }); DispenserBlock.registerBehavior( Items.CHEST, new OptionalDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); for (AbstractChestedHorse abstractChestedHorse : blockSource.level() .getEntitiesOfClass( AbstractChestedHorse.class, new AABB(blockPos), abstractChestedHorsex -> abstractChestedHorsex.isAlive() && !abstractChestedHorsex.hasChest() )) { if (abstractChestedHorse.isTamed() && abstractChestedHorse.getSlot(499).set(item)) { item.shrink(1); this.setSuccess(true); return item; } } return super.execute(blockSource, item); } } ); DispenserBlock.registerBehavior(Items.OAK_BOAT, new BoatDispenseItemBehavior(EntityType.OAK_BOAT)); DispenserBlock.registerBehavior(Items.SPRUCE_BOAT, new BoatDispenseItemBehavior(EntityType.SPRUCE_BOAT)); DispenserBlock.registerBehavior(Items.BIRCH_BOAT, new BoatDispenseItemBehavior(EntityType.BIRCH_BOAT)); DispenserBlock.registerBehavior(Items.JUNGLE_BOAT, new BoatDispenseItemBehavior(EntityType.JUNGLE_BOAT)); DispenserBlock.registerBehavior(Items.DARK_OAK_BOAT, new BoatDispenseItemBehavior(EntityType.DARK_OAK_BOAT)); DispenserBlock.registerBehavior(Items.ACACIA_BOAT, new BoatDispenseItemBehavior(EntityType.ACACIA_BOAT)); DispenserBlock.registerBehavior(Items.CHERRY_BOAT, new BoatDispenseItemBehavior(EntityType.CHERRY_BOAT)); DispenserBlock.registerBehavior(Items.MANGROVE_BOAT, new BoatDispenseItemBehavior(EntityType.MANGROVE_BOAT)); DispenserBlock.registerBehavior(Items.PALE_OAK_BOAT, new BoatDispenseItemBehavior(EntityType.PALE_OAK_BOAT)); DispenserBlock.registerBehavior(Items.BAMBOO_RAFT, new BoatDispenseItemBehavior(EntityType.BAMBOO_RAFT)); DispenserBlock.registerBehavior(Items.OAK_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.OAK_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.SPRUCE_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.SPRUCE_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.BIRCH_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.BIRCH_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.JUNGLE_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.JUNGLE_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.DARK_OAK_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.DARK_OAK_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.ACACIA_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.ACACIA_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.CHERRY_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.CHERRY_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.MANGROVE_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.MANGROVE_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.PALE_OAK_CHEST_BOAT, new BoatDispenseItemBehavior(EntityType.PALE_OAK_CHEST_BOAT)); DispenserBlock.registerBehavior(Items.BAMBOO_CHEST_RAFT, new BoatDispenseItemBehavior(EntityType.BAMBOO_CHEST_RAFT)); DispenseItemBehavior dispenseItemBehavior = new DefaultDispenseItemBehavior() { private final DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior(); @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { DispensibleContainerItem dispensibleContainerItem = (DispensibleContainerItem)item.getItem(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); Level level = blockSource.level(); if (dispensibleContainerItem.emptyContents(null, level, blockPos, null)) { dispensibleContainerItem.checkExtraContent(null, level, item, blockPos); return this.consumeWithRemainder(blockSource, item, new ItemStack(Items.BUCKET)); } else { return this.defaultDispenseItemBehavior.dispense(blockSource, item); } } }; DispenserBlock.registerBehavior(Items.LAVA_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.WATER_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.POWDER_SNOW_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.SALMON_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.COD_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.PUFFERFISH_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.TROPICAL_FISH_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.AXOLOTL_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.TADPOLE_BUCKET, dispenseItemBehavior); DispenserBlock.registerBehavior(Items.BUCKET, new DefaultDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { LevelAccessor levelAccessor = blockSource.level(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); BlockState blockState = levelAccessor.getBlockState(blockPos); if (blockState.getBlock() instanceof BucketPickup bucketPickup) { ItemStack itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState); if (itemStack.isEmpty()) { return super.execute(blockSource, item); } else { levelAccessor.gameEvent(null, GameEvent.FLUID_PICKUP, blockPos); Item item2 = itemStack.getItem(); return this.consumeWithRemainder(blockSource, item, new ItemStack(item2)); } } else { return super.execute(blockSource, item); } } }); DispenserBlock.registerBehavior(Items.FLINT_AND_STEEL, new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { ServerLevel serverLevel = blockSource.level(); this.setSuccess(true); Direction direction = blockSource.state().getValue(DispenserBlock.FACING); BlockPos blockPos = blockSource.pos().relative(direction); BlockState blockState = serverLevel.getBlockState(blockPos); if (BaseFireBlock.canBePlacedAt(serverLevel, blockPos, direction)) { serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos)); serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos); } else if (CampfireBlock.canLight(blockState) || CandleBlock.canLight(blockState) || CandleCakeBlock.canLight(blockState)) { serverLevel.setBlockAndUpdate(blockPos, blockState.setValue(BlockStateProperties.LIT, true)); serverLevel.gameEvent(null, GameEvent.BLOCK_CHANGE, blockPos); } else if (blockState.getBlock() instanceof TntBlock) { if (TntBlock.prime(serverLevel, blockPos)) { serverLevel.removeBlock(blockPos, false); } else { this.setSuccess(false); } } else { this.setSuccess(false); } if (this.isSuccess()) { item.hurtAndBreak(1, serverLevel, null, itemx -> {}); } return item; } }); DispenserBlock.registerBehavior(Items.BONE_MEAL, new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { this.setSuccess(true); Level level = blockSource.level(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); if (!BoneMealItem.growCrop(item, level, blockPos) && !BoneMealItem.growWaterPlant(item, level, blockPos, null)) { this.setSuccess(false); } else if (!level.isClientSide) { level.levelEvent(1505, blockPos, 15); } return item; } }); DispenserBlock.registerBehavior(Blocks.TNT, new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { ServerLevel serverLevel = blockSource.level(); if (!serverLevel.getGameRules().getBoolean(GameRules.RULE_TNT_EXPLODES)) { this.setSuccess(false); return item; } else { BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); PrimedTnt primedTnt = new PrimedTnt(serverLevel, blockPos.getX() + 0.5, blockPos.getY(), blockPos.getZ() + 0.5, null); serverLevel.addFreshEntity(primedTnt); serverLevel.playSound(null, primedTnt.getX(), primedTnt.getY(), primedTnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); serverLevel.gameEvent(null, GameEvent.ENTITY_PLACE, blockPos); item.shrink(1); this.setSuccess(true); return item; } } }); DispenserBlock.registerBehavior(Items.WITHER_SKELETON_SKULL, new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { Level level = blockSource.level(); Direction direction = blockSource.state().getValue(DispenserBlock.FACING); BlockPos blockPos = blockSource.pos().relative(direction); if (level.isEmptyBlock(blockPos) && WitherSkullBlock.canSpawnMob(level, blockPos, item)) { level.setBlock(blockPos, Blocks.WITHER_SKELETON_SKULL.defaultBlockState().setValue(SkullBlock.ROTATION, RotationSegment.convertToSegment(direction)), 3); level.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos); BlockEntity blockEntity = level.getBlockEntity(blockPos); if (blockEntity instanceof SkullBlockEntity) { WitherSkullBlock.checkSpawn(level, blockPos, (SkullBlockEntity)blockEntity); } item.shrink(1); this.setSuccess(true); } else { this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item)); } return item; } }); DispenserBlock.registerBehavior(Blocks.CARVED_PUMPKIN, new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { Level level = blockSource.level(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); CarvedPumpkinBlock carvedPumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN; if (level.isEmptyBlock(blockPos) && carvedPumpkinBlock.canSpawnGolem(level, blockPos)) { if (!level.isClientSide) { level.setBlock(blockPos, carvedPumpkinBlock.defaultBlockState(), 3); level.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos); } item.shrink(1); this.setSuccess(true); } else { this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item)); } return item; } }); DispenserBlock.registerBehavior(Blocks.SHULKER_BOX.asItem(), new ShulkerBoxDispenseBehavior()); for (DyeColor dyeColor : DyeColor.values()) { DispenserBlock.registerBehavior(ShulkerBoxBlock.getBlockByColor(dyeColor).asItem(), new ShulkerBoxDispenseBehavior()); } DispenserBlock.registerBehavior( Items.GLASS_BOTTLE.asItem(), new OptionalDispenseItemBehavior() { private ItemStack takeLiquid(BlockSource blockSource, ItemStack itemStack, ItemStack itemStack2) { blockSource.level().gameEvent(null, GameEvent.FLUID_PICKUP, blockSource.pos()); return this.consumeWithRemainder(blockSource, itemStack, itemStack2); } @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { this.setSuccess(false); ServerLevel serverLevel = blockSource.level(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); BlockState blockState = serverLevel.getBlockState(blockPos); if (blockState.is( BlockTags.BEEHIVES, blockStateBase -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockStateBase.getBlock() instanceof BeehiveBlock ) && (Integer)blockState.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) { ((BeehiveBlock)blockState.getBlock()) .releaseBeesAndResetHoneyLevel(serverLevel, blockState, blockPos, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED); this.setSuccess(true); return this.takeLiquid(blockSource, item, new ItemStack(Items.HONEY_BOTTLE)); } else if (serverLevel.getFluidState(blockPos).is(FluidTags.WATER)) { this.setSuccess(true); return this.takeLiquid(blockSource, item, PotionContents.createItemStack(Items.POTION, Potions.WATER)); } else { return super.execute(blockSource, item); } } } ); DispenserBlock.registerBehavior(Items.GLOWSTONE, new OptionalDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { Direction direction = blockSource.state().getValue(DispenserBlock.FACING); BlockPos blockPos = blockSource.pos().relative(direction); Level level = blockSource.level(); BlockState blockState = level.getBlockState(blockPos); this.setSuccess(true); if (blockState.is(Blocks.RESPAWN_ANCHOR)) { if ((Integer)blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) { RespawnAnchorBlock.charge(null, level, blockPos, blockState); item.shrink(1); } else { this.setSuccess(false); } return item; } else { return super.execute(blockSource, item); } } }); DispenserBlock.registerBehavior(Items.SHEARS.asItem(), new ShearsDispenseItemBehavior()); DispenserBlock.registerBehavior(Items.BRUSH.asItem(), new OptionalDispenseItemBehavior() { @Override protected ItemStack execute(BlockSource blockSource, ItemStack item) { ServerLevel serverLevel = blockSource.level(); BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); List list = serverLevel.getEntitiesOfClass(Armadillo.class, new AABB(blockPos), EntitySelector.NO_SPECTATORS); if (list.isEmpty()) { this.setSuccess(false); return item; } else { for (Armadillo armadillo : list) { if (armadillo.brushOffScute()) { item.hurtAndBreak(16, serverLevel, null, itemx -> {}); return item; } } this.setSuccess(false); return item; } } }); DispenserBlock.registerBehavior(Items.HONEYCOMB, new OptionalDispenseItemBehavior() { @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); Level level = blockSource.level(); BlockState blockState = level.getBlockState(blockPos); Optional optional = HoneycombItem.getWaxed(blockState); if (optional.isPresent()) { level.setBlockAndUpdate(blockPos, (BlockState)optional.get()); level.levelEvent(3003, blockPos, 0); item.shrink(1); this.setSuccess(true); return item; } else { return super.execute(blockSource, item); } } }); DispenserBlock.registerBehavior( Items.POTION, new DefaultDispenseItemBehavior() { private final DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior(); @Override public ItemStack execute(BlockSource blockSource, ItemStack item) { PotionContents potionContents = item.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); if (!potionContents.is(Potions.WATER)) { return this.defaultDispenseItemBehavior.dispense(blockSource, item); } else { ServerLevel serverLevel = blockSource.level(); BlockPos blockPos = blockSource.pos(); BlockPos blockPos2 = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); if (!serverLevel.getBlockState(blockPos2).is(BlockTags.CONVERTABLE_TO_MUD)) { return this.defaultDispenseItemBehavior.dispense(blockSource, item); } else { if (!serverLevel.isClientSide) { for (int i = 0; i < 5; i++) { serverLevel.sendParticles( ParticleTypes.SPLASH, blockPos.getX() + serverLevel.random.nextDouble(), blockPos.getY() + 1, blockPos.getZ() + serverLevel.random.nextDouble(), 1, 0.0, 0.0, 0.0, 1.0 ); } } serverLevel.playSound(null, blockPos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); serverLevel.gameEvent(null, GameEvent.FLUID_PLACE, blockPos); serverLevel.setBlockAndUpdate(blockPos2, Blocks.MUD.defaultBlockState()); return this.consumeWithRemainder(blockSource, item, new ItemStack(Items.GLASS_BOTTLE)); } } } } ); DispenserBlock.registerBehavior(Items.MINECART, new MinecartDispenseItemBehavior(EntityType.MINECART)); DispenserBlock.registerBehavior(Items.CHEST_MINECART, new MinecartDispenseItemBehavior(EntityType.CHEST_MINECART)); DispenserBlock.registerBehavior(Items.FURNACE_MINECART, new MinecartDispenseItemBehavior(EntityType.FURNACE_MINECART)); DispenserBlock.registerBehavior(Items.TNT_MINECART, new MinecartDispenseItemBehavior(EntityType.TNT_MINECART)); DispenserBlock.registerBehavior(Items.HOPPER_MINECART, new MinecartDispenseItemBehavior(EntityType.HOPPER_MINECART)); DispenserBlock.registerBehavior(Items.COMMAND_BLOCK_MINECART, new MinecartDispenseItemBehavior(EntityType.COMMAND_BLOCK_MINECART)); } }