跳转至

KubeJS NBT 数据操作(物品 / 实体 / 方块的"隐藏数据")

原文:原创教程(ぴよまる)

NBT(Named Binary Tag,命名二进制标签)是 Minecraft 里给物品、实体、方块实体挂附加数据的方式——附魔、自定义名称、箱子里的物品、玩家的存档……底层全是 NBT。这篇讲 KubeJS 6.x(1.20.1)里怎么读写 NBT,以及带 NBT 的物品、配方、实体操作。


1. NBT 长什么样

NBT 就是一层层 键: 值 包起来的数据,用 {} 表示一组(CompoundTag),[] 表示列表(ListTag):

// 这是一把"锋利 III 的钻石剑"的 NBT(游戏里实际存的格式)
// Enchantments 是一个"列表",列表里每个元素又是一组数据 {}
// id 是附魔 ID 字符串,lvl 是等级(3s 的 s 表示 short 短整数)
// display 是一组数据,Name 是物品显示名(用 JSON 文本格式)
{
  Enchantments: [{ id: "minecraft:sharpness", lvl: 3s }],
  display: { Name: '{"text":"神器"}' }
}

💡 类型后缀:3s 短整数、3L 长整数、3.0f 浮点、3.0d 双精度。整数不写后缀默认是 int。字符串不用后缀。布尔是 true/false


2. 读取物品的 NBT

2.1 判断有没有 NBT + 读整个 NBT

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let item = event.item              // 拿到被使用的物品对象
  if (item.hasNBT()) {               // hasNBT():判断这个物品身上有没有 NBT 数据(true/false)
    let nbt = item.nbt               // 取出物品的 NBT 数据(对象),没有 NBT 时可能是 null
    console.log(nbt)                 // 打印到日志(logs/server.log)
  }
})

2.2 读具体的值(get 系列)

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let nbt = event.item.nbt           // 拿到物品的 NBT 数据(对象)
  if (!nbt) return                   // 没有 NBT 就提前结束(return 后面的代码不执行)

  // 读不同类型的数据,方法名都是 get + 类型
  nbt.getString('name')      // 读字符串:返回 "我的剑" 这样的值
  nbt.getInt('level')        // 读整数:返回 5 这样的值
  nbt.getBoolean('locked')   // 读布尔:返回 true 或 false
  nbt.getDouble('damage')    // 读小数:返回 3.5 这样的值
  nbt.getList('items', 10)   // 读列表:返回一个数组(10 是元素类型,通常写 10 就行)
  nbt.getCompound('data')    // 读嵌套的一组数据:返回一个 NBT 对象
})

2.3 检查有没有某个键(contains)

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let nbt = event.item.nbt           // 拿到物品的 NBT 数据(对象)
  if (nbt && nbt.contains('level')) {   // contains('键名'):判断 NBT 里有没有这个键
    event.player.tell('这把武器有等级标签!')   // 有就告诉玩家
  }
})

⚠️ 注意顺序:先 item.hasNBT() 或判断 nbt != null,再调 nbt.getXxx(),否则没 NBT 时会报错。


3. 写入 / 修改物品 NBT

3.1 put 系列(推荐)

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let nbt = event.item.nbt           // 拿到物品的 NBT 数据(对象)
  if (!nbt) return                   // 没有 NBT 就提前结束

  nbt.putString('owner', event.player.username)   // 写入字符串:记录"这把武器的拥有者"
  nbt.putInt('kills', 0)                           // 写入整数:击杀数先归零
  nbt.putBoolean('locked', true)                   // 写入布尔:标记"已锁定"
  nbt.putDouble('power', 2.5)                      // 写入小数
  event.player.tell('已写入标签!')                 // 提示玩家
})

3.2 直接当对象赋值(KubeJS 支持)

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let nbt = event.item.nbt           // 拿到物品的 NBT 数据(对象)
  if (!nbt) return                   // 没有 NBT 就提前结束

  nbt.owner = event.player.username  // 直接点属性赋值:等价于 putString('owner', ...)
  nbt.kills = 0                      // 等价于 putInt('kills', 0)
  nbt.locked = true                  // 等价于 putBoolean('locked', true)
})

3.3 删除键 / 清空 NBT

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  let nbt = event.item.nbt           // 拿到物品的 NBT 数据(对象)
  if (!nbt) return                   // 没有 NBT 就提前结束

  nbt.remove('kills')                // 删除 NBT 里的"kills"这个键
  event.item.removeNBT()             // removeNBT():把物品的 NBT 全部清空(物品变回"干净"状态)
})

4. 创建"带 NBT"的物品

4.1 Item.of(物品ID, NBT文本)

// Item.of 的第二个参数可以直接写 NBT 文本(和游戏里 /give 的写法一样)
// 生成一把"锋利 III 的钻石剑"
let sword = Item.of('minecraft:diamond_sword', '{Enchantments:[{id:"minecraft:sharpness",lvl:3s}]}')
// 生成一个写着自定义名的苹果
let apple = Item.of('minecraft:apple', '{display:{Name:\'{"text":"幸运苹果"}\'}}')

4.2 发给玩家 / 掉落

ItemEvents.rightClicked(event => {   // 监听"玩家右键使用物品"事件
  if (event.item.id === 'kubejs:give_sword') {   // 判断:用的是一个"发剑器"物品
    // 生成带附魔的钻石剑,发给玩家
    let sword = Item.of('minecraft:diamond_sword', '{Enchantments:[{id:"minecraft:sharpness",lvl:3s}]}')
    event.player.give(sword)   // give():把物品给玩家(物品放进背包)
  }
})

4.3 withNBT:给现有物品加 NBT(返回新物品)

