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.

This is the lowest level of the API. payload is 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:

  • packet_id - the numeric type of the packet
  • payload - the raw bytes after the header (get and set)
  • player - the player involved, or None for packets sent before login completes
  • sub_client_id - 0 for the primary client, 1-3 for split-screen

Observe packets

The simplest use is watching traffic. Filter by packet_id - logging every packet is overwhelming:

from endstone.event import event_handler, PacketSendEvent

    # packet IDs come from the Bedrock protocol; they can change between versions
    TEXT_PACKET = 9

    @event_handler
    def on_packet_send(self, event: PacketSendEvent):
        if event.packet_id == self.TEXT_PACKET:
            who = event.player.name if event.player else "<pre-login>"
            self.logger.info(f"text packet to {who}: {len(event.payload)} bytes")

Drop a packet

Because the events are cancellable, setting is_cancelled stops the packet from being sent or processed:

from endstone.event import event_handler, PacketReceiveEvent

    @event_handler
    def on_packet_receive(self, event: PacketReceiveEvent):
        if event.packet_id == self.BLOCKED_PACKET:
            event.is_cancelled = True

Rewrite a payload

To change a packet, decode payload, edit it, and assign the new bytes back. Decoding is on you - the snippet below just shows the read/modify/write shape:

    @event_handler
    def on_packet_send(self, event: PacketSendEvent):
        if event.packet_id != self.TEXT_PACKET:
            return

        data = bytearray(event.payload)
        # ... decode per the Bedrock protocol, modify, re-encode ...
        event.payload = bytes(data)

player can be None for packets exchanged during the login handshake - always check it before using the player.

On this page