MCIO Plugins MCIO Plugins
首页
爱发电 (opens new window)
无法下载? (opens new window)

人间工作P

我每天都好困… 最近在学习和进行 VOCALOID 创作
首页
爱发电 (opens new window)
无法下载? (opens new window)
  • PluginBase

  • Item-NBT-API

    • Item-NBT-API 简介
    • 使用 Maven
    • 使用 Gradle
    • 常见问题解答
    • 用法
      • 基本用法
        • 基础概述
        • 基本的获取与设置数据
        • 序列化和反序列化
        • 比较 NBT 数据
        • 合并复合标签
        • 解析嵌套数据
        • 访问/创建列表以及它们的数据
        • 处理物品
        • ItemMeta 用法
        • 在 1.20.5+ 更改原版物品 NBT
        • 处理实体和方块实体
        • 访问原版 nbt
        • 访问自定义数据
        • 模拟 "/data merge" 命令
        • 处理方块
      • 其它
        • NBT文件
        • 转换 Minecraft 对象到 NBT 和字符串
        • 物品
        • 序列化/反序列化物品
        • 实体和方块实体
        • 游戏档案 GameProfile
        • 接口代理
        • 自定义处理器
        • Data fixer 工具类
    • 使用示例
  • 开发文档
  • Item-NBT-API
2025-06-16
目录

用法

翻译自 Item-NBT-API wiki (opens new window)

# 基本用法

# 基础概述

大多数接口都在 NBT (opens new window) 类里面。这个 wiki 不一定覆盖了你的一些特定用法,所以必要时可以翻阅 Javadoc (opens new window),或者问你的 IDE 要智能提示!

# 基本的获取与设置数据

ReadableNBT (opens new window) 和 ReadWriteNBT (opens new window) 用于读取和写入 nbt:

// 创建一个空的 NBT 标签
ReadWriteNBT nbt = NBT.createNBTObject();

// 设置 NBT
nbt.setString("Stringtest", "Teststring");
nbt.setInteger("Inttest", 42);
nbt.setDouble("Doubletest", 1.5);
nbt.setBoolean("Booleantest", true);
// 更多的用法可用!去问你的 IDE,或者 Javadoc!

// 获取 NBT
String s1 = nbt.getString("Stringtest");
int i1 = nbt.getInteger("Inttest");
double d = nbt.getDouble("Doubletest");
boolean b = nbt.getBoolean("Booleantest");
// 或者可以这样
String s2 = nbt.getOrDefault("Stringtest", "fallback_value");
Integer i2 = nbt.getOrNull("Inttest", Integer.class);

// 键操作
nbt.getKeys(); // 获取所有键
nbt.hasTag("key"); // 检查一个标签是否存在
nbt.hasTag("key", NbtType.NBTTagString); // 检查一个标签是否存在,并且类型是否符合
nbt.removeKey("key"); // 删除一个标签
NBTType nbtType = nbt.getType("key"); // 获取标签类型

// 子符合标签
nbt.getCompound("subtag"); // 获取一个子标签,或者 null
nbt.getOrCreateCompound("subtag"); // 获取或创建一个子标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 序列化和反序列化

转换 NBT 到字符串,以及将字符串转换回 NBT

// 将 SNBT 解析为 NBT
ReadWriteNBT nbt = NBT.parseNBT("{Health:20.0f,Motion:[0.0d,10.0d,0.0d],Silent:1b}");
// 将 NBT 转换回 SNBT (对所有 NBT 对象有效)
String snbt = nbt.toString();
// 再次解析回 NBT
ReadWriteNBT nbt2 = NBT.parseNBT(snbt);
1
2
3
4
5
6

# 比较 NBT 数据

就像其它 Java 对象一样,你可以使用 equals 来比较 NBT。

要看具体哪些没有匹配,你可以使用 ReadableNBT#extractDifference:

ReadWriteNBT nbt1 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:1b}},intArray:[I;1,2]");
ReadWriteNBT nbt2 = NBT.parseNBT("{intTag:1,compoundTag:{floatTag:20.0f,booleanTag:0b}},intArray:[I;1,2,3],alsoIntTag:2");

