Skip to main content

Overview

Paper’s event system enables plugins to respond to game events through a listener-based architecture. The system is designed for high performance with minimal overhead, using array-backed handler lists and priority-based execution.

Core Components

Event Class

All events extend org.bukkit.event.Event, which provides:
Every event must have a static getHandlerList() method that returns the same HandlerList instance as the instance getHandlers() method.

HandlerList

The HandlerList class is the key to Paper’s event performance:
Key Design Decisions:
  • Array-backed storage - Handlers are “baked” into an array for fast iteration
  • Priority-based slots - Listeners are organized by EventPriority (LOWEST to MONITOR)
  • Lazy baking - The handler array is only rebuilt when listeners change
  • Global tracking - All HandlerList instances are tracked for batch operations
The handler array is the secret to this system’s speed - array iteration is significantly faster than list iteration.

RegisteredListener

RegisteredListener wraps:
  • The Listener instance (the plugin class with event methods)
  • The EventExecutor (invokes the specific handler method)
  • The Plugin that registered the listener
  • The EventPriority for execution order
  • Ignore cancelled flag

EventExecutor

The EventExecutor interface handles calling event handler methods:
Paper uses EventExecutorFactory to create optimized executors that:
  • Directly invoke handler methods without reflection overhead
  • Validate method signatures at registration time
  • Handle accessibility automatically
Paper’s event executors are generated at runtime for optimal performance, avoiding reflection during event firing.

Event Lifecycle

1. Event Registration

When a plugin registers listeners:

2. Handler Baking

Before event firing, handlers are “baked” into an array:
Baking happens automatically on-demand. The handler array remains valid until a listener is added or removed.

3. Event Firing

When an event is fired:

Event Priorities

Paper executes event handlers in priority order:
The MONITOR priority should never modify the event or its outcome - it’s for logging and observation only.

Synchronous vs Asynchronous Events

Events can be synchronous or asynchronous:

Synchronous Events (default)

  • Fired on the main server thread
  • Can safely modify game state
  • Must complete quickly to avoid lag
  • Most game events are synchronous

Asynchronous Events

Asynchronous event caveats:
  • Cannot be fired from synchronous event handlers (throws IllegalStateException)
  • May fire multiple times simultaneously
  • Handlers can block without affecting server performance
  • Cannot safely modify game state directly
  • Not included in plugin timing reports
Async event handlers that need to modify game state should schedule synchronous tasks using the scheduler.

Cancellable Events

Events implementing Cancellable can be cancelled:
Listeners can ignore cancelled events:

Performance Optimizations

Handlers are stored in a volatile array field that’s iterated during event firing. Array iteration is significantly faster than list iteration, especially for frequently-fired events.
The handler array is only rebuilt when listeners are added or removed. Most of the time, events use the pre-baked array for maximum speed.
Paper generates optimized event executors at runtime instead of using reflection, eliminating the overhead of Method.invoke().
Using an EnumMap for priority slots provides O(1) access while maintaining order, making registration and baking efficient.

Global Handler Operations

Paper tracks all HandlerList instances for batch operations:
bakeAll() is called after all plugins load to pre-bake all handler arrays before events start firing.

Event Type Tracking

Paper tracks which event types have been instantiated:
This enables:
  • Debugging which events are in use
  • Performance monitoring
  • Event system introspection

Best Practices

Don’t default everything to HIGHEST - use NORMAL for most handlers and only use other priorities when you specifically need to run before or after other plugins.
Event handlers should complete quickly. Long-running operations should be scheduled asynchronously to avoid blocking the server thread.
Never modify events or game state in MONITOR handlers - this priority is for observation only.
Each registered handler adds overhead. Combine related logic into fewer handlers when possible.