From 12ecbc64a096015d436ae825058ee4f0ebd49fae Mon Sep 17 00:00:00 2001 From: CraftBukkit/Spigot Date: Wed, 1 May 2019 20:18:01 +1000 Subject: [PATCH] SPIGOT-4802: Add CrossbowMeta By: md_5 --- .../inventory/CraftItemFactory.java | 2 + .../craftbukkit/inventory/CraftItemStack.java | 2 + .../inventory/CraftMetaCrossbow.java | 202 ++++++++++++++++++ .../craftbukkit/inventory/CraftMetaItem.java | 5 +- .../craftbukkit/inventory/ItemMetaTest.java | 9 + 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java index a01368f697..a4320f8446 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java @@ -252,6 +252,8 @@ public final class CraftItemFactory implements ItemFactory { return new CraftMetaBlockState(meta, material); case TROPICAL_FISH_BUCKET: return meta instanceof CraftMetaTropicalFishBucket ? meta : new CraftMetaTropicalFishBucket(meta); + case CROSSBOW: + return meta instanceof CraftMetaCrossbow ? meta : new CraftMetaCrossbow(meta); default: return new CraftMetaItem(meta); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 73dd802205..42f7bb0f7d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -503,6 +503,8 @@ public final class CraftItemStack extends ItemStack { return new CraftMetaBlockState(item.getTag(), CraftMagicNumbers.getMaterial(item.getItem())); case TROPICAL_FISH_BUCKET: return new CraftMetaTropicalFishBucket(item.getTag()); + case CROSSBOW: + return new CraftMetaCrossbow(item.getTag()); default: return new CraftMetaItem(item.getTag()); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java new file mode 100644 index 0000000000..b3ace0bf2d --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java @@ -0,0 +1,202 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import net.minecraft.server.ItemArrow; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.CrossbowMeta; + +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +public class CraftMetaCrossbow extends CraftMetaItem implements CrossbowMeta { + + static final ItemMetaKey CHARGED = new ItemMetaKey("Charged", "charged"); + static final ItemMetaKey CHARGED_PROJECTILES = new ItemMetaKey("ChargedProjectiles", "charged-projectiles"); + // + private boolean charged; + private List chargedProjectiles; + + CraftMetaCrossbow(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaCrossbow)) { + return; + } + + CraftMetaCrossbow crossbow = (CraftMetaCrossbow) meta; + this.charged = crossbow.charged; + + if (crossbow.hasChargedProjectiles()) { + this.chargedProjectiles = new ArrayList<>(crossbow.chargedProjectiles); + } + } + + CraftMetaCrossbow(NBTTagCompound tag) { + super(tag); + + charged = tag.getBoolean(CHARGED.NBT); + + if (tag.hasKeyOfType(CHARGED_PROJECTILES.NBT, CraftMagicNumbers.NBT.TAG_LIST)) { + NBTTagList list = tag.getList(CHARGED_PROJECTILES.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + + if (list != null && !list.isEmpty()) { + chargedProjectiles = new ArrayList<>(); + + for (int i = 0; i < list.size(); i++) { + NBTTagCompound nbttagcompound1 = list.getCompound(i); + + chargedProjectiles.add(CraftItemStack.asCraftMirror(net.minecraft.server.ItemStack.a(nbttagcompound1))); + } + } + } + } + + CraftMetaCrossbow(Map map) { + super(map); + + Boolean charged = SerializableMeta.getObject(Boolean.class, map, CHARGED.BUKKIT, true); + if (charged != null) { + this.charged = charged; + } + + Iterable projectiles = SerializableMeta.getObject(Iterable.class, map, CHARGED_PROJECTILES.BUKKIT, true); + if (projectiles != null) { + for (Object stack : projectiles) { + if (stack instanceof ItemStack) { + addChargedProjectile((ItemStack) stack); + } + } + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + tag.setBoolean(CHARGED.NBT, charged); + if (hasChargedProjectiles()) { + NBTTagList list = new NBTTagList(); + + for (ItemStack item : chargedProjectiles) { + NBTTagCompound saved = new NBTTagCompound(); + CraftItemStack.asNMSCopy(item).save(saved); + list.add(saved); + } + + tag.set(CHARGED_PROJECTILES.NBT, list); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case CROSSBOW: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isCrossbowEmpty(); + } + + boolean isCrossbowEmpty() { + return !(hasChargedProjectiles()); + } + + @Override + public boolean hasChargedProjectiles() { + return chargedProjectiles != null; + } + + @Override + public List getChargedProjectiles() { + return (chargedProjectiles == null) ? null : ImmutableList.copyOf(chargedProjectiles); + } + + @Override + public void setChargedProjectiles(List projectiles) { + chargedProjectiles = null; + charged = false; + + if (projectiles == null) { + return; + } + + for (ItemStack i : projectiles) { + addChargedProjectile(i); + } + } + + @Override + public void addChargedProjectile(ItemStack item) { + Preconditions.checkArgument(item != null, "item"); + Preconditions.checkArgument(CraftMagicNumbers.getItem(item.getType()) instanceof ItemArrow, "Item %s is not an arrow", item); + + if (chargedProjectiles == null) { + chargedProjectiles = new ArrayList<>(); + } + + charged = true; + chargedProjectiles.add(item); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaCrossbow) { + CraftMetaCrossbow that = (CraftMetaCrossbow) meta; + + return this.charged == that.charged + && (hasChargedProjectiles() ? that.hasChargedProjectiles() && this.chargedProjectiles.equals(that.chargedProjectiles) : !that.hasChargedProjectiles()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaCrossbow || isCrossbowEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasChargedProjectiles()) { + hash = 61 * hash + (this.charged ? 1 : 0); + hash = 61 * hash + chargedProjectiles.hashCode(); + } + + return original != hash ? CraftMetaCrossbow.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaCrossbow clone() { + return (CraftMetaCrossbow) super.clone(); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + builder.put(CHARGED.BUKKIT, charged); + if (hasChargedProjectiles()) { + builder.put(CHARGED_PROJECTILES.BUKKIT, chargedProjectiles); + } + + return builder; + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 5b8c694f9a..e09ecd58a1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -153,6 +153,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { .put(CraftMetaCharge.class, "FIREWORK_EFFECT") .put(CraftMetaKnowledgeBook.class, "KNOWLEDGE_BOOK") .put(CraftMetaTropicalFishBucket.class, "TROPICAL_FISH_BUCKET") + .put(CraftMetaCrossbow.class, "CROSSBOW") .put(CraftMetaItem.class, "UNSPECIFIC") .build(); @@ -1403,7 +1404,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { CraftMetaCharge.EXPLOSION.NBT, CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, CraftMetaKnowledgeBook.BOOK_RECIPES.NBT, - CraftMetaTropicalFishBucket.VARIANT.NBT + CraftMetaTropicalFishBucket.VARIANT.NBT, + CraftMetaCrossbow.CHARGED.NBT, + CraftMetaCrossbow.CHARGED_PROJECTILES.NBT )); } return HANDLED_TAGS; diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java index 176b8f5284..6abde9030d 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java @@ -35,6 +35,7 @@ import org.bukkit.inventory.meta.BannerMeta; import org.bukkit.inventory.meta.BlockDataMeta; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.CrossbowMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.FireworkEffectMeta; import org.bukkit.inventory.meta.FireworkMeta; @@ -335,6 +336,14 @@ public class ItemMetaTest extends AbstractTestingBase { cleanStack.setItemMeta(meta); return cleanStack; } + }, + new StackProvider(Material.CROSSBOW) { + @Override ItemStack operate(ItemStack cleanStack) { + final CrossbowMeta meta = (CrossbowMeta) cleanStack.getItemMeta(); + meta.addChargedProjectile(new ItemStack(Material.ARROW)); + cleanStack.setItemMeta(meta); + return cleanStack; + } } );