From b501e56e80e4145889887618abcfed7f38140c1b Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sun, 27 Oct 2024 18:47:50 +0100
Subject: [PATCH] Fix NPE and StackOverflowError for dispensers

---
 ...ckOverflowError-and-NPE-for-some-dis.patch | 309 ++++++++++++++++++
 ...tackOverflowError-for-some-dispenses.patch |  95 ------
 2 files changed, 309 insertions(+), 95 deletions(-)
 create mode 100644 patches/server/Fix-possible-StackOverflowError-and-NPE-for-some-dis.patch
 delete mode 100644 patches/server/Fix-possible-StackOverflowError-for-some-dispenses.patch

diff --git a/patches/server/Fix-possible-StackOverflowError-and-NPE-for-some-dis.patch b/patches/server/Fix-possible-StackOverflowError-and-NPE-for-some-dis.patch
new file mode 100644
index 0000000000..f2f6c41a72
--- /dev/null
+++ b/patches/server/Fix-possible-StackOverflowError-and-NPE-for-some-dis.patch
@@ -0,0 +1,309 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Sat, 29 Oct 2022 17:02:42 -0700
+Subject: [PATCH] Fix possible StackOverflowError and NPE for some dispenses
+
+For saddles, carpets, horse armor, and chests for horse-likes
+a BlockDispenseEvent handler that always mutated the item without
+changing the type would result in a SO error because when it went
+to find the replacement dispense behavior (since the item "changed")
+it didn't properly handle if the replacement was the same instance
+of dispense behavior.
+
+Additionally equippable mob heads, wither skulls, and carved pumpkins
+are subject to the same possible error.
+
+Furthermore since 1.21.2, the DISPENSER_REGISTRY map doesn't have a default
+return value anymore and some dispense behaviors like equippable and
+regular items will not have a defined behavior in that map and might throw
+a NPE in that case.
+
+== AT ==
+public net.minecraft.world.level.block.DispenserBlock getDispenseMethod(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/core/dispenser/DispenseItemBehavior;
+
+diff --git a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
+             stack.grow(1);
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                 idispensebehavior.dispense(pointer, eventStack);
+                 return stack;
+diff --git a/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
+         if (!dropper && !event.getItem().getType().equals(craftItem.getType())) {
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(sourceblock, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior.getClass() != DefaultDispenseItemBehavior.class) {
+                 idispensebehavior.dispense(sourceblock, eventStack);
+             } else {
+diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     stack.grow(1);
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     stack.grow(1);
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                         stack.grow(1);
+                         // Chain to handler for new item
+                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                        DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
+-                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
++                        DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
++                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+                             idispensebehavior.dispense(pointer, eventStack);
+                             return stack;
+                         }
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
+-                    if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
++                    if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+                     }
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     if (!event.getItem().equals(craftItem)) {
+                         // Chain to handler for new item
+                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                        DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                        DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                         if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                             idispensebehavior.dispense(pointer, eventStack);
+                             return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                         if (!event.getItem().equals(craftItem)) {
+                             // Chain to handler for new item
+                             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                                 idispensebehavior.dispense(pointer, eventStack);
+                                 return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     stack.grow(1);
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     stack.shrink(1);
+                     this.setSuccess(true);
+                 } else {
+-                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
++                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
+                 }
+ 
+                 return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     stack.shrink(1);
+                     this.setSuccess(true);
+                 } else {
+-                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
++                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
+                 }
+ 
+                 return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                 if (!event.getItem().equals(craftItem)) {
+                     // Chain to handler for new item
+                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                    DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                    DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                     if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                         idispensebehavior.dispense(pointer, eventStack);
+                         return stack;
+@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
+                     if (!event.getItem().equals(craftItem)) {
+                         // Chain to handler for new item
+                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                        DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
+-                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
++                        DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
++                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+                             idispensebehavior.dispense(pointer, eventStack);
+                             return stack;
+                         }
+diff --git a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
+ 
+     @Override
+     protected ItemStack execute(BlockSource pointer, ItemStack stack) {
+-        return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack) ? stack : super.execute(pointer, stack);
++        return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this) ? stack : super.execute(pointer, stack); // Paper - fix possible StackOverflowError
+     }
+ 
+-    public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack) {
++    @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
++    public static boolean dispenseEquipment(BlockSource pointer, ItemStack armor) {
++        // Paper start
++        return dispenseEquipment(pointer, armor, null);
++    }
++    public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
+         BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
+         List<LivingEntity> list = pointer.level().getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), (entityliving) -> {
+             return entityliving.canEquipWithDispenser(stack);
+@@ -0,0 +0,0 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
+                 stack.grow(1);
+                 // Chain to handler for new item
+                 ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
+-                if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
++                DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
++                if (idispensebehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || idispensebehavior != currentBehavior)) { // Paper - fix possible StackOverflowError
+                     idispensebehavior.dispense(pointer, eventStack);
+                     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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
+             stack.grow(1);
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                 idispensebehavior.dispense(pointer, eventStack);
+                 return stack;
+diff --git a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+@@ -0,0 +0,0 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
+             stack.grow(1);
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                 idispensebehavior.dispense(pointer, eventStack);
+                 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+@@ -0,0 +0,0 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
+         if (!event.getItem().equals(craftItem)) {
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                 idispensebehavior.dispense(pointer, eventStack);
+                 return stack;
+diff --git a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
++++ b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+@@ -0,0 +0,0 @@ public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
+             if (!event.getItem().equals(craftItem)) {
+                 // Chain to handler for new item
+                 ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-                DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++                DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+                 if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+                     idispensebehavior.dispense(pointer, eventStack);
+                     return stack;
+diff --git a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
++++ b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
+@@ -0,0 +0,0 @@ public class DispenserBlock extends BaseEntityBlock {
+         }
+     }
+ 
++    // Paper start - Fix NPE with equippable and items without behavior
++    public static DispenseItemBehavior getDispenseBehavior(BlockSource pointer, ItemStack stack) {
++        return ((DispenserBlock) pointer.state().getBlock()).getDispenseMethod(pointer.level(), stack);
++    }
++    // Paper end - Fix NPE with equippable and items without behavior
++
+     public DispenseItemBehavior getDispenseMethod(Level world, ItemStack stack) {
+         if (!stack.isItemEnabled(world.enabledFeatures())) {
+             return DispenserBlock.DEFAULT_BEHAVIOR;
+diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -0,0 +0,0 @@ public class CraftEventFactory {
+         if (!event.getItem().equals(craftItem)) {
+             // Chain to handler for new item
+             ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+-            net.minecraft.core.dispenser.DispenseItemBehavior itemBehavior = net.minecraft.world.level.block.DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
++            net.minecraft.core.dispenser.DispenseItemBehavior itemBehavior = net.minecraft.world.level.block.DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+             if (itemBehavior != net.minecraft.core.dispenser.DispenseItemBehavior.NOOP && itemBehavior != instance) {
+                 itemBehavior.dispense(pointer, eventStack);
+                 return itemStack;
diff --git a/patches/server/Fix-possible-StackOverflowError-for-some-dispenses.patch b/patches/server/Fix-possible-StackOverflowError-for-some-dispenses.patch
deleted file mode 100644
index a34f91c7f8..0000000000
--- a/patches/server/Fix-possible-StackOverflowError-for-some-dispenses.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <jake.m.potrebic@gmail.com>
-Date: Sat, 29 Oct 2022 17:02:42 -0700
-Subject: [PATCH] Fix possible StackOverflowError for some dispenses
-
-For saddles, carpets, horse armor, and chests for horse-likes
-a BlockDispenseEvent handler that always mutated the item without
-changing the type would result in a SO error because when it went
-to find the replacement dispense behavior (since the item "changed")
-it didn't properly handle if the replacement was the same instance
-of dispense behavior.
-
-Additionally equippable mob heads, wither skulls, and carved pumpkins
-are subject to the same possible error.
-
-diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
-+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
-@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
-                         // Chain to handler for new item
-                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
-                         DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
--                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
-+                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
-                             idispensebehavior.dispense(pointer, eventStack);
-                             return stack;
-                         }
-@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
-                     // Chain to handler for new item
-                     ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
-                     DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
--                    if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
-+                    if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
-                         idispensebehavior.dispense(pointer, eventStack);
-                         return stack;
-                     }
-@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
-                     stack.shrink(1);
-                     this.setSuccess(true);
-                 } else {
--                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
-+                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
-                 }
- 
-                 return stack;
-@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
-                     stack.shrink(1);
-                     this.setSuccess(true);
-                 } else {
--                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
-+                    this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
-                 }
- 
-                 return stack;
-@@ -0,0 +0,0 @@ public interface DispenseItemBehavior {
-                         // Chain to handler for new item
-                         ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
-                         DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
--                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
-+                        if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE && idispensebehavior != this) { // Paper - fix possible StackOverflowError
-                             idispensebehavior.dispense(pointer, eventStack);
-                             return stack;
-                         }
-diff --git a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
-+++ b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
-@@ -0,0 +0,0 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
- 
-     @Override
-     protected ItemStack execute(BlockSource pointer, ItemStack stack) {
--        return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack) ? stack : super.execute(pointer, stack);
-+        return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, null) ? stack : super.execute(pointer, stack); // Paper - fix possible StackOverflowError
-     }
- 
--    public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack) {
-+    @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
-+    public static boolean dispenseEquipment(BlockSource pointer, ItemStack armor) {
-+        // Paper start
-+        return dispenseEquipment(pointer, armor, null);
-+    }
-+    public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
-         BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
-         List<LivingEntity> list = pointer.level().getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), (entityliving) -> {
-             return entityliving.canEquipWithDispenser(stack);
-@@ -0,0 +0,0 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
-                 // Chain to handler for new item
-                 ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
-                 DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
--                if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE) {
-+                if (idispensebehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || idispensebehavior != EquipmentDispenseItemBehavior.INSTANCE)) { // Paper - fix possible StackOverflowError
-                     idispensebehavior.dispense(pointer, eventStack);
-                     return true;
-                 }