package com.bergerkiller.bukkit.common.utils; import java.io.DataInputStream; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import org.bukkit.block.BlockState; import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.LivingEntity; import org.bukkit.inventory.Inventory; import com.bergerkiller.bukkit.common.conversion.Conversion; import com.bergerkiller.bukkit.common.conversion.type.HandleConverter; import com.bergerkiller.bukkit.common.internal.CommonNMS; import com.bergerkiller.bukkit.common.nbt.CommonTag; import com.bergerkiller.bukkit.common.nbt.CommonTagCompound; import com.bergerkiller.bukkit.common.nbt.CommonTagList; import com.bergerkiller.bukkit.common.nbt.NBTTagInfo; import com.bergerkiller.bukkit.common.reflection.classes.EntityLivingRef; import com.bergerkiller.bukkit.common.reflection.classes.NBTRef; import net.minecraft.server.AttributeMapServer; import net.minecraft.server.Entity; import net.minecraft.server.FoodMetaData; import net.minecraft.server.GenericAttributes; import net.minecraft.server.InventoryEnderChest; import net.minecraft.server.ItemStack; import net.minecraft.server.MobEffect; import net.minecraft.server.NBTCompressedStreamTools; import net.minecraft.server.NBTTagCompound; import net.minecraft.server.NBTTagList; import net.minecraft.server.PlayerInventory; /** * Contains utility functions for dealing with NBT data */ public class NBTUtil { /** * Creates an NBT Tag handle to store the data specified in<br> * All primitive types, including byte[] and int[], and list/maps are supported * * @param name of the handle * @param data to store in this handle initially * @return new handle */ public static Object createHandle(Object data) { return NBTTagInfo.findInfo(data).createHandle(data); } /** * Obtains the raw data from an NBT Tag handle. * NBTTagList and NBTTagCompound return a List and Map of NBT Tags respectively. * If a null handle is specified, null is returned to indicate no data. * * @param nbtTagHandle to get the value of * @return the NBTTag data */ public static Object getData(Object nbtTagHandle) { if (nbtTagHandle == null) { return null; } return NBTTagInfo.findInfo(nbtTagHandle).getData(nbtTagHandle); } /** * Gets the type Id of the tag used to identify it * * @param nbtTagHandle to read from * @return tag type id */ public static byte getTypeId(Object nbtTagHandle) { if (nbtTagHandle == null) { return (byte) 0; } return NBTRef.getTypeId.invoke(nbtTagHandle).byteValue(); } /** * Reads an NBTTagCompound handle from an input stream. * This method expects an Input Stream containing GZIP-compressed data. * * @param stream to read from * @return NBTTagCompound * @throws IOException */ public static Object readCompound(InputStream stream) throws IOException { return NBTCompressedStreamTools.a(stream); } /** * Reads an NBTTagCompound handle from an input stream. * This method expects an Input Stream containing raw, uncompressed data. * * @param stream to read from * @return NBTTagCompound * @throws IOException */ public static Object readCompoundUncompressed(InputStream stream) throws IOException { return NBTCompressedStreamTools.a(stream); } /** * Writes an NBTTagCompound to an output stream. * This method writes the compound as GZIP-compressed data. * * @param compound to write * @param stream to write to * @throws IOException */ public static void writeCompound(Object compound, OutputStream stream) throws IOException { NBTCompressedStreamTools.a((NBTTagCompound) compound, stream); } /** * Writes an NBTTagCompound to an output stream. * This method writes the compound as raw, uncompressed data. * * @param compound to write * @param stream to write to * @throws IOException */ public static void writeCompoundUncompressed(Object compound, OutputStream stream) throws IOException { NBTCompressedStreamTools.a((NBTTagCompound) compound, stream); } /** * Reads a mob effect from an NBT Tag Compound * * @param compound to read a mob effect from * @return Loaded MobEffect */ public static Object loadMobEffect(CommonTagCompound compound) { return MobEffect.b((NBTTagCompound) compound.getHandle()); } /** * Saves entity data to the Tag Compound specified * * @param entity to save * @param compound to save to, use null to save to a new compound * @return the compound to which was saved */ public static CommonTagCompound saveEntity(org.bukkit.entity.Entity entity, CommonTagCompound compound) { if (compound == null) { compound = new CommonTagCompound(); } ((Entity) Conversion.toEntityHandle.convert(entity)).e((NBTTagCompound) compound.getHandle()); return compound; } /** * Loads an entity with data from the Tag Compound specified * * @param entity to load * @param compound to load from */ public static void loadEntity(org.bukkit.entity.Entity entity, CommonTagCompound compound) { ((Entity) Conversion.toEntityHandle.convert(entity)).f((NBTTagCompound) compound.getHandle()); } /** * Saves an itemstack to a Tag Compound * * @param item ItemStack * @param compound TagCompound */ public static void saveItemStack(org.bukkit.inventory.ItemStack item, CommonTagCompound compound) { ItemStack stack = (ItemStack) HandleConverter.toItemStackHandle.convert(item); stack.save((NBTTagCompound) compound.getHandle()); } /** * Saves a tag list to an output * * @param list Tag list * @param out Output */ public static void saveList(CommonTagList list, DataOutput out) { NBTRef.writeTag(list.getHandle(), out); } /** * Creates an inventory from a tag list * * @param tags Tag list * @return Inventory */ public static Inventory createInventory(CommonTagList tags) { Inventory inv = new CraftInventoryCustom(null, tags.size()); for(int i = 0; i < tags.size(); i++) { CommonTagCompound tag = (CommonTagCompound) tags.get(i); if(!tag.isEmpty()) { inv.setItem(i, CraftItemStack.asCraftMirror(ItemStack.createStack( (NBTTagCompound) tag.getHandle()))); } } return inv; } /** * Creates a tag list from a DataInputStream * * @param in InputStream * @return Tag list */ public static CommonTagList createList(DataInputStream in) { return new CommonTagList(NBTRef.readTag(in)); } /** * Saves food meta data to the NBT Tag Compound specified * * @param foodMetaData to save * @param compound to save to, use null to save to a new compound * @return the compound to which was saved */ public static CommonTagCompound saveFoodMetaData(Object foodMetaData, CommonTagCompound compound) { if (compound == null) { compound = new CommonTagCompound(); } ((FoodMetaData) foodMetaData).b((NBTTagCompound) compound.getHandle()); return compound; } /** * Loads Food Meta Data with data from the Tag Compound specified * * @param foodMetaData to load * @param compound to load from */ public static void loadFoodMetaData(Object foodMetaData, CommonTagCompound compound) { ((FoodMetaData) foodMetaData).a((NBTTagCompound) compound.getHandle()); } /** * Saves inventory data to the tag list specified * * @param inventory to save * @param list to save to, use null for a new list * @return the saved tag list */ public static CommonTagList saveInventory(org.bukkit.inventory.Inventory inventory, CommonTagList list) { final Object inventoryHandle = Conversion.toInventoryHandle.convert(inventory); if (inventoryHandle == null) { throw new IllegalArgumentException("This kind of inventory lacks a handle to load"); } if (inventoryHandle instanceof PlayerInventory) { if (list == null) { list = new CommonTagList(); } ((PlayerInventory) inventoryHandle).a((NBTTagList) list.getHandle()); } else if (inventoryHandle instanceof InventoryEnderChest) { Object handle = ((InventoryEnderChest) inventoryHandle).h(); if (list == null) { return (CommonTagList) CommonTag.create(handle); } else { List<?> data = (List<?>) getData(handle); for (Object elem : data) { list.addValue(elem); } return list; } } else { throw new IllegalArgumentException("This kind of inventory has an unknown type of handle: " + inventoryHandle.getClass().getName()); } return list; } /** * Loads inventory data from the Tag List specified * * @param inventory to load * @param list to load from */ public static void loadInventory(org.bukkit.inventory.Inventory inventory, CommonTagList list) { final Object inventoryHandle = Conversion.toInventoryHandle.convert(inventory); NBTTagList nbt = (NBTTagList) list.getHandle(); if (inventoryHandle == null) { throw new IllegalArgumentException("This kind of inventory lacks a handle to save"); } else if (inventoryHandle instanceof PlayerInventory) { ((PlayerInventory) inventoryHandle).b(nbt); } else if (inventoryHandle instanceof InventoryEnderChest) { ((InventoryEnderChest) inventoryHandle).a(nbt); } else { throw new IllegalArgumentException("This kind of inventory has an unknown type of handle: " + inventoryHandle.getClass().getName()); } } /** * Resets all attributes set for an Entity to the defaults. * This should be called prior to loading in new attributes using * {@link #loadAttributes(LivingEntity, CommonTagList)} * * @param livingEntity to reset */ public static void resetAttributes(LivingEntity livingEntity) { Object livingHandle = Conversion.toEntityHandle.convert(livingEntity); // Clear old attributes and force a re-create EntityLivingRef.attributeMap.set(livingHandle, null); EntityLivingRef.resetAttributes.invoke(livingHandle); } /** * Loads the attributes for an Entity, applying the new attributes to the entity * * @param livingEntity to load * @param data to load from */ public static void loadAttributes(LivingEntity livingEntity, CommonTagList data) { AttributeMapServer map = CommonNMS.getEntityAttributes(livingEntity); GenericAttributes.a(map, (NBTTagList) data.getHandle()); } /** * Saves the current attributes of an Entity to a new CommonTagList * * @param livingEntity to save * @return CommonTagList containing the saved data */ public static CommonTagList saveAttributes(LivingEntity livingEntity) { AttributeMapServer map = CommonNMS.getEntityAttributes(livingEntity); return (CommonTagList) CommonTag.create(GenericAttributes.a(map)); } /** * Loads the Block State information from a data compound * * @param blockState to load * @param data to load into the blockState */ public static void loadBlockState(BlockState blockState, CommonTagCompound data) { CommonNMS.getNative(blockState).a((NBTTagCompound) data.getHandle()); } /** * Saves the Block State information to a data compound * * @param blockState to save * @return compound with saved data */ public static CommonTagCompound saveBlockState(BlockState blockState) { return saveBlockState(blockState, null); } /** * Saves the Block State information to a data compound * * @param blockState to save * @param data to save the blockState to, null to create a new compound * @return compound with saved data */ public static CommonTagCompound saveBlockState(BlockState blockState, CommonTagCompound data) { if (data == null) { data = new CommonTagCompound(); } CommonNMS.getNative(blockState).b((NBTTagCompound) data.getHandle()); return data; } }