速查表
NMS Packet 速查表
NMS Packet Reference
適用版本:Paper 1.21.11 / 26.2(Mojang 官方命名;26.1 起原版不再混淆)。兩版寫法不同處以 // @1.21.11: 行尾標註或 // @only <版本> 區塊區分
套件根:net.minecraft.network.protocol
封包發送用法見
Skills/nms/nms-packet-sender/SKILL.md封包攔截用法見Skills/nms/nms-packet-interceptor/SKILL.md執行緒規則見Skills/_shared/nms-threading.md
Clientbound 封包(伺服器 → 客戶端)
Display / 顯示
ClientboundSetActionBarTextPacket
import net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket;
import net.minecraft.network.chat.Component;
// 建構
ClientboundSetActionBarTextPacket packet =
new ClientboundSetActionBarTextPacket(Component.literal("Hello"));
| 欄位 | 類型 | 說明 |
|---|---|---|
text |
Component |
Action bar 顯示的文字 |
ClientboundSetTitleTextPacket
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
ClientboundSetTitleTextPacket packet =
new ClientboundSetTitleTextPacket(Component.literal("§6Boss 戰開始!"));
ClientboundSetSubtitleTextPacket
import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket;
ClientboundSetSubtitleTextPacket packet =
new ClientboundSetSubtitleTextPacket(Component.literal("準備好了嗎?"));
ClientboundSetTitlesAnimationPacket
控制 title 的淡入/顯示/淡出時間(單位:tick)。
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
// fadeIn=10, stay=70, fadeOut=20 ticks
ClientboundSetTitlesAnimationPacket packet =
new ClientboundSetTitlesAnimationPacket(10, 70, 20);
ClientboundClearTitlesPacket
import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket;
// resetTimes=true 同時重設計時器
ClientboundClearTitlesPacket packet = new ClientboundClearTitlesPacket(true);
Entity / 實體
ClientboundSetEntityDataPacket
同步實體的 DataTracker 元資料(名稱、發光、靜音等)。
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.network.syncher.SynchedEntityData;
// 取得實體的 entityData 快照
List<SynchedEntityData.DataValue<?>> packedItems = entity.getEntityData().packAll();
if (packedItems != null) {
ClientboundSetEntityDataPacket packet =
new ClientboundSetEntityDataPacket(entity.getId(), packedItems);
}
| 欄位 | 類型 | 說明 |
|---|---|---|
id |
int |
NMS entity ID |
packedItems |
List<SynchedEntityData.DataValue<?>> |
元資料列表 |
ClientboundSetEntityMotionPacket
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
import net.minecraft.world.phys.Vec3;
ClientboundSetEntityMotionPacket packet =
new ClientboundSetEntityMotionPacket(entity.getId(), entity.getDeltaMovement());
// 或手動指定速度
ClientboundSetEntityMotionPacket packet2 =
new ClientboundSetEntityMotionPacket(entity.getId(), new Vec3(0.0, 0.5, 0.0));
ClientboundSetEquipmentPacket
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.world.entity.EquipmentSlot;
import com.mojang.datafixers.util.Pair;
List<Pair<EquipmentSlot, net.minecraft.world.item.ItemStack>> slots = List.of(
Pair.of(EquipmentSlot.MAINHAND, swordItemStack)
);
ClientboundSetEquipmentPacket packet =
new ClientboundSetEquipmentPacket(entity.getId(), slots);
ClientboundEntityEventPacket
觸發客戶端實體動畫(死亡、受傷、魔法等)。
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
// eventId: 2=受傷動畫, 3=死亡動畫, 6=馬跳躍, 18=貓發情
ClientboundEntityEventPacket packet =
new ClientboundEntityEventPacket(entity, (byte) 2);
常用 eventId 值:
| ID | 效果 | 適用實體 |
|---|---|---|
| 2 | 受傷動畫 | 所有 LivingEntity |
| 3 | 死亡動畫 | 所有 LivingEntity |
| 6 | 跳躍粒子 | Horse |
| 11 | 施法動畫 | Witch |
| 18 | 愛心粒子 | 可繁殖動物 |
| 29 | 盾牌格擋粒子 | Player |
ClientboundTeleportEntityPacket
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
ClientboundTeleportEntityPacket packet =
new ClientboundTeleportEntityPacket(entity);
World / 世界
ClientboundLevelParticlesPacket
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
import net.minecraft.core.particles.ParticleTypes;
ClientboundLevelParticlesPacket packet = new ClientboundLevelParticlesPacket(
ParticleTypes.FLAME, // 粒子類型
true, // overrideLimiter:遠距離也顯示
false, // alwaysShow:無視客戶端「粒子:減少」設定(1.21.4+ 新增)
x, y, z, // 中心座標
0.5f, 0.5f, 0.5f, // 隨機偏移範圍
0.0f, // speed(影響粒子初速度)
10 // 數量
);
常用粒子類型(ParticleTypes.xxx):
| 常數 | 效果 |
|---|---|
FLAME |
火焰 |
HEART |
愛心 |
EXPLOSION |
爆炸 |
SMOKE |
煙霧 |
END_ROD |
末影柱粒子 |
ENCHANT |
附魔粒子 |
HAPPY_VILLAGER |
綠色星星 |
ANGRY_VILLAGER |
黑色星星 |
CRIT |
暴擊粒子 |
SOUL_FIRE_FLAME |
靈魂火焰 |
DRAGON_BREATH |
龍息 |
ClientboundBlockUpdatePacket
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Blocks;
ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(
new BlockPos(x, y, z),
Blocks.STONE.defaultBlockState()
);
ClientboundExplodePacket
類名為
ClientboundExplodePacket(非ClientboundExplosionPacket)。此建構子在 1.21.2、26.x 皆有改版,以下為 26.2 版本。
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.random.WeightedList;
import net.minecraft.world.phys.Vec3;
// 純客戶端爆炸效果,不破壞方塊
ClientboundExplodePacket packet = new ClientboundExplodePacket(
new Vec3(x, y, z), // 中心座標
3.0f, // 半徑(影響視覺效果)
0, // 受影響方塊數(0 = 不破壞)
java.util.Optional.empty(), // 玩家 knockback
ParticleTypes.EXPLOSION_EMITTER, // 爆炸粒子
SoundEvents.GENERIC_EXPLODE, // 音效
WeightedList.of() // 方塊碎屑粒子(空 = 無)
);
Player / 玩家狀態
ClientboundSetHealthPacket
import net.minecraft.network.protocol.game.ClientboundSetHealthPacket;
// health: 0.0–20.0, foodLevel: 0–20, saturation: 0.0–5.0
ClientboundSetHealthPacket packet =
new ClientboundSetHealthPacket(20.0f, 20, 5.0f);
ClientboundSetExperiencePacket
import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
// progress: 0.0–1.0(進度條), totalExp: 總經驗, level: 等級
ClientboundSetExperiencePacket packet =
new ClientboundSetExperiencePacket(0.5f, 100, 10);
ClientboundContainerSetSlotPacket
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
// containerId=-1=玩家背包, -2=所有格子, 0+=容器開啟ID
ClientboundContainerSetSlotPacket packet = new ClientboundContainerSetSlotPacket(
-1, // containerId(-1 = 玩家背包)
stateId, // stateId(通常用 0)
slotIndex, // 格子索引(0=頭盔, 1=胸甲, 2=褲子, 3=靴子, 9-44=背包)
nmsItemStack
);
Network / 網路
ClientboundCustomPayloadPacket(Plugin Message)
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
import net.minecraft.resources.Identifier;
Identifier channel = Identifier.fromNamespaceAndPath("myplugin", "sync");
byte[] data = /* your data */;
// 1.20.5+:CustomPacketPayload 改為 type() + StreamCodec;任意 channel 原始位元組用 Paper 的 DiscardedPayload
ClientboundCustomPayloadPacket packet =
new ClientboundCustomPayloadPacket(new DiscardedPayload(channel, data));
ClientboundDisconnectPacket
import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket;
import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; // 1.20.2+ 移至 common
// Game 階段踢出
ClientboundDisconnectPacket packet =
new ClientboundDisconnectPacket(Component.literal("你被踢出了"));
Serverbound 封包(客戶端 → 伺服器)
Movement / 移動
ServerboundMovePlayerPacket 系列
| 類名 | 傳送內容 |
|---|---|
ServerboundMovePlayerPacket.Pos |
位置(x, y, z)+ onGround |
ServerboundMovePlayerPacket.PosRot |
位置 + 角度(yaw, pitch)+ onGround |
ServerboundMovePlayerPacket.Rot |
僅角度 + onGround |
ServerboundMovePlayerPacket.StatusOnly |
僅 onGround 狀態 |
攔截範例:
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
if (msg instanceof ServerboundMovePlayerPacket.PosRot move) {
double x = move.getX(player.getX());
double y = move.getY(player.getY());
double z = move.getZ(player.getZ());
float yaw = move.getYRot(player.getYRot());
}
Interaction / 互動
ServerboundInteractPacket / ServerboundAttackPacket
26.2:ServerboundInteractPacket 改為 record,攻擊拆成獨立的 ServerboundAttackPacket。
// @only 26.2
import net.minecraft.network.protocol.game.ServerboundAttackPacket;
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.phys.Vec3;
if (msg instanceof ServerboundInteractPacket interact) {
int entityId = interact.entityId();
InteractionHand hand = interact.hand(); // MAIN_HAND / OFF_HAND
Vec3 location = interact.location(); // 互動點(相對實體)
boolean sneaking = interact.usingSecondaryAction();
} else if (msg instanceof ServerboundAttackPacket attack) {
int entityId = attack.entityId();
}
1.21.11:同一個 ServerboundInteractPacket 涵蓋互動與攻擊,透過 dispatch(Handler) 區分。
// @only 1.21.11
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.phys.Vec3;
if (msg instanceof ServerboundInteractPacket interact) {
int entityId = interact.getEntityId();
boolean sneaking = interact.isUsingSecondaryAction();
interact.dispatch(new ServerboundInteractPacket.Handler() {
@Override public void onInteraction(InteractionHand hand) { /* 右鍵實體 */ }
@Override public void onInteraction(InteractionHand hand, Vec3 location) { /* 右鍵實體(含互動點) */ }
@Override public void onAttack() { /* 左鍵攻擊 */ }
});
}
ServerboundUseItemPacket
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.world.InteractionHand;
if (msg instanceof ServerboundUseItemPacket use) {
InteractionHand hand = use.getHand(); // MAIN_HAND or OFF_HAND
}
Chat / 聊天
ServerboundChatPacket
import net.minecraft.network.protocol.game.ServerboundChatPacket;
if (msg instanceof ServerboundChatPacket chat) {
String message = chat.message(); // 聊天文字
// chat.timeStamp() — 訊息時間戳記
}
ServerboundChatCommandPacket
import net.minecraft.network.protocol.game.ServerboundChatCommandPacket;
if (msg instanceof ServerboundChatCommandPacket cmd) {
String command = cmd.command(); // 不含 "/" 的指令字串
}
Player Action / 玩家動作
ServerboundPlayerActionPacket
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action;
if (msg instanceof ServerboundPlayerActionPacket action) {
Action type = action.getAction();
// Action.START_DESTROY_BLOCK — 開始挖掘
// Action.STOP_DESTROY_BLOCK — 停止挖掘
// Action.ABORT_DESTROY_BLOCK — 取消挖掘
// Action.DROP_ALL_ITEMS — 丟出整組物品(Q+Ctrl)
// Action.DROP_ITEM — 丟出單個物品(Q)
// Action.RELEASE_USE_ITEM — 放開使用鍵(弓射擊)
// Action.SWAP_ITEM_WITH_OFFHAND — 換手(F 鍵)
}
ServerboundSwingPacket
import net.minecraft.network.protocol.game.ServerboundSwingPacket;
if (msg instanceof ServerboundSwingPacket swing) {
InteractionHand hand = swing.getHand(); // 揮動哪隻手
}
FriendlyByteBuf 常用操作
import net.minecraft.network.FriendlyByteBuf;
import io.netty.buffer.Unpooled;
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
// 寫入
buf.writeInt(42);
buf.writeUtf("hello");
buf.writeBoolean(true);
buf.writeFloat(3.14f);
buf.writeVarInt(1000); // 壓縮整數(封包常用)
buf.writeIdentifier(Identifier.withDefaultNamespace("stone"));
// 讀取
int i = buf.readInt();
String s = buf.readUtf(32767); // 最大長度
boolean b = buf.readBoolean();
float f = buf.readFloat();
int vi = buf.readVarInt();
相關技能
Skills/nms/nms-packet-sender/SKILL.md— 發送封包Skills/nms/nms-packet-interceptor/SKILL.md— 攔截封包Skills/_shared/nms-threading.md— 執行緒安全docs/paper-nms/network.md— Netty pipeline 結構