Minecraft Wiki

除另有声明,转载时均必须注明出处若簡繁轉換出錯,請以遊戲內為準请勇于扩充与修正内容有兴趣逛逛我们的微博沟通交流,欢迎到社区专页需要协助,请在告示板留言

了解更多

Minecraft Wiki
Advertisement

这个Java类可以读取NBT结构并返回一个输入流中最上层的标签,也能通过一个输出流向最上层的标签写入NBT结构。本页下方附有代码的文档概览。

备注一: 适用于快照12w07a,添加了ID为11的新标签类型"Int Array Tag(整型数组标签)"。 快照12w07a及后续版本中生成的世界会使用此标签。

备注二: 此代码已被更新,包含了上述ID为11的新增的整型数组标签。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * NBT输入/输出类
 *
 * @see <a href="https://github.com/udoprog/c10t/blob/master/docs/NBT.txt">Online NBT specification</a>
 */
public class Tag {
    private final Type type;
    private Type listType = null;
    private final String name;
    private Object value;

    /**
     * 标签类型的枚举。
     */
    public enum Type {
        TAG_End,
        TAG_Byte,
        TAG_Short,
        TAG_Int,
        TAG_Long,
        TAG_Float,
        TAG_Double,
        TAG_Byte_Array,
        TAG_String,
        TAG_List,
        TAG_Compound,
        TAG_Int_Array
    }

    /**
     * 创建TAG_List或TAG_Compound。
     *
     * @param type 仅可为TAG_List或TAG_Compound。
     * @param name 新标签的名称,传入空值以创建未命名标签。
     * @param value  要添加至新标签的标签列表。
     */
    public Tag(Type type, String name, Tag[] value) {
        this(type, name, (Object) value);
    }

    /**
     * 使用空列表创建TAG_List。使用{@link Tag#addTag(Tag)}来向该列表添加标签。
     *
     * @param name 新标签的名称,传入空值以创建未命名标签。
     * @param listType 此空列表元素的类型。
     */
    public Tag(String name, Type listType) {
        this(Type.TAG_List, name, listType);
    }

    /**
     * 创建NBT标签。
     *
     * @param type {@link Type}枚举的任意值。
     * @param name 新标签的名称,传入空值以创建未命名标签。
     * @param value 一个符合type所定义类型的对象,或传入一个{@link Type}值以创建此类型的空列表。
     */
    public Tag(Type type, String name, Object value) {
        switch (type) {
        case TAG_End:
            if (value != null)
                throw new IllegalArgumentException();
            break;
        case TAG_Byte:
            if (!(value instanceof Byte))
                throw new IllegalArgumentException();
            break;
        case TAG_Short:
            if (!(value instanceof Short))
                throw new IllegalArgumentException();
            break;
        case TAG_Int:
            if (!(value instanceof Integer))
                throw new IllegalArgumentException();
            break;
        case TAG_Long:
            if (!(value instanceof Long))
                throw new IllegalArgumentException();
            break;
        case TAG_Float:
            if (!(value instanceof Float))
                throw new IllegalArgumentException();
            break;
        case TAG_Double:
            if (!(value instanceof Double))
                throw new IllegalArgumentException();
            break;
        case TAG_Byte_Array:
            if (!(value instanceof byte[]))
                throw new IllegalArgumentException();
            break;
        case TAG_String:
            if (!(value instanceof String))
                throw new IllegalArgumentException();
            break;
        case TAG_List:
            if (value instanceof Type) {
                this.listType = (Type) value;
                value = new Tag[0];
            } else {
                if (!(value instanceof Tag[]))
                    throw new IllegalArgumentException();
                this.listType = (((Tag[]) value)[0]).getType();
            }
            break;
        case TAG_Compound:
            if (!(value instanceof Tag[]))
                throw new IllegalArgumentException();
            break;
        case TAG_Int_Array:
            if (!(value instanceof int[]))
                throw new IllegalArgumentException();
            break;
        default:
            throw new IllegalArgumentException();
        }
        this.type = type;
        this.name = name;
        this.value = value;
    }

