--- a/net/minecraft/world/item/BucketItem.java
+++ b/net/minecraft/world/item/BucketItem.java
@@ -30,6 +_,7 @@
 import net.minecraft.world.phys.HitResult;
 
 public class BucketItem extends Item implements DispensibleContainerItem {
+    private static @Nullable ItemStack itemLeftInHandAfterPlayerBucketEmptyEvent = null; // Paper - Fix PlayerBucketEmptyEvent result itemstack
     public final Fluid content;
 
     public BucketItem(Fluid content, Item.Properties properties) {
@@ -56,12 +_,22 @@
             } else if (this.content == Fluids.EMPTY) {
                 BlockState blockState = level.getBlockState(blockPos);
                 if (blockState.getBlock() instanceof BucketPickup bucketPickup) {
+                    // CraftBukkit start
+                    ItemStack dummyFluid = bucketPickup.pickupBlock(player, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState);
+                    if (dummyFluid.isEmpty()) return InteractionResult.FAIL; // Don't fire event if the bucket won't be filled.
+                    org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, blockPos, blockPos, playerPovHitResult.getDirection(), itemInHand, dummyFluid.getItem(), hand);
+
+                    if (event.isCancelled()) {
+                        player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541
+                        return InteractionResult.FAIL;
+                    }
+                    // CraftBukkit end
                     ItemStack itemStack = bucketPickup.pickupBlock(player, level, blockPos, blockState);
                     if (!itemStack.isEmpty()) {
                         player.awardStat(Stats.ITEM_USED.get(this));
                         bucketPickup.getPickupSound().ifPresent(soundEvent -> player.playSound(soundEvent, 1.0F, 1.0F));
                         level.gameEvent(player, GameEvent.FLUID_PICKUP, blockPos);
-                        ItemStack itemStack1 = ItemUtils.createFilledResult(itemInHand, player, itemStack);
+                        ItemStack itemStack1 = ItemUtils.createFilledResult(itemInHand, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit
                         if (!level.isClientSide) {
                             CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)player, itemStack);
                         }
@@ -74,7 +_,7 @@
             } else {
                 BlockState blockState = level.getBlockState(blockPos);
                 BlockPos blockPos2 = blockState.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? blockPos : blockPos1;
-                if (this.emptyContents(player, level, blockPos2, playerPovHitResult)) {
+                if (this.emptyContents(player, level, blockPos2, playerPovHitResult, playerPovHitResult.getDirection(), blockPos, itemInHand, hand)) { // CraftBukkit
                     this.checkExtraContent(player, level, itemInHand, blockPos2);
                     if (player instanceof ServerPlayer) {
                         CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, blockPos2, itemInHand);
@@ -91,6 +_,13 @@
     }
 
     public static ItemStack getEmptySuccessItem(ItemStack bucketStack, Player player) {
+        // Paper start - Fix PlayerBucketEmptyEvent result itemstack
+        if (itemLeftInHandAfterPlayerBucketEmptyEvent != null) {
+            ItemStack itemInHand = itemLeftInHandAfterPlayerBucketEmptyEvent;
+            itemLeftInHandAfterPlayerBucketEmptyEvent = null;
+            return itemInHand;
+        }
+        // Paper end - Fix PlayerBucketEmptyEvent result itemstack
         return !player.hasInfiniteMaterials() ? new ItemStack(Items.BUCKET) : bucketStack;
     }
 
@@ -100,6 +_,12 @@
 
     @Override
     public boolean emptyContents(@Nullable LivingEntity entity, Level level, BlockPos pos, @Nullable BlockHitResult hitResult) {
+        // CraftBukkit start
+        return this.emptyContents(entity, level, pos, hitResult, null, null, null, InteractionHand.MAIN_HAND);
+    }
+
+    public boolean emptyContents(@Nullable LivingEntity entity, Level level, BlockPos pos, @Nullable BlockHitResult hitResult, Direction direction, BlockPos clicked, ItemStack itemstack, InteractionHand hand) {
+        // CraftBukkit end
         if (!(this.content instanceof FlowingFluid flowingFluid)) {
             return false;
         } else {
@@ -110,8 +_,19 @@
                 || canBeReplaced
                 || block instanceof LiquidBlockContainer liquidBlockContainer
                     && liquidBlockContainer.canPlaceLiquid(entity, level, pos, blockState, this.content);
+
+            // CraftBukkit start
+            if (flag && entity instanceof Player player) {
+                org.bukkit.event.player.PlayerBucketEmptyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, clicked, direction, itemstack, hand);
+                if (event.isCancelled()) {
+                    player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541
+                    return false;
+                }
+                itemLeftInHandAfterPlayerBucketEmptyEvent = event.getItemStack() != null ? event.getItemStack().equals(org.bukkit.craftbukkit.inventory.CraftItemStack.asNewCraftStack(net.minecraft.world.item.Items.BUCKET)) ? null : org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; // Paper - Fix PlayerBucketEmptyEvent result itemstack
+            }
+            // CraftBukkit end
             if (!flag) {
-                return hitResult != null && this.emptyContents(entity, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null);
+                return hitResult != null && this.emptyContents(entity, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null, direction, clicked, itemstack, hand); // CraftBukkit
             } else if (level.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) {
                 int x = pos.getX();
                 int y = pos.getY();
