218 lines
6.1 KiB
Markdown
218 lines
6.1 KiB
Markdown
---
|
|
title: Event System
|
|
type: docs
|
|
weight: 1
|
|
---
|
|
|
|
Hytale uses an event-driven architecture where your plugin can subscribe to and react to game events.
|
|
|
|
## The EventBus
|
|
|
|
The `EventBus` is the central hub for all events. It manages event registration and dispatching.
|
|
|
|
## Registering Event Listeners
|
|
|
|
Use the `EventRegistry` from your plugin to register listeners:
|
|
|
|
```java
|
|
@Override
|
|
public void start() {
|
|
// Lambda syntax - PlayerReadyEvent fires when player is fully loaded
|
|
// Note: PlayerReadyEvent has a String key, so use registerGlobal()
|
|
getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
|
|
Player player = event.getPlayer();
|
|
getLogger().at(Level.INFO).log("Player ready: " + player.getDisplayName());
|
|
});
|
|
|
|
// Method reference
|
|
getEventRegistry().register(PlayerDisconnectEvent.class, this::onPlayerDisconnect);
|
|
}
|
|
|
|
private void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
|
PlayerRef playerRef = event.getPlayerRef();
|
|
getLogger().at(Level.INFO).log("Player left: " + playerRef.getUsername());
|
|
}
|
|
```
|
|
|
|
## Registration Methods
|
|
|
|
### Basic Registration
|
|
|
|
```java
|
|
// Register with default priority (NORMAL)
|
|
getEventRegistry().register(PlayerConnectEvent.class, event -> {
|
|
// Handle event
|
|
});
|
|
```
|
|
|
|
{{< callout type="warning" >}}
|
|
**Important:** The simple `register(Class, Consumer)` method only works for events with a `Void` key type (like `PlayerConnectEvent`). For keyed events (like `PlayerReadyEvent` which has a `String` key), you must use `registerGlobal()` instead. Check the event's type parameter to determine which method to use.
|
|
{{< /callout >}}
|
|
|
|
### With Priority
|
|
|
|
```java
|
|
// Register with specific priority
|
|
getEventRegistry().register(EventPriority.EARLY, PlayerConnectEvent.class, event -> {
|
|
// This runs before NORMAL priority handlers
|
|
});
|
|
```
|
|
|
|
### With Key
|
|
|
|
Some events support key-based registration for filtering:
|
|
|
|
```java
|
|
// Only listen to events for a specific key
|
|
getEventRegistry().register(SomeKeyedEvent.class, myKey, event -> {
|
|
// Only called when event key matches myKey
|
|
});
|
|
```
|
|
|
|
### Global Registration
|
|
|
|
Listen to all events of a type, regardless of key:
|
|
|
|
```java
|
|
getEventRegistry().registerGlobal(KeyedEvent.class, event -> {
|
|
// Called for all instances of this event
|
|
});
|
|
```
|
|
|
|
### Unhandled Registration
|
|
|
|
Handle events that weren't processed by any key-specific listener:
|
|
|
|
```java
|
|
getEventRegistry().registerUnhandled(KeyedEvent.class, event -> {
|
|
// Called when no specific handler matched
|
|
});
|
|
```
|
|
|
|
### Async Global and Unhandled
|
|
|
|
For async events with global or unhandled registration:
|
|
|
|
```java
|
|
// Async global - all keys
|
|
getEventRegistry().registerAsyncGlobal(AsyncKeyedEvent.class, future ->
|
|
future.thenApply(event -> {
|
|
// Process async
|
|
return event;
|
|
})
|
|
);
|
|
|
|
// Async unhandled - unmatched keys
|
|
getEventRegistry().registerAsyncUnhandled(AsyncKeyedEvent.class, future ->
|
|
future.thenApply(event -> {
|
|
// Handle unmatched
|
|
return event;
|
|
})
|
|
);
|
|
```
|
|
|
|
## EventRegistration
|
|
|
|
Registration methods return an `EventRegistration` object:
|
|
|
|
```java
|
|
EventRegistration<?, PlayerConnectEvent> registration =
|
|
getEventRegistry().register(PlayerConnectEvent.class, this::onConnect);
|
|
|
|
// Later, you can unregister manually if needed
|
|
registration.unregister();
|
|
```
|
|
|
|
{{< callout type="info" >}}
|
|
Event registrations are automatically cleaned up when your plugin shuts down. Manual unregistration is rarely needed.
|
|
{{< /callout >}}
|
|
|
|
## Event Interface Hierarchy
|
|
|
|
Events implement different interfaces based on their capabilities:
|
|
|
|
| Interface | Description |
|
|
|-----------|-------------|
|
|
| `IBaseEvent<K>` | Base interface for all events |
|
|
| `IEvent<K>` | Synchronous events |
|
|
| `IAsyncEvent<K>` | Asynchronous events |
|
|
| `ICancellable` | Events that can be cancelled |
|
|
| `ICancellableEcsEvent` | ECS events that can be cancelled |
|
|
|
|
## Getting Registered Events
|
|
|
|
Query which events are registered:
|
|
|
|
```java
|
|
// Get all registered event classes
|
|
Set<Class<? extends IBaseEvent<?>>> eventClasses =
|
|
HytaleServer.get().getEventBus().getRegisteredEventClasses();
|
|
|
|
// Get registered event names
|
|
Set<String> eventNames =
|
|
HytaleServer.get().getEventBus().getRegisteredEventClassNames();
|
|
|
|
// Get a specific registry by name
|
|
EventBusRegistry<?, ?, ?> registry =
|
|
HytaleServer.get().getEventBus().getRegistry("PlayerConnectEvent");
|
|
```
|
|
|
|
## Complete Example
|
|
|
|
```java
|
|
public class WelcomePlugin extends JavaPlugin {
|
|
|
|
public WelcomePlugin(JavaPluginInit init) {
|
|
super(init);
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
// Handle player connection
|
|
getEventRegistry().register(PlayerConnectEvent.class, event -> {
|
|
PlayerRef playerRef = event.getPlayerRef();
|
|
getLogger().at(Level.INFO).log("Player connecting: " + playerRef.getUsername());
|
|
|
|
// You can set the world the player spawns in
|
|
World lobbyWorld = Universe.get().getWorld("lobby");
|
|
if (lobbyWorld != null) {
|
|
event.setWorld(lobbyWorld);
|
|
}
|
|
});
|
|
|
|
// Welcome message when fully ready
|
|
// Note: PlayerReadyEvent has a String key, so use registerGlobal()
|
|
getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
|
|
Player player = event.getPlayer();
|
|
player.sendMessage(Message.raw("Welcome to the server, " + player.getDisplayName() + "!"));
|
|
});
|
|
|
|
// Goodbye message on disconnect
|
|
getEventRegistry().register(PlayerDisconnectEvent.class, event -> {
|
|
PlayerRef playerRef = event.getPlayerRef();
|
|
getLogger().at(Level.INFO).log(playerRef.getUsername() + " has left the server");
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## Player Event Lifecycle
|
|
|
|
Understanding when each event fires:
|
|
|
|
```
|
|
PlayerSetupConnectEvent → Can cancel/redirect connection
|
|
↓
|
|
PlayerConnectEvent → Player connecting, set spawn world
|
|
↓
|
|
AddPlayerToWorldEvent → Player added to world
|
|
↓
|
|
PlayerReadyEvent → Player fully loaded, safe to interact
|
|
↓
|
|
(gameplay events)
|
|
↓
|
|
DrainPlayerFromWorldEvent → Player leaving world
|
|
↓
|
|
PlayerDisconnectEvent → Player disconnected
|
|
```
|