网络数据包

发包

发送一个数据包非常简单,只需调用EntityPlayer#sendPacket

收包

使得服务器认为收到了一个玩家的数据包并进行处理,调用EntityPlayer#receivePacket

收发包监听

对于一个Packet的子类,我们可以监听它实例的发送或接收

只需创建一个PacketListener实例并注册到你的模块中

监听器不一定在主线程上触发

例如我们需要监听PacketC2sChatMessage

// in your module
@Override
public void onLoad()
{
    this.register(new PacketListener<>(PacketC2sChatMessage::create, packetEvent->
    {
        packetEvent.setCancelled(true); // 取消收包,如果你想这么做的话
    }));
}

从数据包事件中获取和修改数据包

当数据包监听器被调用时,相关信息会被封装在数据包事件PacketEvent.Specialized<P>

其中包含了这个数据包,使用时调用packetEvent.getPacket()但不要将这个结果储存,因为它随时可能会改变

如果需要修改发包的内容,你可能需要先确保这个数据包是副本(因为Mojang可能会把同一个数据包发送给不同玩家),通过packetEvent.ensureCopied()

this.register(new PacketListener<>(PacketS2cWindowSlotUpdate::create, packetEvent->
{
    // 确保数据包是副本
    packetEvent.ensureCopied();
    // 修改数据包
    packetEvent.getPacket().setItemStack(ItemStack.empty());
}));

同步监听

异步监听能进行的操作有限

有时处理收发包时你可能需要进行同步操作

这时调用sync方法,参数是你要执行的操作action

如果该监听器本身是在主线程中触发,则该action会立即完成

否则该数据包会被推迟到主线程下一个tick再继续处理

如果MC本身就要在主线程处理这个数据包(如PacketC2sCloseWindow),则调用并sync()不会导致更多延迟

this.register(new PacketListener<>(PacketC2sCloseWindow::create, packetEvent->
{
    // 如果需要同步处理
    packetEvent.sync(()->
    {
        // 如果需要则取消事件
        packetEvent.setCancelled(true); // 取消事件
    });
}));