--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -83,16 +_,45 @@
                 Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
                 EntityType<?> type = ((SpawnEggItem)item.getItem()).getType(blockSource.level().registryAccess(), item);
 
+                // CraftBukkit start
+                ServerLevel serverLevel = blockSource.level();
+                ItemStack singleItemStack = item.copyWithCount(1);
+                org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+                org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
+
+                org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+                serverLevel.getCraftServer().getPluginManager().callEvent(event);
+
+                if (event.isCancelled()) {
+                    return item;
+                }
+
+                boolean shrink = true;
+                if (!event.getItem().equals(craftItem)) {
+                    shrink = false;
+                    // Chain to handler for new item
+                    ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                    DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                    if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                        dispenseBehavior.dispense(blockSource, eventStack);
+                        return item;
+                    }
+                    // Paper start - track changed items in the dispense event
+                    singleItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified
+                    type = ((SpawnEggItem) singleItemStack.getItem()).getType(serverLevel.registryAccess(), singleItemStack);
+                    // Paper end - track changed item from dispense event
+                }
                 try {
                     type.spawn(
-                        blockSource.level(), item, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false
+                        blockSource.level(), singleItemStack, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false // Paper - track changed item in dispense event
                     );
                 } catch (Exception var6) {
                     LOGGER.error("Error while dispensing spawn egg from dispenser at {}", blockSource.pos(), var6);
                     return ItemStack.EMPTY;
                 }
 
-                item.shrink(1);
+                if (shrink) item.shrink(1);
+                // CraftBukkit end
                 blockSource.level().gameEvent(null, GameEvent.ENTITY_PLACE, blockSource.pos());
                 return item;
             }
@@ -110,12 +_,38 @@
                     Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
                     BlockPos blockPos = blockSource.pos().relative(direction);
                     ServerLevel serverLevel = blockSource.level();
+                    // CraftBukkit start
+                    ItemStack singleItemStack = item.copyWithCount(1);
+                    org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+                    org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
+
+                    org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+                    serverLevel.getCraftServer().getPluginManager().callEvent(event);
+
+                    if (event.isCancelled()) {
+                        return item;
+                    }
+
+                    boolean shrink = true;
+                    if (!event.getItem().equals(craftItem)) {
+                        shrink = false;
+                        // Chain to handler for new item
+                        ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                        DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                        if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                            dispenseBehavior.dispense(blockSource, eventStack);
+                            return item;
+                        }
+                    }
+                    // CraftBukkit end
+
+                    final ItemStack newStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // Paper - use event itemstack (unwrap is fine here because the stack won't be modified)
                     Consumer<ArmorStand> consumer = EntityType.appendDefaultStackConfig(
-                        armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, item, null
+                        armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, newStack, null // Paper - track changed items in the dispense event
                     );
                     ArmorStand armorStand = EntityType.ARMOR_STAND.spawn(serverLevel, consumer, blockPos, EntitySpawnReason.DISPENSER, false, false);
                     if (armorStand != null) {
-                        item.shrink(1);
+                        if (shrink) item.shrink(1); // Paper
                     }
 
                     return item;
@@ -135,8 +_,35 @@
                             new AABB(blockPos),
                             abstractChestedHorse1 -> abstractChestedHorse1.isAlive() && !abstractChestedHorse1.hasChest()
                         )) {
-                        if (abstractChestedHorse.isTamed() && abstractChestedHorse.getSlot(499).set(item)) {
-                            item.shrink(1);
+                        if (abstractChestedHorse.isTamed()/* && abstractChestedHorse.getSlot(499).set(item)*/) {
+                            // CraftBukkit start
+                            ItemStack singleCopy = item.copyWithCount(1);
+                            ServerLevel world = blockSource.level();
+                            org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos());
+                            org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleCopy);
+                            org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), abstractChestedHorse.getBukkitLivingEntity());
+                            world.getCraftServer().getPluginManager().callEvent(event);
+
+                            if (event.isCancelled()) {
+                                this.setSuccess(false);
+                                return item;
+                            }
+
+                            boolean shrink = true;
+                            if (!event.getItem().equals(craftItem)) {
+                                shrink = false;
+                                // Chain to handler for new item
+                                ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                                DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                                if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                                    dispenseBehavior.dispense(blockSource, eventStack);
+                                    return item;
+                                }
+                            }
+                            abstractChestedHorse.getSlot(499).set(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
+                            // CraftBukkit end
+
+                            if (shrink) item.shrink(1); // Paper - actually handle here
                             this.setSuccess(true);
                             return item;
                         }
