diff options
author | Wesley Wolfe <weswolf@aol.com> | 2012-12-21 07:37:53 -0600 |
---|---|---|
committer | Wesley Wolfe <weswolf@aol.com> | 2012-12-21 10:36:34 -0600 |
commit | 708252c020590de1c3a87ae628d56c05465a2167 (patch) | |
tree | f0ba5eee5c90d26e31b54188cc1f3d74c9c598b2 /src/main | |
parent | f377a7680fb29ac55870fb6f73c2b5262241b8ef (diff) | |
download | craftbukkit-708252c020590de1c3a87ae628d56c05465a2167.tar craftbukkit-708252c020590de1c3a87ae628d56c05465a2167.tar.gz craftbukkit-708252c020590de1c3a87ae628d56c05465a2167.tar.lz craftbukkit-708252c020590de1c3a87ae628d56c05465a2167.tar.xz craftbukkit-708252c020590de1c3a87ae628d56c05465a2167.zip |
Implement 1.4.6 ItemMeta. Adds BUKKIT-3236, BUKKIT-3237
Some meta functionality is refactored into common methods.
CraftItemStack uses the ItemMetaKey identifiers for enchantments.
Refactored unit test to include extra functionality; initially only
checking the presence of the DelegateDeserialization annotation.
Diffstat (limited to 'src/main')
6 files changed, 838 insertions, 58 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java index b2bcf344..81c6b72d 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java @@ -62,6 +62,12 @@ public final class CraftItemFactory implements ItemFactory { return meta instanceof CraftMetaPotion ? meta : new CraftMetaPotion(meta); case MAP: return meta instanceof CraftMetaMap ? meta : new CraftMetaMap(meta); + case FIREWORK: + return meta instanceof CraftMetaFirework ? meta : new CraftMetaFirework(meta); + case FIREWORK_CHARGE: + return meta instanceof CraftMetaCharge ? meta : new CraftMetaCharge(meta); + case ENCHANTED_BOOK: + return meta instanceof CraftMetaEnchantedBook ? meta : new CraftMetaEnchantedBook(meta); default: return new CraftMetaItem(meta); } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 7de7c1d0..5f2a6bd1 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -1,5 +1,9 @@ package org.bukkit.craftbukkit.inventory; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_ID; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_LVL; + import java.util.Map; import net.minecraft.server.EnchantmentManager; @@ -7,10 +11,10 @@ import net.minecraft.server.NBTTagCompound; import net.minecraft.server.NBTTagList; import org.apache.commons.lang.Validate; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; import org.bukkit.Material; import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import com.google.common.collect.ImmutableMap; @@ -162,24 +166,24 @@ public final class CraftItemStack extends ItemStack { if (!makeTag(handle)) { return; } - NBTTagList list = getEnchantmentList(handle), listCopy; + NBTTagList list = getEnchantmentList(handle); if (list == null) { - list = new NBTTagList("ench"); - handle.tag.set("ench", list); + list = new NBTTagList(ENCHANTMENTS.NBT); + handle.tag.set(ENCHANTMENTS.NBT, list); } int size = list.size(); for (int i = 0; i < size; i++) { NBTTagCompound tag = (NBTTagCompound) list.get(i); - short id = tag.getShort("id"); + short id = tag.getShort(ENCHANTMENTS_ID.NBT); if (id == ench.getId()) { - tag.setShort("lvl", (short) level); + tag.setShort(ENCHANTMENTS_LVL.NBT, (short) level); return; } } NBTTagCompound tag = new NBTTagCompound(); - tag.setShort("id", (short) ench.getId()); - tag.setShort("lvl", (short) level); + tag.setShort(ENCHANTMENTS_ID.NBT, (short) ench.getId()); + tag.setShort(ENCHANTMENTS_LVL.NBT, (short) level); list.add(tag); } @@ -219,7 +223,7 @@ public final class CraftItemStack extends ItemStack { int index = Integer.MIN_VALUE, size = list.size(), level; for (int i = 0; i < size; i++) { - short id = ((NBTTagCompound) list.get(i)).getShort("id"); + short id = ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_ID.NBT); if (id == ench.getId()) { index = i; break; @@ -230,22 +234,22 @@ public final class CraftItemStack extends ItemStack { return 0; } if (index == 0 && size == 0) { - handle.tag.o("ench"); + handle.tag.o(ENCHANTMENTS.NBT); if (handle.tag.d()) { handle.tag = null; } } - listCopy = new NBTTagList("ench"); + listCopy = new NBTTagList(ENCHANTMENTS.NBT); level = Integer.MAX_VALUE; for (int i = 0; i < size; i++) { if (i == index) { - level = ((NBTTagCompound) list.get(i)).getShort("id"); + level = ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_ID.NBT); continue; } listCopy.add(list.get(i)); } - handle.tag.set("ench", listCopy); + handle.tag.set(ENCHANTMENTS.NBT, listCopy); return level; } @@ -263,10 +267,10 @@ public final class CraftItemStack extends ItemStack { } for (int i = 0; i < list.size(); i++) { - short id = ((NBTTagCompound) list.get(i)).getShort("id"); - short level = ((NBTTagCompound) list.get(i)).getShort("lvl"); + int id = 0xffff & ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_LVL.NBT); - result.put(Enchantment.getById(id), (int) level); + result.put(Enchantment.getById(id), level); } return result.build(); @@ -309,6 +313,12 @@ public final class CraftItemStack extends ItemStack { return new CraftMetaPotion(item.tag); case MAP: return new CraftMetaMap(item.tag); + case FIREWORK: + return new CraftMetaFirework(item.tag); + case FIREWORK_CHARGE: + return new CraftMetaCharge(item.tag); + case ENCHANTED_BOOK: + return new CraftMetaEnchantedBook(item.tag); default: return new CraftMetaItem(item.tag); } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java new file mode 100644 index 00000000..84ce4bbd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java @@ -0,0 +1,134 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.FireworkEffect; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta.Deserializers; +import org.bukkit.inventory.meta.FireworkEffectMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta { + static final ItemMetaKey EXPLOSION = new ItemMetaKey("Explosion", "firework-effect"); + + private FireworkEffect effect; + + CraftMetaCharge(CraftMetaItem meta) { + super(meta); + + if (meta instanceof CraftMetaCharge) { + effect = ((CraftMetaCharge) meta).effect; + } + } + + CraftMetaCharge(Map<String, Object> map) { + super(map); + + effect = SerializableMeta.getObject(FireworkEffect.class, map, EXPLOSION.BUKKIT, true); + } + + CraftMetaCharge(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(EXPLOSION.NBT)) { + effect = CraftMetaFirework.getEffect(tag.getCompound(EXPLOSION.NBT)); + } + } + + public void setEffect(FireworkEffect effect) { + this.effect = effect; + } + + public boolean hasEffect() { + return effect != null; + } + + public FireworkEffect getEffect() { + return effect; + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + if (hasEffect()) { + itemTag.set(EXPLOSION.NBT, CraftMetaFirework.getExplosion(effect)); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case FIREWORK_CHARGE: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && !hasChargeMeta(); + } + + boolean hasChargeMeta() { + return hasEffect(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaCharge) { + CraftMetaCharge that = (CraftMetaCharge) meta; + + return (hasEffect() ? that.hasEffect() && this.effect.equals(that.effect) : !that.hasEffect()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaCharge || !hasChargeMeta()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasEffect()) { + hash = 61 * hash + effect.hashCode(); + } + + return hash != original ? CraftMetaCharge.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaCharge clone() { + return (CraftMetaCharge) super.clone(); + } + + @Override + Builder<String, Object> serialize(Builder<String, Object> builder) { + super.serialize(builder); + + if (hasEffect()) { + builder.put(EXPLOSION.BUKKIT, effect); + } + + return builder; + } + + @Override + Deserializers deserializer() { + return Deserializers.FIREWORK_EFFECT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java new file mode 100644 index 00000000..4dd39444 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java @@ -0,0 +1,170 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta.Deserializers; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorageMeta { + static final ItemMetaKey STORED_ENCHANTMENTS = new ItemMetaKey("StoredEnchantments", "stored-enchants"); + + private Map<Enchantment, Integer> enchantments; + + CraftMetaEnchantedBook(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaEnchantedBook)) { + return; + } + + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + if (that.hasEnchants()) { + this.enchantments = new HashMap<Enchantment, Integer>(that.enchantments); + } + } + + CraftMetaEnchantedBook(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(STORED_ENCHANTMENTS.NBT)) { + return; + } + + enchantments = buildEnchantments(tag, STORED_ENCHANTMENTS); + } + + CraftMetaEnchantedBook(Map<String, Object> map) { + super(map); + + enchantments = buildEnchantments(map, STORED_ENCHANTMENTS); + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + applyEnchantments(enchantments, itemTag, STORED_ENCHANTMENTS); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case ENCHANTED_BOOK: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isEnchantedEmpty(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaEnchantedBook) { + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + return (hasStoredEnchants() ? that.hasStoredEnchants() && this.enchantments.equals(that.enchantments) : !that.hasStoredEnchants()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaEnchantedBook || isEnchantedEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasStoredEnchants()) { + hash = 61 * hash + enchantments.hashCode(); + } + + return original != hash ? CraftMetaEnchantedBook.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaEnchantedBook clone() { + CraftMetaEnchantedBook meta = (CraftMetaEnchantedBook) super.clone(); + + if (hasStoredEnchants()) { + meta.enchantments = new HashMap<Enchantment, Integer>(enchantments); + } + + return meta; + } + + @Override + Builder<String, Object> serialize(Builder<String, Object> builder) { + super.serialize(builder); + + serializeEnchantments(enchantments, builder, STORED_ENCHANTMENTS); + + return builder; + } + + @Override + Deserializers deserializer() { + return Deserializers.ENCHANTED; + } + + boolean isEnchantedEmpty() { + return !hasStoredEnchants(); + } + + public boolean hasStoredEnchant(Enchantment ench) { + return hasStoredEnchants() ? enchantments.containsKey(ench) : false; + } + + public int getStoredEnchantLevel(Enchantment ench) { + Integer level = hasStoredEnchants() ? enchantments.get(ench) : null; + if (level == null) { + return 0; + } + return level; + } + + public Map<Enchantment, Integer> getStoredEnchants() { + return hasStoredEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.<Enchantment, Integer>of(); + } + + public boolean addStoredEnchant(Enchantment ench, int level, boolean ignoreRestrictions) { + if (enchantments == null) { + enchantments = new HashMap<Enchantment, Integer>(4); + } + + if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) { + Integer old = enchantments.put(ench, level); + return old == null || old != level; + } + return false; + } + + public boolean removeStoredEnchant(Enchantment ench) { + return hasStoredEnchants() ? enchantments.remove(ench) != null : false; + } + + public boolean hasStoredEnchants() { + return !(enchantments == null || enchantments.isEmpty()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java new file mode 100644 index 00000000..67cc8b1c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java @@ -0,0 +1,392 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific.To; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta.Deserializers; +import org.bukkit.inventory.meta.FireworkMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaFirework extends CraftMetaItem implements FireworkMeta { + /* + "Fireworks", "Explosion", "Explosions", "Flight", "Type", "Trail", "Flicker", "Colors", "FadeColors"; + + Fireworks + - Compound: Fireworks + -- Byte: Flight + -- List: Explosions + --- Compound: Explosion + ---- IntArray: Colors + ---- Byte: Type + ---- Boolean: Trail + ---- Boolean: Flicker + ---- IntArray: FadeColors + */ + + @Specific(To.NBT) + static final ItemMetaKey FIREWORKS = new ItemMetaKey("Fireworks"); + static final ItemMetaKey FLIGHT = new ItemMetaKey("Flight", "power"); + static final ItemMetaKey EXPLOSIONS = new ItemMetaKey("Explosions", "firework-effects"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_COLORS = new ItemMetaKey("Colors"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TYPE = new ItemMetaKey("Type"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TRAIL = new ItemMetaKey("Trail"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FLICKER = new ItemMetaKey("Flicker"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FADE = new ItemMetaKey("FadeColors"); + + private List<FireworkEffect> effects; + private int power; + + CraftMetaFirework(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaFirework)) { + return; + } + + CraftMetaFirework that = (CraftMetaFirework) meta; + + this.power = that.power; + + if (that.hasEffects()) { + this.effects = new ArrayList<FireworkEffect>(that.effects); + } + } + + CraftMetaFirework(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(FIREWORKS.NBT)) { + return; + } + + NBTTagCompound fireworks = tag.getCompound(FIREWORKS.NBT); + + power = 0xff & fireworks.getByte(FLIGHT.NBT); + + if (!fireworks.hasKey(EXPLOSIONS.NBT)) { + return; + } + + NBTTagList fireworkEffects = fireworks.getList(EXPLOSIONS.NBT); + List<FireworkEffect> effects = this.effects = new ArrayList<FireworkEffect>(fireworkEffects.size()); + + for (int i = 0; i < fireworkEffects.size(); i++) { + effects.add(getEffect((NBTTagCompound) fireworkEffects.get(i))); + } + } + + static FireworkEffect getEffect(NBTTagCompound explosion) { + FireworkEffect.Builder effect = FireworkEffect.builder() + .flicker(explosion.getBoolean(EXPLOSION_FLICKER.NBT)) + .trail(explosion.getBoolean(EXPLOSION_TRAIL.NBT)) + .with(getEffectType(0xff & explosion.getByte(EXPLOSION_TYPE.NBT))); + + for (int color : explosion.getIntArray(EXPLOSION_COLORS.NBT)) { + effect.withColor(Color.fromRGB(color)); + } + + for (int color : explosion.getIntArray(EXPLOSION_FADE.NBT)) { + effect.withFade(Color.fromRGB(color)); + } + + return effect.build(); + } + + static NBTTagCompound getExplosion(FireworkEffect effect) { + NBTTagCompound explosion = new NBTTagCompound(); + + if (effect.hasFlicker()) { + explosion.setBoolean(EXPLOSION_FLICKER.NBT, true); + } + + if (effect.hasTrail()) { + explosion.setBoolean(EXPLOSION_TRAIL.NBT, true); + } + + addColors(explosion, EXPLOSION_COLORS, effect.getColors()); + addColors(explosion, EXPLOSION_FADE, effect.getFadeColors()); + + explosion.setByte(EXPLOSION_TYPE.NBT, (byte) getNBT(effect.getType())); + + return explosion; + } + + static int getNBT(Type type) { + switch (type) { + case BALL: + return 0; + case BALL_LARGE: + return 1; + case STAR: + return 2; + case CREEPER: + return 3; + case BURST: + return 4; + default: + throw new AssertionError(type); + } + } + + static Type getEffectType(int nbt) { + switch (nbt) { + case 0: + return Type.BALL; + case 1: + return Type.BALL_LARGE; + case 2: + return Type.STAR; + case 3: + return Type.CREEPER; + case 4: + return Type.BURST; + default: + throw new AssertionError(nbt); + } + } + + CraftMetaFirework(Map<String, Object> map) { + super(map); + + Integer power = SerializableMeta.getObject(Integer.class, map, FLIGHT.BUKKIT, true); + if (power != null) { + this.power = power; + } + + Iterable<?> effects = SerializableMeta.getObject(Iterable.class, map, EXPLOSIONS.BUKKIT, true); + safelyAddEffects(effects); + } + + public boolean hasEffects() { + return !(effects == null || effects.isEmpty()); + } + + void safelyAddEffects(Iterable<?> collection) { + if (collection == null || (collection instanceof Collection && ((Collection<?>) collection).isEmpty())) { + return; + } + + List<FireworkEffect> effects = this.effects; + if (effects == null) { + effects = this.effects = new ArrayList<FireworkEffect>(); + } + + for (Object obj : collection) { + if (obj instanceof FireworkEffect) { + effects.add((FireworkEffect) obj); + } else { + throw new IllegalArgumentException(obj + " in " + collection + " is not a FireworkEffect"); + } + } + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + if (isFireworkEmpty()) { + return; + } + + NBTTagCompound fireworks = itemTag.getCompound(FIREWORKS.NBT); + itemTag.setCompound(FIREWORKS.NBT, fireworks); + + if (hasEffects()) { + NBTTagList effects = new NBTTagList(EXPLOSIONS.NBT); + for (FireworkEffect effect : this.effects) { + effects.add(getExplosion(effect)); + } + + if (effects.size() > 0) { + fireworks.set(EXPLOSIONS.NBT, effects); + } + } + + if (hasPower()) { + fireworks.setByte(FLIGHT.NBT, (byte) power); + } + } + + static void addColors(NBTTagCompound compound, ItemMetaKey key, List<Color> colors) { + if (colors.isEmpty()) { + return; + } + + final int[] colorArray = new int[colors.size()]; + int i = 0; + for (Color color : colors) { + colorArray[i++] = color.asRGB(); + } + + compound.setIntArray(key.NBT, colorArray); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case FIREWORK: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isFireworkEmpty(); + } + + boolean isFireworkEmpty() { + return !(hasEffects() || hasPower()); + } + + boolean hasPower() { + return power != 0; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + + if (meta instanceof CraftMetaFirework) { + CraftMetaFirework that = (CraftMetaFirework) meta; + + return (hasPower() ? that.hasPower() && this.power == that.power : !that.hasPower()) + && (hasEffects() ? that.hasEffects() && this.effects.equals(that.effects) : !that.hasEffects()); + } + + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaFirework || isFireworkEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasPower()) { + hash = 61 * hash + power; + } + if (hasEffects()) { + hash = 61 * hash + 13 * effects.hashCode(); + } + return hash != original ? CraftMetaFirework.class.hashCode() ^ hash : hash; + } + + @Override + Builder<String, Object> serialize(Builder<String, Object> builder) { + super.serialize(builder); + + if (hasEffects()) { + builder.put(EXPLOSIONS.BUKKIT, ImmutableList.copyOf(effects)); + } + + if (hasPower()) { + builder.put(FLIGHT.BUKKIT, power); + } + + return builder; + } + + @Override + Deserializers deserializer() { + return Deserializers.FIREWORK; + } + + @Override + public CraftMetaFirework clone() { + CraftMetaFirework meta = (CraftMetaFirework) super.clone(); + + if (this.effects != null) { + meta.effects = new ArrayList<FireworkEffect>(this.effects); + } + + return meta; + } + + public void addEffect(FireworkEffect effect) { + Validate.notNull(effect, "Effect cannot be null"); + if (this.effects == null) { + this.effects = new ArrayList<FireworkEffect>(); + } + this.effects.add(effect); + } + + public void addEffects(FireworkEffect...effects) { + Validate.notNull(effects, "Effects cannot be null"); + if (effects.length == 0) { + return; + } + + List<FireworkEffect> list = this.effects; + if (list == null) { + list = this.effects = new ArrayList<FireworkEffect>(); + } + + for (FireworkEffect effect : effects) { + Validate.notNull(effect, "Effect cannot be null"); + list.add(effect); + } + } + + public void addEffects(Iterable<FireworkEffect> effects) { + Validate.notNull(effects, "Effects cannot be null"); + safelyAddEffects(effects); + } + + public List<FireworkEffect> getEffects() { + return this.effects == null ? ImmutableList.<FireworkEffect>of() : ImmutableList.copyOf(this.effects); + } + + public int getEffectsSize() { + return this.effects == null ? 0 : this.effects.size(); + } + + public void removeEffect(int index) { + if (this.effects == null) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); + } else { + this.effects.remove(index); + } + } + + public void clearEffects() { + this.effects = null; + } + + public int getPower() { + return this.power; + } + + public void setPower(int power) { + Validate.isTrue(power > 0, "Power cannot be less than zero: ", power); + Validate.isTrue(power < 0x80, "Power cannot be more than 127: ", power); + this.power = power; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 7c52e853..f8f9f261 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -1,5 +1,9 @@ package org.bukkit.craftbukkit.inventory; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -18,6 +22,7 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.DelegateDeserialization; import org.bukkit.configuration.serialization.SerializableAs; import org.bukkit.craftbukkit.Overridden; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; @@ -50,7 +55,20 @@ import com.google.common.collect.ImmutableMap; */ @DelegateDeserialization(CraftMetaItem.SerializableMeta.class) class CraftMetaItem implements ItemMeta, Repairable { + static class ItemMetaKey { + + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.FIELD) + @interface Specific { + enum To { + BUKKIT, + NBT, + ; + } + To value(); + } + final String BUKKIT; final String NBT; @@ -99,6 +117,24 @@ class CraftMetaItem implements ItemMeta, Repairable { return new CraftMetaPotion(map); } }, + ENCHANTED { + @Override + CraftMetaEnchantedBook deserialize(Map<String, Object> map) { + return new CraftMetaEnchantedBook(map); + } + }, + FIREWORK { + @Override + CraftMetaFirework deserialize(Map<String, Object> map) { + return new CraftMetaFirework(map); + } + }, + FIREWORK_EFFECT { + @Override + CraftMetaCharge deserialize(Map<String, Object> map) { + return new CraftMetaCharge(map); + } + }, UNSPECIFIC { @Override CraftMetaItem deserialize(Map<String, Object> map) { @@ -155,10 +191,13 @@ class CraftMetaItem implements ItemMeta, Repairable { } static final ItemMetaKey NAME = new ItemMetaKey("Name", "display-name"); + @Specific(Specific.To.NBT) static final ItemMetaKey DISPLAY = new ItemMetaKey("display"); static final ItemMetaKey LORE = new ItemMetaKey("Lore", "lore"); static final ItemMetaKey ENCHANTMENTS = new ItemMetaKey("ench", "enchants"); + @Specific(Specific.To.NBT) static final ItemMetaKey ENCHANTMENTS_ID = new ItemMetaKey("id"); + @Specific(Specific.To.NBT) static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl"); static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost"); @@ -204,21 +243,29 @@ class CraftMetaItem implements ItemMeta, Repairable { } } - if (tag.hasKey(ENCHANTMENTS.NBT)) { - NBTTagList ench = tag.getList(ENCHANTMENTS.NBT); - enchantments = new HashMap<Enchantment, Integer>(ench.size()); + this.enchantments = buildEnchantments(tag, ENCHANTMENTS); - for (int i = 0; i < ench.size(); i++) { - short id = ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_ID.NBT); - short level = ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + if (tag.hasKey(REPAIR.NBT)) { + repairCost = tag.getInt(REPAIR.NBT); + } + } - addEnchant(Enchantment.getById(id), (int) level, true); - } + static Map<Enchantment, Integer> buildEnchantments(NBTTagCompound tag, ItemMetaKey key) { + if (!tag.hasKey(key.NBT)) { + return null; } - if (tag.hasKey(REPAIR.NBT)) { - repairCost = tag.getInt(REPAIR.NBT); + NBTTagList ench = tag.getList(key.NBT); + Map<Enchantment, Integer> enchantments = new HashMap<Enchantment, Integer>(ench.size()); + + for (int i = 0; i < ench.size(); i++) { + int id = 0xffff & ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + + enchantments.put(Enchantment.getById(id), level); } + + return enchantments; } CraftMetaItem(Map<String, Object> map) { @@ -228,23 +275,31 @@ class CraftMetaItem implements ItemMeta, Repairable { lore = (List<String>) map.get(LORE.BUKKIT); } - Map<?, ?> ench = SerializableMeta.getObject(Map.class, map, ENCHANTMENTS.BUKKIT, true); - if (ench != null) { - enchantments = new HashMap<Enchantment, Integer>(ench.size()); - for (Map.Entry<?, ?> entry : ench.entrySet()) { - Enchantment enchantment = Enchantment.getByName(entry.getKey().toString()); - - if ((enchantment != null) && (entry.getValue() instanceof Integer)) { - addEnchant(enchantment, (Integer) entry.getValue(), true); - } - } - } + enchantments = buildEnchantments(map, ENCHANTMENTS); if (map.containsKey(REPAIR.BUKKIT)) { repairCost = (Integer) map.get(REPAIR.BUKKIT); } } + static Map<Enchantment, Integer> buildEnchantments(Map<String, Object> map, ItemMetaKey key) { + Map<?, ?> ench = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true); + if (ench == null) { + return null; + } + + Map<Enchantment, Integer> enchantments = new HashMap<Enchantment, Integer>(ench.size()); + for (Map.Entry<?, ?> entry : ench.entrySet()) { + Enchantment enchantment = Enchantment.getByName(entry.getKey().toString()); + + if ((enchantment != null) && (entry.getValue() instanceof Integer)) { + enchantments.put(enchantment, (Integer) entry.getValue()); + } + } + + return enchantments; + } + @Overridden void applyToItem(NBTTagCompound itemTag) { if (hasDisplayName()) { @@ -259,24 +314,30 @@ class CraftMetaItem implements ItemMeta, Repairable { setDisplayTag(itemTag, LORE.NBT, list); } - if (hasEnchants()) { - NBTTagList list = new NBTTagList(ENCHANTMENTS.NBT); + applyEnchantments(enchantments, itemTag, ENCHANTMENTS); - for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) { - NBTTagCompound subtag = new NBTTagCompound(); + if (hasRepairCost()) { + itemTag.setInt(REPAIR.NBT, repairCost); + } + } - subtag.setShort(ENCHANTMENTS_ID.NBT, (short) entry.getKey().getId()); - subtag.setShort(ENCHANTMENTS_LVL.NBT, (short) (int) entry.getValue()); + static void applyEnchantments(Map<Enchantment, Integer> enchantments, NBTTagCompound tag, ItemMetaKey key) { + if (enchantments == null || enchantments.size() == 0) { + return; + } - list.add(subtag); - } + NBTTagList list = new NBTTagList(key.NBT); - itemTag.set(ENCHANTMENTS.NBT, list); - } + for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) { + NBTTagCompound subtag = new NBTTagCompound(); - if (hasRepairCost()) { - itemTag.setInt(REPAIR.NBT, repairCost); + subtag.setShort(ENCHANTMENTS_ID.NBT, (short) entry.getKey().getId()); + subtag.setShort(ENCHANTMENTS_LVL.NBT, entry.getValue().shortValue()); + + list.add(subtag); } + + tag.set(key.NBT, list); } void setDisplayTag(NBTTagCompound tag, String key, NBTBase value) { @@ -459,13 +520,7 @@ class CraftMetaItem implements ItemMeta, Repairable { builder.put(LORE.BUKKIT, ImmutableList.copyOf(lore)); } - if (hasEnchants()) { - ImmutableMap.Builder<String, Integer> enchantments = ImmutableMap.builder(); - for (Map.Entry<Enchantment, Integer> enchant : this.enchantments.entrySet()) { - enchantments.put(enchant.getKey().getName(), enchant.getValue()); - } - builder.put(ENCHANTMENTS.BUKKIT, enchantments.build()); - } + serializeEnchantments(enchantments, builder, ENCHANTMENTS); if (hasRepairCost()) { builder.put(REPAIR.BUKKIT, repairCost); @@ -474,6 +529,19 @@ class CraftMetaItem implements ItemMeta, Repairable { return builder; } + static void serializeEnchantments(Map<Enchantment, Integer> enchantments, ImmutableMap.Builder<String, Object> builder, ItemMetaKey key) { + if (enchantments == null || enchantments.isEmpty()) { + return; + } + + ImmutableMap.Builder<String, Integer> enchants = ImmutableMap.builder(); + for (Map.Entry<? extends Enchantment, Integer> enchant : enchantments.entrySet()) { + enchants.put(enchant.getKey().getName(), enchant.getValue()); + } + + builder.put(key.BUKKIT, enchants.build()); + } + @Overridden SerializableMeta.Deserializers deserializer() { return SerializableMeta.Deserializers.UNSPECIFIC; @@ -505,6 +573,6 @@ class CraftMetaItem implements ItemMeta, Repairable { @Override public final String toString() { - return serialize().toString(); // TODO: cry + return deserializer().toString() + "_META:" + serialize(); // TODO: cry } } |