--- a/net/minecraft/world/item/BucketItem.java
+++ b/net/minecraft/world/item/BucketItem.java
@@ -4,7 +4,10 @@
 import net.minecraft.advancements.CriteriaTriggers;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
+import net.minecraft.core.Holder;
 import net.minecraft.core.particles.ParticleTypes;
+import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
+import net.minecraft.server.level.ServerLevel;
 import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.sounds.SoundEvent;
 import net.minecraft.sounds.SoundEvents;
@@ -13,6 +16,7 @@
 import net.minecraft.tags.FluidTags;
 import net.minecraft.world.InteractionHand;
 import net.minecraft.world.InteractionResultHolder;
+import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.level.ClipContext;
 import net.minecraft.world.level.Level;
@@ -27,8 +31,17 @@
 import net.minecraft.world.level.material.Fluids;
 import net.minecraft.world.phys.BlockHitResult;
 import net.minecraft.world.phys.HitResult;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.DummyGeneratorAccess;
+import org.bukkit.event.player.PlayerBucketEmptyEvent;
+import org.bukkit.event.player.PlayerBucketFillEvent;
+// CraftBukkit end
 
 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 fluid, Item.Properties settings) {
@@ -38,123 +51,181 @@
 
     @Override
     public InteractionResultHolder<ItemStack> use(Level world, Player user, InteractionHand hand) {
-        ItemStack itemStack = user.getItemInHand(hand);
-        BlockHitResult blockHitResult = getPlayerPOVHitResult(
-            world, user, this.content == Fluids.EMPTY ? ClipContext.Fluid.SOURCE_ONLY : ClipContext.Fluid.NONE
-        );
-        if (blockHitResult.getType() == HitResult.Type.MISS) {
-            return InteractionResultHolder.pass(itemStack);
-        } else if (blockHitResult.getType() != HitResult.Type.BLOCK) {
-            return InteractionResultHolder.pass(itemStack);
+        ItemStack itemstack = user.getItemInHand(hand);
+        BlockHitResult movingobjectpositionblock = getPlayerPOVHitResult(world, user, this.content == Fluids.EMPTY ? ClipContext.Fluid.SOURCE_ONLY : ClipContext.Fluid.NONE);
+
+        if (movingobjectpositionblock.getType() == HitResult.Type.MISS) {
+            return InteractionResultHolder.pass(itemstack);
+        } else if (movingobjectpositionblock.getType() != HitResult.Type.BLOCK) {
+            return InteractionResultHolder.pass(itemstack);
         } else {
-            BlockPos blockPos = blockHitResult.getBlockPos();
-            Direction direction = blockHitResult.getDirection();
-            BlockPos blockPos2 = blockPos.relative(direction);
-            if (!world.mayInteract(user, blockPos) || !user.mayUseItemAt(blockPos2, direction, itemStack)) {
-                return InteractionResultHolder.fail(itemStack);
-            } else if (this.content == Fluids.EMPTY) {
-                BlockState blockState = world.getBlockState(blockPos);
-                if (blockState.getBlock() instanceof BucketPickup bucketPickup) {
-                    ItemStack itemStack2 = bucketPickup.pickupBlock(user, world, blockPos, blockState);
-                    if (!itemStack2.isEmpty()) {
-                        user.awardStat(Stats.ITEM_USED.get(this));
-                        bucketPickup.getPickupSound().ifPresent(sound -> user.playSound(sound, 1.0F, 1.0F));
-                        world.gameEvent(user, GameEvent.FLUID_PICKUP, blockPos);
-                        ItemStack itemStack3 = ItemUtils.createFilledResult(itemStack, user, itemStack2);
-                        if (!world.isClientSide) {
-                            CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)user, itemStack2);
+            BlockPos blockposition = movingobjectpositionblock.getBlockPos();
+            Direction enumdirection = movingobjectpositionblock.getDirection();
+            BlockPos blockposition1 = blockposition.relative(enumdirection);
+
+            if (world.mayInteract(user, blockposition) && user.mayUseItemAt(blockposition1, enumdirection, itemstack)) {
+                BlockState iblockdata;
+                ItemStack itemstack1;
+
+                if (this.content == Fluids.EMPTY) {
+                    iblockdata = world.getBlockState(blockposition);
+                    Block block = iblockdata.getBlock();
+
+                    if (block instanceof BucketPickup) {
+                        BucketPickup ifluidsource = (BucketPickup) block;
+                        // CraftBukkit start
+                        ItemStack dummyFluid = ifluidsource.pickupBlock(user, DummyGeneratorAccess.INSTANCE, blockposition, iblockdata);
+                        if (dummyFluid.isEmpty()) return InteractionResultHolder.fail(itemstack); // Don't fire event if the bucket won't be filled.
+                        PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) world, user, blockposition, blockposition, movingobjectpositionblock.getDirection(), itemstack, dummyFluid.getItem(), hand);
+
+                        if (event.isCancelled()) {
+                            // ((ServerPlayer) user).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-5163 (see PlayerInteractManager) // Paper - Don't resend blocks
+                            ((ServerPlayer) user).getBukkitEntity().updateInventory(); // SPIGOT-4541
+                            return InteractionResultHolder.fail(itemstack);
                         }
+                        // CraftBukkit end
 
-                        return InteractionResultHolder.sidedSuccess(itemStack3, world.isClientSide());
-                    }
-                }
+                        itemstack1 = ifluidsource.pickupBlock(user, world, blockposition, iblockdata);
+                        if (!itemstack1.isEmpty()) {
+                            user.awardStat(Stats.ITEM_USED.get(this));
+                            ifluidsource.getPickupSound().ifPresent((soundeffect) -> {
+                                user.playSound(soundeffect, 1.0F, 1.0F);
+                            });
+                            world.gameEvent((Entity) user, (Holder) GameEvent.FLUID_PICKUP, blockposition);
+                            ItemStack itemstack2 = ItemUtils.createFilledResult(itemstack, user, CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit
 
-                return InteractionResultHolder.fail(itemStack);
-            } else {
-                BlockState blockState2 = world.getBlockState(blockPos);
-                BlockPos blockPos3 = blockState2.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? blockPos : blockPos2;
-                if (this.emptyContents(user, world, blockPos3, blockHitResult)) {
-                    this.checkExtraContent(user, world, itemStack, blockPos3);
-                    if (user instanceof ServerPlayer) {
-                        CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)user, blockPos3, itemStack);
+                            if (!world.isClientSide) {
+                                CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer) user, itemstack1);
+                            }
+
+                            return InteractionResultHolder.sidedSuccess(itemstack2, world.isClientSide());
+                        }
                     }
 
-                    user.awardStat(Stats.ITEM_USED.get(this));
-                    ItemStack itemStack4 = ItemUtils.createFilledResult(itemStack, user, getEmptySuccessItem(itemStack, user));
-                    return InteractionResultHolder.sidedSuccess(itemStack4, world.isClientSide());
+                    return InteractionResultHolder.fail(itemstack);
                 } else {
-                    return InteractionResultHolder.fail(itemStack);
+                    iblockdata = world.getBlockState(blockposition);
+                    BlockPos blockposition2 = iblockdata.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? blockposition : blockposition1;
+
+                    if (this.emptyContents(user, world, blockposition2, movingobjectpositionblock, movingobjectpositionblock.getDirection(), blockposition, itemstack, hand)) { // CraftBukkit
+                        this.checkExtraContent(user, world, itemstack, blockposition2);
+                        if (user instanceof ServerPlayer) {
+                            CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer) user, blockposition2, itemstack);
+                        }
+
+                        user.awardStat(Stats.ITEM_USED.get(this));
+                        itemstack1 = ItemUtils.createFilledResult(itemstack, user, BucketItem.getEmptySuccessItem(itemstack, user));
+                        return InteractionResultHolder.sidedSuccess(itemstack1, world.isClientSide());
+                    } else {
+                        return InteractionResultHolder.fail(itemstack);
+                    }
                 }
