diff options
Diffstat (limited to 'patches/server/0922-Add-drops-to-shear-events.patch')
-rw-r--r-- | patches/server/0922-Add-drops-to-shear-events.patch | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/patches/server/0922-Add-drops-to-shear-events.patch b/patches/server/0922-Add-drops-to-shear-events.patch new file mode 100644 index 0000000000..354ac06ddc --- /dev/null +++ b/patches/server/0922-Add-drops-to-shear-events.patch @@ -0,0 +1,410 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 18 May 2021 12:32:02 -0700 +Subject: [PATCH] Add drops to shear events + + +diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +index f32f8d5cb22feb885a53d3b56c04ad4219d2bafa..44b79a7c2f8b95a484d1999fa2167ce588f7985b 100644 +--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +@@ -103,11 +103,14 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior { + if (entityliving instanceof Shearable ishearable) { + if (ishearable.readyForShearing()) { + // 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()); ++ if (event.isCancelled()) { ++ // Paper end - Add drops to shear events + continue; + } + // CraftBukkit end +- ishearable.shear(SoundSource.BLOCKS); ++ ishearable.shear(SoundSource.BLOCKS, CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events + worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition); + return true; + } +diff --git a/src/main/java/net/minecraft/world/entity/Shearable.java b/src/main/java/net/minecraft/world/entity/Shearable.java +index 5e8cc5cfac8888628c6d513148f41be09ca65a2c..2ee48ac3b665db2b02bcb1a30ec972d43a3725b0 100644 +--- a/src/main/java/net/minecraft/world/entity/Shearable.java ++++ b/src/main/java/net/minecraft/world/entity/Shearable.java +@@ -3,7 +3,13 @@ package net.minecraft.world.entity; + import net.minecraft.sounds.SoundSource; + + public interface Shearable { ++ default void shear(SoundSource soundCategory, java.util.List<net.minecraft.world.item.ItemStack> drops) { this.shear(soundCategory); } // Paper - Add drops to shear events + void shear(SoundSource shearedSoundCategory); + + boolean readyForShearing(); ++ // Paper start - custom shear drops; ensure all implementing entities override this ++ default java.util.List<net.minecraft.world.item.ItemStack> generateDefaultDrops() { ++ return java.util.Collections.emptyList(); ++ } ++ // Paper end - custom shear drops + } +diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java +index aa125e3043b120935aaa015803f065f99eb8d050..0c21959f57ae88fcd0a4d6dc911c1ce347c96528 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java +@@ -123,11 +123,18 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo + return InteractionResult.sidedSuccess(this.level().isClientSide); + } else if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { + // CraftBukkit start +- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { +- return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List<ItemStack> drops = this.generateDefaultDrops(); ++ 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(SoundSource.PLAYERS); ++ this.shear(SoundSource.PLAYERS, drops); // Paper - custom shear drops + this.gameEvent(GameEvent.SHEAR, player); + if (!this.level().isClientSide) { + itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); +@@ -164,6 +171,22 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo + + @Override + public void shear(SoundSource shearedSoundCategory) { ++ // Paper start - custom shear drops ++ this.shear(shearedSoundCategory, this.generateDefaultDrops()); ++ } ++ ++ @Override ++ public java.util.List<ItemStack> generateDefaultDrops() { ++ java.util.List<ItemStack> dropEntities = new java.util.ArrayList<>(5); ++ for (int i = 0; i < 5; ++i) { ++ dropEntities.add(new ItemStack(this.getVariant().getBlockState().getBlock())); ++ } ++ return dropEntities; ++ } ++ ++ @Override ++ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) { // If drops is null, need to generate drops ++ // Paper end - custom shear drops + this.level().playSound((Player) null, (Entity) this, SoundEvents.MOOSHROOM_SHEAR, shearedSoundCategory, 1.0F, 1.0F); + if (!this.level().isClientSide()) { + Cow entitycow = (Cow) EntityType.COW.create(this.level()); +@@ -193,17 +216,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo + this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - from above and add Bukkit remove cause + // CraftBukkit end + +- for (int i = 0; i < 5; ++i) { +- // CraftBukkit start +- ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), new ItemStack(this.getVariant().blockState.getBlock())); +- EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); +- Bukkit.getPluginManager().callEvent(event); +- if (event.isCancelled()) { +- continue; +- } +- this.level().addFreshEntity(entityitem); +- // CraftBukkit end ++ // Paper start - custom shear drops; moved drop generation to separate method ++ for (final ItemStack drop : drops) { ++ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), drop); ++ this.spawnAtLocation(entityitem); + } ++ // Paper end - custom shear drops + } + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java +index 6b26af41423110bd982eb8c0eea0cba5e9fdc633..38ac2759894660be1ee7ba59b0bd1270158e9232 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java +@@ -256,11 +256,18 @@ public class Sheep extends Animal implements Shearable { + if (itemstack.is(Items.SHEARS)) { + if (!this.level().isClientSide && this.readyForShearing()) { + // CraftBukkit start +- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { +- return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List<ItemStack> drops = this.generateDefaultDrops(); ++ 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(SoundSource.PLAYERS); ++ this.shear(SoundSource.PLAYERS, drops); // Paper + this.gameEvent(GameEvent.SHEAR, player); + itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); + return InteractionResult.SUCCESS; +@@ -274,13 +281,30 @@ public class Sheep extends Animal implements Shearable { + + @Override + public void shear(SoundSource shearedSoundCategory) { ++ // Paper start - custom shear drops ++ this.shear(shearedSoundCategory, this.generateDefaultDrops()); ++ } ++ ++ @Override ++ public java.util.List<ItemStack> generateDefaultDrops() { ++ int count = 1 + this.random.nextInt(3); ++ java.util.List<ItemStack> dropEntities = new java.util.ArrayList<>(count); ++ for (int j = 0; j < count; ++j) { ++ dropEntities.add(new ItemStack(Sheep.ITEM_BY_DYE.get(this.getColor()))); ++ } ++ return dropEntities; ++ } ++ ++ @Override ++ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) { ++ // Paper end - custom shear drops + this.level().playSound((Player) null, (Entity) this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F); + this.setSheared(true); + int i = 1 + this.random.nextInt(3); + +- for (int j = 0; j < i; ++j) { ++ for (final ItemStack drop : drops) { // Paper - custom shear drops (moved drop generation to separate method) + this.forceDrops = true; // CraftBukkit +- ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.getColor()), 1); ++ ItemEntity entityitem = this.spawnAtLocation(drop, 1); // Paper - custom shear drops + this.forceDrops = false; // CraftBukkit + + if (entityitem != null) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java +index 2de1a2f666da9db1832907e1651dbff948e37252..5c2ed3c39c8eb850f3be1e2ea5b5a7ea266e16d1 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java ++++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java +@@ -146,11 +146,18 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + + if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { + // CraftBukkit start +- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { +- return InteractionResult.PASS; ++ // Paper start - custom shear drops ++ java.util.List<ItemStack> drops = this.generateDefaultDrops(); ++ 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(SoundSource.PLAYERS); ++ this.shear(SoundSource.PLAYERS, drops); // Paper + this.gameEvent(GameEvent.SHEAR, player); + if (!this.level().isClientSide) { + itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); +@@ -164,12 +171,28 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + + @Override + public void shear(SoundSource shearedSoundCategory) { ++ // Paper start - custom shear drops ++ this.shear(shearedSoundCategory, this.generateDefaultDrops()); ++ } ++ ++ @Override ++ public java.util.List<ItemStack> generateDefaultDrops() { ++ return java.util.Collections.singletonList(new ItemStack(Items.CARVED_PUMPKIN)); ++ } ++ ++ @Override ++ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) { ++ // Paper end - custom shear drops + this.level().playSound((Player) null, (Entity) this, SoundEvents.SNOW_GOLEM_SHEAR, shearedSoundCategory, 1.0F, 1.0F); + if (!this.level().isClientSide()) { + this.setPumpkin(false); +- this.forceDrops = true; // CraftBukkit +- this.spawnAtLocation(new ItemStack(Items.CARVED_PUMPKIN), this.getEyeHeight()); +- this.forceDrops = false; // CraftBukkit ++ // Paper start - custom shear drops (moved drop generation to separate method) ++ for (final ItemStack drop : drops) { ++ this.forceDrops = true; ++ this.spawnAtLocation(drop, this.getEyeHeight()); ++ this.forceDrops = false; ++ } ++ // Paper end - custom shear drops + } + + } +diff --git a/src/main/java/net/minecraft/world/entity/monster/Bogged.java b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +index dc6230458e09f7555eee7f6a567ff60ad454666b..9d50b9ac8084f3db1844cc7ad1ce9153614ff9d9 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +@@ -80,12 +80,19 @@ public class Bogged extends AbstractSkeleton implements Shearable { + + if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { + // 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 - expose drops in event ++ java.util.List<net.minecraft.world.item.ItemStack> drops = generateDefaultDrops(); ++ final org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); ++ if (event != null) { ++ if (event.isCancelled()) { ++ if (player instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) this.resendPossiblyDesyncedDataValues(java.util.List.of(Bogged.DATA_SHEARED), serverPlayer); ++ return InteractionResult.PASS; ++ } ++ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); ++ // Paper end - expose drops in event + } + // CraftBukkit end +- this.shear(SoundSource.PLAYERS); ++ this.shear(SoundSource.PLAYERS, drops); // Paper - expose drops in event + this.gameEvent(GameEvent.SHEAR, player); + if (!this.level().isClientSide) { + itemstack.hurtAndBreak(1, player, getSlotForHand(hand)); +@@ -140,12 +147,31 @@ public class Bogged extends AbstractSkeleton implements Shearable { + + @Override + public void shear(SoundSource shearedSoundCategory) { ++ // Paper start - shear drop API ++ this.shear(shearedSoundCategory, generateDefaultDrops()); ++ } ++ ++ @Override ++ public void shear(SoundSource shearedSoundCategory, java.util.List<net.minecraft.world.item.ItemStack> drops) { ++ // Paper end - shear drop API + this.level().playSound((Player) null, (Entity) this, SoundEvents.BOGGED_SHEAR, shearedSoundCategory, 1.0F, 1.0F); +- this.spawnShearedMushrooms(); ++ this.spawnDrops(drops); // Paper - shear drop API + this.setSheared(true); + } + + private void spawnShearedMushrooms() { ++ // Paper start - shear drops API ++ this.spawnDrops(generateDefaultDrops()); // Only here for people calling spawnSheardMushrooms. Not used otherwise. ++ } ++ private void spawnDrops(java.util.List<net.minecraft.world.item.ItemStack> drops) { ++ drops.forEach(stack -> { ++ this.forceDrops = true; ++ this.spawnAtLocation(stack, this.getBbHeight()); ++ this.forceDrops = false; ++ }); ++ } ++ private void generateShearedMushrooms(java.util.function.Consumer<ItemStack> stackConsumer) { ++ // Paper end - shear drops API + Level world = this.level(); + + if (world instanceof ServerLevel worldserver) { +@@ -156,12 +182,21 @@ public class Bogged extends AbstractSkeleton implements Shearable { + while (objectlistiterator.hasNext()) { + ItemStack itemstack = (ItemStack) objectlistiterator.next(); + +- this.spawnAtLocation(itemstack, this.getBbHeight()); ++ stackConsumer.accept(itemstack); // Paper + } + } + + } + ++ // Paper start - shear drops API ++ @Override ++ public java.util.List<ItemStack> generateDefaultDrops() { ++ final java.util.List<ItemStack> drops = new java.util.ArrayList<>(); ++ this.generateShearedMushrooms(drops::add); ++ return drops; ++ } ++ // Paper end - shear drops API ++ + @Override + public boolean readyForShearing() { + return !this.isSheared() && this.isAlive(); +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 1d958323eccd4cf5e369e99e32d2f1dec0959591..56869e0d001d984c6b73f5a92c60508e2366eb61 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1673,20 +1673,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<ItemStack> 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<ItemStack> 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/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +index 631076ebbf90d0d597ee2fc49f036d1fba34ab51..206b5fcb259e0f40f35ae42fe8ebcc986c23ee52 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +@@ -68,6 +68,16 @@ public final class CraftItemStack extends ItemStack { + return stack; + } + ++ // Paper start ++ public static java.util.List<net.minecraft.world.item.ItemStack> asNMSCopy(java.util.List<? extends ItemStack> originals) { ++ final java.util.List<net.minecraft.world.item.ItemStack> 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/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java b/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e612515f7709dbe5d1fa5751337cdc34fce10a98 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java +@@ -0,0 +1,34 @@ ++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.AbstractTestingBase; ++import org.junit.jupiter.params.ParameterizedTest; ++import org.junit.jupiter.params.provider.MethodSource; ++ ++import static org.junit.jupiter.api.Assertions.assertEquals; ++ ++class ShearableDropsTest extends AbstractTestingBase { ++ ++ static Iterable<ClassInfo> 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"); ++ } ++} |