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:
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:
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: LOWEST → LOW → NORMAL (the default) → HIGH → HIGHEST → MONITOR. 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
...