@@ -174,8 +_,45 @@
                 DispensibleContainerItem dispensibleContainerItem = (DispensibleContainerItem)item.getItem();
                 BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
                 Level level = blockSource.level();
+                // CraftBukkit start
+                BlockState state = level.getBlockState(blockPos);
+                ItemStack dispensedItem = item; // Paper - track changed item from the dispense event
+                // Paper start - correctly check if the bucket place will succeed
+                /* Taken from SolidBucketItem#emptyContents */
+                boolean willEmptyContentsSolidBucketItem = dispensibleContainerItem instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(blockPos) && state.isAir();
+                /* Taken from BucketItem#emptyContents */
+                boolean willEmptyBucketItem = dispensibleContainerItem instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.content) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, blockPos, state, bucketItem.content)));
+                if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
+                // Paper end - correctly check if the bucket place will succeed
+                    org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+                    org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+                    org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
+                    level.getCraftServer().getPluginManager().callEvent(event);
+
+                    if (event.isCancelled()) {
+                        return item;
+                    }
+
+                    if (!event.getItem().equals(craftItem)) {
+                        // Chain to handler for new item
+                        ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                        DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                        if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                            dispenseBehavior.dispense(blockSource, eventStack);
+                            return item;
+                        }
+                    }
+
+                    // Paper start - track changed item from dispense event
+                    dispensedItem = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe here as the stack isn't mutated
+                    dispensibleContainerItem = (DispensibleContainerItem) dispensedItem.getItem();
+                    // Paper end - track changed item from dispense event
+                }
+                // CraftBukkit end
+
                 if (dispensibleContainerItem.emptyContents(null, level, blockPos, null)) {
-                    dispensibleContainerItem.checkExtraContent(null, level, item, blockPos);
+                    dispensibleContainerItem.checkExtraContent(null, level, dispensedItem, blockPos); // Paper - track changed item from dispense event
                     return this.consumeWithRemainder(blockSource, item, new ItemStack(Items.BUCKET));
                 } else {
                     return this.defaultDispenseItemBehavior.dispense(blockSource, item);
@@ -198,12 +_,19 @@
                 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);
+                    ItemStack itemStack = bucketPickup.pickupBlock(null, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState); // CraftBukkit
                     if (itemStack.isEmpty()) {
                         return super.execute(blockSource, item);
                     } else {
                         levelAccessor.gameEvent(null, GameEvent.FLUID_PICKUP, blockPos);
                         Item item1 = itemStack.getItem();
+                        // Paper start - Call BlockDispenseEvent
+                        ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                        if (result != null) {
+                            return result;
+                        }
+                        // Paper end - Call BlockDispenseEvent
+                        itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState); // CraftBukkit - from above
                         return this.consumeWithRemainder(blockSource, item, new ItemStack(item1));
                     }
                 } else {
@@ -218,15 +_,26 @@
                 this.setSuccess(true);
                 Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
                 BlockPos blockPos = blockSource.pos().relative(direction);
+                // Paper start - Call BlockDispenseEvent
+                ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                if (result != null) {
+                    this.setSuccess(false);
+                    return result;
+                }
+                // Paper end - Call BlockDispenseEvent
                 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);
