SPIGOT-1592: Implement ItemMeta for Spawn Eggs

The Minecraft implementation of spawn eggs is able to construct an entity using all data that is present in the save format, however since the Bukkit API has no such way to construct an entity unattached to a world, and it appears creating such a way is a very challenging task, the decision was instead made to add this API now that 1.11 has entities which may not be represented by data values.
In the future it may be possible to implement a more expanded API cognate with this one.
This commit is contained in:
Matthew 2016-12-06 21:15:10 +11:00 committed by md_5
parent 9dee10873d
commit 04202c0ace
5 changed files with 164 additions and 0 deletions

View File

@ -91,6 +91,8 @@ public final class CraftItemFactory implements ItemFactory {
return meta instanceof CraftMetaEnchantedBook ? meta : new CraftMetaEnchantedBook(meta); return meta instanceof CraftMetaEnchantedBook ? meta : new CraftMetaEnchantedBook(meta);
case BANNER: case BANNER:
return meta instanceof CraftMetaBanner ? meta : new CraftMetaBanner(meta); return meta instanceof CraftMetaBanner ? meta : new CraftMetaBanner(meta);
case MONSTER_EGG:
return meta instanceof CraftMetaSpawnEgg ? meta : new CraftMetaSpawnEgg(meta);
case FURNACE: case FURNACE:
case CHEST: case CHEST:
case TRAPPED_CHEST: case TRAPPED_CHEST:

View File

@ -348,6 +348,8 @@ public final class CraftItemStack extends ItemStack {
return new CraftMetaEnchantedBook(item.getTag()); return new CraftMetaEnchantedBook(item.getTag());
case BANNER: case BANNER:
return new CraftMetaBanner(item.getTag()); return new CraftMetaBanner(item.getTag());
case MONSTER_EGG:
return new CraftMetaSpawnEgg(item.getTag());
case FURNACE: case FURNACE:
case CHEST: case CHEST:
case TRAPPED_CHEST: case TRAPPED_CHEST:

View File

@ -115,6 +115,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
.put(CraftMetaLeatherArmor.class, "LEATHER_ARMOR") .put(CraftMetaLeatherArmor.class, "LEATHER_ARMOR")
.put(CraftMetaMap.class, "MAP") .put(CraftMetaMap.class, "MAP")
.put(CraftMetaPotion.class, "POTION") .put(CraftMetaPotion.class, "POTION")
.put(CraftMetaSpawnEgg.class, "SPAWN_EGG")
.put(CraftMetaEnchantedBook.class, "ENCHANTED") .put(CraftMetaEnchantedBook.class, "ENCHANTED")
.put(CraftMetaFirework.class, "FIREWORK") .put(CraftMetaFirework.class, "FIREWORK")
.put(CraftMetaCharge.class, "FIREWORK_EFFECT") .put(CraftMetaCharge.class, "FIREWORK_EFFECT")
@ -839,6 +840,7 @@ class CraftMetaItem implements ItemMeta, Repairable {
CraftMetaPotion.DEFAULT_POTION.NBT, CraftMetaPotion.DEFAULT_POTION.NBT,
CraftMetaSkull.SKULL_OWNER.NBT, CraftMetaSkull.SKULL_OWNER.NBT,
CraftMetaSkull.SKULL_PROFILE.NBT, CraftMetaSkull.SKULL_PROFILE.NBT,
CraftMetaSpawnEgg.ENTITY_TAG.NBT,
CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT,
CraftMetaBook.BOOK_TITLE.NBT, CraftMetaBook.BOOK_TITLE.NBT,
CraftMetaBook.BOOK_AUTHOR.NBT, CraftMetaBook.BOOK_AUTHOR.NBT,

View File

@ -0,0 +1,148 @@
package org.bukkit.craftbukkit.inventory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap.Builder;
import java.util.Map;
import net.minecraft.server.MinecraftKey;
import net.minecraft.server.NBTTagCompound;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.meta.SpawnEggMeta;
@DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
static final ItemMetaKey ENTITY_TAG = new ItemMetaKey("EntityTag", "entity-tag");
@ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT)
static final ItemMetaKey ENTITY_ID = new ItemMetaKey("id");
private EntityType spawnedType;
CraftMetaSpawnEgg(CraftMetaItem meta) {
super(meta);
if (!(meta instanceof CraftMetaSpawnEgg)) {
return;
}
CraftMetaSpawnEgg egg = (CraftMetaSpawnEgg) meta;
this.spawnedType = egg.spawnedType;
}
CraftMetaSpawnEgg(NBTTagCompound tag) {
super(tag);
if (tag.hasKey(ENTITY_TAG.NBT)) {
NBTTagCompound entityTag = tag.getCompound(ENTITY_TAG.NBT);
if (entityTag.hasKey(ENTITY_ID.NBT)) {
this.spawnedType = EntityType.fromName(new MinecraftKey(entityTag.getString(ENTITY_ID.NBT)).a()); // PAIL: rename
}
}
}
CraftMetaSpawnEgg(Map<String, Object> map) {
super(map);
String entityType = SerializableMeta.getString(map, ENTITY_ID.BUKKIT, true);
setSpawnedType(EntityType.fromName(entityType));
}
@Override
void applyToItem(NBTTagCompound tag) {
super.applyToItem(tag);
if (hasSpawnedType()) {
NBTTagCompound entityTag = new NBTTagCompound();
entityTag.setString(ENTITY_ID.NBT, new MinecraftKey(spawnedType.getName()).toString());
tag.set(ENTITY_TAG.NBT, entityTag);
}
}
@Override
boolean applicableTo(Material type) {
switch (type) {
case MONSTER_EGG:
return true;
default:
return false;
}
}
@Override
boolean isEmpty() {
return super.isEmpty() && isSpawnEggEmpty();
}
boolean isSpawnEggEmpty() {
return !hasSpawnedType();
}
boolean hasSpawnedType() {
return spawnedType != null;
}
@Override
public EntityType getSpawnedType() {
return spawnedType;
}
@Override
public void setSpawnedType(EntityType type) {
Preconditions.checkArgument(type == null || type.getName() != null, "Spawn egg type must have name (%s)", type);
this.spawnedType = type;
}
@Override
boolean equalsCommon(CraftMetaItem meta) {
if (!super.equalsCommon(meta)) {
return false;
}
if (meta instanceof CraftMetaSpawnEgg) {
CraftMetaSpawnEgg that = (CraftMetaSpawnEgg) meta;
return hasSpawnedType() ? that.hasSpawnedType() && this.spawnedType.equals(that.spawnedType) : !that.hasSpawnedType();
}
return true;
}
@Override
boolean notUncommon(CraftMetaItem meta) {
return super.notUncommon(meta) && (meta instanceof CraftMetaSpawnEgg || isSpawnEggEmpty());
}
@Override
int applyHash() {
final int original;
int hash = original = super.applyHash();
if (hasSpawnedType()) {
hash = 73 * hash + spawnedType.hashCode();
}
return original != hash ? CraftMetaSpawnEgg.class.hashCode() ^ hash : hash;
}
@Override
Builder<String, Object> serialize(Builder<String, Object> builder) {
super.serialize(builder);
if (hasSpawnedType()) {
builder.put(ENTITY_ID.BUKKIT, spawnedType.getName());
}
return builder;
}
@Override
public CraftMetaSpawnEgg clone() {
CraftMetaSpawnEgg clone = (CraftMetaSpawnEgg) super.clone();
clone.spawnedType = spawnedType;
return clone;
}
}

View File

@ -24,6 +24,7 @@ import org.bukkit.craftbukkit.inventory.ItemStackTest.StackWrapper;
import org.bukkit.craftbukkit.inventory.ItemStackTest.BukkitWrapper; import org.bukkit.craftbukkit.inventory.ItemStackTest.BukkitWrapper;
import org.bukkit.craftbukkit.inventory.ItemStackTest.CraftWrapper; import org.bukkit.craftbukkit.inventory.ItemStackTest.CraftWrapper;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BannerMeta; import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BlockStateMeta;
@ -35,6 +36,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.inventory.meta.SpawnEggMeta;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionType; import org.bukkit.potion.PotionType;
@ -255,6 +257,14 @@ public class ItemMetaTest extends AbstractTestingBase {
cleanStack.setItemMeta(meta); cleanStack.setItemMeta(meta);
return cleanStack; return cleanStack;
} }
},
new StackProvider(Material.MONSTER_EGG) {
@Override ItemStack operate(ItemStack cleanStack) {
final SpawnEggMeta meta = (SpawnEggMeta) cleanStack.getItemMeta();
meta.setSpawnedType(EntityType.ZOMBIE);
cleanStack.setItemMeta(meta);
return cleanStack;
}
} }
); );