    public Type getType() {
        return type;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object newValue)
    {
        switch (type) {
        case TAG_End:
            if (value != null)
                throw new IllegalArgumentException();
            break;
        case TAG_Byte:
            if (!(value instanceof Byte))
                throw new IllegalArgumentException();
            break;
        case TAG_Short:
            if (!(value instanceof Short))
                throw new IllegalArgumentException();
            break;
        case TAG_Int:
            if (!(value instanceof Integer))
                throw new IllegalArgumentException();
            break;
        case TAG_Long:
            if (!(value instanceof Long))
                throw new IllegalArgumentException();
            break;
        case TAG_Float:
            if (!(value instanceof Float))
                throw new IllegalArgumentException();
            break;
        case TAG_Double:
            if (!(value instanceof Double))
                throw new IllegalArgumentException();
            break;
        case TAG_Byte_Array:
            if (!(value instanceof byte[]))
                throw new IllegalArgumentException();
            break;
        case TAG_String:
            if (!(value instanceof String))
                throw new IllegalArgumentException();
            break;
        case TAG_List:
            if (value instanceof Type) {
                this.listType = (Type) value;
                value = new Tag[0];
            } else {
                if (!(value instanceof Tag[]))
                    throw new IllegalArgumentException();
                this.listType = (((Tag[]) value)[0]).getType();
            }
            break;
        case TAG_Compound:
            if (!(value instanceof Tag[]))
                throw new IllegalArgumentException();
            break;
        case TAG_Int_Array:
            if (!(value instanceof int[]))
                throw new IllegalArgumentException();
            break;
        default:
            throw new IllegalArgumentException();
        }

        value = newValue;
    }

    public Type getListType() {
        return listType;
    }

    /**
     * 向TAG_List或TAG_Compound添加标签。
     */
    public void addTag(Tag tag) {
        if (type != Type.TAG_List && type != Type.TAG_Compound)
            throw new RuntimeException();
        Tag[] subtags = (Tag[]) value;

        int index = subtags.length;

        //For TAG_Compund entries, we need to add the tag BEFORE the end,
        //or the new tag gets placed after the TAG_End, messing up the data.
        //TAG_End MUST be kept at the very end of the TAG_Compound.
        if(type == Type.TAG_Compound) index--;
        insertTag(tag, index);
    }

    /**
     * 向TAG_List或TAG_Compound的指定位置添加标签。
     */
    public void insertTag(Tag tag, int index) {
        if (type != Type.TAG_List && type != Type.TAG_Compound)
            throw new RuntimeException();
        Tag[] subtags = (Tag[]) value;
        if (subtags.length > 0)
            if (type == Type.TAG_List && tag.getType() != getListType())
                throw new IllegalArgumentException();
        if (index > subtags.length)
            throw new IndexOutOfBoundsException();
        Tag[] newValue = new Tag[subtags.length + 1];
        System.arraycopy(subtags, 0, newValue, 0, index);
        newValue[index] = tag;
        System.arraycopy(subtags, index, newValue, index + 1, subtags.length - index);
        value = newValue;
    }

    /**
     * 在TAG_List或TAG_Compound的指定位置移除标签。
     *
     * @return 被移除的标签。
     */
    public Tag removeTag(int index) {
        if (type != Type.TAG_List && type != Type.TAG_Compound)
            throw new RuntimeException();
        Tag[] subtags = (Tag[]) value;
        Tag victim = subtags[index];
        Tag[] newValue = new Tag[subtags.length - 1];
        System.arraycopy(subtags, 0, newValue, 0, index);
        index++;
        System.arraycopy(subtags, index, newValue, index - 1, subtags.length - index);
        value = newValue;
        return victim;
    }