+                    // CraftBukkit start - Ignition by dispensing flint and steel
+                    if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(serverLevel, blockPos, blockSource.pos()).isCancelled()) {
+                        serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos));
+                        serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos);
+                    }
+                    // CraftBukkit end
                 } 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)) {
+                    if (TntBlock.prime(serverLevel, blockPos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(serverLevel, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, blockSource.pos()))) { // CraftBukkit - TNTPrimeEvent
                         serverLevel.removeBlock(blockPos, false);
                     } else {
                         this.setSuccess(false);
@@ -248,11 +_,46 @@
                 this.setSuccess(true);
                 Level level = blockSource.level();
                 BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
+                // Paper start - Call BlockDispenseEvent
+                ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                if (result != null) {
+                    this.setSuccess(false);
+                    return result;
+                }
+                // Paper end - Call BlockDispenseEvent
+                level.captureTreeGeneration = true; // CraftBukkit
                 if (!BoneMealItem.growCrop(item, level, blockPos) && !BoneMealItem.growWaterPlant(item, level, blockPos, null)) {
                     this.setSuccess(false);
                 } else if (!level.isClientSide) {
                     level.levelEvent(1505, blockPos, 15);
                 }
+                // CraftBukkit start
+                level.captureTreeGeneration = false;
+                if (!level.capturedBlockStates.isEmpty()) {
+                    org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType;
+                    net.minecraft.world.level.block.SaplingBlock.treeType = null;
+                    org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level.getWorld());
+                    List<org.bukkit.block.BlockState> states = new java.util.ArrayList<>(level.capturedBlockStates.values());
+                    level.capturedBlockStates.clear();
+                    org.bukkit.event.world.StructureGrowEvent structureEvent = null;
+                    if (treeType != null) {
+                        structureEvent = new org.bukkit.event.world.StructureGrowEvent(location, treeType, false, null, states);
+                        org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
+                    }
+
+                    org.bukkit.event.block.BlockFertilizeEvent fertilizeEvent = new org.bukkit.event.block.BlockFertilizeEvent(location.getBlock(), null, states);
+                    fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled());
+                    org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent);
+
+                    if (!fertilizeEvent.isCancelled()) {
+                        for (org.bukkit.block.BlockState state : states) {
+                            org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) state;
+                            craftBlockState.place(craftBlockState.getFlags());
+                            blockSource.level().checkCapturedTreeStateForObserverNotify(blockPos, craftBlockState); // Paper - notify observers even if grow failed
+                        }
+                    }
+                }
+                // CraftBukkit end
 
                 return item;
             }
@@ -266,11 +_,36 @@
                     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);
+                    // CraftBukkit start
+                    ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink at end and single item in event
+                    org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+                    org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
+
+                    org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockPos.getX() + 0.5D, (double) blockPos.getY(), (double) blockPos.getZ() + 0.5D));
+                    serverLevel.getCraftServer().getPluginManager().callEvent(event);
+
+                    if (event.isCancelled()) {
+                        return item;
+                    }
+
+                    boolean shrink = true;
+                    if (!event.getItem().equals(craftItem)) {
+                        shrink = false;
+                        // Chain to handler for new item
+                        ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                        DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                        if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                            dispenseBehavior.dispense(blockSource, eventStack);
+                            return item;
+                        }
+                    }
+
+                    PrimedTnt primedTnt = new PrimedTnt(serverLevel, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), null);
+                    // CraftBukkit end
                     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);
+                    serverLevel.gameEvent(null, GameEvent.ENTITY_PLACE, org.bukkit.craftbukkit.util.CraftVector.toBlockPos(event.getVelocity())); // Paper - update game event position
+                    if (shrink) item.shrink(1); // Paper
                     this.setSuccess(true);
                     return item;
                 }
@@ -284,6 +_,13 @@
                     Level level = blockSource.level();
                     Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
                     BlockPos blockPos = blockSource.pos().relative(direction);
+                    // Paper start - Call BlockDispenseEvent
+                    ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                    if (result != null) {
+                        this.setSuccess(false);
+                        return result;
+                    }
+                    // Paper end - Call BlockDispenseEvent
                     if (level.isEmptyBlock(blockPos) && WitherSkullBlock.canSpawnMob(level, blockPos, item)) {
                         level.setBlock(
                             blockPos,
@@ -299,7 +_,7 @@
                         item.shrink(1);
                         this.setSuccess(true);
                     } else {
-                        this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item));
+                        this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError
                     }
 
                     return item;
