From 3ee076a766a545db1aab7e63076f9d3f8b409619 Mon Sep 17 00:00:00 2001 From: Doc Date: Sun, 12 Jan 2025 01:05:20 -0300 Subject: [PATCH 1/9] Add EntityHarvestBlockEvent --- .../event/entity/EntityHarvestBlockEvent.java | 76 +++++++++++++++++++ .../event/player/PlayerHarvestBlockEvent.java | 3 + 2 files changed, 79 insertions(+) create mode 100644 paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java new file mode 100644 index 000000000000..a00770244f3f --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java @@ -0,0 +1,76 @@ +package org.bukkit.event.entity; + +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import java.util.List; + +/** + * This event is called whenever an entity harvests a block. + *
+ * For the player case please use {@link org.bukkit.event.player.PlayerHarvestBlockEvent} + *
+ * A 'harvest' is when a block drops an item (usually some sort of crop) and + * changes state, but is not broken in order to drop the item. + *
+ * This event is not called for when a block is broken, to handle that, listen + * for {@link org.bukkit.event.block.BlockBreakEvent} and + * {@link org.bukkit.event.block.BlockDropItemEvent}. + */ +public class EntityHarvestBlockEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final Block harvestedBlock; + private final List itemsHarvested; + + public EntityHarvestBlockEvent(@NotNull Entity entity, @NotNull Block harvestedBlock, @NotNull List itemsHarvested) { + super(entity); + this.harvestedBlock = harvestedBlock; + this.itemsHarvested = itemsHarvested; + } + + /** + * Gets the block that is being harvested. + * + * @return The block that is being harvested + */ + @NotNull + public Block getHarvestedBlock() { + return harvestedBlock; + } + + /** + * Gets a list of items that are being harvested from this block. + * + * @return A list of items that are being harvested from this block + */ + @NotNull + public List getItemsHarvested() { + return itemsHarvested; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java index a7b580033d6c..4ddba370724a 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java @@ -5,6 +5,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityHarvestBlockEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; @@ -13,6 +14,8 @@ /** * This event is called whenever a player harvests a block. *
+ * For the entity case please use {@link org.bukkit.event.entity.EntityHarvestBlockEvent} + *
* A 'harvest' is when a block drops an item (usually some sort of crop) and * changes state, but is not broken in order to drop the item. *
From be215496dcda031ec239a53592c162e7a674e5ea Mon Sep 17 00:00:00 2001 From: Pedro <3602279+Doc94@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:58:06 -0300 Subject: [PATCH 2/9] Docs updates for PlayerHarvestBlockEvent Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com> --- .../java/org/bukkit/event/player/PlayerHarvestBlockEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java index 4ddba370724a..6f71307b6d07 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java @@ -14,7 +14,7 @@ /** * This event is called whenever a player harvests a block. *
- * For the entity case please use {@link org.bukkit.event.entity.EntityHarvestBlockEvent} + * For cases involving entities, please use {@link EntityHarvestBlockEvent}. *
* A 'harvest' is when a block drops an item (usually some sort of crop) and * changes state, but is not broken in order to drop the item. From fad72c12f54102a5f56cefdae52e3c392bf4dd8a Mon Sep 17 00:00:00 2001 From: Doc Date: Sun, 12 Jan 2025 12:03:05 -0300 Subject: [PATCH 3/9] Move EntityHarvestBlockEvent to paper package and HandlerList change # Conflicts: # paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch # paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch # paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java --- .../paper}/event/entity/EntityHarvestBlockEvent.java | 9 +++++---- .../org/bukkit/event/player/PlayerHarvestBlockEvent.java | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) rename paper-api/src/main/java/{org/bukkit => io/papermc/paper}/event/entity/EntityHarvestBlockEvent.java (90%) diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java similarity index 90% rename from paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java rename to paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index a00770244f3f..52b686b9ff11 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -1,9 +1,10 @@ -package org.bukkit.event.entity; +package io.papermc.paper.event.entity; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -22,7 +23,7 @@ */ public class EntityHarvestBlockEvent extends EntityEvent implements Cancellable { - private static final HandlerList handlers = new HandlerList(); + private static final HandlerList HANDLER_LIST = new HandlerList(); private boolean cancel = false; private final Block harvestedBlock; private final List itemsHarvested; @@ -66,11 +67,11 @@ public void setCancelled(boolean cancel) { @NotNull @Override public HandlerList getHandlers() { - return handlers; + return HANDLER_LIST; } @NotNull public static HandlerList getHandlerList() { - return handlers; + return HANDLER_LIST; } } diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java index 6f71307b6d07..2a762e201099 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java @@ -5,7 +5,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; -import org.bukkit.event.entity.EntityHarvestBlockEvent; +import io.papermc.paper.event.entity.EntityHarvestBlockEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; From b3d0f50a79f32584af770784a3be5abdfb8f41ca Mon Sep 17 00:00:00 2001 From: Doc Date: Sun, 12 Jan 2025 12:05:17 -0300 Subject: [PATCH 4/9] [ci skip] Make EntityHarvestBlockEvent follow the annotation standard --- .../paper/event/entity/EntityHarvestBlockEvent.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index 52b686b9ff11..22c7a0f8353a 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -6,7 +6,8 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.entity.EntityEvent; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; import java.util.List; /** @@ -21,14 +22,17 @@ * for {@link org.bukkit.event.block.BlockBreakEvent} and * {@link org.bukkit.event.block.BlockDropItemEvent}. */ +@NullMarked public class EntityHarvestBlockEvent extends EntityEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); + private boolean cancel = false; private final Block harvestedBlock; private final List itemsHarvested; - public EntityHarvestBlockEvent(@NotNull Entity entity, @NotNull Block harvestedBlock, @NotNull List itemsHarvested) { + @ApiStatus.Internal + public EntityHarvestBlockEvent(final Entity entity, final Block harvestedBlock, final List itemsHarvested) { super(entity); this.harvestedBlock = harvestedBlock; this.itemsHarvested = itemsHarvested; @@ -39,7 +43,6 @@ public EntityHarvestBlockEvent(@NotNull Entity entity, @NotNull Block harvestedB * * @return The block that is being harvested */ - @NotNull public Block getHarvestedBlock() { return harvestedBlock; } @@ -49,7 +52,6 @@ public Block getHarvestedBlock() { * * @return A list of items that are being harvested from this block */ - @NotNull public List getItemsHarvested() { return itemsHarvested; } @@ -64,13 +66,11 @@ public void setCancelled(boolean cancel) { this.cancel = cancel; } - @NotNull @Override public HandlerList getHandlers() { return HANDLER_LIST; } - @NotNull public static HandlerList getHandlerList() { return HANDLER_LIST; } From d445daf33b166fd18301df44204ec9235144e9b1 Mon Sep 17 00:00:00 2001 From: Doc Date: Mon, 10 Feb 2025 22:14:41 -0300 Subject: [PATCH 5/9] [ci skip] improvement in docs for mutable list --- .../io/papermc/paper/event/entity/EntityHarvestBlockEvent.java | 2 +- .../java/org/bukkit/event/player/PlayerHarvestBlockEvent.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index 22c7a0f8353a..aa2bca5cdaa0 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -50,7 +50,7 @@ public Block getHarvestedBlock() { /** * Gets a list of items that are being harvested from this block. * - * @return A list of items that are being harvested from this block + * @return A mutable list of items that are being harvested from this block */ public List getItemsHarvested() { return itemsHarvested; diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java index 2a762e201099..ce25d2eaa4ce 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerHarvestBlockEvent.java @@ -70,7 +70,7 @@ public EquipmentSlot getHand() { /** * Gets a list of items that are being harvested from this block. * - * @return A list of items that are being harvested from this block + * @return A mutable list of items that are being harvested from this block */ @NotNull public List getItemsHarvested() { From 7602ba41f53906f1377d493d6c9ee3c37a26aaf4 Mon Sep 17 00:00:00 2001 From: Doc Date: Fri, 14 Feb 2025 20:41:55 -0300 Subject: [PATCH 6/9] [ci skip] mention Fox behaviour for EntityHarvestBlockEvent#getItemsHarvested() --- .../io/papermc/paper/event/entity/EntityHarvestBlockEvent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index aa2bca5cdaa0..3aff9b87c78f 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -51,6 +51,9 @@ public Block getHarvestedBlock() { * Gets a list of items that are being harvested from this block. * * @return A mutable list of items that are being harvested from this block + * @apiNote {@link org.bukkit.entity.Fox} has a behavior where, if it does not have an item + * in {@link org.bukkit.inventory.EquipmentSlot#HAND}, it will take one unit from the first + * item in its inventory stack and use it in the hand slot. */ public List getItemsHarvested() { return itemsHarvested; From 769ed16cc6928f60f0bc3fbb6c5acc288ab24d1b Mon Sep 17 00:00:00 2001 From: Tamion <70228790+notTamion@users.noreply.github.com> Date: Sat, 15 Feb 2025 09:40:45 +0100 Subject: [PATCH 7/9] clarify Fox behaivour --- .../papermc/paper/event/entity/EntityHarvestBlockEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index 3aff9b87c78f..8c823dade942 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -51,9 +51,9 @@ public Block getHarvestedBlock() { * Gets a list of items that are being harvested from this block. * * @return A mutable list of items that are being harvested from this block - * @apiNote {@link org.bukkit.entity.Fox} has a behavior where, if it does not have an item - * in {@link org.bukkit.inventory.EquipmentSlot#HAND}, it will take one unit from the first - * item in its inventory stack and use it in the hand slot. + * @apiNote {@link org.bukkit.entity.Fox} has a behavior where, if it does not have + * an item in its mouth ({@link org.bukkit.inventory.EquipmentSlot#HAND}), it will + * take one unit from the first ItemStack in this harvest and put it there. */ public List getItemsHarvested() { return itemsHarvested; From 058ff8fd3e6d0c2e27c298bc282f3567f9ffd975 Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 22 Nov 2025 10:10:28 -0300 Subject: [PATCH 8/9] Fix Conflicts mess git --- .../world/entity/animal/Fox.java.patch | 30 ++++++++++++++++++- .../world/level/block/CaveVines.java.patch | 14 +++++---- .../level/block/ComposterBlock.java.patch | 30 ++++++++++++++++++- .../craftbukkit/event/CraftEventFactory.java | 16 ++++++++-- 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Fox.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Fox.java.patch index f47760008099..4e3a5beb01b7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Fox.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Fox.java.patch @@ -146,11 +146,39 @@ ); } } -@@ -955,6 +_,7 @@ +@@ -955,15 +_,32 @@ private void pickSweetBerries(BlockState state) { int ageValue = state.getValue(SweetBerryBushBlock.AGE); state.setValue(SweetBerryBushBlock.AGE, 1); + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(Fox.this, this.blockPos, state.setValue(SweetBerryBushBlock.AGE, 1))) return; // CraftBukkit - call EntityChangeBlockEvent int i = 1 + Fox.this.level().random.nextInt(2) + (ageValue == 3 ? 1 : 0); ItemStack itemBySlot = Fox.this.getItemBySlot(EquipmentSlot.MAINHAND); ++ // Paper start - call HarvestBlockEvent for the item dropped ++ ItemStack itemHarvest = new ItemStack(Items.SWEET_BERRIES, i); ++ io.papermc.paper.event.entity.EntityHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityHarvestBlockEvent(Fox.this.level(), this.blockPos, Fox.this, java.util.Collections.singletonList(itemHarvest)); ++ java.util.ArrayList itemsHarvested = new java.util.ArrayList<>(event.getItemsHarvested()); ++ if (event.isCancelled()) { ++ return; ++ } if (itemBySlot.isEmpty()) { +- Fox.this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.SWEET_BERRIES)); ++ if (!itemsHarvested.isEmpty()) { ++ org.bukkit.inventory.ItemStack firstItemForSlot = itemsHarvested.getFirst().clone(); ++ if (firstItemForSlot.getAmount() == 1) { ++ itemsHarvested.removeFirst(); ++ } else { ++ itemsHarvested.set(0, firstItemForSlot.subtract()); ++ } ++ Fox.this.setItemSlot(EquipmentSlot.MAINHAND, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(firstItemForSlot.asOne())); ++ } ++ // Paper end + i--; + } + +- if (i > 0) { +- Block.popResource(Fox.this.level(), this.blockPos, new ItemStack(Items.SWEET_BERRIES, i)); ++ for (org.bukkit.inventory.ItemStack itemStack : itemsHarvested) { // Paper - handle harvest items ++ Block.popResource(Fox.this.level(), this.blockPos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack)); // Paper + } + + Fox.this.playSound(SoundEvents.SWEET_BERRY_BUSH_PICK_BERRIES, 1.0F, 1.0F); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch index deaefa83917c..3c108887c990 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch @@ -13,14 +13,14 @@ Block.dropFromBlockInteractLootTable( serverLevel, BuiltInLootTables.HARVEST_CAVE_VINE, -@@ -30,8 +_,25 @@ +@@ -30,8 +_,29 @@ level.getBlockEntity(pos), null, entity, - (serverLevel1, itemStack) -> Block.popResource(serverLevel1, pos, itemStack) + (serverLevel1, itemStack) -> drops.add(itemStack) // Paper - call player harvest block event - store drops from loottable ); -+ // Paper start - call player harvest block event ++ // Paper start - call harvest block event + if (entity instanceof net.minecraft.world.entity.player.Player player) { + org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent( + level, pos, player, net.minecraft.world.InteractionHand.MAIN_HAND, drops @@ -32,11 +32,15 @@ + Block.popResource(level, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack)); + } + } else { -+ for (net.minecraft.world.item.ItemStack itemStack : drops) { -+ Block.popResource(level, pos, itemStack); ++ io.papermc.paper.event.entity.EntityHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityHarvestBlockEvent(level, pos, entity, drops); ++ if (event.isCancelled()) { ++ return InteractionResult.SUCCESS; // We need to return a success either way, because making it PASS or FAIL will result in a bug where cancelling while harvesting w/ block in hand places block ++ } ++ for (org.bukkit.inventory.ItemStack itemStack : event.getItemsHarvested()) { ++ Block.popResource(level, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack)); + } + } -+ // Paper end - call player harvest block event ++ // Paper end - call harvest block event float f = Mth.randomBetween(serverLevel.random, 0.8F, 1.2F); serverLevel.playSound(null, pos, SoundEvents.CAVE_VINES_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, f); BlockState blockState = state.setValue(BERRIES, false); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch index 6be7c6727b2f..51a2a99f6502 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch @@ -33,7 +33,7 @@ stack.shrink(1); return blockState; } else { -@@ -286,6 +_,14 @@ +@@ -286,11 +_,40 @@ } public static BlockState extractProduce(Entity entity, BlockState state, Level level, BlockPos pos) { @@ -48,6 +48,34 @@ if (!level.isClientSide()) { Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1.01, 0.5).offsetRandom(level.random, 0.7F); ItemEntity itemEntity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), new ItemStack(Items.BONE_MEAL)); +- itemEntity.setDefaultPickUpDelay(); +- level.addFreshEntity(itemEntity); ++ // Paper start - call HarvestBlockEvent for the item dropped ++ // itemEntity.setDefaultPickUpDelay(); // used later ++ // level.addFreshEntity(itemEntity); // used later ++ java.util.List itemsHarvested; ++ if (entity instanceof Player player) { ++ org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent(level, pos, player, InteractionHand.MAIN_HAND, java.util.Collections.singletonList(itemEntity.getItem())); ++ itemsHarvested = event.getItemsHarvested(); ++ if (event.isCancelled()) { ++ return state; ++ } ++ } else { ++ io.papermc.paper.event.entity.EntityHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityHarvestBlockEvent(level, pos, entity, java.util.Collections.singletonList(itemEntity.getItem())); ++ itemsHarvested = event.getItemsHarvested(); ++ if (event.isCancelled()) { ++ return state; ++ } ++ } ++ for (org.bukkit.inventory.ItemStack itemStack : itemsHarvested) { ++ ItemEntity harvestItemEntity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack)); ++ harvestItemEntity.setDefaultPickUpDelay(); ++ level.addFreshEntity(harvestItemEntity); ++ } ++ // Paper end + } + + BlockState blockState = empty(entity, state, level, pos); @@ -305,14 +_,39 @@ return blockState; } 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 d5496e5fbb5f..210037e9c327 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 @@ -10,6 +10,7 @@ import io.papermc.paper.connection.HorriblePlayerLoginEventHack; import io.papermc.paper.connection.PlayerConnection; import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; +import io.papermc.paper.event.entity.EntityHarvestBlockEvent; import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent; import java.util.ArrayList; import java.util.Collections; @@ -319,10 +320,21 @@ public static EntityEnterLoveModeEvent callEntityEnterLoveModeEvent(net.minecraf return entityEnterLoveModeEvent; } + /** + * Entity Harvest Block Event + */ + public static EntityHarvestBlockEvent callEntityHarvestBlockEvent(Level world, BlockPos blockposition, net.minecraft.world.entity.Entity who, List itemsToHarvest) { + List bukkitItemsToHarvest = itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList()); + org.bukkit.entity.Entity entity = who.getBukkitEntity(); + EntityHarvestBlockEvent entityHarvestBlockEvent = new EntityHarvestBlockEvent(entity, CraftBlock.at(world, blockposition), bukkitItemsToHarvest); + entityHarvestBlockEvent.callEvent(); + return entityHarvestBlockEvent; + } + public static PlayerHarvestBlockEvent callPlayerHarvestBlockEvent(Level world, BlockPos pos, net.minecraft.world.entity.player.Player player, InteractionHand hand, List itemsToHarvest) { - List bukkitItemsToHarvest = new ArrayList<>(itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList())); + List bukkitItemsToHarvest = itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList()); PlayerHarvestBlockEvent playerHarvestBlockEvent = new PlayerHarvestBlockEvent((Player) player.getBukkitEntity(), CraftBlock.at(world, pos), CraftEquipmentSlot.getHand(hand), bukkitItemsToHarvest); - Bukkit.getPluginManager().callEvent(playerHarvestBlockEvent); + playerHarvestBlockEvent.callEvent(); return playerHarvestBlockEvent; } From ac398c1f2fdb8ad1c8512b3a7e8ab9bf403f6d46 Mon Sep 17 00:00:00 2001 From: Doc Date: Sat, 22 Nov 2025 10:18:55 -0300 Subject: [PATCH 9/9] Clone Items and make immutable --- .../event/entity/EntityHarvestBlockEvent.java | 23 ++++++++++++++----- .../craftbukkit/event/CraftEventFactory.java | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java index 8c823dade942..ed1aa386bccc 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityHarvestBlockEvent.java @@ -8,6 +8,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; +import java.util.Collections; import java.util.List; /** @@ -29,7 +30,7 @@ public class EntityHarvestBlockEvent extends EntityEvent implements Cancellable private boolean cancel = false; private final Block harvestedBlock; - private final List itemsHarvested; + private List itemsHarvested; @ApiStatus.Internal public EntityHarvestBlockEvent(final Entity entity, final Block harvestedBlock, final List itemsHarvested) { @@ -44,24 +45,34 @@ public EntityHarvestBlockEvent(final Entity entity, final Block harvestedBlock, * @return The block that is being harvested */ public Block getHarvestedBlock() { - return harvestedBlock; + return this.harvestedBlock; } /** - * Gets a list of items that are being harvested from this block. + * Gets an immutable list of items that are being harvested from this block. * - * @return A mutable list of items that are being harvested from this block + * @return An immutable list of items that are being harvested from this block * @apiNote {@link org.bukkit.entity.Fox} has a behavior where, if it does not have * an item in its mouth ({@link org.bukkit.inventory.EquipmentSlot#HAND}), it will * take one unit from the first ItemStack in this harvest and put it there. + * @apiNote This list contains copy of the items; you need to set the items to the list to change them. */ public List getItemsHarvested() { - return itemsHarvested; + return Collections.unmodifiableList(this.itemsHarvested); + } + + /** + * Sets the items that are being harvested from this block. + * + * @param itemsHarvested A list of items that are being harvested from this block + */ + public void setItemsHarvested(List itemsHarvested) { + this.itemsHarvested = itemsHarvested; } @Override public boolean isCancelled() { - return cancel; + return this.cancel; } @Override 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 210037e9c327..c3c2bd7ba45e 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 @@ -324,7 +324,7 @@ public static EntityEnterLoveModeEvent callEntityEnterLoveModeEvent(net.minecraf * Entity Harvest Block Event */ public static EntityHarvestBlockEvent callEntityHarvestBlockEvent(Level world, BlockPos blockposition, net.minecraft.world.entity.Entity who, List itemsToHarvest) { - List bukkitItemsToHarvest = itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList()); + List bukkitItemsToHarvest = itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).map(org.bukkit.inventory.ItemStack::clone).collect(Collectors.toList()); org.bukkit.entity.Entity entity = who.getBukkitEntity(); EntityHarvestBlockEvent entityHarvestBlockEvent = new EntityHarvestBlockEvent(entity, CraftBlock.at(world, blockposition), bukkitItemsToHarvest); entityHarvestBlockEvent.callEvent();