From 66ed50064cb47d14346e410c98118d88b8e6da9f Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Tue, 18 May 2021 12:32:02 -0700 Subject: [PATCH] Add drops to shear events --- .../ShearsDispenseItemBehavior.java.patch | 9 ++- .../world/entity/Shearable.java.patch | 12 +++- .../entity/animal/MushroomCow.java.patch | 69 +++++++++++++------ .../world/entity/animal/Sheep.java.patch | 50 +++++++++++--- .../world/entity/animal/SnowGolem.java.patch | 42 +++++++++-- .../world/entity/monster/Bogged.java.patch | 61 +++++++++++++--- .../craftbukkit/event/CraftEventFactory.java | 12 ++-- .../craftbukkit/inventory/CraftItemStack.java | 10 +++ .../paper/entity/ShearableDropsTest.java | 35 ++++++++++ 9 files changed, 246 insertions(+), 54 deletions(-) create mode 100644 paper-server/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch index a4da08d62d..d9c636c5a9 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch @@ -60,18 +60,21 @@ Iterator iterator = list.iterator(); while (iterator.hasNext()) { -@@ -73,8 +102,13 @@ +@@ -73,8 +102,16 @@ if (entityliving instanceof Shearable ishearable) { if (ishearable.readyForShearing()) { - ishearable.shear(world, SoundSource.BLOCKS, shears); - world.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, pos); + // CraftBukkit start -+ if (CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem).isCancelled()) { ++ // Paper start - Add drops to shear events ++ org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops(worldserver, itemstack)); ++ if (event.isCancelled()) { ++ // Paper end - Add drops to shear events + continue; + } + // CraftBukkit end -+ ishearable.shear(worldserver, SoundSource.BLOCKS, itemstack); ++ ishearable.shear(worldserver, SoundSource.BLOCKS, itemstack, CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events + worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition); return true; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch index d3bbca183d..aa779fc65d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch @@ -1,8 +1,18 @@ --- a/net/minecraft/world/entity/Shearable.java +++ b/net/minecraft/world/entity/Shearable.java -@@ -8,4 +8,5 @@ +@@ -5,7 +5,15 @@ + import net.minecraft.world.item.ItemStack; + + public interface Shearable { ++ default void shear(ServerLevel world, SoundSource soundCategory, ItemStack shears, java.util.List drops) { this.shear(world, soundCategory, shears); } // Paper - Add drops to shear events void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears); boolean readyForShearing(); + net.minecraft.world.level.Level level(); // Shearable API - expose default level needed for shearing. ++ ++ // Paper start - custom shear drops; ensure all implementing entities override this ++ default java.util.List generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) { ++ return java.util.Collections.emptyList(); ++ } ++ // Paper end - custom shear drops } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch index 206b65eecb..a7cff6ad18 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch @@ -1,53 +1,80 @@ --- a/net/minecraft/world/entity/animal/MushroomCow.java +++ b/net/minecraft/world/entity/animal/MushroomCow.java -@@ -42,6 +42,12 @@ +@@ -42,6 +42,13 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.storage.loot.BuiltInLootTables; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.entity.EntityDropItemEvent; +import org.bukkit.event.entity.EntityTransformEvent; +// CraftBukkit end public class MushroomCow extends Cow implements Shearable, VariantHolder { -@@ -120,6 +126,11 @@ +@@ -120,7 +127,19 @@ if (world instanceof ServerLevel) { ServerLevel worldserver = (ServerLevel) world; +- this.shear(worldserver, SoundSource.PLAYERS, itemstack); + // CraftBukkit start -+ if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { -+ return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List drops = this.generateDefaultDrops(worldserver, itemstack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); ++ if (event != null) { ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); ++ // Paper end - custom shear drops + } + // CraftBukkit end - this.shear(worldserver, SoundSource.PLAYERS, itemstack); ++ this.shear(worldserver, SoundSource.PLAYERS, itemstack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); -@@ -163,11 +174,19 @@ - world.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5D), this.getZ(), 1, 0.0D, 0.0D, 0.0D, 0.0D); - this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (worldserver1, itemstack1) -> { - for (int i = 0; i < itemstack1.getCount(); ++i) { -- worldserver1.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), itemstack1.copyWithCount(1))); -+ // CraftBukkit start -+ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), itemstack1.copyWithCount(1)); -+ EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); -+ Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ continue; -+ } -+ worldserver1.addFreshEntity(entityitem); -+ // CraftBukkit end - } + } +@@ -158,16 +177,32 @@ + @Override + public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears) { ++ // Paper start - custom shear drops ++ this.shear(world, shearedSoundCategory, shears, this.generateDefaultDrops(world, shears)); ++ } ++ ++ @Override ++ public java.util.List generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) { ++ final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (ignored, stack) -> { ++ for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1)); ++ }); ++ return drops; ++ } ++ ++ @Override ++ public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears, java.util.List drops) { ++ // Paper end - custom shear drops + world.playSound((Player) null, (Entity) this, SoundEvents.MOOSHROOM_SHEAR, shearedSoundCategory, 1.0F, 1.0F); + this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), (entitycow) -> { + world.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5D), this.getZ(), 1, 0.0D, 0.0D, 0.0D, 0.0D); +- this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (worldserver1, itemstack1) -> { +- for (int i = 0; i < itemstack1.getCount(); ++i) { +- worldserver1.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), itemstack1.copyWithCount(1))); +- } +- ++ // Paper start - custom shear drops; moved drop generation to separate method ++ drops.forEach(drop -> { ++ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), drop); ++ this.spawnAtLocation(world, entityitem); ++ // Paper end - custom shear drops; moved drop generation to separate method }); - }); + }, EntityTransformEvent.TransformReason.SHEARED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SHEARED); // CraftBukkit } @Override -@@ -263,7 +282,7 @@ +@@ -263,7 +298,7 @@ } static MushroomCow.Variant byName(String name) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Sheep.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Sheep.java.patch index e36530769d..ce52e0ef57 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Sheep.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Sheep.java.patch @@ -21,29 +21,61 @@ public class Sheep extends Animal implements Shearable { -@@ -160,6 +165,11 @@ +@@ -160,7 +165,19 @@ ServerLevel worldserver = (ServerLevel) world; if (this.readyForShearing()) { +- this.shear(worldserver, SoundSource.PLAYERS, itemstack); + // CraftBukkit start -+ if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { -+ return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List drops = this.generateDefaultDrops(worldserver, itemstack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); ++ if (event != null) { ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); ++ // Paper end - custom shear drops + } + // CraftBukkit end - this.shear(worldserver, SoundSource.PLAYERS, itemstack); ++ this.shear(worldserver, SoundSource.PLAYERS, itemstack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); -@@ -178,7 +188,9 @@ + return InteractionResult.SUCCESS_SERVER; +@@ -175,10 +192,29 @@ + + @Override + public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears) { ++ // Paper start - custom shear drops ++ this.shear(world, shearedSoundCategory, shears, this.generateDefaultDrops(world, shears)); ++ } ++ ++ @Override ++ public java.util.List generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) { ++ final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.SHEAR_SHEEP, shears, (ignored, stack) -> { ++ for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1)); ++ }); ++ return drops; ++ } ++ ++ @Override ++ public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears, java.util.List drops) { ++ final ServerLevel worldserver1 = world; // Named for lambda consumption ++ // Paper end - custom shear drops world.playSound((Player) null, (Entity) this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F); - this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_SHEEP, shears, (worldserver1, itemstack1) -> { - for (int i = 0; i < itemstack1.getCount(); ++i) { +- this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_SHEEP, shears, (worldserver1, itemstack1) -> { +- for (int i = 0; i < itemstack1.getCount(); ++i) { +- ItemEntity entityitem = this.spawnAtLocation(worldserver1, itemstack1.copyWithCount(1), 1.0F); ++ drops.forEach(itemstack1 -> { // Paper - custom drops - loop in generated default drops ++ if (true) { // Paper - custom drops - loop in generated default drops + this.forceDrops = true; // CraftBukkit - ItemEntity entityitem = this.spawnAtLocation(worldserver1, itemstack1.copyWithCount(1), 1.0F); ++ ItemEntity entityitem = this.spawnAtLocation(worldserver1, itemstack1, 1.0F); // Paper - custom drops - copy already done above + this.forceDrops = false; // CraftBukkit if (entityitem != null) { entityitem.setDeltaMovement(entityitem.getDeltaMovement().add((double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F), (double) (this.random.nextFloat() * 0.05F), (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F))); -@@ -276,6 +288,12 @@ +@@ -276,6 +312,12 @@ @Override public void ate() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch index 18394afab0..c07f86335e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch @@ -32,22 +32,52 @@ this.level().gameEvent((Holder) GameEvent.BLOCK_PLACE, blockposition, GameEvent.Context.of(this, iblockdata)); } } -@@ -153,6 +160,11 @@ +@@ -153,7 +160,19 @@ if (world instanceof ServerLevel) { ServerLevel worldserver = (ServerLevel) world; +- this.shear(worldserver, SoundSource.PLAYERS, itemstack); + // CraftBukkit start -+ if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { -+ return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List drops = this.generateDefaultDrops(worldserver, itemstack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); ++ if (event != null) { ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); ++ // Paper end - custom shear drops + } + // CraftBukkit end - this.shear(worldserver, SoundSource.PLAYERS, itemstack); ++ this.shear(worldserver, SoundSource.PLAYERS, itemstack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); -@@ -169,7 +181,9 @@ + } +@@ -166,10 +185,29 @@ + + @Override + public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears) { ++ // Paper start - custom shear drops ++ this.shear(world, shearedSoundCategory, shears, this.generateDefaultDrops(world, shears)); ++ } ++ ++ @Override ++ public java.util.List generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) { ++ final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.SHEAR_SNOW_GOLEM, shears, (ignored, stack) -> { ++ drops.add(stack); ++ }); ++ return drops; ++ } ++ ++ @Override ++ public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears, java.util.List drops) { ++ final ServerLevel worldserver1 = world; // Named for lambda consumption ++ // Paper end - custom shear drops world.playSound((Player) null, (Entity) this, SoundEvents.SNOW_GOLEM_SHEAR, shearedSoundCategory, 1.0F, 1.0F); this.setPumpkin(false); - this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_SNOW_GOLEM, shears, (worldserver1, itemstack1) -> { +- this.dropFromShearingLootTable(world, BuiltInLootTables.SHEAR_SNOW_GOLEM, shears, (worldserver1, itemstack1) -> { ++ drops.forEach(itemstack1 -> { // Paper - custom shear drops + this.forceDrops = true; // CraftBukkit this.spawnAtLocation(worldserver1, itemstack1, this.getEyeHeight()); + this.forceDrops = false; // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Bogged.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Bogged.java.patch index f023e46529..1cd68614bd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Bogged.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Bogged.java.patch @@ -1,24 +1,69 @@ --- a/net/minecraft/world/entity/monster/Bogged.java +++ b/net/minecraft/world/entity/monster/Bogged.java -@@ -79,6 +79,12 @@ +@@ -27,6 +27,7 @@ + import net.minecraft.world.level.Level; + import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.level.storage.loot.BuiltInLootTables; ++import org.bukkit.craftbukkit.event.CraftEventFactory; + + public class Bogged extends AbstractSkeleton implements Shearable { + +@@ -79,7 +80,20 @@ if (world instanceof ServerLevel) { ServerLevel worldserver = (ServerLevel) world; +- this.shear(worldserver, SoundSource.PLAYERS, itemstack); + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { -+ this.getEntityData().markDirty(Bogged.DATA_SHEARED); // CraftBukkit - mark dirty to restore sheared state to clients -+ return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List drops = this.generateDefaultDrops(worldserver, itemstack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); ++ if (event != null) { ++ if (event.isCancelled()) { ++ // this.getEntityData().markDirty(Bogged.DATA_SHEARED); // CraftBukkit - mark dirty to restore sheared state to clients // Paper - no longer needed ++ return InteractionResult.PASS; ++ } ++ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); ++ // Paper end - custom shear drops + } + // CraftBukkit end - this.shear(worldserver, SoundSource.PLAYERS, itemstack); ++ this.shear(worldserver, SoundSource.PLAYERS, itemstack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); -@@ -139,9 +145,11 @@ + } +@@ -133,15 +147,36 @@ + + @Override + public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears) { ++ // Paper start - custom shear drops ++ this.shear(world, shearedSoundCategory, shears, this.generateDefaultDrops(world, shears)); ++ } ++ ++ @Override ++ public java.util.List generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) { ++ final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.BOGGED_SHEAR, shears, (ignored, stack) -> { ++ drops.add(stack); ++ }); ++ return drops; ++ } ++ ++ @Override ++ public void shear(ServerLevel world, SoundSource shearedSoundCategory, ItemStack shears, java.util.List drops) { ++ // Paper end - custom shear drops + world.playSound((Player) null, (Entity) this, SoundEvents.BOGGED_SHEAR, shearedSoundCategory, 1.0F, 1.0F); +- this.spawnShearedMushrooms(world, shears); ++ this.spawnShearedMushrooms(world, shears, drops); // Paper - custom shear drops + this.setSheared(true); } - private void spawnShearedMushrooms(ServerLevel world, ItemStack shears) { +- private void spawnShearedMushrooms(ServerLevel world, ItemStack shears) { +- this.dropFromShearingLootTable(world, BuiltInLootTables.BOGGED_SHEAR, shears, (worldserver1, itemstack1) -> { ++ // Paper start - custom shear drops ++ private void spawnShearedMushrooms(ServerLevel world, ItemStack shears, java.util.List drops) { ++ final ServerLevel worldserver1 = world; // Named for lambda consumption + this.forceDrops = true; // Paper - Add missing forceDrop toggles - this.dropFromShearingLootTable(world, BuiltInLootTables.BOGGED_SHEAR, shears, (worldserver1, itemstack1) -> { ++ drops.forEach(itemstack1 -> { ++ // Paper end - custom shear drops this.spawnAtLocation(worldserver1, itemstack1, this.getBbHeight()); }); + this.forceDrops = false; // Paper - Add missing forceDrop toggles diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 1c87019f5e..ea4e1bf4bf 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -1689,20 +1689,20 @@ public class CraftEventFactory { player.level().getCraftServer().getPluginManager().callEvent(event); } - public static BlockShearEntityEvent callBlockShearEntityEvent(Entity animal, org.bukkit.block.Block dispenser, CraftItemStack is) { - BlockShearEntityEvent bse = new BlockShearEntityEvent(dispenser, animal.getBukkitEntity(), is); + public static BlockShearEntityEvent callBlockShearEntityEvent(Entity animal, org.bukkit.block.Block dispenser, CraftItemStack is, List drops) { // Paper - custom shear drops + BlockShearEntityEvent bse = new BlockShearEntityEvent(dispenser, animal.getBukkitEntity(), is, Lists.transform(drops, CraftItemStack::asCraftMirror)); // Paper - custom shear drops Bukkit.getPluginManager().callEvent(bse); return bse; } - public static boolean handlePlayerShearEntityEvent(net.minecraft.world.entity.player.Player player, Entity sheared, ItemStack shears, InteractionHand hand) { + public static PlayerShearEntityEvent handlePlayerShearEntityEvent(net.minecraft.world.entity.player.Player player, Entity sheared, ItemStack shears, InteractionHand hand, List drops) { // Paper - custom shear drops if (!(player instanceof ServerPlayer)) { - return true; + return null; // Paper - custom shear drops } - PlayerShearEntityEvent event = new PlayerShearEntityEvent((Player) player.getBukkitEntity(), sheared.getBukkitEntity(), CraftItemStack.asCraftMirror(shears), (hand == InteractionHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND)); + PlayerShearEntityEvent event = new PlayerShearEntityEvent((Player) player.getBukkitEntity(), sheared.getBukkitEntity(), CraftItemStack.asCraftMirror(shears), (hand == InteractionHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND), Lists.transform(drops, CraftItemStack::asCraftMirror)); // Paper - custom shear drops Bukkit.getPluginManager().callEvent(event); - return !event.isCancelled(); + return event; // Paper - custom shear drops } public static Cancellable handleStatisticsIncrease(net.minecraft.world.entity.player.Player entityHuman, net.minecraft.stats.Stat statistic, int current, int newValue) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 0b7bc5e836..ffd7ba14be 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -74,6 +74,16 @@ public final class CraftItemStack extends ItemStack { return stack; } + // Paper start + public static java.util.List asNMSCopy(java.util.List originals) { + final java.util.List items = new java.util.ArrayList<>(originals.size()); + for (final ItemStack original : originals) { + items.add(asNMSCopy(original)); + } + return items; + } + // Paper end + public static net.minecraft.world.item.ItemStack copyNMSStack(net.minecraft.world.item.ItemStack original, int amount) { net.minecraft.world.item.ItemStack stack = original.copy(); stack.setCount(amount); diff --git a/paper-server/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java b/paper-server/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java new file mode 100644 index 0000000000..5e6dfc93c8 --- /dev/null +++ b/paper-server/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java @@ -0,0 +1,35 @@ +package io.papermc.paper.entity; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfoList; +import io.github.classgraph.ScanResult; +import java.util.ArrayList; +import net.minecraft.world.entity.Shearable; +import org.bukkit.support.environment.Normal; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Normal +class ShearableDropsTest { + + static Iterable parameters() { + try (ScanResult scanResult = new ClassGraph() + .enableClassInfo() + .enableMethodInfo() + .whitelistPackages("net.minecraft") + .scan() + ) { + return new ArrayList<>(scanResult.getClassesImplementing(Shearable.class.getName())); + } + } + + @ParameterizedTest + @MethodSource("parameters") + void checkShearableDropOverrides(final ClassInfo classInfo) { + final MethodInfoList generateDefaultDrops = classInfo.getDeclaredMethodInfo("generateDefaultDrops"); + assertEquals(1, generateDefaultDrops.size(), classInfo.getName() + " doesn't implement Shearable#generateDefaultDrops"); + } +}