@@ -312,6 +_,13 @@
                 Level level = blockSource.level();
                 BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
                 CarvedPumpkinBlock carvedPumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN;
+                // Paper start - Call BlockDispenseEvent
+                ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                if (result != null) {
+                    this.setSuccess(false);
+                    return result;
+                }
+                // Paper end - Call BlockDispenseEvent
                 if (level.isEmptyBlock(blockPos) && carvedPumpkinBlock.canSpawnGolem(level, blockPos)) {
                     if (!level.isClientSide) {
                         level.setBlock(blockPos, carvedPumpkinBlock.defaultBlockState(), 3);
@@ -321,7 +_,7 @@
                     item.shrink(1);
                     this.setSuccess(true);
                 } else {
-                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item));
+                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError
                 }
 
                 return item;
@@ -347,6 +_,12 @@
                     ServerLevel serverLevel = blockSource.level();
                     BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
                     BlockState blockState = serverLevel.getBlockState(blockPos);
+                    // Paper start - Call BlockDispenseEvent
+                    ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                    if (result != null) {
+                        return result;
+                    }
+                    // Paper end - Call BlockDispenseEvent
                     if (blockState.is(
                             BlockTags.BEEHIVES,
                             blockStateBase -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockStateBase.getBlock() instanceof BeehiveBlock
@@ -375,6 +_,13 @@
                 this.setSuccess(true);
                 if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
                     if (blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) {
+                        // Paper start - Call BlockDispenseEvent
+                        ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                        if (result != null) {
+                            this.setSuccess(false);
+                            return result;
+                        }
+                        // Paper end - Call BlockDispenseEvent
                         RespawnAnchorBlock.charge(null, level, blockPos, blockState);
                         item.shrink(1);
                     } else {
@@ -398,6 +_,28 @@
                     this.setSuccess(false);
                     return item;
                 } else {
+                    // CraftBukkit start
+                    org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+                    org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
+
+                    org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity());
+                    serverLevel.getCraftServer().getPluginManager().callEvent(event);
+
+                    if (event.isCancelled()) {
+                        this.setSuccess(false);
+                        return item;
+                    }
+
+                    if (!event.getItem().equals(craftItem)) {
+                        // Chain to handler for new item
+                        ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+                        DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack);
+                        if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+                            dispenseBehavior.dispense(blockSource, eventStack);
+                            return item;
+                        }
+                    }
+                    // CraftBukkit end
                     for (Armadillo armadillo : entitiesOfClass) {
                         if (armadillo.brushOffScute()) {
                             item.hurtAndBreak(16, serverLevel, null, item1 -> {});
@@ -418,6 +_,13 @@
                 BlockState blockState = level.getBlockState(blockPos);
                 Optional<BlockState> waxed = HoneycombItem.getWaxed(blockState);
                 if (waxed.isPresent()) {
+                    // Paper start - Call BlockDispenseEvent
+                    ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+                    if (result != null) {
+                        this.setSuccess(false);
+                        return result;
+                    }
+                    // Paper end - Call BlockDispenseEvent
                     level.setBlockAndUpdate(blockPos, waxed.get());
                     level.levelEvent(3003, blockPos, 0);
                     item.shrink(1);
@@ -445,6 +_,12 @@
                         if (!serverLevel.getBlockState(blockPos1).is(BlockTags.CONVERTABLE_TO_MUD)) {
                             return this.defaultDispenseItemBehavior.dispense(blockSource, item);
                         } else {
+                            // Paper start - Call BlockDispenseEvent
+                            ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos1, item, this);
+                            if (result != null) {
+                                return result;
+                            }
+                            // Paper end - Call BlockDispenseEvent
                             if (!serverLevel.isClientSide) {
                                 for (int i = 0; i < 5; i++) {
                                     serverLevel.sendParticles(
