From d0da07044fddfd207080e124e04f821b3defdc88 Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:00:46 -0400 Subject: [PATCH 1/6] Add Copper Golem pre-sort event --- .../ItemTransportingEntitySortEvent.java | 97 +++++++++++++++++++ .../craftbukkit/event/CraftEventFactory.java | 8 ++ 2 files changed, 105 insertions(+) create mode 100644 paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java new file mode 100644 index 000000000000..6ce5b7880226 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java @@ -0,0 +1,97 @@ +package io.papermc.paper.event.entity; + +import org.bukkit.block.Container; +import org.bukkit.entity.CopperGolem; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when an item-transporting entity (typically a {@link CopperGolem}, + * although other entities may be possible through non-API means) + * is inspecting a destination {@link Container} to decide if the item it is holding + * belongs in said container. This gives plugin developers an opportunity to + * override the {@link CopperGolem}'s sorting behavior. + */ +@NullMarked +public class ItemTransportingEntitySortEvent extends EntityEvent { + protected static final HandlerList HANDLER_LIST = new HandlerList(); + + private final ItemStack itemStack; + private final Inventory containerInventory; + private boolean overrideDefaultBehaviour = false; + private boolean itemBelongs = true; + + @ApiStatus.Internal + public ItemTransportingEntitySortEvent( + final Entity entity, + final ItemStack itemStack, + final Inventory containerInventory + ) { + super(entity); + this.containerInventory = containerInventory; + this.itemStack = itemStack; + } + + /** + * Sets if the item belongs in the container. + * + * @param belongs Whether the item belongs or not + */ + public void setItemBelongs(boolean belongs) { + this.overrideDefaultBehaviour = true; + this.itemBelongs = belongs; + } + + /** + * Gets if the held item stack belongs in the container. + * + * @return Whether the item stack belongs. + */ + public boolean itemBelongs() { + return this.itemBelongs; + } + + /** + * Whether this event will override the default behavior of the entity. + * For example, if {@link ItemTransportingEntitySortEvent#setItemBelongs(boolean)} is not called, the entity + * will use the default behavior of adding to empty chests and chests containing a matching item stack. + * Once the function is called, the entity will defer to the plugin's decision. + * + * @return Whether the default behavior is being overridden. + */ + public boolean isOverridingDefaultBehaviour() { + return this.overrideDefaultBehaviour; + } + + /** + * Gets the container the entity is comparing to the held item. + * + * @return The potential destination container + */ + public Inventory getContainerInventory() { + return this.containerInventory; + } + + /** + * Gets the held item stack being compared against the container. + * + * @return The held item stack + */ + public ItemStack getHeldItemStack() { + return this.itemStack; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} 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 6d485bb4d916..0973a06c8f21 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 @@ -7,11 +7,13 @@ import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Either; import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.block.TileStateInventoryHolder; import io.papermc.paper.block.bed.BedEnterProblem; import io.papermc.paper.connection.HorriblePlayerLoginEventHack; import io.papermc.paper.connection.PlayerConnection; import io.papermc.paper.event.block.BlockLockCheckEvent; import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; +import io.papermc.paper.event.entity.ItemTransportingEntitySortEvent; import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent; import io.papermc.paper.event.player.PlayerBedFailEnterEvent; import io.papermc.paper.event.player.PlayerToggleEntityAgeLockEvent; @@ -104,6 +106,8 @@ import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.craftbukkit.entity.CraftSpellcaster; +import org.bukkit.craftbukkit.inventory.CraftContainer; +import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.inventory.CraftItemType; @@ -2417,4 +2421,8 @@ public static boolean callPlayerToggleEntityAgeLockEvent(net.minecraft.world.ent } return true; } + + public static ItemTransportingEntitySortEvent createItemTransportingEntitySortEvent(PathfinderMob mob, Container container) { + return new ItemTransportingEntitySortEvent(mob.getBukkitEntity(), mob.getMainHandItem().asBukkitCopy(), new CraftInventory(container)); + } } From fa58ac0add8164e2411153a5887b72b3c5eceb06 Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:10:09 -0400 Subject: [PATCH 2/6] Add patch to use new pre-sort event --- .../TransportItemsBetweenContainers.java.patch | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch index d5c3556c7c9a..5e718d26c450 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -12,3 +12,18 @@ return isValidTarget ? transportItemTarget : null; } } +@@ -505,7 +_,13 @@ + } + + private static boolean matchesLeavingItemsRequirement(final PathfinderMob body, final Container container) { +- return container.isEmpty() || hasItemMatchingHandItem(body, container); ++ io.papermc.paper.event.entity.ItemTransportingEntitySortEvent sortEvent = org.bukkit.craftbukkit.event.CraftEventFactory.createItemTransportingEntitySortEvent(body, container); ++ sortEvent.callEvent(); ++ if (sortEvent.isOverridingDefaultBehaviour()) { ++ return sortEvent.itemBelongs(); ++ } else { ++ return container.isEmpty() || hasItemMatchingHandItem(body, container); ++ } + } + + private static boolean hasItemMatchingHandItem(final PathfinderMob body, final Container container) { From 9e1f2b1d9ff495f494af150d9d54633330be1430 Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:41:13 -0400 Subject: [PATCH 3/6] Add comment to pre-sort patch --- .../ai/behavior/TransportItemsBetweenContainers.java.patch | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch index 5e718d26c450..ed86fb87b1f4 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -12,11 +12,12 @@ return isValidTarget ? transportItemTarget : null; } } -@@ -505,7 +_,13 @@ +@@ -505,7 +_,15 @@ } private static boolean matchesLeavingItemsRequirement(final PathfinderMob body, final Container container) { - return container.isEmpty() || hasItemMatchingHandItem(body, container); ++ // Paper start - Add event for container assessment + io.papermc.paper.event.entity.ItemTransportingEntitySortEvent sortEvent = org.bukkit.craftbukkit.event.CraftEventFactory.createItemTransportingEntitySortEvent(body, container); + sortEvent.callEvent(); + if (sortEvent.isOverridingDefaultBehaviour()) { @@ -24,6 +25,7 @@ + } else { + return container.isEmpty() || hasItemMatchingHandItem(body, container); + } ++ // Paper end - Add event for container assessment } private static boolean hasItemMatchingHandItem(final PathfinderMob body, final Container container) { From bc04ab0a7500ac8b53be56edb5b44dce2404c46a Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:20:30 -0400 Subject: [PATCH 4/6] Use tristate instead of two booleans --- .../ItemTransportingEntitySortEvent.java | 35 ++++++++----------- ...TransportItemsBetweenContainers.java.patch | 8 +++-- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java index 6ce5b7880226..a64d5b220f95 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java @@ -21,10 +21,15 @@ public class ItemTransportingEntitySortEvent extends EntityEvent { protected static final HandlerList HANDLER_LIST = new HandlerList(); + public enum ItemTransportingEntityDecision { + ALLOW_DESTINATION, + REJECT_DESTINATION, + USE_VANILLA_BEHAVIOUR + } + private final ItemStack itemStack; private final Inventory containerInventory; - private boolean overrideDefaultBehaviour = false; - private boolean itemBelongs = true; + private ItemTransportingEntityDecision decision; @ApiStatus.Internal public ItemTransportingEntitySortEvent( @@ -35,37 +40,25 @@ public ItemTransportingEntitySortEvent( super(entity); this.containerInventory = containerInventory; this.itemStack = itemStack; + this.decision = ItemTransportingEntityDecision.USE_VANILLA_BEHAVIOUR; } /** * Sets if the item belongs in the container. * - * @param belongs Whether the item belongs or not + * @param d Whether the item belongs in the chest. */ - public void setItemBelongs(boolean belongs) { - this.overrideDefaultBehaviour = true; - this.itemBelongs = belongs; + public void setDecision(boolean d) { + this.decision = d ? ItemTransportingEntityDecision.ALLOW_DESTINATION : ItemTransportingEntityDecision.REJECT_DESTINATION; } /** * Gets if the held item stack belongs in the container. * - * @return Whether the item stack belongs. - */ - public boolean itemBelongs() { - return this.itemBelongs; - } - - /** - * Whether this event will override the default behavior of the entity. - * For example, if {@link ItemTransportingEntitySortEvent#setItemBelongs(boolean)} is not called, the entity - * will use the default behavior of adding to empty chests and chests containing a matching item stack. - * Once the function is called, the entity will defer to the plugin's decision. - * - * @return Whether the default behavior is being overridden. + * @return Whether the item stack belongs in the chest. */ - public boolean isOverridingDefaultBehaviour() { - return this.overrideDefaultBehaviour; + public ItemTransportingEntityDecision getDecision() { + return this.decision; } /** diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch index ed86fb87b1f4..b805ddf579ed 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -12,7 +12,7 @@ return isValidTarget ? transportItemTarget : null; } } -@@ -505,7 +_,15 @@ +@@ -505,7 +_,17 @@ } private static boolean matchesLeavingItemsRequirement(final PathfinderMob body, final Container container) { @@ -20,8 +20,10 @@ + // Paper start - Add event for container assessment + io.papermc.paper.event.entity.ItemTransportingEntitySortEvent sortEvent = org.bukkit.craftbukkit.event.CraftEventFactory.createItemTransportingEntitySortEvent(body, container); + sortEvent.callEvent(); -+ if (sortEvent.isOverridingDefaultBehaviour()) { -+ return sortEvent.itemBelongs(); ++ if (sortEvent.getDecision() != ++ io.papermc.paper.event.entity.ItemTransportingEntitySortEvent.ItemTransportingEntityDecision.USE_VANILLA_BEHAVIOUR) { ++ return sortEvent.getDecision() ++ == io.papermc.paper.event.entity.ItemTransportingEntitySortEvent.ItemTransportingEntityDecision.ALLOW_DESTINATION; + } else { + return container.isEmpty() || hasItemMatchingHandItem(body, container); + } From b24e6930ef1ea37a384a6bb59985ddfd90934f8a Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:37:24 -0400 Subject: [PATCH 5/6] Use consistent name for default golem sort behaviour --- .../paper/event/entity/ItemTransportingEntitySortEvent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java index a64d5b220f95..e86999dfe69c 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/ItemTransportingEntitySortEvent.java @@ -24,7 +24,7 @@ public class ItemTransportingEntitySortEvent extends EntityEvent { public enum ItemTransportingEntityDecision { ALLOW_DESTINATION, REJECT_DESTINATION, - USE_VANILLA_BEHAVIOUR + DEFAULT } private final ItemStack itemStack; @@ -40,7 +40,7 @@ public ItemTransportingEntitySortEvent( super(entity); this.containerInventory = containerInventory; this.itemStack = itemStack; - this.decision = ItemTransportingEntityDecision.USE_VANILLA_BEHAVIOUR; + this.decision = ItemTransportingEntityDecision.DEFAULT; } /** From 2739fa4381675d942a6150d9114fbb293a223164 Mon Sep 17 00:00:00 2001 From: Jeremy <23155614+jsbrn@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:02:36 -0400 Subject: [PATCH 6/6] Apply behaviour enum rename patch --- .../ai/behavior/TransportItemsBetweenContainers.java.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch index b805ddf579ed..e41d9dedb298 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -21,7 +21,7 @@ + io.papermc.paper.event.entity.ItemTransportingEntitySortEvent sortEvent = org.bukkit.craftbukkit.event.CraftEventFactory.createItemTransportingEntitySortEvent(body, container); + sortEvent.callEvent(); + if (sortEvent.getDecision() != -+ io.papermc.paper.event.entity.ItemTransportingEntitySortEvent.ItemTransportingEntityDecision.USE_VANILLA_BEHAVIOUR) { ++ io.papermc.paper.event.entity.ItemTransportingEntitySortEvent.ItemTransportingEntityDecision.DEFAULT) { + return sortEvent.getDecision() + == io.papermc.paper.event.entity.ItemTransportingEntitySortEvent.ItemTransportingEntityDecision.ALLOW_DESTINATION; + } else {