速查表
Bukkit ↔ NMS 橋接速查表
Bukkit ↔ NMS Bridge Reference
適用版本:Paper 1.21.11 / 26.2(Mojang 官方命名;26.1 起原版不再混淆)。兩版寫法不同處以 // @1.21.11: 行尾標註或 // @only <版本> 區塊區分
橋接套件:org.bukkit.craftbukkit.*
Paper 1.20.5+ 已移除 CraftBukkit 的版本號 relocation,套件固定為
org.bukkit.craftbukkit(無v1_21_R1後綴);v1_xx_Rx只存在於 Spigot 與 Paper 1.20.4 以前。 需相容 Spigot / 舊版時請搭配Skills/nms/nms-reflection-bridge/SKILL.md。
核心轉換表
Bukkit → NMS(getHandle)
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
| Bukkit 類型 | NMS 類型 | 轉換方式 |
|---|---|---|
Player |
ServerPlayer |
((CraftPlayer) player).getHandle() |
LivingEntity |
LivingEntity(NMS) |
((CraftLivingEntity) entity).getHandle() |
Entity(通用) |
Entity(NMS) |
((CraftEntity) entity).getHandle() |
World |
ServerLevel |
((CraftWorld) world).getHandle() |
ItemStack(Bukkit) |
ItemStack(NMS) |
CraftItemStack.asNMSCopy(item) |
ItemStack(Bukkit,可能為 CraftItemStack) |
ItemStack(NMS,shared) |
CraftItemStack.unwrap(item) |
NMS → Bukkit(getBukkitEntity)
| NMS 類型 | Bukkit 類型 | 轉換方式 |
|---|---|---|
ServerPlayer |
Player |
(Player) serverPlayer.getBukkitEntity() |
Entity(NMS) |
Entity(Bukkit) |
nmsEntity.getBukkitEntity() |
LivingEntity(NMS) |
LivingEntity(Bukkit) |
(LivingEntity) nmsLiving.getBukkitEntity() |
ItemStack(NMS) |
ItemStack(Bukkit) |
CraftItemStack.asBukkitCopy(nmsItem) |
ServerLevel |
World |
nmsLevel.getWorld() |
完整轉換範例
Player 轉換
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
// Bukkit → NMS
Player bukkit = Bukkit.getPlayer("Steve");
ServerPlayer nms = ((CraftPlayer) bukkit).getHandle();
// 取得連線
ServerGamePacketListenerImpl conn = nms.connection;
// 常用 NMS-only 欄位
int latency = nms.connection.latency(); // 延遲(ms)
int entityId = nms.getId(); // NMS entity ID
java.util.UUID uuid = nms.getUUID(); // UUID(同 Bukkit)
World / Level 轉換
import net.minecraft.server.level.ServerLevel;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.World;
// Bukkit → NMS
World bukkit = Bukkit.getWorld("world");
ServerLevel nms = ((CraftWorld) bukkit).getHandle();
// NMS 世界操作
nms.addFreshEntity(mob, CreatureSpawnEvent.SpawnReason.CUSTOM);
net.minecraft.world.level.block.state.BlockState state =
nms.getBlockState(new net.minecraft.core.BlockPos(0, 64, 0));
// NMS → Bukkit
World back = nms.getWorld();
ItemStack 轉換
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
// Bukkit → NMS(副本,修改不影響原物品)
org.bukkit.inventory.ItemStack bukkit = player.getInventory().getItemInMainHand();
ItemStack nms = CraftItemStack.asNMSCopy(bukkit);
// Bukkit → NMS(共用引用,若是 CraftItemStack 則 zero-copy;否則回傳副本)
ItemStack nmsShared = CraftItemStack.unwrap(bukkit);
// NMS → Bukkit(副本)
org.bukkit.inventory.ItemStack back = CraftItemStack.asBukkitCopy(nms);
// 空物品判斷
boolean isEmpty = nms.isEmpty(); // 優先用 NMS isEmpty()
Adventure Component ↔ NMS Component
Paper 推薦使用 Adventure API,但 NMS 封包需要 NMS Component。net.minecraft.network.chat.Component.Serializer(JSON)已移除,
改用 Paper 內建的 PaperAdventure 直接轉換(不需經過 JSON,也不需 RegistryAccess)。
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component;
// Adventure → NMS(用於封包建構)
Component adventure = Component.text("Hello").color(net.kyori.adventure.text.format.NamedTextColor.GREEN);
net.minecraft.network.chat.Component nmsComponent = PaperAdventure.asVanilla(adventure);
// NMS → Adventure(從封包讀取文字)
net.minecraft.network.chat.Component fromPacket = /* packet.text() */;
Component backAdventure = PaperAdventure.asAdventure(fromPacket);
⚠️
RegistryAccess.EMPTY適合純文字與格式代碼,若需要 hover/click event 或特殊資料包文字,需使用伺服器的完整RegistryAccess:RegistryAccess reg = ((CraftServer) Bukkit.getServer()).getServer().registryAccess();
CraftBukkit 套件動態取得(相容 Spigot / 舊版)
Paper 1.20.5+ 固定為 org.bukkit.craftbukkit;Spigot 與 1.20.4 以前的 Paper 才有 v1_xx_Rx 後綴。
若同一個 jar 需跨這些平台運作,動態取得套件名:
/** 取得 CraftBukkit 套件名(Paper 1.20.5+:"org.bukkit.craftbukkit")*/
public static String getCraftBukkitPackage() {
String serverClass = Bukkit.getServer().getClass().getName();
// Paper 1.20.5+: "org.bukkit.craftbukkit.CraftServer";Spigot: "org.bukkit.craftbukkit.v1_21_R1.CraftServer"
return serverClass.substring(0, serverClass.lastIndexOf('.'));
}
/** 動態取得 CraftPlayer class */
public static Class<?> getCraftPlayerClass() throws ClassNotFoundException {
return Class.forName(getCraftBukkitPackage() + ".entity.CraftPlayer");
}
/** 動態呼叫 getHandle() */
public static Object getHandle(Player player) throws ReflectiveOperationException {
return getCraftPlayerClass().getMethod("getHandle").invoke(player);
}
版本號對照
| MC 版本 | CraftBukkit 套件(Paper) | Paper Dev Bundle |
|---|---|---|
| 26.2 | org.bukkit.craftbukkit |
26.2.build.132-stable |
| 26.3 | org.bukkit.craftbukkit |
26.3.build.<n>-beta |
Spigot 等其他伺服器的 CraftBukkit 套件名可能不同,跨平台時用上方的
getCraftBukkitPackage()動態取得。
RegistryAccess 取得方式
不同情境下取得 RegistryAccess:
import net.minecraft.core.RegistryAccess;
import org.bukkit.craftbukkit.CraftServer;
// 方法 1:空 registry(純文字渲染適用)
RegistryAccess empty = RegistryAccess.EMPTY;
// 方法 2:完整伺服器 registry(推薦,支援所有資料包功能)
RegistryAccess full = ((CraftServer) Bukkit.getServer()).getServer().registryAccess();
// 方法 3:從 ServerLevel 取得
RegistryAccess fromLevel = serverLevel.registryAccess();
常用 NMS-only 操作(無 Bukkit 等效)
讀取玩家延遲(ms)
int ping = ((CraftPlayer) player).getHandle().connection.latency();
發送封包(不通過 Bukkit 事件)
((CraftPlayer) player).getHandle().connection.send(packet);
取得實體的 DataTracker
import net.minecraft.network.syncher.SynchedEntityData;
SynchedEntityData data = nmsEntity.getEntityData();
// 讀取特定 key 的值
// data.get(EntityDataAccessor<T> key)
強制同步實體位置
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
ClientboundTeleportEntityPacket syncPacket = new ClientboundTeleportEntityPacket(nmsEntity);
// 廣播給視野內玩家
for (ServerPlayer viewer : serverLevel.players()) {
viewer.connection.send(syncPacket);
}
橋接 API 廢棄警告
下列 CraftBukkit 橋接方法已被標記或計畫廢棄,避免依賴:
| 方法 | 狀態 | 替代方案 |
|---|---|---|
CraftItemStack.unwrap(null) |
NPE 風險 | 先檢查 item != null |
import org.bukkit.craftbukkit.v1_21_R1.* |
Paper 1.20.5+ 不存在此套件,編譯失敗 | 改用 org.bukkit.craftbukkit.*(Paperweight userdev) |
Bukkit.getUnsafe().serialize() |
不穩定 | 使用 Adventure 序列化 |
相關技能
Skills/nms/nms-reflection-bridge/SKILL.md— 無版本綁定的反射橋接Skills/nms/nms-packet-sender/SKILL.md— 使用橋接發送封包Skills/nms/nms-custom-entity/SKILL.md— 使用橋接生成實體docs/paper-nms/packets.md— 封包類型參考docs/paper-nms/entities.md— 實體類層次