Packets
Inspect, cancel, and rewrite the raw packets between client and server.
Endstone raises an event for every packet that crosses the wire: PacketReceiveEvent for packets coming from a client, and PacketSendEvent for packets going to one. Both are cancellable and expose the packet's raw bytes, so you can observe, block, or rewrite traffic the higher-level API doesn't reach. The examples alias the namespace with namespace es = endstone;.
This is the lowest level of the API. getPayload() returns the raw packet data excluding the header - to read or change anything inside it you must decode and re-encode it yourself according to the Bedrock protocol (varints and all). Get it wrong and the client disconnects. Reach for a higher-level API first; use packets only when nothing else exposes what you need.
Register a packet handler like any other event. Each event gives you:
getPacketId()- the numeric type of the packetgetPayload()/setPayload()- the raw bytes after the headergetPlayer()- the player involved, ornullptrfor packets sent before login completesgetSubClientId()-0for the primary client,1-3for split-screen
Observe packets
The simplest use is watching traffic. Filter by getPacketId() - logging every packet is overwhelming:
#include <endstone/endstone.hpp>
namespace es = endstone;
class MyPlugin : public es::Plugin {
public:
void onEnable() override
{
registerEvent(&MyPlugin::onPacketSend, *this);
}
// packet IDs come from the Bedrock protocol; they can change between versions
static constexpr int TEXT_PACKET = 9;
void onPacketSend(es::PacketSendEvent &event)
{
if (event.getPacketId() == TEXT_PACKET) {
auto *player = event.getPlayer();
std::string who = player ? player->getName() : "<pre-login>";
getLogger().info("text packet to {}: {} bytes", who, event.getPayload().size());
}
}
};Drop a packet
Because the events are cancellable, setCancelled(true) stops the packet from being sent or processed:
void onPacketReceive(es::PacketReceiveEvent &event)
{
if (event.getPacketId() == BLOCKED_PACKET) {
event.setCancelled(true);
}
}Rewrite a payload
To change a packet, copy getPayload(), edit it, and hand the new bytes back with setPayload(). Decoding is on you - the snippet below just shows the read/modify/write shape:
void onPacketSend(es::PacketSendEvent &event)
{
if (event.getPacketId() != TEXT_PACKET) {
return;
}
std::string data{event.getPayload()}; // copy the raw bytes
// ... decode per the Bedrock protocol, modify, re-encode ...
event.setPayload(data);
}getPlayer() can return nullptr for packets exchanged during the login handshake - always check it before using the player.