    /**
     * 在TAG_List或TAG_Compound中移除标签,若目标标签不是此标签的子标签,则会在嵌套标签中查找。
     *
     * @param tag 要查找的标签。
     */
    public void removeSubTag(Tag tag) {
        if (type != Type.TAG_List && type != Type.TAG_Compound)
            throw new RuntimeException();
        if (tag == null)
            return;
        Tag[] subtags = (Tag[]) value;
        for (int i = 0; i < subtags.length; i++) {
            if (subtags[i] == tag) {
                removeTag(i);
                return;
            } else {
                if (subtags[i].type == Type.TAG_List || subtags[i].type == Type.TAG_Compound) {
                    subtags[i].removeSubTag(tag);
                }
            }
        }
    }

    /**
     * 在TAG_Compound中查找第一个带有指定名称的嵌套标签。
     *
     * @param name 要查找的名称,可传入空值以查找未命名标签。
     * @return 第一个带有指定名称的嵌套标签。
     */
    public Tag findTagByName(String name) {
        return findNextTagByName(name, null);
    }

    /**
     * 在TAG_List或TAG_Compound中查找在同名标签后出现的第一个带有指定名称的嵌套标签。
     *
     * @param name 要查找的名称,可传入空值以查找未命名标签。
     * @param found 之前查找到的同名标签。
     * @return 同名标签后出现的第一个带有指定名称的嵌套标签。
     */
    public Tag findNextTagByName(String name, Tag found) {
        if (type != Type.TAG_List && type != Type.TAG_Compound)
            return null;
        Tag[] subtags = (Tag[]) value;
        for (Tag subtag : subtags) {
            if ((subtag.name == null && name == null) || (subtag.name != null && subtag.name.equals(name))) {
                return subtag;
            } else {
                Tag newFound = subtag.findTagByName(name);
                if (newFound != null)
                    if (newFound == found)
                        continue;
                    else
                        return newFound;
            }
        }
        return null;
    }

    /**
     * 从InputStream中读取标签及其嵌套标签。
     *
     * @param is 要读取的流,如FileInputStream。
     * @return 从流中读取到的NBT标签或结构。
     * @throws IOException 若InputStream中无有效的NBT结构或发生了另一个IOException,则抛出IOException。
     */
    public static Tag readFrom(InputStream is) throws IOException {
        DataInputStream dis = new DataInputStream(new GZIPInputStream(is));
        byte type = dis.readByte();
        Tag tag = null;

        if (type == 0) {
            tag = new Tag(Type.TAG_End, null, null);
        } else {
            tag = new Tag(Type.values()[type], dis.readUTF(), readPayload(dis, type));
        }

        dis.close();

        return tag;
    }

    private static Object readPayload(DataInputStream dis, byte type) throws IOException {
        switch (type) {
        case 0:
            return null;
        case 1:
            return dis.readByte();
        case 2:
            return dis.readShort();
        case 3:
            return dis.readInt();
        case 4:
            return dis.readLong();
        case 5:
            return dis.readFloat();
        case 6:
            return dis.readDouble();
        case 7:
            int length = dis.readInt();
            byte[] ba = new byte[length];
            dis.readFully(ba);
            return ba;
        case 8:
            return dis.readUTF();
        case 9:
            byte lt = dis.readByte();
            int ll = dis.readInt();
            Tag[] lo = new Tag[ll];
            for (int i = 0; i < ll; i++) {
                lo[i] = new Tag(Type.values()[lt], null, readPayload(dis, lt));
            }
            if (lo.length == 0)
                return Type.values()[lt];
            else
                return lo;
        case 10:
            byte stt;
            Tag[] tags = new Tag[0];
            do {
                stt = dis.readByte();
                String name = null;
                if (stt != 0) {
                    name = dis.readUTF();
                }
                Tag[] newTags = new Tag[tags.length + 1];
                System.arraycopy(tags, 0, newTags, 0, tags.length);
                newTags[tags.length] = new Tag(Type.values()[stt], name, readPayload(dis, stt));
                tags = newTags;
            } while (stt != 0);
            return tags;
        case 11:
            int len = dis.readInt();
            int[] ia = new int[len];
            for (int i=0;i<len;i++)
                ia[i] = dis.readInt();
            return ia;
 
        }
        return null;
    }

