---
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 >}}
