From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 11 Dec 2022 23:47:22 -0800
Subject: [PATCH] Fix inconsistencies in dispense events regarding stack size

The javadocs for BlockDispenseEvent suggest the ItemStack is a single
item which is being dispensed. Before this fix, sometimes it was the whole
stack before a single item had been taken. This fixes that so the stack size
is always 1.

diff --git a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
index dff30954e4c588ee4cc79d3f6dab6fb456934d65..ddb264443f2e38b6348226016f9139727c588898 100644
--- a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
@@ -49,7 +49,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
         }
 
         // CraftBukkit start
-        ItemStack itemstack1 = stack.split(1);
+        ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
         org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
         CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -59,12 +59,13 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
         }
 
         if (event.isCancelled()) {
-            stack.grow(1);
+            // stack.grow(1); // Paper - shrink below
             return stack;
         }
 
+        boolean shrink = true; // Paper
         if (!event.getItem().equals(craftItem)) {
-            stack.grow(1);
+            shrink = false; // Paper - shrink below
             // Chain to handler for new item
             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
             DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -80,8 +81,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
             abstractboat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit
             EntityType.createDefaultStackConfig(worldserver, stack, (Player) null).accept(abstractboat);
             abstractboat.setYRot(enumdirection.toYRot());
-            if (!worldserver.addFreshEntity(abstractboat)) stack.grow(1); // CraftBukkit
-            // itemstack.shrink(1); // CraftBukkit - handled during event processing
+            if (worldserver.addFreshEntity(abstractboat) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
         }
 
         return stack;
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 8f9b86e50717746e55232293d9e5ac05b8616aa0..0d12605dc84dad49faa18bf1fd058c3c168623ee 100644
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -106,7 +106,7 @@ public interface DispenseItemBehavior {
 
                 // CraftBukkit start
                 ServerLevel worldserver = pointer.level();
-                ItemStack itemstack1 = stack.split(1);
+                ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
                 org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
                 CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -116,12 +116,13 @@ public interface DispenseItemBehavior {
                 }
 
                 if (event.isCancelled()) {
-                    stack.grow(1);
+                    // stack.grow(1); // Paper - shrink below
                     return stack;
                 }
 
+                boolean shrink = true; // Paper
                 if (!event.getItem().equals(craftItem)) {
-                    stack.grow(1);
+                    shrink = false; // Paper - shrink below
                     // Chain to handler for new item
                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                     DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -142,7 +143,7 @@ public interface DispenseItemBehavior {
                     return ItemStack.EMPTY;
                 }
 
-                // itemstack.shrink(1); // Handled during event processing
+                if (shrink) stack.shrink(1); // Paper - actually handle here
                 // CraftBukkit end
                 pointer.level().gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, pointer.pos());
                 return stack;
@@ -164,7 +165,7 @@ public interface DispenseItemBehavior {
                 ServerLevel worldserver = pointer.level();
 
                 // CraftBukkit start
-                ItemStack itemstack1 = stack.split(1);
+                ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
                 org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
                 CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -174,12 +175,13 @@ public interface DispenseItemBehavior {
                 }
 
                 if (event.isCancelled()) {
-                    stack.grow(1);
+                    // stack.grow(1); // Paper - shrink below
                     return stack;
                 }
 
+                boolean shrink = true; // Paper
                 if (!event.getItem().equals(craftItem)) {
-                    stack.grow(1);
+                    shrink = false; // Paper - shrink below
                     // Chain to handler for new item
                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                     DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -197,7 +199,7 @@ public interface DispenseItemBehavior {
                 ArmorStand entityarmorstand = (ArmorStand) EntityType.ARMOR_STAND.spawn(worldserver, consumer, blockposition, EntitySpawnReason.DISPENSER, false, false);
 
                 if (entityarmorstand != null) {
-                    // itemstack.shrink(1); // CraftBukkit - Handled during event processing
+                    if (shrink) stack.shrink(1); // Paper - actually handle here
                 }
 
                 return stack;
@@ -217,7 +219,7 @@ public interface DispenseItemBehavior {
 
                 if (!list.isEmpty()) {
                     // CraftBukkit start
-                    ItemStack itemstack1 = stack.split(1);
+                    ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
                     ServerLevel world = pointer.level();
                     org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
                     CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
@@ -228,12 +230,13 @@ public interface DispenseItemBehavior {
                     }
 
                     if (event.isCancelled()) {
-                        stack.grow(1);
+                        // stack.grow(1); // Paper - shrink below
                         return stack;
                     }
 
+                    boolean shrink = true; // Paper
                     if (!event.getItem().equals(craftItem)) {
-                        stack.grow(1);
+                        shrink = false; // Paper - shrink below
                         // Chain to handler for new item
                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                         DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -244,6 +247,7 @@ public interface DispenseItemBehavior {
                     }
                     ((Saddleable) list.get(0)).equipSaddle(CraftItemStack.asNMSCopy(event.getItem()), SoundSource.BLOCKS); // Paper - track changed items in dispense event
                     // CraftBukkit end
+                    if (shrink) stack.shrink(1); // Paper - actually handle here
                     this.setSuccess(true);
                     return stack;
                 } else {
@@ -270,7 +274,7 @@ public interface DispenseItemBehavior {
                     entityhorsechestedabstract = (AbstractChestedHorse) iterator1.next();
                     // CraftBukkit start
                 } while (!entityhorsechestedabstract.isTamed());
-                ItemStack itemstack1 = stack.split(1);
+                ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below
                 ServerLevel world = pointer.level();
                 org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
                 CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
@@ -281,10 +285,13 @@ public interface DispenseItemBehavior {
                 }
 
                 if (event.isCancelled()) {
+                    // stack.grow(1); // Paper - shrink below (this was actually missing and should be here, added it commented out to be consistent)
                     return stack;
                 }
 
+                boolean shrink = true; // Paper
                 if (!event.getItem().equals(craftItem)) {
+                    shrink = false; // Paper - shrink below
                     // Chain to handler for new item
                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                     DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -296,7 +303,7 @@ public interface DispenseItemBehavior {
                 entityhorsechestedabstract.getSlot(499).set(CraftItemStack.asNMSCopy(event.getItem()));
                 // CraftBukkit end
 
-                // itemstack.shrink(1); // CraftBukkit - handled above
+                if (shrink) stack.shrink(1); // Paper - actually handle here
                 this.setSuccess(true);
                 return stack;
             }
@@ -344,7 +351,7 @@ public interface DispenseItemBehavior {
                 if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
                 // Paper end - correctly check if the bucket place will succeed
                     org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
-                    CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                    CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
                     BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
                     if (!DispenserBlock.eventFired) {
@@ -409,7 +416,7 @@ public interface DispenseItemBehavior {
 
                         // CraftBukkit start
                         org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-                        CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                        CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
                         BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
                         if (!DispenserBlock.eventFired) {
@@ -447,7 +454,7 @@ public interface DispenseItemBehavior {
 
                 // CraftBukkit start
                 org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
 
                 BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
                 if (!DispenserBlock.eventFired) {
@@ -509,7 +516,7 @@ public interface DispenseItemBehavior {
                 BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
                 // CraftBukkit start
                 org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
-                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
                 BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
                 if (!DispenserBlock.eventFired) {
@@ -576,7 +583,7 @@ public interface DispenseItemBehavior {
                 // CraftBukkit start
                 // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
 
-                ItemStack itemstack1 = stack.split(1);
+                ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
                 org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
                 CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -586,12 +593,13 @@ public interface DispenseItemBehavior {
                 }
 
                 if (event.isCancelled()) {
-                    stack.grow(1);
+                    // stack.grow(1); // Paper - shrink below
                     return stack;
                 }
 
+                boolean shrink = true; // Paper
                 if (!event.getItem().equals(craftItem)) {
-                    stack.grow(1);
+                    shrink = false; // Paper - shrink below
                     // Chain to handler for new item
                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                     DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -607,7 +615,7 @@ public interface DispenseItemBehavior {
                 worldserver.addFreshEntity(entitytntprimed);
                 worldserver.playSound((Player) null, entitytntprimed.getX(), entitytntprimed.getY(), entitytntprimed.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
                 worldserver.gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, blockposition);
-                // itemstack.shrink(1); // CraftBukkit - handled above
+                if (shrink) stack.shrink(1); // Paper - actually handle here
                 return stack;
             }
         });
@@ -620,7 +628,7 @@ public interface DispenseItemBehavior {
 
                 // CraftBukkit start
                 org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
                 BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
                 if (!DispenserBlock.eventFired) {
@@ -669,7 +677,7 @@ public interface DispenseItemBehavior {
 
                 // CraftBukkit start
                 org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
                 BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
                 if (!DispenserBlock.eventFired) {
@@ -731,7 +739,7 @@ public interface DispenseItemBehavior {
 
                 // CraftBukkit start
                 org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+                CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - only single item in event
 
                 BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
                 if (!DispenserBlock.eventFired) {
@@ -813,7 +821,7 @@ public interface DispenseItemBehavior {
                     ItemStack itemstack1 = stack;
                     ServerLevel world = pointer.level();
                     org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
-                    CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+                    CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); // Paper - ignore stack size on damageable items
 
                     BlockDispenseEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
                     if (!DispenserBlock.eventFired) {
diff --git a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
index a43ea83dbbd5946096cdde31af766674bda6c3be..bf8c511739265c6a9cd277752e844481598f8966 100644
--- a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
@@ -42,7 +42,7 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
         } else {
             LivingEntity entityliving = (LivingEntity) list.getFirst();
             EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(stack);
-            ItemStack itemstack1 = stack.split(1);
+            ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
 
             // CraftBukkit start
             Level world = pointer.level();
@@ -55,12 +55,13 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
             }
 
             if (event.isCancelled()) {
-                stack.grow(1);
+                // stack.grow(1); // Paper - shrink below
                 return false;
             }
 
+            boolean shrink = true; // Paper
             if (!event.getItem().equals(craftItem)) {
-                stack.grow(1);
+                shrink = false; // Paper - shrink below
                 // Chain to handler for new item
                 ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
                 DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -79,6 +80,7 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
                 entityinsentient.setPersistenceRequired();
             }
 
+            if (shrink) stack.shrink(1); // Paper - shrink here
             return true;
         }
     }
diff --git a/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
index aae9ec8f3bd39685b37251bef3f9ac846d65c192..3588896b7413be73ade6b3f8fd111d02c48ec550 100644
--- a/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
@@ -69,7 +69,7 @@ public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
         Vec3 vec3d1 = new Vec3(d0, d1 + d3, d2);
         // CraftBukkit start
         // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.createMinecart(worldserver, vec3d1.x, vec3d1.y, vec3d1.z, this.entityType, EntitySpawnReason.DISPENSER, itemstack, (EntityHuman) null);
-        ItemStack itemstack1 = stack.split(1);
+        ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
         org.bukkit.block.Block block2 = CraftBlock.at(worldserver, pointer.pos());
         CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -79,12 +79,13 @@ public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
         }
 
         if (event.isCancelled()) {
-            stack.grow(1);
+            // stack.grow(1); // Paper - shrink below
             return stack;
         }
 
+        boolean shrink = true; // Paper
         if (!event.getItem().equals(craftItem)) {
-            stack.grow(1);
+            shrink = false; // Paper - shrink below
             // Chain to handler for new item
             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
             DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -98,8 +99,7 @@ public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
         AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, itemstack1, (Player) null);
 
         if (entityminecartabstract != null) {
-            if (!worldserver.addFreshEntity(entityminecartabstract)) stack.grow(1);
-            // itemstack.shrink(1); // CraftBukkit - handled during event processing
+            if (worldserver.addFreshEntity(entityminecartabstract) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
             // CraftBukkit end
         }
 
diff --git a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
index 281439e430fb8e587549da783bdd93432f8f957f..54c72cf472e06e214eb61bd8615a0bb27690c807 100644
--- a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
@@ -38,7 +38,7 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
 
         // CraftBukkit start
         // IProjectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, itemstack, enumdirection), worldserver, itemstack, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // CraftBukkit - call when finish the BlockDispenseEvent
-        ItemStack itemstack1 = stack.split(1);
+        ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
         org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
         CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
 
@@ -48,12 +48,13 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
         }
 
         if (event.isCancelled()) {
-            stack.grow(1);
+            // stack.grow(1); // Paper - shrink below
             return stack;
         }
 
+        boolean shrink = true; // Paper
         if (!event.getItem().equals(craftItem)) {
-            stack.grow(1);
+            shrink = false; // Paper - shrink below
             // Chain to handler for new item
             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
             DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
@@ -68,7 +69,7 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
             Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, CraftItemStack.unwrap(event.getItem()), enumdirection), worldserver, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
             iprojectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity());
         }
-        // itemstack.shrink(1); // CraftBukkit - Handled during event processing
+        if (shrink) stack.shrink(1); // Paper - actually handle here
         // CraftBukkit end
         return stack;
     }
diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
index afad4fa3ca1a3186c4569ea073f776dac16817e1..65ed3d77a51b8299517e0c165403b0c5ac413475 100644
--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
@@ -38,7 +38,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
         ServerLevel worldserver = pointer.level();
         // CraftBukkit start
         org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
-        CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+        CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
 
         BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
         if (!DispenserBlock.eventFired) {
diff --git a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
index 16b435216dc7c6a3f8c1c0f9e2323e6afb3a6cb9..8f9fde5489c0e1d0a91203536caddec5a9c96f6c 100644
--- a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
@@ -34,7 +34,7 @@ public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
 
             // CraftBukkit start
             org.bukkit.block.Block bukkitBlock = CraftBlock.at(pointer.level(), pointer.pos());
-            CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack);
+            CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
 
             BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
             if (!DispenserBlock.eventFired) {