Skip to content

Commit 2a4a115

Browse files
Add EntityEquipmentChangedEvent (#12011)
1 parent a3781ff commit 2a4a115

File tree

3 files changed

+154
-5
lines changed

3 files changed

+154
-5
lines changed

paper-api/src/main/java/com/destroystokyo/paper/event/player/PlayerArmorChangeEvent.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.bukkit.entity.Player;
66
import org.bukkit.event.HandlerList;
77
import org.bukkit.event.player.PlayerEvent;
8+
import org.bukkit.inventory.EquipmentSlot;
89
import org.bukkit.inventory.ItemStack;
910
import org.jetbrains.annotations.ApiStatus;
1011
import org.jspecify.annotations.NullMarked;
@@ -16,8 +17,10 @@
1617
* Called when the player themselves change their armor items
1718
* <p>
1819
* Not currently called for environmental factors though it <strong>MAY BE IN THE FUTURE</strong>
20+
* @apiNote Use {@link io.papermc.paper.event.entity.EntityEquipmentChangedEvent} for all entity equipment changes
1921
*/
2022
@NullMarked
23+
@ApiStatus.Obsolete(since = "1.21.4")
2124
public class PlayerArmorChangeEvent extends PlayerEvent {
2225

2326
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -38,11 +41,27 @@ public PlayerArmorChangeEvent(final Player player, final SlotType slotType, fina
3841
* Gets the type of slot being altered.
3942
*
4043
* @return type of slot being altered
44+
* @deprecated {@link SlotType} does not accurately represent what item types are valid in each slot. Use {@link #getSlot()} instead.
4145
*/
46+
@Deprecated(since = "1.21.4")
4247
public SlotType getSlotType() {
4348
return this.slotType;
4449
}
4550

51+
/**
52+
* Gets the slot being altered.
53+
*
54+
* @return slot being altered
55+
*/
56+
public EquipmentSlot getSlot() {
57+
return switch (this.slotType) {
58+
case HEAD -> EquipmentSlot.HEAD;
59+
case CHEST -> EquipmentSlot.CHEST;
60+
case LEGS -> EquipmentSlot.LEGS;
61+
case FEET -> EquipmentSlot.FEET;
62+
};
63+
}
64+
4665
/**
4766
* Gets the existing item that's being replaced
4867
*
@@ -70,6 +89,10 @@ public static HandlerList getHandlerList() {
7089
return HANDLER_LIST;
7190
}
7291

92+
/**
93+
* @deprecated {@link SlotType} does not accurately represent what item types are valid in each slot.
94+
*/
95+
@Deprecated(since = "1.21.4")
7396
public enum SlotType {
7497
HEAD(NETHERITE_HELMET, DIAMOND_HELMET, GOLDEN_HELMET, IRON_HELMET, CHAINMAIL_HELMET, LEATHER_HELMET, CARVED_PUMPKIN, PLAYER_HEAD, SKELETON_SKULL, ZOMBIE_HEAD, CREEPER_HEAD, WITHER_SKELETON_SKULL, TURTLE_HELMET, DRAGON_HEAD, PIGLIN_HEAD),
7598
CHEST(NETHERITE_CHESTPLATE, DIAMOND_CHESTPLATE, GOLDEN_CHESTPLATE, IRON_CHESTPLATE, CHAINMAIL_CHESTPLATE, LEATHER_CHESTPLATE, ELYTRA),
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package io.papermc.paper.event.entity;
2+
3+
import java.util.Collections;
4+
import java.util.Map;
5+
import org.bukkit.entity.LivingEntity;
6+
import org.bukkit.event.HandlerList;
7+
import org.bukkit.event.entity.EntityEvent;
8+
import org.bukkit.inventory.EquipmentSlot;
9+
import org.bukkit.inventory.ItemStack;
10+
import org.jetbrains.annotations.ApiStatus;
11+
import org.jetbrains.annotations.Contract;
12+
import org.jetbrains.annotations.Unmodifiable;
13+
import org.jspecify.annotations.NullMarked;
14+
15+
/**
16+
* Called whenever a change to an entity's equipment has been detected. This event is called after effects from
17+
* attribute modifiers and enchantments have been updated.
18+
* <p>
19+
* Examples of actions that can trigger this event:
20+
* <ul>
21+
* <li>An entity being added to a world.</li>
22+
* <li>A player logging in.</li>
23+
* <li>The durability of an equipment item changing.</li>
24+
* <li>A dispenser equipping an item onto an entity.</li>
25+
* <li>An entity picking up an armor or weapon item from the ground.</li>
26+
* <li>A player changing their equipped armor.</li>
27+
* <li>A player changes their currently held item.</li>
28+
* </ul>
29+
*/
30+
@NullMarked
31+
public class EntityEquipmentChangedEvent extends EntityEvent {
32+
33+
private static final HandlerList HANDLER_LIST = new HandlerList();
34+
35+
private final Map<EquipmentSlot, EquipmentChange> equipmentChanges;
36+
37+
@ApiStatus.Internal
38+
public EntityEquipmentChangedEvent(final LivingEntity entity, final Map<EquipmentSlot, EquipmentChange> equipmentChanges) {
39+
super(entity);
40+
41+
this.equipmentChanges = equipmentChanges;
42+
}
43+
44+
@Override
45+
public LivingEntity getEntity() {
46+
return (LivingEntity) this.entity;
47+
}
48+
49+
/**
50+
* Gets a map of changed slots to their respective equipment changes.
51+
*
52+
* @return the equipment changes map
53+
*/
54+
public @Unmodifiable Map<EquipmentSlot, EquipmentChange> getEquipmentChanges() {
55+
return Collections.unmodifiableMap(this.equipmentChanges);
56+
}
57+
58+
@Override
59+
public HandlerList getHandlers() {
60+
return HANDLER_LIST;
61+
}
62+
63+
public static HandlerList getHandlerList() {
64+
return HANDLER_LIST;
65+
}
66+
67+
/**
68+
* Represents a change in equipment for a single equipment slot.
69+
*/
70+
@ApiStatus.NonExtendable
71+
public interface EquipmentChange {
72+
73+
/**
74+
* Gets the existing item that is being replaced.
75+
*
76+
* @return the existing item
77+
*/
78+
@Contract(pure = true, value = "-> new")
79+
ItemStack oldItem();
80+
81+
/**
82+
* Gets the new item that is replacing the existing item.
83+
*
84+
* @return the new item
85+
*/
86+
@Contract(pure = true, value = "-> new")
87+
ItemStack newItem();
88+
}
89+
}

paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,20 +1336,57 @@
13361336
Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
13371337
if (map != null) {
13381338
this.handleHandSwap(map);
1339-
@@ -2595,6 +_,13 @@
1339+
@@ -2586,6 +_,20 @@
1340+
@Nullable
1341+
private Map<EquipmentSlot, ItemStack> collectEquipmentChanges() {
1342+
Map<EquipmentSlot, ItemStack> map = null;
1343+
+ // Paper start - EntityEquipmentChangedEvent
1344+
+ record EquipmentChangeImpl(org.bukkit.inventory.ItemStack oldItem, org.bukkit.inventory.ItemStack newItem) implements io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange {
1345+
+ @Override
1346+
+ public org.bukkit.inventory.ItemStack oldItem() {
1347+
+ return this.oldItem.clone();
1348+
+ }
1349+
+
1350+
+ @Override
1351+
+ public org.bukkit.inventory.ItemStack newItem() {
1352+
+ return this.newItem.clone();
1353+
+ }
1354+
+ }
1355+
+ Map<org.bukkit.inventory.EquipmentSlot, io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange> equipmentChanges = null;
1356+
+ // Paper end - EntityEquipmentChangedEvent
1357+
1358+
for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) {
1359+
ItemStack itemStack = switch (equipmentSlot.getType()) {
1360+
@@ -2595,11 +_,20 @@
13401361
};
13411362
ItemStack itemBySlot = this.getItemBySlot(equipmentSlot);
13421363
if (this.equipmentHasChanged(itemStack, itemBySlot)) {
1343-
+ // Paper start - PlayerArmorChangeEvent
1364+
+ // Paper start - EntityEquipmentChangedEvent, PlayerArmorChangeEvent
1365+
+ final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemStack);
1366+
+ final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemBySlot);
13441367
+ if (this instanceof ServerPlayer && equipmentSlot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR) {
1345-
+ final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemStack);
1346-
+ final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemBySlot);
13471368
+ new com.destroystokyo.paper.event.player.PlayerArmorChangeEvent((org.bukkit.entity.Player) this.getBukkitEntity(), com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.valueOf(equipmentSlot.name()), oldItem, newItem).callEvent();
13481369
+ }
1349-
+ // Paper end - PlayerArmorChangeEvent
1370+
+ // Paper end - EntityEquipmentChangedEvent, PlayerArmorChangeEvent
13501371
if (map == null) {
13511372
map = Maps.newEnumMap(EquipmentSlot.class);
1373+
+ equipmentChanges = Maps.newEnumMap(org.bukkit.inventory.EquipmentSlot.class); // Paper - EntityEquipmentChangedEvent
13521374
}
1375+
1376+
map.put(equipmentSlot, itemBySlot);
1377+
+ equipmentChanges.put(org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(equipmentSlot), new EquipmentChangeImpl(oldItem, newItem)); // Paper - EntityEquipmentChangedEvent
1378+
AttributeMap attributes = this.getAttributes();
1379+
if (!itemStack.isEmpty()) {
1380+
this.stopLocationBasedEffects(itemStack, equipmentSlot, attributes);
1381+
@@ -2624,6 +_,8 @@
1382+
}
1383+
}
1384+
}
1385+
+
1386+
+ new io.papermc.paper.event.entity.EntityEquipmentChangedEvent(this.getBukkitLivingEntity(), equipmentChanges).callEvent(); // Paper - EntityEquipmentChangedEvent
1387+
}
1388+
1389+
return map;
13531390
@@ -2664,7 +_,7 @@
13541391
this.lastBodyItemStack = itemStack;
13551392
}

0 commit comments

Comments
 (0)