Paper/patches/server/0958-Fix-equipment-slot-and-group-API.patch
2024-10-31 20:35:06 -07:00

137 lines
7.3 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 22 May 2024 10:01:19 -0700
Subject: [PATCH] Fix equipment slot and group API
Adds the following:
- Add test for EquipmentSlotGroup
- Expose LivingEntity#canUseSlot
Co-authored-by: SoSeDiK <mrsosedik@gmail.com>
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index a62d17b72c675120b447e625cb3dc437681bdf20..f16067b674118a47735ad22797988d50b4415040 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -1230,4 +1230,11 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
this.getHandle().setYBodyRot(bodyYaw);
}
// Paper end - body yaw API
+
+ // Paper start - Expose canUseSlot
+ @Override
+ public boolean canUseEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) {
+ return this.getHandle().canUseSlot(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot));
+ }
+ // Paper end - Expose canUseSlot
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
index e62baea16df017f1e394e3c706157e158066eb93..656c9a6d8cd42891141ee29ec91ab5d166051ed6 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
@@ -135,6 +135,10 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
case HEAD:
this.setHelmet(item);
break;
+ // Paper start
+ case BODY:
+ throw new IllegalArgumentException("BODY is not valid for players!");
+ // Paper end
default:
throw new IllegalArgumentException("Could not set slot " + slot + " - not a valid slot for PlayerInventory");
}
@@ -162,6 +166,10 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
return java.util.Objects.requireNonNullElseGet(this.getChestplate(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull
case HEAD:
return java.util.Objects.requireNonNullElseGet(this.getHelmet(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull
+ // Paper start
+ case BODY:
+ throw new IllegalArgumentException("BODY is not valid for players!");
+ // Paper end
default:
throw new IllegalArgumentException("Could not get slot " + slot + " - not a valid slot for PlayerInventory");
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
index 71d0ade48d450b76be9cb396ac613fa4c783679e..b24857e34d87ea4fa9aa0db0ba684014c2625cc1 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
@@ -1704,7 +1704,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
if (this.attributeModifiers == null) return LinkedHashMultimap.create(); // Paper - don't change the components
SetMultimap<Attribute, AttributeModifier> result = LinkedHashMultimap.create();
for (Map.Entry<Attribute, AttributeModifier> entry : this.attributeModifiers.entries()) {
- if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) {
+ if (entry.getValue().getSlotGroup().test(slot)) { // Paper - correctly test slot against group
result.put(entry.getKey(), entry.getValue());
}
}
@@ -1778,9 +1778,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
while (iter.hasNext()) {
Map.Entry<Attribute, AttributeModifier> entry = iter.next();
- // Explicitly match against null because (as of MC 1.13) AttributeModifiers without a -
- // set slot are active in any slot.
- if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) {
+ if (entry.getValue().getSlotGroup().test(slot)) { // Paper - correctly test slot against group
iter.remove();
++removed;
}
diff --git a/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java b/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a26dc8f4787276468417196440cb8f73670a56c7
--- /dev/null
+++ b/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.inventory.item;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import net.minecraft.world.entity.EquipmentSlot;
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
+import org.bukkit.inventory.EquipmentSlotGroup;
+import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@Normal
+class EquipmentSlotGroupTest {
+
+ static List<EquipmentSlotGroup> apiValues() throws ReflectiveOperationException {
+ final List<EquipmentSlotGroup> apiValues = new ArrayList<>();
+ for (final Field field : EquipmentSlotGroup.class.getDeclaredFields()) {
+ if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isFinal(field.getModifiers()) || !field.getType().equals(EquipmentSlotGroup.class)) {
+ continue;
+ }
+ apiValues.add((EquipmentSlotGroup) field.get(null));
+ }
+ if (apiValues.isEmpty()) {
+ throw new RuntimeException("Didn't find any api " + EquipmentSlotGroup.class.getSimpleName());
+ }
+ return apiValues;
+ }
+
+ @ParameterizedTest
+ @MethodSource("apiValues")
+ void testBukkitToNms(final EquipmentSlotGroup slotGroup) {
+ final net.minecraft.world.entity.EquipmentSlotGroup nmsGroup = CraftEquipmentSlot.getNMSGroup(slotGroup);
+ assertNotNull(nmsGroup, "No nms slot group found for " + slotGroup);
+ assertEquals(nmsGroup.getSerializedName(), slotGroup.toString(), "slot group name mismatch");
+ for (final EquipmentSlot slot : EquipmentSlot.values()) {
+ assertEquals(nmsGroup.test(slot), slotGroup.test(CraftEquipmentSlot.getSlot(slot)));
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(net.minecraft.world.entity.EquipmentSlotGroup.class)
+ void testNmsToBukkit(final net.minecraft.world.entity.EquipmentSlotGroup slotGroup) {
+ final EquipmentSlotGroup apiGroup = CraftEquipmentSlot.getSlot(slotGroup);
+ assertNotNull(apiGroup, "No api slot group found for " + slotGroup);
+ assertEquals(apiGroup.toString(), slotGroup.getSerializedName(), "slot group name mismatch");
+ }
+}