diff --git a/patches/api/0419-Add-PlayerWashItemEvent.patch b/patches/api/0419-Add-PlayerWashItemEvent.patch new file mode 100644 index 0000000000..ea4d57ed6c --- /dev/null +++ b/patches/api/0419-Add-PlayerWashItemEvent.patch @@ -0,0 +1,85 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 30 Dec 2021 13:47:25 -0800 +Subject: [PATCH] Add PlayerWashItemEvent + + +diff --git a/src/main/java/io/papermc/paper/event/block/PlayerWashItemEvent.java b/src/main/java/io/papermc/paper/event/block/PlayerWashItemEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d1ec150d0ebc2f41ac7e458eca2f9798218a0c3b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/block/PlayerWashItemEvent.java +@@ -0,0 +1,73 @@ ++package io.papermc.paper.event.block; ++ ++import com.google.common.base.Preconditions; ++import org.bukkit.block.Block; ++import org.bukkit.block.BlockState; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.block.CauldronLevelChangeEvent; ++import org.bukkit.inventory.EquipmentSlot; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Objects; ++ ++/** ++ * Called when a player washes an item in a cauldron. ++ */ ++public class PlayerWashItemEvent extends CauldronLevelChangeEvent { ++ ++ private final ItemStack washedItem; ++ private final EquipmentSlot hand; ++ ++ @ApiStatus.Internal ++ public PlayerWashItemEvent(@NotNull Block block, @NotNull HumanEntity entity, @NotNull ChangeReason reason, @NotNull BlockState newBlock, @NotNull ItemStack washedItem, @NotNull EquipmentSlot hand) { ++ super(block, entity, reason, newBlock); ++ Preconditions.checkArgument(hand.isHand(), "Only valid equipment slots are the two hands"); ++ this.washedItem = washedItem; ++ this.hand = hand; ++ } ++ ++ /** ++ * Helper method for {@link #getEntity()} since this event ++ * is only called with a {@link HumanEntity}. ++ * ++ * @return the player who initiated the item wash ++ */ ++ public @NotNull HumanEntity getPlayer() { ++ return this.getEntity(); ++ } ++ ++ @Override ++ public @NotNull HumanEntity getEntity() { ++ return (HumanEntity) Objects.requireNonNull(super.getEntity()); ++ } ++ ++ /** ++ * Gets the washed item. Changes made to this item ++ * will be reflected in the player's inventory. ++ * ++ * @return the washed item ++ */ ++ public @NotNull ItemStack getWashedItem() { ++ return this.washedItem; ++ } ++ ++ /** ++ * Gets the item the player is washing. ++ * ++ * @return the item being washed ++ */ ++ public @NotNull ItemStack getUnwashedItem() { ++ return this.getPlayer().getInventory().getItem(this.hand); ++ } ++ ++ /** ++ * Get the player's hand that initiated the wash. ++ * ++ * @return the hand ++ */ ++ public @NotNull EquipmentSlot getHand() { ++ return this.hand; ++ } ++} diff --git a/patches/server/0969-Add-PlayerWashItemEvent.patch b/patches/server/0969-Add-PlayerWashItemEvent.patch new file mode 100644 index 0000000000..36a5909312 --- /dev/null +++ b/patches/server/0969-Add-PlayerWashItemEvent.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 30 Dec 2021 13:47:39 -0800 +Subject: [PATCH] Add PlayerWashItemEvent + + +diff --git a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java +index c9cd5fe3e942d1ce0133c17d6dd2747b70878d4e..fbd52dad91ac8f928f6526ddcd42d3b56745fd3f 100644 +--- a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java ++++ b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java +@@ -55,16 +55,16 @@ public interface CauldronInteraction { + return InteractionResult.PASS; + } else { + if (!world.isClientSide) { +- // CraftBukkit start +- if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) { +- return InteractionResult.SUCCESS; +- } +- // CraftBukkit end + ItemStack itemstack1 = new ItemStack(Blocks.SHULKER_BOX); + + if (itemstack.hasTag()) { + itemstack1.setTag(itemstack.getTag().copy()); + } ++ // CraftBukkit start // Paper - move to after itemstack creation ++ if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH, itemstack1, enumhand)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + + entityhuman.setItemInHand(enumhand, itemstack1); + entityhuman.awardStat(Stats.CLEAN_SHULKER_BOX); +@@ -79,15 +79,15 @@ public interface CauldronInteraction { + return InteractionResult.PASS; + } else { + if (!world.isClientSide) { +- // CraftBukkit start +- if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) { +- return InteractionResult.SUCCESS; +- } +- // CraftBukkit end + ItemStack itemstack1 = itemstack.copy(); + + itemstack1.setCount(1); + BannerBlockEntity.removeLastPattern(itemstack1); ++ // CraftBukkit start // Paper - move to after itemstack creation ++ if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH, itemstack1, enumhand)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + if (!entityhuman.getAbilities().instabuild) { + itemstack.shrink(1); + } +@@ -119,12 +119,19 @@ public interface CauldronInteraction { + return InteractionResult.PASS; + } else { + if (!world.isClientSide) { ++ // Paper start - make itemstack copy ++ ItemStack copy = itemstack.copy(); ++ idyeable.clearColor(copy); ++ // Paper end + // CraftBukkit start +- if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) { ++ if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH, copy, enumhand)) { // Paper + return InteractionResult.SUCCESS; + } + // CraftBukkit end +- idyeable.clearColor(itemstack); ++ // Paper start - move clear to before event ++ itemstack.setCount(0); ++ entityhuman.setItemInHand(enumhand, copy); ++ // Paper end + entityhuman.awardStat(Stats.CLEAN_ARMOR); + // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit + } +diff --git a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +index e11eced0bf15dfecaf64f5e1c28e973c38746095..6110c43950a1090d634607513d6ff9fa73703a66 100644 +--- a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +@@ -90,10 +90,15 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + } + + public static boolean lowerFillLevel(BlockState iblockdata, Level world, BlockPos blockposition, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { ++ // Paper start ++ return LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entity, reason, null, null); ++ } ++ public static boolean lowerFillLevel(BlockState iblockdata, Level world, BlockPos blockposition, Entity entity, CauldronLevelChangeEvent.ChangeReason reason, @javax.annotation.Nullable net.minecraft.world.item.ItemStack washedItem, @javax.annotation.Nullable net.minecraft.world.InteractionHand hand) { ++ // Paper end + int i = (Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) - 1; + BlockState iblockdata1 = i == 0 ? Blocks.CAULDRON.defaultBlockState() : (BlockState) iblockdata.setValue(LayeredCauldronBlock.LEVEL, i); + +- return LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata1, entity, reason); ++ return LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata1, entity, reason, true, washedItem, hand); // Paper + } + + // CraftBukkit start +@@ -104,18 +109,34 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + + public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable + // Paper end ++ // Paper start ++ return LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, newBlock, entity, reason, sendGameEvent, null, null); ++ } ++ public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent, @javax.annotation.Nullable net.minecraft.world.item.ItemStack washedItem, @javax.annotation.Nullable net.minecraft.world.InteractionHand hand) { ++ // Paper end + CraftBlockState newState = CraftBlockStates.getBlockState(world, blockposition); + newState.setData(newBlock); + +- CauldronLevelChangeEvent event = new CauldronLevelChangeEvent( ++ // Paper start ++ CauldronLevelChangeEvent event = null; ++ if (washedItem == null || hand == null) { ++ event = new CauldronLevelChangeEvent( ++ // Paper end + world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), + (entity == null) ? null : entity.getBukkitEntity(), reason, newState + ); ++ // Paper start ++ } else if (entity instanceof net.minecraft.world.entity.player.Player player) { ++ event = new io.papermc.paper.event.block.PlayerWashItemEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition), player.getBukkitEntity(), reason, newState, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(washedItem), ((hand == net.minecraft.world.InteractionHand.OFF_HAND) ? org.bukkit.inventory.EquipmentSlot.OFF_HAND : org.bukkit.inventory.EquipmentSlot.HAND)); ++ } ++ if (event != null) { ++ // Paper end + world.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + newState.update(true); ++ } // Paper + if (sendGameEvent) world.gameEvent(GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(newBlock)); // Paper + return true; + }