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