# Event System

The Hytale event system provides a powerful mechanism for plugins to react to game events. It supports both synchronous
and asynchronous events with priority-based dispatching.

## Event Architecture

### Event Interface Hierarchy

```
IBaseEvent<KeyType>
├── IEvent<KeyType>        // Synchronous events
└── IAsyncEvent<KeyType>   // Asynchronous events (CompletableFuture)

ICancellable               // Mixin interface for cancellable events
```

### Key Classes

| Class                  | Description                                |
|------------------------|--------------------------------------------|
| `IEvent<K>`            | Synchronous event interface                |
| `IAsyncEvent<K>`       | Asynchronous event interface               |
| `ICancellable`         | Interface for events that can be cancelled |
| `EventRegistry`        | Plugin-specific event registration         |
| `SyncEventBusRegistry` | Global synchronous event bus               |
| `EventPriority`        | Event listener priority levels             |

## Event Priorities

Events are dispatched to listeners in priority order:

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

Lower values run first. Use `FIRST` sparingly - typically for monitoring/logging. Use `LAST` for final processing after
other plugins have had a chance to modify the event.

## Registering Event Listeners

### Basic Registration

```java
@Override
protected void setup() {
    // Register with default NORMAL priority
    getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect);
}

private void onPlayerConnect(PlayerConnectEvent event) {
    getLogger().info("Player connected: " + event.getPlayer().getName());
}
```

### With Priority

```java
// Run before other listeners
getEventRegistry().register(EventPriority.FIRST, PlayerConnectEvent.class, event -> {
    getLogger().debug("Logging player connect (first)");
});

// Run after other listeners
getEventRegistry().register(EventPriority.LAST, PlayerConnectEvent.class, event -> {
    getLogger().debug("Final processing (last)");
});
```

### Global Listeners

Global listeners receive all events of a type, regardless of the event's key:

```java
// Receive all entity events
getEventRegistry().registerGlobal(EntityEvent.class, event -> {
    // Handle any entity event
});
```

### Keyed Events

Some events are keyed (e.g., by world name). You can listen for specific keys:

```java
// Listen for events in a specific world
getEventRegistry().register(AddPlayerToWorldEvent.class, "worldName", event -> {
    // Only triggered for "worldName"
});
```

### Unhandled Events

Register for events that no other listener handled:

```java
getEventRegistry().registerUnhandled(CustomEvent.class, event -> {
    // Only called if no other listener handled this event
});
```

## Asynchronous Events

Async events return `CompletableFuture` and are handled on separate threads:

```java
// Register async event listener
getEventRegistry().registerAsync(AsyncDataLoadEvent.class, event -> {
    return CompletableFuture.supplyAsync(() -> {
        // Perform async operation
        return loadData(event.getDataId());
    });
});

// Global async listener
getEventRegistry().registerAsyncGlobal(AsyncEvent.class, this::handleAsync);

// Unhandled async listener
getEventRegistry().registerAsyncUnhandled(AsyncEvent.class, this::handleUnhandledAsync);
```

## Cancellable Events

Events implementing `ICancellable` can be cancelled by listeners:

```java
getEventRegistry().register(PlayerInteractEvent.class, event -> {
    if (shouldBlock(event)) {
        event.setCancelled(true);
    }
});
```

When checking if an event was cancelled:

```java
getEventRegistry().register(EventPriority.LAST, PlayerInteractEvent.class, event -> {
    if (!event.isCancelled()) {
        // Event was not cancelled by earlier listeners
        processInteraction(event);
    }
});
```

## Common Event Types

### Server Lifecycle Events

| Event                  | Description             |
|------------------------|-------------------------|
| `BootEvent`            | Server boot complete    |
| `ShutdownEvent`        | Server shutting down    |
| `PrepareUniverseEvent` | Universe initialization |

### Player Events

| Event                       | Description                      |
|-----------------------------|----------------------------------|
| `PlayerConnectEvent`        | Player connected to server       |
| `PlayerDisconnectEvent`     | Player disconnected              |
| `AddPlayerToWorldEvent`     | Player added to a world          |
| `DrainPlayerFromWorldEvent` | Player removed from a world      |
| `PlayerInteractEvent`       | Player interaction (cancellable) |
| `PlayerMouseButtonEvent`    | Mouse button input (cancellable) |

