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 +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 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 -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 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; - }