    /**
     * 向OutputStream写入标签及其嵌套标签。
     *
     * @param os 要写入的流,如FileOutputStream。
     * @throws IOException 若此非有效的NBT结构或发生了另一个IOException,则抛出IOException。
     */
    public void writeTo(OutputStream os) throws IOException {
        GZIPOutputStream gzos;
        DataOutputStream dos = new DataOutputStream(gzos = new GZIPOutputStream(os));
        dos.writeByte(type.ordinal());
        if (type != Type.TAG_End) {
            dos.writeUTF(name);
            writePayload(dos);
        }
        gzos.flush();
        gzos.close();
    }

    private void writePayload(DataOutputStream dos) throws IOException {
        switch (type) {
        case TAG_End:
            break;
        case TAG_Byte:
            dos.writeByte((Byte) value);
            break;
        case TAG_Short:
            dos.writeShort((Short) value);
            break;
        case TAG_Int:
            dos.writeInt((Integer) value);
            break;
        case TAG_Long:
            dos.writeLong((Long) value);
            break;
        case TAG_Float:
            dos.writeFloat((Float) value);
            break;
        case TAG_Double:
            dos.writeDouble((Double) value);
            break;
        case TAG_Byte_Array:
            byte[] ba = (byte[]) value;
            dos.writeInt(ba.length);
            dos.write(ba);
            break;
        case TAG_String:
            dos.writeUTF((String) value);
            break;
        case TAG_List:
            Tag[] list = (Tag[]) value;
            dos.writeByte(getListType().ordinal());
            dos.writeInt(list.length);
            for (Tag tt : list) {
                tt.writePayload(dos);
            }
            break;
        case TAG_Compound:
            Tag[] subtags = (Tag[]) value;
            for (Tag st : subtags) {
                Tag subtag = st;
                Type type = subtag.getType();
                dos.writeByte(type.ordinal());
                if (type != Type.TAG_End) {
                    dos.writeUTF(subtag.getName());
                    subtag.writePayload(dos);
                }
            }
            break;
        case TAG_Int_Array:
            int[] ia = (int[]) value;
            dos.writeInt(ia.length);
            for (int i=0;i<ia.length;i++)
                dos.writeInt(ia[i]);
            break;
 
        }
    }

    /**
     * 将此NBT结构输出至System.out。
     */
    public void print() {
        print(this, 0);
    }

    private String getTypeString(Type type) {
        switch (type) {
        case TAG_End:
            return "TAG_End";
        case TAG_Byte:
            return "TAG_Byte";
        case TAG_Short:
            return "TAG_Short";
        case TAG_Int:
            return "TAG_Int";
        case TAG_Long:
            return "TAG_Long";
        case TAG_Float:
            return "TAG_Float";
        case TAG_Double:
            return "TAG_Double";
        case TAG_Byte_Array:
            return "TAG_Byte_Array";
        case TAG_String:
            return "TAG_String";
        case TAG_List:
            return "TAG_List";
        case TAG_Compound:
            return "TAG_Compound";
        case TAG_Int_Array:
            return "TAG_Int_Array";
 
        }
        return null;
    }

    private void indent(int indent) {
        for (int i = 0; i < indent; i++) {
            System.out.print("   ");
        }
    }