### Entity Events

| Event                       | Description                 |
|-----------------------------|-----------------------------|
| `EntityRemoveEvent`         | Entity removed from world   |
| `LivingEntityUseBlockEvent` | Living entity using a block |

### World Events

| Event                  | Description                 |
|------------------------|-----------------------------|
| `AddWorldEvent`        | World added to universe     |
| `RemoveWorldEvent`     | World removed               |
| `StartWorldEvent`      | World started               |
| `AllWorldsLoadedEvent` | All worlds finished loading |

### ECS Events

| Event               | Description       |
|---------------------|-------------------|
| `UseBlockEvent`     | Block usage event |
| `DiscoverZoneEvent` | Zone discovery    |

## Creating Custom Events

### Synchronous Event

```java
public class MyCustomEvent implements IEvent<String> {
    private final String key;
    private final Player player;
    private final String data;
    
    public MyCustomEvent(String key, Player player, String data) {
        this.key = key;
        this.player = player;
        this.data = data;
    }
    
    @Override
    public String getKey() {
        return key;
    }
    
    public Player getPlayer() {
        return player;
    }
    
    public String getData() {
        return data;
    }
}
```

### Cancellable Event

```java
public class MyCancellableEvent implements IEvent<String>, ICancellable {
    private final String key;
    private boolean cancelled = false;
    
    public MyCancellableEvent(String key) {
        this.key = key;
    }
    
    @Override
    public String getKey() {
        return key;
    }
    
    @Override
    public boolean isCancelled() {
        return cancelled;
    }
    
    @Override
    public void setCancelled(boolean cancelled) {
        this.cancelled = cancelled;
    }
}
```

### Async Event

```java
public class MyAsyncEvent implements IAsyncEvent<String> {
    private final String key;
    
    public MyAsyncEvent(String key) {
        this.key = key;
    }
    
    @Override
    public String getKey() {
        return key;
    }
}
```

## Dispatching Events

To dispatch custom events:

```java
// Get the event bus
SyncEventBusRegistry<String, MyCustomEvent> eventBus = getEventBus(MyCustomEvent.class);

// Dispatch event
MyCustomEvent event = new MyCustomEvent("key", player, "data");
eventBus.dispatch(event);

// For cancellable events, check result
MyCancellableEvent cancellable = new MyCancellableEvent("key");
eventBus.dispatch(cancellable);
if (!cancellable.isCancelled()) {
    // Proceed with action
}
```

## Event Registration Management

### Unregistering Listeners

Event registration returns an `EventRegistration` that can be used to unregister:

```java
EventRegistration registration = getEventRegistry().register(
    PlayerConnectEvent.class, 
    this::onConnect
);

// Later, to unregister:
registration.unregister();
```

### Combining Registrations

Multiple registrations can be combined for bulk management:

```java
EventRegistration reg1 = getEventRegistry().register(Event1.class, this::handle1);
EventRegistration reg2 = getEventRegistry().register(Event2.class, this::handle2);

EventRegistration combined = reg1.combine(reg2);

// Unregister both at once
combined.unregister();
```

### Conditional Listeners

Registrations can be conditionally enabled:

```java
// Listener only active while condition is true
Supplier<Boolean> isEnabled = () -> config.get().featureEnabled;
// The registration checks isEnabled before calling the listener
```

## Best Practices

1. **Use appropriate priorities** - Don't use `FIRST` unless you need to monitor/log before modifications
2. **Check cancellation state** - If using `LATE` or `LAST`, check if event was cancelled
3. **Don't block in sync handlers** - Use async events for long-running operations
4. **Clean up registrations** - Unregister listeners in `shutdown()` if needed
5. **Use keyed events** - When possible, listen for specific keys to reduce unnecessary handler calls
6. **Handle exceptions** - Wrap handler code in try-catch to prevent crashing other listeners
7. **Document custom events** - When creating custom events, document their key type and when they're dispatched
