Add drops to shear events

This commit is contained in:
Jake Potrebic 2021-05-18 12:32:02 -07:00
parent 57894618cf
commit 66ed50064c
9 changed files with 246 additions and 54 deletions

View file

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

View file

@ -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<net.minecraft.world.item.ItemStack> 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<net.minecraft.world.item.ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+ return java.util.Collections.emptyList();
+ }
+ // Paper end - custom shear drops
}

View file

@ -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<MushroomCow.Variant> {
@@ -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<ItemStack> 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<ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+ final java.util.List<ItemStack> 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<ItemStack> 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) {

View file

@ -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<ItemStack> 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<ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+ final java.util.List<ItemStack> 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<ItemStack> 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() {

View file

@ -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<ItemStack> 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<ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+ final java.util.List<ItemStack> 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<ItemStack> 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

View file

@ -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<ItemStack> 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<ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+ final java.util.List<ItemStack> 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<ItemStack> 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<ItemStack> 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

View file

@ -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<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) {

View file

@ -74,6 +74,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);

View file

@ -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<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");
}
}