--- title: Event Priorities type: docs weight: 2 --- Event priorities control the order in which event listeners are called. This is important when multiple plugins or handlers need to react to the same event. ## Priority Values Hytale provides five priority levels: | Priority | Value | Description | |----------|-------|-------------| | `FIRST` | -21844 | Runs first, before all others | | `EARLY` | -10922 | Runs early, after FIRST | | `NORMAL` | 0 | Default priority | | `LATE` | 10922 | Runs late, after NORMAL | | `LAST` | 21844 | Runs last, after all others | {{< callout type="info" >}} Lower values run first. The values are spread across the `short` range to allow for custom priorities between the standard levels. {{< /callout >}} ## Using EventPriority ### With Enum ```java getEventRegistry().register( EventPriority.EARLY, PlayerConnectEvent.class, event -> { // This runs before NORMAL handlers } ); getEventRegistry().register( EventPriority.LATE, PlayerConnectEvent.class, event -> { // This runs after NORMAL handlers } ); ``` ### With Custom Value ```java // Use a custom priority value (between -32768 and 32767) short customPriority = -5000; // Between EARLY and NORMAL getEventRegistry().register( customPriority, PlayerConnectEvent.class, event -> { // Custom priority handler } ); ``` ## Execution Order When an event is fired, handlers are called in priority order: ``` FIRST (-21844) ↓ EARLY (-10922) ↓ NORMAL (0) ← Default ↓ LATE (10922) ↓ LAST (21844) ``` ### Example ```java @Override public void start() { getEventRegistry().register(EventPriority.LAST, PlayerReadyEvent.class, event -> { getLogger().at(Level.INFO).log("3. LAST handler"); }); getEventRegistry().register(EventPriority.FIRST, PlayerReadyEvent.class, event -> { getLogger().at(Level.INFO).log("1. FIRST handler"); }); getEventRegistry().register(PlayerReadyEvent.class, event -> { getLogger().at(Level.INFO).log("2. NORMAL handler (default)"); }); } // Output when player is ready: // 1. FIRST handler // 2. NORMAL handler (default) // 3. LAST handler ``` ## When to Use Each Priority ### FIRST Use for: - Setting up event data before other handlers - Logging/debugging that should see the original event - Critical validation that must run before anything else ```java getEventRegistry().register(EventPriority.FIRST, PlayerChatEvent.class, event -> { // Log original message before any filtering logChat(event.getSender(), event.getContent()); }); ``` ### EARLY Use for: - Modifications that other plugins should see - Permission checks before regular handlers - Data preprocessing ```java getEventRegistry().register(EventPriority.EARLY, PlayerChatEvent.class, event -> { // Filter profanity before other plugins process the message String filtered = filterProfanity(event.getContent()); event.setContent(filtered); }); ``` ### NORMAL Use for: - Standard event handling - Most plugin functionality - Default when priority doesn't matter ```java getEventRegistry().register(PlayerChatEvent.class, event -> { // Standard chat processing processMessage(event.getSender(), event.getContent()); }); ``` ### LATE Use for: - Reactions to modifications made by other handlers - Analytics/statistics gathering - Secondary effects ```java getEventRegistry().register(EventPriority.LATE, PlayerChatEvent.class, event -> { // Record the final message after all modifications recordChatHistory(event.getSender(), event.getContent()); }); ``` ### LAST Use for: - Final cleanup or overrides - Monitoring cancellation status - Logging final event state ```java getEventRegistry().register(EventPriority.LAST, PlayerChatEvent.class, event -> { if (event.isCancelled()) { getLogger().at(Level.INFO).log("Chat was cancelled by another plugin"); } }); ``` ## With Cancellable Events Priority is especially important with cancellable events: ```java // FIRST handler might cancel getEventRegistry().register(EventPriority.FIRST, BreakBlockEvent.class, event -> { if (isProtectedArea(event.getTargetBlock())) { event.setCancelled(true); } }); // NORMAL handler should check cancellation getEventRegistry().register(BreakBlockEvent.class, event -> { if (!event.isCancelled()) { // Process block break trackBlockBreak(event.getTargetBlock(), event.getBlockType()); } }); // LAST handler for monitoring getEventRegistry().register(EventPriority.LAST, BreakBlockEvent.class, event -> { if (event.isCancelled()) { logProtectedBlockAttempt(event.getTargetBlock()); } }); ``` ## Async Events and Priority Async events also support priorities: ```java getEventRegistry().registerAsync( EventPriority.EARLY, PlayerChatEvent.class, future -> future.thenApply(event -> { // Early async processing event.setContent(filterContent(event.getContent())); return event; }) ); getEventRegistry().registerAsync( EventPriority.LATE, PlayerChatEvent.class, future -> future.thenApply(event -> { // Late async processing - sees modified content logFinalMessage(event.getSender(), event.getContent()); return event; }) ); ``` ## Best Practices {{< callout type="tip" >}} **Priority Guidelines:** - Use `FIRST` sparingly - only for logging or critical checks - Protection/permission plugins should use `EARLY` - Feature plugins should use `NORMAL` (default) - Analytics/logging should use `LATE` - Use `LAST` only when you need to see the final event state {{< /callout >}} {{< callout type="warning" >}} **Avoid Priority Conflicts:** - Document your plugin's priority choices - Check cancellation status at NORMAL and later priorities - Don't rely on running "after" specific plugins {{< /callout >}}