mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-26 20:46:59 +01:00
e284bb1215
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: fb23cbb3 Define surefire plugin version d022084a Define ordering for MetadataStoreTest 99a7f6f0 PR-910: Match generic max absorption attribute name style with the rest c7390d71 PR-909: Update tests to JUnit 5 CraftBukkit Changes: f0661c351 PR-1230: Move unstructured PDC NBT serialisation to SNBT 452fcb599 PR-1256: Update tests to JUnit 5
325 lines
14 KiB
Diff
325 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 1b2c0fda262ca6d5bd2ce22bc2e934803bd6f602..cae548dbb5a3027e2de0bfbe38918ec7c2803e8e 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -157,6 +157,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);
|
|
}
|
|
@@ -205,6 +222,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);
|
|
}
|
|
|
|
@@ -819,6 +837,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());
|
|
}
|
|
@@ -1116,6 +1135,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 40aad6a209fbded448e3db21207a85d9f2875e71..4dcbd1ed61070c12adf5871a595875ca60110c5d 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
@@ -188,28 +188,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) {
|
|
@@ -235,57 +218,40 @@ public final class CraftItemStack extends ItemStack {
|
|
if (this.handle == null) {
|
|
return 0;
|
|
}
|
|
- return EnchantmentHelper.getItemEnchantmentLevel(CraftEnchantment.getRaw(ench), handle);
|
|
+ // Paper start - replace to allow custom enchantments
|
|
+ final ListTag enchantments = this.handle.getEnchantmentTags();
|
|
+ for (int i = 0; i < enchantments.size(); i++) {
|
|
+ final CompoundTag tag = enchantments.getCompound(i);
|
|
+ final String id = tag.getString(CraftMetaItem.ENCHANTMENTS_ID.NBT);
|
|
+ final Enchantment enchant = Enchantment.getByKey(CraftNamespacedKey.fromStringOrNull(id));
|
|
+ if (ench.equals(enchant)) {
|
|
+ return EnchantmentHelper.getEnchantmentLevel(tag);
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+ // Paper end - replace to allow custom enchantments
|
|
}
|
|
|
|
@Override
|
|
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 087080e4df07d87b55b42436cffc2f772d883db3..edcb2ab754b99cdd82ee69b3f61ba9046593955f 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;
|
|
@@ -276,7 +279,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;
|
|
@@ -287,7 +290,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
|
|
@@ -308,7 +311,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()) {
|
|
@@ -391,13 +394,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);
|
|
@@ -550,13 +553,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();
|
|
@@ -832,14 +835,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()) {
|
|
@@ -1228,7 +1231,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);
|
|
@@ -1470,4 +1473,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
|
|
+
|
|
}
|