    private void print(Tag t, int indent) {
        Type type = t.getType();
        if (type == Type.TAG_End)
            return;
        String name = t.getName();
        indent(indent);
        System.out.print(getTypeString(t.getType()));
        if (name != null)
            System.out.print("(\"" + t.getName() + "\")");
        if (type == Type.TAG_Byte_Array) {
            byte[] b = (byte[]) t.getValue();
            System.out.println(": [" + b.length + " bytes]");
        } else if (type == Type.TAG_List) {
            Tag[] subtags = (Tag[]) t.getValue();
            System.out.println(": " + subtags.length + " entries of type " + getTypeString(t.getListType()));
            for (Tag st : subtags) {
                print(st, indent + 1);
            }
            indent(indent);
            System.out.println("}");
        } else if (type == Type.TAG_Compound) {
            Tag[] subtags = (Tag[]) t.getValue();
            System.out.println(": " + (subtags.length - 1) + " entries");
            indent(indent);
            System.out.println("{");
            for (Tag st : subtags) {
                print(st, indent + 1);
            }
            indent(indent);
            System.out.println("}");
        } else if (type == Type.TAG_Int_Array) {
            int[] i = (int[]) t.getValue();
            System.out.println(": [" + i.length * 4 + " bytes]");
 
        } else {
            System.out.println(": " + t.getValue());
        }
    }

}

若发现漏洞,请修复此页。 若以上代码不适合您,可参考JNBT项目。以上代码与JNBT项目无关。

文档[]

以下是从上方的类中提取出的函数及对应文档说明,未包含私有函数。这些函数被分为了四类:构造函数,数据值相关函数,标签操作函数与输入/输出函数。这份文档也许能帮助您更好地理解并使用以上代码。

构造函数:

  • Tag(Type type,String name,Tag[] value)
    • 描述: 创建TAG_List或TAG_Compound。
    • type: 仅可为TAG_List或TAG_Compound。
    • name: 新标签的名称,传入空值以创建未命名标签。
    • value: 要添加至新标签的标签列表。
  • Tag(String name, Type listType)
    • 描述: 使用空列表创建TAG_List。使用{@link Tag#addTag(Tag)}来向该列表添加标签。
    • name: 新标签的名称,传入空值以创建未命名标签。
    • listType: 此空列表元素的类型。
  • Tag(Type type, String name, Object value)
    • 描述: 创建NBT标签。
    • type: {@link Type}枚举的任意值。
    • name: 新标签的名称,传入空值以创建未命名标签。
    • value: 一个符合type所定义类型的对象,或传入一个{@link Type}值以创建此类型的空列表。


数据值相关函数:

  • getType()
  • getName()
  • getValue()
  • getListType()
  • setValue()


标签操作函数:

  • addTag(Tag tag)
    • 描述: 向TAG_List或TAG_Compound添加标签。
  • insertTag(Tag tag, int index)
    • 描述: 向TAG_List或TAG_Compound的指定位置添加标签。


  • removeTag(int index)
    • 描述: 在TAG_List或TAG_Compound的指定位置移除标签。
    • 返回值: 被移除的标签。
  • removeSubTag(Tag tag)
    • 描述: 在TAG_List或TAG_Compound中移除标签,若目标标签不是此标签的子标签,则会在嵌套标签中查找。
  • findTagByName(String name)
    • 描述: 在TAG_Compound中查找第一个带有指定名称的嵌套标签。
    • name: 要查找的名称,可传入空值以查找未命名标签。
    • 返回值: 第一个带有指定名称的嵌套标签。
  • findNextTagByName(String name, Tag found)
    • 描述: 在TAG_List或TAG_Compound中查找在同名标签后出现的第一个带有指定名称的嵌套标签。
    • name: 要查找的名称,可传入空值以查找未命名标签。
    • found: 之前查找到的同名标签。
    • 返回值: 同名标签后出现的第一个带有指定名称的嵌套标签。


输入/输出函数:

  • readFrom(InputStream is)
    • 描述: 从InputStream中读取标签及其嵌套标签。
    • is: 要读取的流,如FileInputStream。
    • 返回值: 从流中读取到的NBT标签或结构。
    • 抛出异常: 若InputStream中无有效的NBT结构或发生了另一个IOException,则抛出IOException。
  • writeTo(OutputStream os)
    • 描述: 向OutputStream写入标签及其嵌套标签。
    • os: 要写入的流,如FileOutputStream。
    • 抛出异常: 若此非有效的NBT结构或发生了另一个IOException,则抛出IOException。
  • print()
    • 描述: 将此NBT结构输出至System.out。
Advertisement