Prevent internal NPE on ItemStack#damage (#10836)

ItemStack#damage internally uses ItemStack#hurtAndBreak, which
previously would call a Consumer in case the item broke.
Since 1.20.5 the break game event logic however resides in said method
and was using the equipment slot passed, which is null in the case of
the API ItemStack#damage method.

This commit prevents the NPE by first null checking the slot.
Addittionally, hurtAndBreak also now checks if the player has infinite
materials, e.g. is in creative mode, to prevent damaging the item.

As such as filter is undesirable for API calls, this commit also skips
this logic in case of an API invocation.
This commit is contained in:
Bjarne Koll 2024-06-10 08:24:52 +02:00
parent bd9e2e7fe8
commit 5df0660d63
2 changed files with 30 additions and 2 deletions

View file

@ -137,7 +137,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
nmsStack.hurtAndBreak(amount, this.getHandle(), slot);
nmsStack.hurtAndBreakWithoutChecks(amount, this.getHandle(), slot);
}
// Paper end - ItemStack damage API
+

View file

@ -10,6 +10,34 @@ the logic associated with damaging them
== AT ==
public net.minecraft.world.entity.LivingEntity entityEventForEquipmentBreak(Lnet/minecraft/world/entity/EquipmentSlot;)B
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder {
return;
}
}
+ // Paper start - ItemStack damage API - split hurtAndBreak to skip pre-checks like creative mode
+ this.hurtAndBreakWithoutChecks(amount, entity, slot);
+ }
+ }
+ public void hurtAndBreakWithoutChecks(int amount, LivingEntity entity, @org.checkerframework.checker.nullness.qual.Nullable EquipmentSlot slot) {
+ {
+ // Paper end - ItemStack damage API - split hurtAndBreak to skip pre-checks like creative mode
RandomSource randomsource = entity.getRandom();
ServerPlayer entityplayer;
@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder {
}
this.hurtAndBreak(amount, randomsource, entity, () -> { // Paper - Add EntityDamageItemEvent
- entity.broadcastBreakEvent(slot);
+ if (slot != null) entity.broadcastBreakEvent(slot); // Paper - ItemStack damage API - slot is nullable
Item item = this.getItem();
// CraftBukkit start - Check for item breaking
if (this.count == 1 && entity instanceof net.minecraft.world.entity.player.Player) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@ -59,7 +87,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+
+ private void damageItemStack0(final net.minecraft.world.item.ItemStack nmsStack, final int amount, final net.minecraft.world.entity.EquipmentSlot slot) {
+ nmsStack.hurtAndBreak(amount, this.getHandle(), slot);
+ nmsStack.hurtAndBreakWithoutChecks(amount, this.getHandle(), slot);
+ }
+ // Paper end - ItemStack damage API
}