ReadWriteNBT diff1 = nbt1.extractDifference(nbt2);
ReadWriteNBT diff2 = nbt2.extractDifference(nbt1);
1
2
3
4
5

对于上面的示例,调用 ReadableNBT#toString 会产生这样的结果:

diff1: {compoundTag:{booleanTag:1b},intArray:[I;1,2]}

diff2: {compoundTag:{booleanTag:0b},intArray:[I;1,2,3],alsoIntTag:2}

# 合并复合标签

你可以从任何给定的符合标签,合并数据到另一个复合标签。这个操作会覆写同名的标签。

示例:

Tag1: {Num1:1, Num2:2} Tag2: {Num1:7, Num3:3}

在合并 Tag2 到 Tag1 后:

tag1.mergeCompound(tag2);
1

Tag1: {Num1:7, Num2:2, Num3:3} Tag2: {Num1:7, Num3:3}

# 解析嵌套数据

要简化使用多重嵌套的 NBT,使用 resolve 方法可以直接获取,或者作用于这些嵌套标签。

复合标签使用点 . 来解析。如果你要在键名使用 .,可以使用捺斜杠 \ 来转义。

// 举个例子,下面这行代码:
nbt.resolveOrCreateCompound("foo.bar.baz");
// 与下面的代码一样,会获取/创建下面这样的子复合标签:
nbt.getOrCreateCompound("foo").getOrCreateCompound("bar").getOrCreateCompound("baz");

// 如果存在,则获取复合标签,反之 null
nbt.resolveCompound("foo.some.key.baz");

// 设置 foo/bar/baz/test 为 42
nbt.resolveOrCreateCompound("foo.bar.baz").setInteger("test", 42);

// 一个简明包含 . 的示例。设置 foo/some.key/baz/other 为 123
nbt.resolveOrCreateCompound("foo.some\\.key.baz").setInteger("other", 123);

// 类似地,你可以从嵌套的复合标签获取其它类型的值
String s = nbt.resolveOrDefault("foo.bar.Stringtest", "fallback_value");
Integer i = nbt.resolveOrNull("foo\\.bar.baz.Inttest", Integer.class);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 访问/创建列表以及它们的数据

注意

如果获取列表的时候,列表不存在,将会自动创建。列表没有 setter!

// 获取或创建一个字符串列表
ReadWriteNBTList<String> stringList = nbt.getStringList("list_key");
stringList.add("value");

// 获取列表的类型,在这个示例中是 NBTTagString
NBTType type = nbt.getListType("list_key");

// 你可以就像普通的 List 一样获取值
nbt.getStringList("list_key").get(0);
// 或者像这样获取
nbt.resolveOrNull("list_key[0]", String.class); // 获取列表的第一个值
// 你也可以使用负数,来获取列表的最后一个值
nbt.resolveOrDefault("list_key[-1]", "fallback_value"); // 获取列表的最后一个值,或使用默认值


// NBT 复合标签列表
// 获取列表 (如果需要的话将会创建)
ReadWriteNBTCompoundList nbtList = nbt.getOrCreateCompound("foo").getCompoundList("other_key");
// 向列表添加新的复合标签
ReadWriteNBT nbtListEntry = nbtList.addCompound();
nbtListEntry.setBoolean("bar", true);
// 向列表添加已有的复合标签
ReadWriteNBT otherNbtListEntry = nbtList.addCompound(NBT.parseNBT("{foo_bar:1b}"));

// 你可以从列表中获取复合标签
nbt.resolveCompound("foo.other_key[0]"); // 获取第一个复合标签,或者 null
nbt.resolveOrCreateCompound("foo.other_key[1]"); // 获取第二个复合标签,或者创建一个
// 或者像这样获取其中的数据
boolean bar = nbt.resolveOrDefault("foo.other_key[0].bar", false);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 处理物品

你可以使用 NBT.get/NBT.modify 方法,向物品读取/写入NBT数据。

// 设置数据
NBT.modify(itemStack, nbt -> {
    nbt.setString("Stringtest", "Teststring");
    nbt.setInteger("Inttest", 42);
    nbt.setDouble("Doubletest", 1.5d);
    nbt.setBoolean("Booleantest", true);
    // 更多的用法可用!去问你的 IDE,或者 Javadoc!
});

