| package net.glowstone.io.nbt; |
| |
| import net.glowstone.GlowServer; |
| import net.glowstone.constants.ItemIds; |
| import net.glowstone.inventory.GlowItemFactory; |
| import net.glowstone.util.nbt.CompoundTag; |
| import net.glowstone.util.nbt.TagType; |
| import org.bukkit.Location; |
| import org.bukkit.Material; |
| import org.bukkit.World; |
| import org.bukkit.inventory.ItemStack; |
| import org.bukkit.util.Vector; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.UUID; |
| |
| /** |
| * Utility methods for transforming various objects to and from NBT. |
| */ |
| public final class NbtSerialization { |
| |
| private NbtSerialization() { |
| } |
| |
| /** |
| * Read an item stack in from an NBT tag. Returns null if no item exists. |
| * @param tag The tag to read from. |
| * @return The resulting ItemStack, or null. |
| */ |
| public static ItemStack readItem(CompoundTag tag) { |
| final Material material; |
| if (tag.isString("id")) { |
| material = ItemIds.getMaterial(tag.getString("id")); |
| } else if (tag.isShort("id")) { |
| material = Material.getMaterial(tag.getShort("id")); |
| } else { |
| return null; |
| } |
| final short damage = tag.isShort("Damage") ? tag.getShort("Damage") : 0; |
| final byte count = tag.isByte("Count") ? tag.getByte("Count") : 0; |
| |
| if (material == null || material == Material.AIR || count == 0) { |
| return null; |
| } |
| ItemStack stack = new ItemStack(material, count, damage); |
| if (tag.isCompound("tag")) { |
| stack.setItemMeta(GlowItemFactory.instance().readNbt(material, tag.getCompound("tag"))); |
| } |
| return stack; |
| } |
| |
| /** |
| * Write an item stack to an NBT tag. Null stacks produce an empty tag, |
| * and if slot is negative it is omitted from the result. |
| * @param stack The stack to write, or null. |
| * @param slot The slot, or negative to omit. |
| * @return The resulting tag. |
| */ |
| public static CompoundTag writeItem(ItemStack stack, int slot) { |
| CompoundTag tag = new CompoundTag(); |
| if (stack == null || stack.getType() == Material.AIR) { |
| return tag; |
| } |
| tag.putString("id", ItemIds.getName(stack.getType())); |
| tag.putShort("Damage", stack.getDurability()); |
| tag.putByte("Count", stack.getAmount()); |
| if (slot >= 0) { |
| tag.putByte("Slot", slot); |
| } |
| CompoundTag meta = GlowItemFactory.instance().writeNbt(stack.getItemMeta()); |
| if (meta != null) { |
| tag.putCompound("tag", meta); |
| } |
| return tag; |
| } |
| |
| /** |
| * Read a full inventory (players, chests, etc.) from a compound list. |
| * @param tagList The list of CompoundTags to read from. |
| * @param start The slot number to consider the inventory's start. |
| * @param size The desired size of the inventory. |
| * @return An array with the contents of the inventory. |
| */ |
| public static ItemStack[] readInventory(List<CompoundTag> tagList, int start, int size) { |
| ItemStack[] items = new ItemStack[size]; |
| for (CompoundTag tag : tagList) { |
| byte slot = tag.isByte("Slot") ? tag.getByte("Slot") : 0; |
| if (slot >= start && slot < start + size) { |
| items[slot - start] = readItem(tag); |
| } |
| } |
| return items; |
| } |
| |
| /** |
| * Write a full inventory (players, chests, etc.) to a compound list. |
| * @param items An array with the contents of the inventory. |
| * @param start The slot number to consider the inventory's start. |
| * @return The list of CompoundTags. |
| */ |
| public static List<CompoundTag> writeInventory(ItemStack[] items, int start) { |
| List<CompoundTag> out = new ArrayList<>(); |
| for (int i = 0; i < items.length; i++) { |
| ItemStack stack = items[i]; |
| if (stack != null) { |
| out.add(writeItem(stack, start + i)); |
| } |
| } |
| return out; |
| } |
| |
| /** |
| * Attempt to resolve a world based on the contents of a compound tag. |
| * @param server The server to look up worlds in. |
| * @param compound The tag to read the world from. |
| * @return The world, or null if none could be found. |
| */ |
| public static World readWorld(GlowServer server, CompoundTag compound) { |
| World world = null; |
| if (compound.isLong("WorldUUIDLeast") && compound.isLong("WorldUUIDMost")) { |
| long uuidLeast = compound.getLong("WorldUUIDLeast"); |
| long uuidMost = compound.getLong("WorldUUIDMost"); |
| world = server.getWorld(new UUID(uuidMost, uuidLeast)); |
| } |
| if (world == null && compound.isString("World")) { |
| world = server.getWorld(compound.getString("World")); |
| } |
| if (world == null && compound.isInt("Dimension")) { |
| int dim = compound.getInt("Dimension"); |
| for (World sWorld : server.getWorlds()) { |
| if (sWorld.getEnvironment().getId() == dim) { |
| world = sWorld; |
| break; |
| } |
| } |
| } |
| return world; |
| } |
| |
| /** |
| * Save world identifiers (UUID and dimension) to a compound tag for |
| * later lookup. |
| * @param world The world to identify. |
| * @param compound The tag to write to. |
| */ |
| public static void writeWorld(World world, CompoundTag compound) { |
| UUID worldUUID = world.getUID(); |
| // world UUID used by Bukkit and code above |
| compound.putLong("WorldUUIDMost", worldUUID.getMostSignificantBits()); |
| compound.putLong("WorldUUIDLeast", worldUUID.getLeastSignificantBits()); |
| // leave a Dimension value for possible Vanilla use |
| compound.putInt("Dimension", world.getEnvironment().getId()); |
| } |
| |
| /** |
| * Read a Location from the "Pos" and "Rotation" children of a tag. If |
| * "Pos" is absent or invalid, null is returned. If "Rotation" is absent |
| * or invalid, it is skipped and a location without rotation is returned. |
| * @param world The world of the location (see readWorld). |
| * @param tag The tag to read from. |
| * @return The location, or null. |
| */ |
| public static Location listTagsToLocation(World world, CompoundTag tag) { |
| // check for position list |
| if (tag.isList("Pos", TagType.DOUBLE)) { |
| List<Double> pos = tag.getList("Pos", TagType.DOUBLE); |
| if (pos.size() == 3) { |
| Location location = new Location(world, pos.get(0), pos.get(1), pos.get(2)); |
| |
| // check for rotation |
| if (tag.isList("Rotation", TagType.FLOAT)) { |
| List<Float> rot = tag.getList("Rotation", TagType.FLOAT); |
| if (rot.size() == 2) { |
| location.setYaw(rot.get(0)); |
| location.setPitch(rot.get(1)); |
| } |
| } |
| |
| return location; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Write a Location to the "Pos" and "Rotation" children of a tag. Does |
| * not save world information, use writeWorld instead. |
| * @param loc The location to write. |
| * @param tag The tag to write to. |
| */ |
| public static void locationToListTags(Location loc, CompoundTag tag) { |
| tag.putList("Pos", TagType.DOUBLE, Arrays.asList(loc.getX(), loc.getY(), loc.getZ())); |
| tag.putList("Rotation", TagType.FLOAT, Arrays.asList(loc.getYaw(), loc.getPitch())); |
| } |
| |
| /** |
| * Create a Vector from a list of doubles. If the list is invalid, a |
| * zero vector is returned. |
| * @param list The list to read from. |
| * @return The Vector. |
| */ |
| public static Vector listToVector(List<Double> list) { |
| if (list.size() == 3) { |
| return new Vector(list.get(0), list.get(1), list.get(2)); |
| } |
| return new Vector(0, 0, 0); |
| } |
| |
| /** |
| * Create a list of doubles from a Vector. |
| * @param vec The vector to write. |
| * @return The list. |
| */ |
| public static List<Double> vectorToList(Vector vec) { |
| return Arrays.asList(vec.getX(), vec.getY(), vec.getZ()); |
| } |
| |
| } |