From afe0785bfe28edf438cba6548a6fface3a72dfaa Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 24 May 2021 11:51:06 +0100 Subject: [PATCH] Added PlayerPurchaseEvent for standalone Merchant GUIs (#5583) --- .../0244-added-PlayerTradeEvent.patch | 93 +++++++++++++------ ...chantCustom-emit-PlayerPurchaseEvent.patch | 59 ++++++++++++ 2 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 Spigot-Server-Patches/0743-Have-CraftMerchantCustom-emit-PlayerPurchaseEvent.patch diff --git a/Spigot-API-Patches/0244-added-PlayerTradeEvent.patch b/Spigot-API-Patches/0244-added-PlayerTradeEvent.patch index 5288a1120..e09b08816 100644 --- a/Spigot-API-Patches/0244-added-PlayerTradeEvent.patch +++ b/Spigot-API-Patches/0244-added-PlayerTradeEvent.patch @@ -1,78 +1,75 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 2 Jul 2020 16:10:10 -0700 -Subject: [PATCH] added PlayerTradeEvent +Subject: [PATCH] Added PlayerTradeEvent +[Amendment: Alexander ] +PlayerTradeEvent is used for player purchases from villagers and wandering +traders, but not custom merchants created via Bukkit.createMerchant(). During +discussions in Discord it was decided that it'd be better to add a new event +that PlayerTradeEvent inherits from than change getVillager()'s annotation to +@Nullable, especially since that'd also infringe on the implication of the +event being about villager trades. -diff --git a/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java -new file mode 100755 -index 0000000000000000000000000000000000000000..198e5464eae6b961c83148a57c18f91a4bb33cf6 +diff --git a/src/main/java/io/papermc/paper/event/player/PlayerPurchaseEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerPurchaseEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c5648055c5e815474bf1e564a5c192ff5c0624fb --- /dev/null -+++ b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java -@@ -0,0 +1,121 @@ ++++ b/src/main/java/io/papermc/paper/event/player/PlayerPurchaseEvent.java +@@ -0,0 +1,112 @@ +package io.papermc.paper.event.player; + -+import org.bukkit.entity.AbstractVillager; ++import java.util.Objects; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; + +/** -+ * Called when a player trades with a villager or wandering trader ++ * Called when a player trades with a standalone merchant GUI. + */ -+public class PlayerTradeEvent extends PlayerEvent implements Cancellable { -+ ++public class PlayerPurchaseEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + private boolean increaseTradeUses; + private boolean rewardExp; -+ private final AbstractVillager villager; + private MerchantRecipe trade; + -+ public PlayerTradeEvent(@NotNull Player player, @NotNull AbstractVillager villager, @NotNull MerchantRecipe trade, boolean rewardExp, boolean increaseTradeUses) { -+ super(player); -+ this.villager = villager; -+ this.trade = trade; ++ public PlayerPurchaseEvent(@NotNull Player player, ++ @NotNull MerchantRecipe trade, ++ boolean rewardExp, ++ boolean increaseTradeUses) { ++ super(Objects.requireNonNull(player, "Player cannot be null!")); ++ setTrade(trade); + this.rewardExp = rewardExp; + this.increaseTradeUses = increaseTradeUses; + } + + /** -+ * Gets the Villager or Wandering trader associated with this event -+ * @return the villager or wandering trader -+ */ -+ @NotNull -+ public AbstractVillager getVillager() { -+ return villager; -+ } -+ -+ /** + * Gets the associated trade with this event + * @return the trade + */ + @NotNull + public MerchantRecipe getTrade() { -+ return trade; ++ return this.trade; + } + + /** + * Sets the trade. This is then used to determine the next prices + * @param trade the trade to use + */ -+ public void setTrade(@Nullable MerchantRecipe trade) { -+ this.trade = trade; ++ public void setTrade(@NotNull MerchantRecipe trade) { ++ this.trade = Objects.requireNonNull(trade, "Trade cannot be null!"); + } + + /** + * @return will trade try to reward exp + */ + public boolean isRewardingExp() { -+ return rewardExp; ++ return this.rewardExp; + } + + /** @@ -87,7 +84,7 @@ index 0000000000000000000000000000000000000000..198e5464eae6b961c83148a57c18f91a + * @return whether or not the trade will count as a use of the trade + */ + public boolean willIncreaseTradeUses() { -+ return increaseTradeUses; ++ return this.increaseTradeUses; + } + + /** @@ -130,4 +127,40 @@ index 0000000000000000000000000000000000000000..198e5464eae6b961c83148a57c18f91a + public static HandlerList getHandlerList() { + return handlers; + } ++ ++} +diff --git a/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java +new file mode 100755 +index 0000000000000000000000000000000000000000..a41fc186746b87f76347dfcc1f80d0969398322b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java +@@ -0,0 +1,29 @@ ++package io.papermc.paper.event.player; ++ ++import org.bukkit.entity.AbstractVillager; ++import org.bukkit.entity.Player; ++import org.bukkit.inventory.MerchantRecipe; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Called when a player trades with a villager or wandering trader ++ */ ++public class PlayerTradeEvent extends PlayerPurchaseEvent { ++ ++ private final AbstractVillager villager; ++ ++ public PlayerTradeEvent(@NotNull Player player, @NotNull AbstractVillager villager, @NotNull MerchantRecipe trade, boolean rewardExp, boolean increaseTradeUses) { ++ super(player, trade, rewardExp, increaseTradeUses); ++ this.villager = villager; ++ } ++ ++ /** ++ * Gets the Villager or Wandering trader associated with this event ++ * @return the villager or wandering trader ++ */ ++ @NotNull ++ public AbstractVillager getVillager() { ++ return this.villager; ++ } ++ +} diff --git a/Spigot-Server-Patches/0743-Have-CraftMerchantCustom-emit-PlayerPurchaseEvent.patch b/Spigot-Server-Patches/0743-Have-CraftMerchantCustom-emit-PlayerPurchaseEvent.patch new file mode 100644 index 000000000..154ff25eb --- /dev/null +++ b/Spigot-Server-Patches/0743-Have-CraftMerchantCustom-emit-PlayerPurchaseEvent.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander +Date: Thu, 6 May 2021 13:01:25 +0100 +Subject: [PATCH] Have CraftMerchantCustom emit PlayerPurchaseEvent + + +diff --git a/src/main/java/net/minecraft/world/item/trading/IMerchant.java b/src/main/java/net/minecraft/world/item/trading/IMerchant.java +index 5644affeadc51ac3209ee9a087b3d2dd887e401c..7c54ca6af424b9d31f73187df9015f5df84762f4 100644 +--- a/src/main/java/net/minecraft/world/item/trading/IMerchant.java ++++ b/src/main/java/net/minecraft/world/item/trading/IMerchant.java +@@ -19,7 +19,7 @@ public interface IMerchant { + + MerchantRecipeList getOffers(); + +- void a(MerchantRecipe merchantrecipe); ++ void a(MerchantRecipe merchantrecipe); default void handlePurchase(MerchantRecipe merchantRecipe) { a(merchantRecipe); } // Paper - OBFHELPER + + void k(ItemStack itemstack); + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java +index 206c133ebc6c44038585236b0628543b8bed278c..a7230776b8e85d5d1873ab2dc4e10c5bf78889bb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java +@@ -81,6 +81,35 @@ public class CraftMerchantCustom extends CraftMerchant { + + @Override + public void a(MerchantRecipe merchantrecipe) { ++ // Paper start ++ /** Based on {@link net.minecraft.world.entity.npc.EntityVillagerAbstract#b(MerchantRecipe)} */ ++ if (getTrader() instanceof net.minecraft.server.level.EntityPlayer) { ++ final net.minecraft.server.level.EntityPlayer trader = (net.minecraft.server.level.EntityPlayer) getTrader(); ++ final io.papermc.paper.event.player.PlayerPurchaseEvent event = new io.papermc.paper.event.player.PlayerPurchaseEvent( ++ trader.getBukkitEntity(), ++ merchantrecipe.asBukkit(), ++ false, // reward xp? ++ true); // should increase uses? ++ event.callEvent(); ++ if (event.isCancelled()) { ++ return; ++ } ++ final org.bukkit.inventory.MerchantRecipe eventTrade = event.getTrade(); ++ if (event.willIncreaseTradeUses()) { ++ eventTrade.setUses(eventTrade.getUses() + 1); ++ } ++ if (event.isRewardingExp() && eventTrade.hasExperienceReward()) { ++ /** Based on {@link net.minecraft.world.entity.npc.EntityVillagerTrader#b(MerchantRecipe)} */ ++ final int xp = 3 + net.minecraft.world.entity.Entity.SHARED_RANDOM.nextInt(4); ++ final World world = trader.getWorld(); ++ world.addEntity(new net.minecraft.world.entity.EntityExperienceOrb( ++ world, trader.locX(), trader.locY() + 0.5d, trader.locZ(), xp, ++ org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, trader, null)); ++ } ++ return; ++ } ++ // Paper end ++ + // increase recipe's uses + merchantrecipe.increaseUses(); + }