// 读取数据
NBT.get(itemStack, nbt -> {
    String stringTest = nbt.getString("Stringtest");
    int intTest = nbt.getOrDefault("Inttest", 0);
    // 做更多操作
});

// 读取数据
String string = NBT.get(itemStack, nbt -> (String) nbt.getString("Stringtest"));

// 修改和获取数据一起
int someValue = NBT.modify(itemStack, nbt -> {
    int i = nbt.getOrDefault("key", 0) + 1;
    nbt.setInteger(i);
    return i;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ItemMeta 用法

警告

永远不要混合 ItemMeta 和 NBT 的使用。
设置 ItemMeta 会覆盖任何对物品 NBT 的更改。

// 错误示例,不要这么做!
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName("Modified!");
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
itemStack.setItemMeta(meta); // 这个操作会撤销对 NBT 的所有更改!


// 正确示例, ItemMeta 和 NBT 都正确应用了
NBT.modify(itemStack, nbt -> nbt.setBoolean("modified", true));
// 在更改 NBT 之后再获取 ItemMeta 快照
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName("Modified!");
itemStack.setItemMeta(meta);
// 如果你对 NBT & ItemMeta 做了任何更进一步的更改,
// 要在更改 NBT 后重新获取 ItemMeta!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

或者,你可以在 NBT 作用于里安全地修改 ItemMeta:

// 使用 NBT 更新 ItemMeta
NBT.modify(itemStack, nbt -> {
    nbt.setInteger("kills", nbt.getOrDefault("kills", 0) + 1);
    nbt.modifyMeta((readOnlyNbt, meta) -> { // 不要在修改 meta 时修改 nbt!
        meta.setDisplayName("Kills: " + readOnlyNbt.getOrDefault("kills", 0));
    });
    // 做更多操作
});
1
2
3
4
5
6
7
8
# 在 1.20.5+ 更改原版物品 NBT

重要事项

自 Minecraft 1.20.5 起,物品在运行时环境不存在原版的 NBT 了。
任何 NBT.get 或 NBT.modify 调用将只会访问物品的 custom_data 组件。

作为一种解决方案,你可以使用以下代码:

// 注意: 这些代码只能在 1.20.5+ 使用!

// 只读取原版数据
NBT.getComponents(item, nbt -> {
    if (nbt.hasTag("minecraft:custom_name")) {
        String customName = nbt.getString("minecraft:custom_name");
    }
});

// 修改原版数据
NBT.modifyComponents(item, nbt -> {
    nbt.setString("minecraft:custom_name", "{\"extra\":[\"foobar\"],\"text\":\"\"}");
});
1
2
3
4
5
6
7
8
9
10
11
12
13

# 处理实体和方块实体

处理实体和方块实体的方式和处理物品类似。

# 访问原版 nbt

为实体编写的示例代码也对方块实体有效。

// 获取数据
boolean silent = NBT.get(entity, nbt -> (boolean) nbt.getBoolean("Silent"));
// 修改数据
NBT.modify(entity, nbt -> {
    nbt.setBoolean("Silent", true);
    nbt.setByte("CanPickUpLoot", (byte) 1);
});
1
2
3
4
5
6
7

# 访问自定义数据

警告

自定义的(方块)实体持久数据储存只在 1.14+ 有效。

如果你需要支持低于 1.14 的版本,你只能使用将数据存储在物品的 NBT 里,然后让实体穿戴这个物品作为解决方案 (举个例子: 怪物的头盔栏位戴一个按钮,然后把数据存到按钮里)。

要读取/储存(方块)实体的自定义数据,你应该使用以 PersistentData 结尾的方法。

重要事项

当处理方块实体数据时,请确保方块实体在世界上存在。

举个例子,你不能在 BlockPlaceEvent 事件添加数据到箱子,因为箱子还没有被放置。在这样的情况下,你可以延时 1 tick 再执行操作,或者在代码手动放置箱子。

// 获取数据
boolean test = NBT.getPersistentData(entity, nbt -> (boolean) nbt.getBoolean("custom_key"));
// 修改数据
NBT.modifyPersistentData(entity, nbt -> {
    nbt.setBoolean("custom_key", true);
    nbt.setByte("custom_byte", (byte) 1);
});
1
2
3
4
5
6
7

注意

如果你计划为玩家储存数据,你可能需要考虑使用外置存储,而非弄乱世界文件夹里的玩家数据。任何写在持久储存里的数据会永远在那里,你无法在移除插件后抛弃冗余的数据,但以其他方式储存是完全可以抛弃冗余数据的。

像是每个玩家一个 NBT文件 的储存解决方案也许已经足够使用了 (而且基本原版都够用了,但你会有你自己的文件,而不是使用世界里面的那个)。在玩家进服时,缓存文件的数据,离开时/服务器关闭时保存数据,可选添加自动保存。

# 模拟 "/data merge" 命令

为物品/方块实体/实体等应用。

NBT.modify(zombie, nbt -> {
    nbt.mergeCompound(NBT.parseNBT("{Silent:1b,Invulnerable:1b,Glowing:1b,IsBaby:1b}"));
});
1
2
3

# 处理方块

你可以使用之前的章节提到的示例,储存数据到方块实体里面 (方块实体有箱子、熔炉等等),但普通的方块没有 NBT 数据可以操作。

所以,你需要使用你自己的方块数据存储来储存自定义方块数据。

在 1.16.4 起,你可以储存数据到区块里,而且 NBT-API 允许你使用 NBTChunk 这么做:

ReadWriteNBT nbt = new NBTChunk(chunk).getPersistentDataContainer();
1

类似地,还有 NBTBlock,允许你储存方块数据到区块数据里。

// 方块数据会储存到区块数据的 "blocks.x_y_z" 子标签
ReadWriteNBT nbt = new NBTBlock(block).getData();
1
2

然而,请记住这些数据是仅用位置链接的,如果方块发生了破坏/更改/爆炸/移动等等,数据依然会在那个位置,除非手动清除/移动!

此外,由于数据储存在区块里,这还会额外占用区块在磁盘中的文件大小。

# 其它

提示

除了这个页面,你也可以在 示例用法 查看一些代码示例。

# NBT文件

NBTFileHandle 允许你处理 NBT 文件。

// 如果文件不存在,会自动创建
NBTFileHandle nbtFile = NBT.getFileHandle(new File("directory", "test.nbt"));
// 设置数据
nbtFile.setString("foo", "bar");
// 在应用更改后保存文件
nbtFile.save();
1
2
3
4
5
6

或者,你可以不维护文件链接而读取 NBT 文件。
不像 NBTFileHandle,如果文件不存在,将不会自动创建。

File file = new File("directory", "test.nbt");
// 从文件读取数据 (如果文件不存在,将会返回一个空的复合标签)
ReadWriteNBT nbt = NBT.readFile(file);
// 保存 NBT 到文件
NBT.writeFile(file, nbt);
1
2
3
4
5

# 转换 Minecraft 对象到 NBT 和字符串

基本的思路是你可以转换数据到不同的形式:
Minecraft 对象 <-> NBT <-> 字符串 (SNBT)

# 物品

虽然 NBT.get/modify 允许你修改物品直接的 NBT (或者自 1.20.5 以来仅修改 custom_data 组件),但 ItemStack 对象的 NBT 表示项目的序列化数据。它包括物品类型、数量以及所有额外的物品 NBT 标签。

举个例子,当调用 NBT.get 将会获得这样的结果:

{foo:"bar",points:12,test:1b}

从 NBT.itemStackToNBT 读取的 ItemStack 对象的 NBT 标签长这样:

{components:{"minecraft:custom_data":{foo:"bar",points:12,test:1b}},count:2,id:"minecraft:stone"}

或者在 1.20.5 以前的版本长这样:

{Count:2b,id:"minecraft:stone",tag:{foo:"bar",points:12,test:1b}}

# 序列化/反序列化物品
// 保存
ReadWriteNBT nbt = NBT.itemStackToNBT(itemStack);
ReadWriteNBT nbt = NBT.itemStackArrayToNBT(itemStacks);
// 恢复
ItemStack itemStack = NBT.itemStackFromNBT(nbt);
ItemStack[] itemStacks = NBT.itemStackArrayFromNBT(nbt);
// 提醒一下可以使用 (NBT <-> String)
String snbt = nbt.toString();
ReadWriteNBT nbt = NBT.parseNBT(snbt);
1
2
3
4
5
6
7
8
9

# 实体和方块实体

// 保存
ReadWriteNBT entityNbt = NBT.createNBTObject();
NBT.get(entity, entityNbt::mergeCompound);
// 恢复
NBT.modify(entity, nbt -> {
    // 你也许也想先过滤实体NBT标签,
    // 示例: 删除一些像是位置、UUID、实体ID 等的数据
    nbt.mergeCompound(entityNbt);
});
1
2
3
4
5
6
7
8
9

# 游戏档案 GameProfile

// 保存
ReadWriteNBT nbt = NBT.gameProfileToNBT(profile);
// 恢复
GameProfile profile = NBT.gameProfileFromNBT(nbt);
1
2
3
4

# 接口代理

你可以定义你自己的接口,继承 NBTProxy 来创建 NBT 包装器。

以 has/get/set 开头的方法将会被解析为它们各自的调用。

interface TestInterface extends NBTProxy {
    // 执行: return nbt.hasTag("kills");
    boolean hasKills();
    // 执行: nbt.setInteger("kills", amount);
    void setKills(int amount);
    // 执行: return nbt.getInteger("kills");
    int getKills();

    // 也支持这样
    default void addKill() {
        setKills(getKills() + 1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Getter 也支持返回另一个继承了 NBTProxy 的接口。

以及使用 @NBTTarget 注解,你可以对数据有着更进一步的控制。它允许设置这些方法 (has/get/set) 的类型,以及数据的键名。

interface TestInterface extends NBTProxy {
    // 将会使用 "other" 键,从数据获取 PointsInterface 复合标签
    @NBTTarget(type = Type.GET, value = "other")
    PointsInterface getOtherInterface();
}

interface PointsInterface extends NBTProxy {
    int getPoints();
    void setPoints(int points);
}
1
2
3
4
5
6
7
8
9
10

要支持其它数据类型比如 ItemStack,你需要重写 init 方法,然后注册合适的处理器。

interface TestInterface extends NBTProxy {
    @Override
    default void init() {
        registerHandler(ItemStack.class, NBTHandlers.ITEM_STACK);
        registerHandler(ReadableNBT.class, NBTHandlers.STORE_READABLE_TAG);
        registerHandler(ReadWriteNBT.class, NBTHandlers.STORE_READWRITE_TAG);
    }

    ItemStack getItem();
    void setItem(ItemStack item);

    ReadWriteNBT getBlockStateTag();
    void setBlockStateTag(ReadableNBT blockState);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

NBTHandlers 类包含了一些预定义的处理器。

# 自定义处理器

如果你需要支持不在 NBTHandlers 里面的自定义数据类型,你可以创建一个新的 NBTHandler 来编写自己的处理器。

你可以参考 NBTHandlers (opens new window) 类来查看默认实现是怎么写的。

# Data fixer 工具类

DataFixerUtil 允许你从旧版本更新 NBT 到新一点的版本

举个例子,给出 1.12.2 版本的输入:

{Count:42,id:"cobblestone",tag:{display:{Name:"test"},ench:[{id:34,lvl:3}]}}

你可以将其更新到 1.20.6:

{components:{"minecraft:custom_name":'{"text":"test"}',"minecraft:enchantments":{levels:{"minecraft:unbreaking":3}}},count:42,id:"minecraft:cobblestone"}

使用以下代码即可:

DataFixerUtil.fixUpItemData(nbt, DataFixerUtil.VERSION1_12_2, DataFixerUtil.VERSION1_20_6);
1

你也可以使用 DataFixerUtil.getCurrentVersion() 来获取当前服务器使用的版本。

上次更新: 2025/06/16, 04:01:40
常见问题解答
使用示例

← 常见问题解答 使用示例→

Theme fork from Vdoing | Copyright © 2018-2025 人间工作P | 到爱发电支持我

除非特别说明,本站点所有文章均以 CC BY-SA 协议授权

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式