NMS · NMS 封包
NMS 封包攔截器
透過 Netty ChannelDuplexHandler 注入玩家連線管線,攔截或修改 Clientbound 與 Serverbound 封包。
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()