+            } else {
+                return InteractionResultHolder.fail(itemstack);
             }
         }
     }
 
     public static ItemStack getEmptySuccessItem(ItemStack stack, 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) : stack;
     }
 
     @Override
-    public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {
-    }
+    public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {}
 
     @Override
     public boolean emptyContents(@Nullable Player player, Level world, BlockPos pos, @Nullable BlockHitResult hitResult) {
-        if (!(this.content instanceof FlowingFluid flowingFluid)) {
+        // CraftBukkit start
+        return this.emptyContents(player, world, pos, hitResult, null, null, null, InteractionHand.MAIN_HAND);
+    }
+
+    public boolean emptyContents(Player entityhuman, Level world, BlockPos blockposition, @Nullable BlockHitResult movingobjectpositionblock, Direction enumdirection, BlockPos clicked, ItemStack itemstack, InteractionHand enumhand) {
+        // CraftBukkit end
+        Fluid fluidtype = this.content;
+
+        if (!(fluidtype instanceof FlowingFluid fluidtypeflowing)) {
             return false;
         } else {
+            BlockState iblockdata;
             Block block;
-            boolean bl;
-            BlockState blockState;
-            boolean var10000;
-            label82: {
-                blockState = world.getBlockState(pos);
-                block = blockState.getBlock();
-                bl = blockState.canBeReplaced(this.content);
-                label70:
-                if (!blockState.isAir() && !bl) {
-                    if (block instanceof LiquidBlockContainer liquidBlockContainer
-                        && liquidBlockContainer.canPlaceLiquid(player, world, pos, blockState, this.content)) {
+            boolean flag;
+            LiquidBlockContainer ifluidcontainer;
+            boolean flag1;
+            label70:
+            {
+                iblockdata = world.getBlockState(blockposition);
+                block = iblockdata.getBlock();
+                flag = iblockdata.canBeReplaced(this.content);
+                if (!iblockdata.isAir() && !flag) {
+                    label67:
+                    {
+                        if (block instanceof LiquidBlockContainer) {
+                            ifluidcontainer = (LiquidBlockContainer) block;
+                            if (ifluidcontainer.canPlaceLiquid(entityhuman, world, blockposition, iblockdata, this.content)) {
+                                break label67;
+                            }
+                        }
+
+                        flag1 = false;
                         break label70;
                     }
-
-                    var10000 = false;
-                    break label82;
                 }
 
-                var10000 = true;
+                flag1 = true;
             }
 
-            boolean bl2 = var10000;
-            if (!bl2) {
-                return hitResult != null && this.emptyContents(player, world, hitResult.getBlockPos().relative(hitResult.getDirection()), null);
+            boolean flag2 = flag1;
+
+            // CraftBukkit start
+            if (flag2 && entityhuman != null) {
+                PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent((ServerLevel) world, entityhuman, blockposition, clicked, enumdirection, itemstack, enumhand);
+                if (event.isCancelled()) {
+                    // ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-4238: needed when looking through entity // Paper - Don't resend blocks
+                    ((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541
+                    return false;
+                }
+                itemLeftInHandAfterPlayerBucketEmptyEvent = event.getItemStack() != null ? event.getItemStack().equals(CraftItemStack.asNewCraftStack(net.minecraft.world.item.Items.BUCKET)) ? null : CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; // Paper - Fix PlayerBucketEmptyEvent result itemstack
+            }
+            // CraftBukkit end
+            if (!flag2) {
+                return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit
             } else if (world.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) {
-                int i = pos.getX();
-                int j = pos.getY();
-                int k = pos.getZ();
-                world.playSound(
-                    player, pos, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F
-                );
+                int i = blockposition.getX();
+                int j = blockposition.getY();
+                int k = blockposition.getZ();
 
-                for (int l = 0; l < 8; l++) {
-                    world.addParticle(ParticleTypes.LARGE_SMOKE, (double)i + Math.random(), (double)j + Math.random(), (double)k + Math.random(), 0.0, 0.0, 0.0);
+                world.playSound(entityhuman, blockposition, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
+
+                for (int l = 0; l < 8; ++l) {
+                    world.addParticle(ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D);
                 }
 
                 return true;
             } else {
-                if (block instanceof LiquidBlockContainer liquidBlockContainer2 && this.content == Fluids.WATER) {
-                    liquidBlockContainer2.placeLiquid(world, pos, blockState, flowingFluid.getSource(false));
-                    this.playEmptySound(player, world, pos);
-                    return true;
+                if (block instanceof LiquidBlockContainer) {
+                    ifluidcontainer = (LiquidBlockContainer) block;
+                    if (this.content == Fluids.WATER) {
+                        ifluidcontainer.placeLiquid(world, blockposition, iblockdata, fluidtypeflowing.getSource(false));
+                        this.playEmptySound(entityhuman, world, blockposition);
+                        return true;
+                    }
                 }
 
-                if (!world.isClientSide && bl && !blockState.liquid()) {
-                    world.destroyBlock(pos, true);
+                if (!world.isClientSide && flag && !iblockdata.liquid()) {
+                    world.destroyBlock(blockposition, true);
                 }
 
-                if (!world.setBlock(pos, this.content.defaultFluidState().createLegacyBlock(), 11) && !blockState.getFluidState().isSource()) {
+                if (!world.setBlock(blockposition, this.content.defaultFluidState().createLegacyBlock(), 11) && !iblockdata.getFluidState().isSource()) {
                     return false;
                 } else {
-                    this.playEmptySound(player, world, pos);
+                    this.playEmptySound(entityhuman, world, blockposition);
                     return true;
                 }
             }
@@ -162,8 +233,9 @@
     }
 
     protected void playEmptySound(@Nullable Player player, LevelAccessor world, BlockPos pos) {
-        SoundEvent soundEvent = this.content.is(FluidTags.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA : SoundEvents.BUCKET_EMPTY;
-        world.playSound(player, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
-        world.gameEvent(player, GameEvent.FLUID_PLACE, pos);
+        SoundEvent soundeffect = this.content.is(FluidTags.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA : SoundEvents.BUCKET_EMPTY;
+
+        world.playSound(player, pos, soundeffect, SoundSource.BLOCKS, 1.0F, 1.0F);
+        world.gameEvent((Entity) player, (Holder) GameEvent.FLUID_PLACE, pos);
     }
 }
