Event listeners

Listen for and handle server events in Python.

Endstone raises an event whenever something happens in the game - a player joins, a block breaks, a message is sent. A plugin reacts by registering a listener for the events it cares about. The scenarios below go from simply observing an event to changing or vetoing what the server does.

Listen for an event

Mark a method with @event_handler and type its parameter with the event you want to receive:

src/endstone_my_plugin/my_plugin.py
from endstone import ColorFormat
from endstone.event import event_handler, PlayerJoinEvent
from endstone.plugin import Plugin

class MyPlugin(Plugin):
    api_version = "0.11"

    @event_handler
    def on_player_join(self, event: PlayerJoinEvent):
        self.server.broadcast_message(
            ColorFormat.YELLOW + f"{event.player.name} has joined the server"
        )

Register the listener

Handlers only fire once registered. Call register_events in on_enable, passing the object that holds them:

src/endstone_my_plugin/my_plugin.py
    def on_enable(self) -> None:
        self.register_events(self)

register_events scans the object and wires up every method marked with @event_handler, so one call covers all the handlers below.

Cancel an event

Many events are cancellable - setting event.is_cancelled = True tells the server not to do the thing it was about to. Here, only operators may break blocks:

from endstone.event import event_handler, BlockBreakEvent

    @event_handler
    def on_block_break(self, event: BlockBreakEvent):
        if not event.player.is_op:
            event.is_cancelled = True
            event.player.send_message("You can't break blocks here.")

Change an event

Some events let you rewrite their data before the server acts on it. PlayerChatEvent exposes the outgoing message:

from endstone.event import event_handler, PlayerChatEvent

    @event_handler
    def on_player_chat(self, event: PlayerChatEvent):
        event.message = event.message.replace("heck", "h*ck")

Run at a set priority

When several listeners handle the same event, they run in order: LOWESTLOWNORMAL (the default) → HIGHHIGHESTMONITOR. Set a priority to control where yours falls. MONITOR runs last and is meant for observing the final outcome, not changing it:

from endstone.event import event_handler, EventPriority, BlockBreakEvent

    @event_handler(priority=EventPriority.MONITOR)
    def log_block_break(self, event: BlockBreakEvent):
        if not event.is_cancelled:
            self.logger.info(f"{event.player.name} broke a block")

Skip already-cancelled events

By default a handler still runs even if an earlier listener cancelled the event. Pass ignore_cancelled=True to bow out when that's already happened:

    @event_handler(ignore_cancelled=True)
    def on_block_break(self, event: BlockBreakEvent):
        # only runs if no earlier listener cancelled the break
        ...

On this page