Skills/nms/nms-nbt-manipulation/SKILL.md
直接操作 CompoundTag 讀寫物品、實體、方塊實體的 NBT 資料(Paper NMS + Mojang-mapped)/ Read and write NBT data on items, entities, and block entities via CompoundTag
npx skillsauth add MrPippi/MPS nms-nbt-manipulationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
nms-nbt-manipulation
透過 NMS CompoundTag 直接讀寫物品、實體、方塊實體的 NBT 資料,繞過 Bukkit PersistentDataContainer API 的限制,實現更底層的持久化與資料操作。
| 參數 | 範例 | 說明 |
|------|------|------|
| package_name | com.example.nbt | 產出類別所在 package |
| target | item / entity / block | 操作目標(物品、實體、方塊實體) |
| class_name | NbtHelper | 工具類名稱 |
ItemNbtHelper.java — 物品 NBT 讀寫工具EntityNbtHelper.java — 實體 NBT 讀寫工具NbtSerializer.java(選)— 自定義物件 NBT 序列化工具參見 Skills/paper-nms/PLATFORM.md。關鍵依賴:
dependencies {
paperweight.paperDevBundle('1.21.1-R0.1-SNAPSHOT')
}
ItemNbtHelper.javapackage com.example.nbt;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public final class ItemNbtHelper {
private ItemNbtHelper() {}
/** 取得物品的 NMS ItemStack 並讀取自定義 NBT 標籤。 */
public static Optional<String> getString(org.bukkit.inventory.ItemStack item, String key) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getTag();
if (tag == null || !tag.contains(key)) return Optional.empty();
return Optional.of(tag.getString(key));
}
/** 寫入字串 NBT 並回傳修改後的 Bukkit ItemStack(不可變模式)。 */
public static org.bukkit.inventory.ItemStack setString(
org.bukkit.inventory.ItemStack item, String key, String value) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getOrCreateTag();
tag.putString(key, value);
return CraftItemStack.asBukkitCopy(nms);
}
/** 讀取整數 NBT。 */
public static int getInt(org.bukkit.inventory.ItemStack item, String key, int def) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getTag();
return (tag != null && tag.contains(key)) ? tag.getInt(key) : def;
}
/** 寫入整數 NBT 並回傳修改後的 Bukkit ItemStack。 */
public static org.bukkit.inventory.ItemStack setInt(
org.bukkit.inventory.ItemStack item, String key, int value) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getOrCreateTag();
tag.putInt(key, value);
return CraftItemStack.asBukkitCopy(nms);
}
/** 移除指定 NBT 鍵。 */
public static org.bukkit.inventory.ItemStack removeKey(
org.bukkit.inventory.ItemStack item, String key) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getTag();
if (tag != null) tag.remove(key);
return CraftItemStack.asBukkitCopy(nms);
}
/** 檢查是否含有指定鍵。 */
public static boolean hasKey(org.bukkit.inventory.ItemStack item, String key) {
ItemStack nms = CraftItemStack.asNMSCopy(item);
CompoundTag tag = nms.getTag();
return tag != null && tag.contains(key);
}
}
EntityNbtHelper.javapackage com.example.nbt;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEntity;
@SuppressWarnings("UnstableApiUsage")
public final class EntityNbtHelper {
private EntityNbtHelper() {}
/**
* 讀取實體的完整 NBT CompoundTag(含位置、速度、自定義資料等)。
* 必須在主執行緒呼叫。
*/
public static CompoundTag getTag(org.bukkit.entity.Entity entity) {
Entity nms = ((CraftEntity) entity).getHandle();
CompoundTag tag = new CompoundTag();
nms.save(tag);
return tag;
}
/** 讀取實體自定義 tag(custom_name, Tags 等之外的自定義鍵)。 */
public static String getString(org.bukkit.entity.Entity entity, String key, String def) {
CompoundTag tag = getTag(entity);
return tag.contains(key) ? tag.getString(key) : def;
}
/**
* 將 CompoundTag 合併回實體(load/merge)。
* 必須在主執行緒呼叫,且不可覆寫 UUID/位置。
*/
public static void mergeTag(org.bukkit.entity.Entity entity, CompoundTag patch) {
Entity nms = ((CraftEntity) entity).getHandle();
CompoundTag current = new CompoundTag();
nms.save(current);
current.merge(patch);
nms.load(current);
}
}
NbtSerializer.java(自定義物件序列化範例)package com.example.nbt;
import net.minecraft.nbt.CompoundTag;
/** 示範如何將自定義物件序列化/反序列化為 CompoundTag。 */
public final class NbtSerializer {
private NbtSerializer() {}
public record PlayerData(String name, int level, double exp) {}
public static CompoundTag serialize(PlayerData data) {
CompoundTag tag = new CompoundTag();
tag.putString("name", data.name());
tag.putInt("level", data.level());
tag.putDouble("exp", data.exp());
return tag;
}
public static PlayerData deserialize(CompoundTag tag) {
return new PlayerData(
tag.getString("name"),
tag.getInt("level"),
tag.getDouble("exp")
);
}
}
src/main/java/com/example/
├── MyNmsPlugin.java
└── nbt/
├── ItemNbtHelper.java
├── EntityNbtHelper.java
└── NbtSerializer.java
ItemNbtHelper 的方法操作 NMS Copy(不修改原物件),可在任何執行緒呼叫EntityNbtHelper.getTag() / mergeTag() 存取實體狀態,必須在主執行緒呼叫nms.save() / nms.load() 不執行緒安全,確保在 Bukkit scheduler 內呼叫Skills/_shared/nms-threading.md| 錯誤 | 原因 | 解法 |
|------|------|------|
| getTag() 回傳 null | 物品無自定義 NBT | 改用 getOrCreateTag() |
| 資料合併後實體行為異常 | load() 覆寫了位置/UUID | 合併前從 patch 移除 Pos、UUID 鍵 |
| ClassCastException: CraftEntity | 外掛替換實體實作 | 改用 nms-reflection-bridge 取得 handle |
| NBT 鍵消失(重載後) | 未使用 PersistentDataContainer | 若需跨重啟持久化,改用 PDC 或 SQL |
development
透過 NMS Scoreboard/Objective/Team API 操作 sidebar、tablist 顯示名稱與計分板(Paper NMS + Mojang-mapped)/ Operate sidebar, tablist, and scoreboard via NMS Scoreboard/Objective/Team API
research
操作 GameProfile 進行 skin 注入,用於 NPC 外觀設定與假玩家實體(Paper NMS + Mojang-mapped)/ Manipulate GameProfile for skin injection used in NPC appearance and fake player entities
tools
透過 ClientboundLevelParticlesPacket 實現進階 NMS 粒子效果:客戶端專屬、大量粒子、自定義參數(Paper NMS + Mojang-mapped)/ Advanced NMS particle effects via ClientboundLevelParticlesPacket with per-client and bulk support
documentation
操作 Minecraft 1.21 DataComponentType 物品組件系統,讀寫 CustomData、MaxStackSize、Enchantments 等組件(Paper NMS + Mojang-mapped)/ Read and write 1.21 DataComponentType item components including CustomData, MaxStackSize, Enchantments