Skip to content
MJP
/ NMS Packet 速查表

速查表

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 結構