// withNBT() 不修改原物品,而是返回一个"带上 NBT 的新物品"
// 用法 1:传一个 NBT 对象
let stick = Item.of('minecraft:stick')                 // 先做一根普通木棍
let marked = stick.withNBT({ owner: 'pyz' })           // 返回一根带 owner=pyz 标签的木棍

// 用法 2:传 NBT 文本
let marked2 = stick.withNBT('{owner:"pyz"}')           // 效果同上

5. 配方里的 NBT

5.1 移除"产出特定 NBT"的配方

ServerEvents.recipes(event => {   // 监听"配方"事件,用来增删改合成配方
  // 删除所有"合成结果是附魔书"的配方
  event.remove({ output: { item: 'minecraft:enchanted_book' } })
})

5.2 合成出带 NBT 的物品(输出带 NBT)

ServerEvents.recipes(event => {   // 监听"配方"事件
  // 合成结果直接写 Item.of + NBT 文本,合出来就是带附魔的剑
  event.shaped(Item.of('minecraft:diamond_sword', '{Enchantments:[{id:"minecraft:sharpness",lvl:3s}]}'), [
    ' A ',   // 合成表第一行:中间是 A(空格是空位)
    ' A ',   // 第二行:中间是 A
    ' B '    // 第三行:中间是 B(摆出来就是一把剑的形状)
  ], {
    A: 'minecraft:diamond',   // A = 钻石
    B: 'minecraft:stick'      // B = 木棍
  }).id('kubejs:enchanted_sword')   // 配方 ID,方便以后删除
})

5.3 用"带 NBT 的材料"做输入(精确匹配)

ServerEvents.recipes(event => {   // 监听"配方"事件
  // 输入材料写 Item.of + NBT 文本:只有"带这个 NBT 的木棍"才能参与合成
  event.shapeless('minecraft:stone', [   // shapeless:无序合成(不要求摆放位置)
    Item.of('minecraft:stick', '{owner:"pyz"}')   // 必须是一根 owner 是 pyz 的木棍
  ])
})

5.4 匹配时忽略 NBT(ignoreNBT / weakNBT)

ServerEvents.recipes(event => {   // 监听"配方"事件
  // 默认 KubeJS 的物品匹配(Ingredient)会看 NBT;想"不管 NBT 只看物品 ID"时用这两个方法:
  event.remove({
    output: {
      item: Ingredient.of('minecraft:enchanted_book').ignoreNBT()   // ignoreNBT():匹配时忽略 NBT
    }
  })
  // weakNBT() 和 ignoreNBT() 效果类似:只比较物品 ID 和数量,不比较 NBT
  Ingredient.of('minecraft:stick').weakNBT()
})

6. 实体 / 玩家的 NBT

6.1 实体的完整 NBT(getFullNBT / setFullNBT / mergeFullNBT)

EntityEvents.spawned(event => {   // 监听"实体生成"事件(世界上出现实体时触发)
  let entity = event.entity       // 拿到生成的实体

  let fullNbt = entity.getFullNBT()   // 读取实体的完整 NBT(位置、血量、AI 开关等全在里面)
  fullNbt.putString('CustomName', '超级僵尸')   // 给实体改自定义名
  entity.setFullNBT(fullNbt)                // setFullNBT():把改好的 NBT 整个写回实体

  // mergeFullNBT():把一部分 NBT 合并进实体(只改传进去的键,不动的键保留原样)
  entity.mergeFullNBT({ Health: 100.0f })   // 只改血量,其他数据不动
})

6.2 persistentData:玩家的"永久存档"就是 NBT

// persistentData 本质是 NBT 的友好封装:能存数字/字符串/布尔/数组/对象,重启不丢
PlayerEvents.loggedIn(event => {   // 监听"玩家登录"事件
  let data = event.player.persistentData   // 玩家的永久存档数据(对象)
  data.joinCount = (data.joinCount || 0) + 1   // 登录次数 +1(没记录过就当 0)
  event.player.tell('这是你第 ' + data.joinCount + ' 次登录!')   // 告诉玩家
})

💡 persistentData 存进去的就是 NBT:data.kills = 5 等价于往玩家 NBT 里写 kills: 5


7. 常用 NBT 速查表

想做什么 写法
判断物品有没有 NBT item.hasNBT()
取物品 NBT item.nbt
读字符串 nbt.getString('键')
读整数 nbt.getInt('键')
读布尔 nbt.getBoolean('键')
读小数 nbt.getDouble('键')
读列表 nbt.getList('键', 10)
读嵌套数据 nbt.getCompound('键')
有没有某个键 nbt.contains('键')
写字符串 nbt.putString('键', '值')
写整数 nbt.putInt('键', 值)
写布尔 nbt.putBoolean('键', 值)
写小数 nbt.putDouble('键', 值)
直接赋值 nbt.键 = 值
删一个键 nbt.remove('键')
清空 NBT item.removeNBT()
做带 NBT 的物品 Item.of('id', '{...}')
给现有物品加 NBT item.withNBT({...})item.withNBT('{...}')
实体的完整 NBT entity.getFullNBT() / entity.setFullNBT(nbt)
只改部分 NBT entity.mergeFullNBT({...})
玩家/实体/方块永久存档 xxx.persistentData
匹配时忽略 NBT Ingredient.of('id').ignoreNBT()

💡 记忆口诀:读用 getXxx('键')、写用 putXxx('键', 值)、判断用 contains('键');物品的 NBT 挂在 item.nbt 上,实体的完整 NBT 用 getFullNBT(),想省事直接用 persistentData 当普通对象存。


本文为 KubeJS 1.20.1 中文文档原创篇目,配套 NBT 字面量格式参考 Minecraft Wiki「物品格式(1.20.5 前)」。