Paper/patches/server/0069-Handle-Item-Meta-Inconsistencies.patch
Nassim Jahnke 275173e538
Updated Upstream (Bukkit/CraftBukkit)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
0c5d8709 SPIGOT-7400: Downgrade maven-resolver due to issues resolving certain depends
255c4fdb SPIGOT-7380: Add PlayerInteractEvent#getClickedPosition and ChiseledBookshelf#getSlot

CraftBukkit Changes:
b6b514b7e SPIGOT-7400: Downgrade maven-resolver due to issues resolving certain depends
fcff84de9 SPIGOT-7399: Revert null check in CraftMetaItem#safelyAdd
44a4b5649 SPIGOT-7380: Add PlayerInteractEvent#getClickedPosition and ChiseledBookshelf#getSlot
676969d01 SPIGOT-7389: Handle setting null items in ChiseledBookshelf Inventory
2023-06-18 13:18:11 +02:00

306 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 28 May 2015 23:00:19 -0400
Subject: [PATCH] Handle Item Meta Inconsistencies
First, Enchantment order would blow away seeing 2 items as the same,
however the Client forces enchantment list in a certain order, as well
as does the /enchant command. Anvils can insert it into forced order,
causing 2 same items to be considered different.
This change makes unhandled NBT Tags and Enchantments use a sorted tree map,
so they will always be in a consistent order.
Additionally, the old enchantment API was never updated when ItemMeta
was added, resulting in 2 different ways to modify an items enchantments.
For consistency, the old API methods now forward to use the
ItemMeta API equivalents, and should deprecate the old API's.
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 6758e72177c0a407cf6c392b10263f095e4d06e8..369f7d470fc6f7235fc5bd6b67e16c4182d5ed36 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -156,6 +156,23 @@ public final class ItemStack {
return this.getItem().getTooltipImage(this);
}
+ // Paper start
+ private static final java.util.Comparator<? super CompoundTag> enchantSorter = java.util.Comparator.comparing(o -> o.getString("id"));
+ private void processEnchantOrder(@Nullable CompoundTag tag) {
+ if (tag == null || !tag.contains("Enchantments", 9)) {
+ return;
+ }
+ ListTag list = tag.getList("Enchantments", 10);
+ if (list.size() < 2) {
+ return;
+ }
+ try {
+ //noinspection unchecked
+ list.sort((java.util.Comparator<? super net.minecraft.nbt.Tag>) enchantSorter); // Paper
+ } catch (Exception ignored) {}
+ }
+ // Paper end
+
public ItemStack(ItemLike item) {
this(item, 1);
}
@@ -204,6 +221,7 @@ public final class ItemStack {
// CraftBukkit start - make defensive copy as this data may be coming from the save thread
this.tag = nbttagcompound.getCompound("tag").copy();
// CraftBukkit end
+ this.processEnchantOrder(this.tag); // Paper
this.getItem().verifyTagAfterLoad(this.tag);
}
@@ -809,6 +827,7 @@ public final class ItemStack {
public void setTag(@Nullable CompoundTag nbt) {
this.tag = nbt;
+ this.processEnchantOrder(this.tag); // Paper
if (this.getItem().canBeDepleted()) {
this.setDamageValue(this.getDamageValue());
}
@@ -1106,6 +1125,7 @@ public final class ItemStack {
ListTag nbttaglist = this.tag.getList("Enchantments", 10);
nbttaglist.add(EnchantmentHelper.storeEnchantment(EnchantmentHelper.getEnchantmentId(enchantment), (byte) level));
+ processEnchantOrder(this.tag); // Paper
}
public boolean isEnchanted() {
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 34a54a99e7484b2934e1d174c1b55f0472ff3812..7fd9a3a7f4cb98d89b7c4cbfda756d581963e5bf 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -178,28 +178,11 @@ public final class CraftItemStack extends ItemStack {
public void addUnsafeEnchantment(Enchantment ench, int level) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
- if (!CraftItemStack.makeTag(this.handle)) {
- return;
- }
- ListTag list = CraftItemStack.getEnchantmentList(this.handle);
- if (list == null) {
- list = new ListTag();
- this.handle.getTag().put(ENCHANTMENTS.NBT, list);
- }
- int size = list.size();
-
- for (int i = 0; i < size; i++) {
- CompoundTag tag = (CompoundTag) list.get(i);
- String id = tag.getString(ENCHANTMENTS_ID.NBT);
- if (ench.getKey().equals(NamespacedKey.fromString(id))) {
- tag.putShort(ENCHANTMENTS_LVL.NBT, (short) level);
- return;
- }
- }
- CompoundTag tag = new CompoundTag();
- tag.putString(ENCHANTMENTS_ID.NBT, ench.getKey().toString());
- tag.putShort(ENCHANTMENTS_LVL.NBT, (short) level);
- list.add(tag);
+ // Paper start - Replace whole method
+ final ItemMeta itemMeta = this.getItemMeta();
+ itemMeta.addEnchant(ench, level, true);
+ this.setItemMeta(itemMeta);
+ // Paper end
}
static boolean makeTag(net.minecraft.world.item.ItemStack item) {
@@ -232,50 +215,22 @@ public final class CraftItemStack extends ItemStack {
public int removeEnchantment(Enchantment ench) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
- ListTag list = CraftItemStack.getEnchantmentList(this.handle), listCopy;
- if (list == null) {
- return 0;
- }
- int index = Integer.MIN_VALUE;
- int level = Integer.MIN_VALUE;
- int size = list.size();
-
- for (int i = 0; i < size; i++) {
- CompoundTag enchantment = (CompoundTag) list.get(i);
- String id = enchantment.getString(ENCHANTMENTS_ID.NBT);
- if (ench.getKey().equals(NamespacedKey.fromString(id))) {
- index = i;
- level = 0xffff & enchantment.getShort(ENCHANTMENTS_LVL.NBT);
- break;
- }
- }
-
- if (index == Integer.MIN_VALUE) {
- return 0;
- }
- if (size == 1) {
- this.handle.getTag().remove(ENCHANTMENTS.NBT);
- if (this.handle.getTag().isEmpty()) {
- this.handle.setTag(null);
- }
- return level;
- }
-
- // This is workaround for not having an index removal
- listCopy = new ListTag();
- for (int i = 0; i < size; i++) {
- if (i != index) {
- listCopy.add(list.get(i));
- }
+ // Paper start - replace entire method
+ int level = getEnchantmentLevel(ench);
+ if (level > 0) {
+ final ItemMeta itemMeta = this.getItemMeta();
+ if (itemMeta == null) return 0;
+ itemMeta.removeEnchant(ench);
+ this.setItemMeta(itemMeta);
}
- this.handle.getTag().put(ENCHANTMENTS.NBT, listCopy);
+ // Paper end
return level;
}
@Override
public Map<Enchantment, Integer> getEnchantments() {
- return CraftItemStack.getEnchantments(this.handle);
+ return this.hasItemMeta() ? this.getItemMeta().getEnchants() : ImmutableMap.<Enchantment, Integer>of(); // Paper - use Item Meta
}
static Map<Enchantment, Integer> getEnchantments(net.minecraft.world.item.ItemStack item) {
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
index 44244166ec4e8ac070404101219efbc0e5f1d168..092695d4b45205fe2290f8f4375e881aaf89817d 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
@@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.ImmutableSortedMap; // Paper
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
@@ -23,6 +24,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
+import java.util.Comparator; // Paper
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -33,6 +35,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeMap; // Paper
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
@@ -275,7 +278,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
private List<String> lore; // null and empty are two different states internally
private Integer customModelData;
private CompoundTag blockData;
- private Map<Enchantment, Integer> enchantments;
+ private EnchantmentMap enchantments; // Paper
private Multimap<Attribute, AttributeModifier> attributeModifiers;
private int repairCost;
private int hideFlag;
@@ -286,7 +289,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
private CompoundTag internalTag;
- final Map<String, Tag> unhandledTags = new HashMap<String, Tag>(); // Visible for testing only
+ final Map<String, Tag> unhandledTags = new TreeMap<String, Tag>(); // Visible for testing only // Paper
private CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftMetaItem.DATA_TYPE_REGISTRY);
private int version = CraftMagicNumbers.INSTANCE.getDataVersion(); // Internal use only
@@ -307,7 +310,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
this.blockData = meta.blockData;
if (meta.enchantments != null) { // Spigot
- this.enchantments = new LinkedHashMap<Enchantment, Integer>(meta.enchantments);
+ this.enchantments = new EnchantmentMap(meta.enchantments); // Paper
}
if (meta.hasAttributeModifiers()) {
@@ -390,13 +393,13 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
}
}
- static Map<Enchantment, Integer> buildEnchantments(CompoundTag tag, ItemMetaKey key) {
+ static EnchantmentMap buildEnchantments(CompoundTag tag, ItemMetaKey key) { // Paper
if (!tag.contains(key.NBT)) {
return null;
}
ListTag ench = tag.getList(key.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND);
- Map<Enchantment, Integer> enchantments = new LinkedHashMap<Enchantment, Integer>(ench.size());
+ EnchantmentMap enchantments = new EnchantmentMap(); // Paper
for (int i = 0; i < ench.size(); i++) {
String id = ((CompoundTag) ench.get(i)).getString(ENCHANTMENTS_ID.NBT);
@@ -549,13 +552,13 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
}
}
- static Map<Enchantment, Integer> buildEnchantments(Map<String, Object> map, ItemMetaKey key) {
+ static EnchantmentMap buildEnchantments(Map<String, Object> map, ItemMetaKey key) { // Paper
Map<?, ?> ench = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true);
if (ench == null) {
return null;
}
- Map<Enchantment, Integer> enchantments = new LinkedHashMap<Enchantment, Integer>(ench.size());
+ EnchantmentMap enchantments = new EnchantmentMap(); // Paper
for (Map.Entry<?, ?> entry : ench.entrySet()) {
// Doctor older enchants
String enchantKey = entry.getKey().toString();
@@ -831,14 +834,14 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
@Override
public Map<Enchantment, Integer> getEnchants() {
- return this.hasEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.<Enchantment, Integer>of();
+ return this.hasEnchants() ? ImmutableSortedMap.copyOfSorted(this.enchantments) : ImmutableMap.<Enchantment, Integer>of(); // Paper
}
@Override
public boolean addEnchant(Enchantment ench, int level, boolean ignoreRestrictions) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
if (this.enchantments == null) {
- this.enchantments = new LinkedHashMap<Enchantment, Integer>(4);
+ this.enchantments = new EnchantmentMap(); // Paper
}
if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) {
@@ -1227,7 +1230,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
clone.customModelData = this.customModelData;
clone.blockData = this.blockData;
if (this.enchantments != null) {
- clone.enchantments = new LinkedHashMap<Enchantment, Integer>(this.enchantments);
+ clone.enchantments = new EnchantmentMap(this.enchantments); // Paper
}
if (this.hasAttributeModifiers()) {
clone.attributeModifiers = LinkedHashMultimap.create(this.attributeModifiers);
@@ -1469,4 +1472,22 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
return CraftMetaItem.HANDLED_TAGS;
}
}
+
+ // Paper start
+ private static class EnchantmentMap extends TreeMap<Enchantment, Integer> {
+ private EnchantmentMap(Map<Enchantment, Integer> enchantments) {
+ this();
+ putAll(enchantments);
+ }
+
+ private EnchantmentMap() {
+ super(Comparator.comparing(o -> o.getKey().toString()));
+ }
+
+ public EnchantmentMap clone() {
+ return (EnchantmentMap) super.clone();
+ }
+ }
+ // Paper end
+
}