Skip to content
MJP
/ NMS 封包攔截器

NMS · NMS 封包

NMS 封包攔截器

透過 Netty ChannelDuplexHandler 注入玩家連線管線,攔截或修改 Clientbound 與 Serverbound 封包。

NMS已發布v1.0.0nms-packet-interceptor更新於 2026年4月19日

觸發關鍵字

封包攔截packet interceptnetty pipelinechannel handler封包監聽修改封包packet listener

NMS Packet Interceptor

目的

在玩家的 Netty 連線管線中注入自訂 ChannelDuplexHandler,於封包進入或離開伺服器時進行讀取、修改或取消。常用於反外掛、封包記錄、自訂通訊協議。


替代方案

不想碰 NMS/Netty 時,改用 PacketEvents 或 ProtocolLib 軟依賴:見 paper-packetevents-filter。不論哪條路都要 fail-open:出錯時讓封包照常通過、停用自己並只記錄一次。


平台需求

  • Paper 1.21.11 / 26.2(兩版皆經編譯驗證;版本差異以行尾 // @1.21.11: 標註)
  • Paperweight userdev 2.0.0-beta.24+
  • Netty 4.x(Paper 內建)

產生的代碼

PacketInterceptor.java(ChannelDuplexHandler)

public final class PacketInterceptor extends ChannelDuplexHandler {

    // Serverbound(客戶端 → 伺服器)
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof Packet<?> packet) {
            Packet<?> modified = inboundFilter.apply(player, packet);
            if (modified == null) return; // 取消封包
            msg = modified;
        }
        super.channelRead(ctx, msg);
    }

    // Clientbound(伺服器 → 客戶端)
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof Packet<?> packet) {
            Packet<?> modified = outboundFilter.apply(player, packet);
            if (modified == null) return; // 取消封包
            msg = modified;
        }
        super.write(ctx, msg, promise);
    }
}

InterceptorManager.java

// 注入(PlayerJoinEvent LOWEST)
channel.pipeline().addBefore("packet_handler", HANDLER_ID, interceptor);

// 移除(PlayerQuitEvent)
channel.eventLoop().execute(() -> channel.pipeline().remove(HANDLER_ID));

執行緒安全

  • channelRead / write 皆在 Netty IO thread 執行
  • 需存取 Entity/World → 用 Bukkit.getScheduler().runTask() 切回主執行緒
  • 移除 handler 必須透過 channel.eventLoop().execute()