--- a/net/minecraft/core/dispenser/DispenseBehaviorItem.java
+++ b/net/minecraft/core/dispenser/DispenseBehaviorItem.java
@@ -7,8 +7,23 @@
 import net.minecraft.world.level.World;
 import net.minecraft.world.level.block.BlockDispenser;
 
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.CraftVector;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
+
 public class DispenseBehaviorItem implements IDispenseBehavior {
 
+    // CraftBukkit start
+    private boolean dropper;
+
+    public DispenseBehaviorItem(boolean dropper) {
+        this.dropper = dropper;
+    }
+    // CraftBukkit end
+
     public DispenseBehaviorItem() {}
 
     @Override
@@ -25,11 +40,19 @@
         IPosition iposition = BlockDispenser.getDispensePosition(sourceblock);
         ItemStack itemstack1 = itemstack.split(1);
 
-        spawnItem(sourceblock.level(), itemstack1, 6, enumdirection, iposition);
+        // CraftBukkit start
+        if (!spawnItem(sourceblock.level(), itemstack1, 6, enumdirection, sourceblock, dropper)) {
+            itemstack.grow(1);
+        }
+        // CraftBukkit end
         return itemstack;
     }
 
-    public static void spawnItem(World world, ItemStack itemstack, int i, EnumDirection enumdirection, IPosition iposition) {
+    // CraftBukkit start - void -> boolean return, IPosition -> ISourceBlock last argument, dropper
+    public static boolean spawnItem(World world, ItemStack itemstack, int i, EnumDirection enumdirection, SourceBlock sourceblock, boolean dropper) {
+        if (itemstack.isEmpty()) return true;
+        IPosition iposition = BlockDispenser.getDispensePosition(sourceblock);
+        // CraftBukkit end
         double d0 = iposition.x();
         double d1 = iposition.y();
         double d2 = iposition.z();
@@ -44,7 +67,39 @@
         double d3 = world.random.nextDouble() * 0.1D + 0.2D;
 
         entityitem.setDeltaMovement(world.random.triangle((double) enumdirection.getStepX() * d3, 0.0172275D * (double) i), world.random.triangle(0.2D, 0.0172275D * (double) i), world.random.triangle((double) enumdirection.getStepZ() * d3, 0.0172275D * (double) i));
+
+        // CraftBukkit start
+        org.bukkit.block.Block block = CraftBlock.at(world, sourceblock.pos());
+        CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
+
+        BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), CraftVector.toBukkit(entityitem.getDeltaMovement()));
+        if (!BlockDispenser.eventFired) {
+            world.getCraftServer().getPluginManager().callEvent(event);
+        }
+
+        if (event.isCancelled()) {
+            return false;
+        }
+
+        entityitem.setItem(CraftItemStack.asNMSCopy(event.getItem()));
+        entityitem.setDeltaMovement(CraftVector.toNMS(event.getVelocity()));
+
+        if (!dropper && !event.getItem().getType().equals(craftItem.getType())) {
+            // Chain to handler for new item
+            ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+            IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.DISPENSER_REGISTRY.get(eventStack.getItem());
+            if (idispensebehavior != IDispenseBehavior.NOOP && idispensebehavior.getClass() != DispenseBehaviorItem.class) {
+                idispensebehavior.dispense(sourceblock, eventStack);
+            } else {
+                world.addFreshEntity(entityitem);
+            }
+            return false;
+        }
+
         world.addFreshEntity(entityitem);
+
+        return true;
+        // CraftBukkit end
     }
 
     protected void playSound(SourceBlock sourceblock) {
