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 extendorg.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
TheHandlerList class is the key to Paper’s event performance:
- 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
HandlerListinstances 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
Listenerinstance (the plugin class with event methods) - The
EventExecutor(invokes the specific handler method) - The
Pluginthat registered the listener - The
EventPriorityfor execution order - Ignore cancelled flag
EventExecutor
TheEventExecutor interface handles calling event handler methods:
EventExecutorFactory to create optimized executors that:
- Directly invoke handler methods without reflection overhead
- Validate method signatures at registration time
- Handle accessibility automatically
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:| Priority | Use Case |
|---|---|
LOWEST | Early modification, before other plugins |
LOW | General early handling |
NORMAL | Default priority for most handlers |
HIGH | Late handling, after most plugins |
HIGHEST | Final modifications |
MONITOR | Read-only observation, should not modify |
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
- 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
Cancellable Events
Events implementingCancellable can be cancelled:
Performance Optimizations
Array-Backed Handler Storage
Array-Backed Handler Storage
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.
Lazy Baking
Lazy Baking
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.
Generated Event Executors
Generated Event Executors
Paper generates optimized event executors at runtime instead of using reflection, eliminating the overhead of
Method.invoke().Priority Slots
Priority Slots
Using an EnumMap for priority slots provides O(1) access while maintaining order, making registration and baking efficient.
Global Handler Operations
Paper tracks allHandlerList 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:- Debugging which events are in use
- Performance monitoring
- Event system introspection
Best Practices
Use Appropriate Priorities
Use Appropriate Priorities
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.
Don't Block in Event Handlers
Don't Block in Event Handlers
Event handlers should complete quickly. Long-running operations should be scheduled asynchronously to avoid blocking the server thread.
Respect MONITOR Priority
Respect MONITOR Priority
Never modify events or game state in MONITOR handlers - this priority is for observation only.
Minimize Event Handler Count
Minimize Event Handler Count
Each registered handler adds overhead. Combine related logic into fewer handlers when possible.