mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-27 13:06:02 +01:00
Store item attributes. Addresses BUKKIT-4523
This commit is contained in:
parent
b6fec0467f
commit
1e7f2ebebd
@ -1,5 +1,6 @@
|
|||||||
package org.bukkit.craftbukkit.inventory;
|
package org.bukkit.craftbukkit.inventory;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
@ -9,13 +10,25 @@ import org.bukkit.inventory.ItemFactory;
|
|||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
public final class CraftItemFactory implements ItemFactory {
|
public final class CraftItemFactory implements ItemFactory {
|
||||||
static final Color DEFAULT_LEATHER_COLOR = Color.fromRGB(0xA06540);
|
static final Color DEFAULT_LEATHER_COLOR = Color.fromRGB(0xA06540);
|
||||||
|
static final Collection<String> KNOWN_NBT_ATTRIBUTE_NAMES;
|
||||||
private static final CraftItemFactory instance;
|
private static final CraftItemFactory instance;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
instance = new CraftItemFactory();
|
instance = new CraftItemFactory();
|
||||||
ConfigurationSerialization.registerClass(CraftMetaItem.SerializableMeta.class);
|
ConfigurationSerialization.registerClass(CraftMetaItem.SerializableMeta.class);
|
||||||
|
KNOWN_NBT_ATTRIBUTE_NAMES = ImmutableSet.<String>builder()
|
||||||
|
.add("generic.attackDamage")
|
||||||
|
.add("generic.followRange")
|
||||||
|
.add("generic.knockbackResistance")
|
||||||
|
.add("generic.maxHealth")
|
||||||
|
.add("generic.movementSpeed")
|
||||||
|
.add("horse.jumpStrength")
|
||||||
|
.add("zombie.spawnReinforcements")
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CraftItemFactory() {
|
private CraftItemFactory() {
|
||||||
|
@ -15,7 +15,10 @@ import java.util.NoSuchElementException;
|
|||||||
|
|
||||||
import net.minecraft.server.NBTBase;
|
import net.minecraft.server.NBTBase;
|
||||||
import net.minecraft.server.NBTTagCompound;
|
import net.minecraft.server.NBTTagCompound;
|
||||||
|
import net.minecraft.server.NBTTagDouble;
|
||||||
|
import net.minecraft.server.NBTTagInt;
|
||||||
import net.minecraft.server.NBTTagList;
|
import net.minecraft.server.NBTTagList;
|
||||||
|
import net.minecraft.server.NBTTagLong;
|
||||||
import net.minecraft.server.NBTTagString;
|
import net.minecraft.server.NBTTagString;
|
||||||
|
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
@ -178,14 +181,28 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
@Specific(Specific.To.NBT)
|
@Specific(Specific.To.NBT)
|
||||||
static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl");
|
static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl");
|
||||||
static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost");
|
static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES = new ItemMetaKey("AttributeModifiers");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES_NAME = new ItemMetaKey("Name");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES_VALUE = new ItemMetaKey("Amount");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES_TYPE = new ItemMetaKey("Operation");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES_UUID_HIGH = new ItemMetaKey("UUIDMost");
|
||||||
|
@Specific(Specific.To.NBT)
|
||||||
|
static final ItemMetaKey ATTRIBUTES_UUID_LOW = new ItemMetaKey("UUIDLeast");
|
||||||
|
|
||||||
private String displayName;
|
private String displayName;
|
||||||
private List<String> lore;
|
private List<String> lore;
|
||||||
private Map<Enchantment, Integer> enchantments;
|
private Map<Enchantment, Integer> enchantments;
|
||||||
private int repairCost;
|
private int repairCost;
|
||||||
|
private final NBTTagList attributes;
|
||||||
|
|
||||||
CraftMetaItem(CraftMetaItem meta) {
|
CraftMetaItem(CraftMetaItem meta) {
|
||||||
if (meta == null) {
|
if (meta == null) {
|
||||||
|
attributes = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +217,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.repairCost = meta.repairCost;
|
this.repairCost = meta.repairCost;
|
||||||
|
this.attributes = meta.attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
CraftMetaItem(NBTTagCompound tag) {
|
CraftMetaItem(NBTTagCompound tag) {
|
||||||
@ -226,6 +244,51 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
if (tag.hasKey(REPAIR.NBT)) {
|
if (tag.hasKey(REPAIR.NBT)) {
|
||||||
repairCost = tag.getInt(REPAIR.NBT);
|
repairCost = tag.getInt(REPAIR.NBT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (tag.get(ATTRIBUTES.NBT) instanceof NBTTagList) {
|
||||||
|
NBTTagList save = null;
|
||||||
|
NBTTagList nbttaglist = tag.getList(ATTRIBUTES.NBT);
|
||||||
|
|
||||||
|
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||||
|
if (!(nbttaglist.get(i) instanceof NBTTagCompound)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NBTTagCompound nbttagcompound = (NBTTagCompound) nbttaglist.get(i);
|
||||||
|
|
||||||
|
if (!(nbttagcompound.get(ATTRIBUTES_UUID_HIGH.NBT) instanceof NBTTagLong)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(nbttagcompound.get(ATTRIBUTES_UUID_LOW.NBT) instanceof NBTTagLong)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(nbttagcompound.get(ATTRIBUTES_NAME.NBT) instanceof NBTTagString) || !CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES.contains(nbttagcompound.getString(ATTRIBUTES_NAME.NBT))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(nbttagcompound.get(ATTRIBUTES_VALUE.NBT) instanceof NBTTagDouble)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(nbttagcompound.get(ATTRIBUTES_TYPE.NBT) instanceof NBTTagInt) || nbttagcompound.getInt(ATTRIBUTES_TYPE.NBT) < 0 || nbttagcompound.getInt(ATTRIBUTES_TYPE.NBT) > 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save == null) {
|
||||||
|
save = new NBTTagList(ATTRIBUTES.NBT);
|
||||||
|
}
|
||||||
|
|
||||||
|
NBTTagCompound entry = new NBTTagCompound();
|
||||||
|
entry.set(ATTRIBUTES_UUID_HIGH.NBT, nbttagcompound.get(ATTRIBUTES_UUID_HIGH.NBT));
|
||||||
|
entry.set(ATTRIBUTES_UUID_LOW.NBT, nbttagcompound.get(ATTRIBUTES_UUID_LOW.NBT));
|
||||||
|
entry.set(ATTRIBUTES_NAME.NBT, nbttagcompound.get(ATTRIBUTES_NAME.NBT));
|
||||||
|
entry.set(ATTRIBUTES_VALUE.NBT, nbttagcompound.get(ATTRIBUTES_VALUE.NBT));
|
||||||
|
entry.set(ATTRIBUTES_TYPE.NBT, nbttagcompound.get(ATTRIBUTES_TYPE.NBT));
|
||||||
|
save.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes = save;
|
||||||
|
} else {
|
||||||
|
attributes = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<Enchantment, Integer> buildEnchantments(NBTTagCompound tag, ItemMetaKey key) {
|
static Map<Enchantment, Integer> buildEnchantments(NBTTagCompound tag, ItemMetaKey key) {
|
||||||
@ -260,6 +323,8 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
if (repairCost != null) {
|
if (repairCost != null) {
|
||||||
setRepairCost(repairCost);
|
setRepairCost(repairCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<Enchantment, Integer> buildEnchantments(Map<String, Object> map, ItemMetaKey key) {
|
static Map<Enchantment, Integer> buildEnchantments(Map<String, Object> map, ItemMetaKey key) {
|
||||||
@ -295,6 +360,10 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
if (hasRepairCost()) {
|
if (hasRepairCost()) {
|
||||||
itemTag.setInt(REPAIR.NBT, repairCost);
|
itemTag.setInt(REPAIR.NBT, repairCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attributes != null) {
|
||||||
|
itemTag.set(ATTRIBUTES.NBT, attributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NBTTagList createStringList(List<String> list, ItemMetaKey key) {
|
static NBTTagList createStringList(List<String> list, ItemMetaKey key) {
|
||||||
@ -346,7 +415,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
|
|
||||||
@Overridden
|
@Overridden
|
||||||
boolean isEmpty() {
|
boolean isEmpty() {
|
||||||
return !(hasDisplayName() || hasEnchants() || hasLore());
|
return !(hasDisplayName() || hasEnchants() || hasLore() || hasAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
@ -365,6 +434,10 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
return this.lore != null && !this.lore.isEmpty();
|
return this.lore != null && !this.lore.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasAttributes() {
|
||||||
|
return this.attributes != null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasRepairCost() {
|
public boolean hasRepairCost() {
|
||||||
return repairCost > 0;
|
return repairCost > 0;
|
||||||
}
|
}
|
||||||
@ -458,6 +531,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
return ((this.hasDisplayName() ? that.hasDisplayName() && this.displayName.equals(that.displayName) : !that.hasDisplayName()))
|
return ((this.hasDisplayName() ? that.hasDisplayName() && this.displayName.equals(that.displayName) : !that.hasDisplayName()))
|
||||||
&& (this.hasEnchants() ? that.hasEnchants() && this.enchantments.equals(that.enchantments) : !that.hasEnchants())
|
&& (this.hasEnchants() ? that.hasEnchants() && this.enchantments.equals(that.enchantments) : !that.hasEnchants())
|
||||||
&& (this.hasLore() ? that.hasLore() && this.lore.equals(that.lore) : !that.hasLore())
|
&& (this.hasLore() ? that.hasLore() && this.lore.equals(that.lore) : !that.hasLore())
|
||||||
|
&& (this.hasAttributes() ? that.hasAttributes() && this.attributes.equals(that.attributes) : !that.hasAttributes())
|
||||||
&& (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost());
|
&& (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +556,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
|
|||||||
hash = 61 * hash + (hasDisplayName() ? this.displayName.hashCode() : 0);
|
hash = 61 * hash + (hasDisplayName() ? this.displayName.hashCode() : 0);
|
||||||
hash = 61 * hash + (hasLore() ? this.lore.hashCode() : 0);
|
hash = 61 * hash + (hasLore() ? this.lore.hashCode() : 0);
|
||||||
hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0);
|
hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0);
|
||||||
|
hash = 61 * hash + (hasAttributes() ? this.attributes.hashCode() : 0);
|
||||||
hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0);
|
hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.bukkit.craftbukkit.inventory;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import net.minecraft.server.CommandAbstract;
|
||||||
|
import net.minecraft.server.IAttribute;
|
||||||
|
import org.bukkit.support.AbstractTestingBase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ItemFactoryTest extends AbstractTestingBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKnownAttributes() throws Throwable {
|
||||||
|
final ZipFile nmsZipFile = new ZipFile(CommandAbstract.class /* Magic class that isn't imported! */.getProtectionDomain().getCodeSource().getLocation().getFile());
|
||||||
|
final Collection<String> names = new HashSet<String>();
|
||||||
|
for (final ZipEntry clazzEntry : Collections.list(nmsZipFile.entries())) {
|
||||||
|
final String entryName = clazzEntry.getName();
|
||||||
|
if (!(entryName.endsWith(".class") && entryName.startsWith("net/minecraft/server/"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class<?> clazz = Class.forName(entryName.substring(0, entryName.length() - ".class".length()).replace('/', '.'));
|
||||||
|
assertThat(entryName, clazz, is(not(nullValue())));
|
||||||
|
for (final Field field : clazz.getDeclaredFields()) {
|
||||||
|
if (IAttribute.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
final String attributeName = ((IAttribute) field.get(null)).a();
|
||||||
|
assertThat("Logical error: duplicate name `" + attributeName + "' in " + clazz.getName(), names.add(attributeName), is(true));
|
||||||
|
assertThat(clazz.getName(), CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES, hasItem(attributeName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat("Extra values detected", CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES, is(names));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user