6.1 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| Event System | docs | 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:
@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
// 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
// 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:
// 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:
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:
getEventRegistry().registerUnhandled(KeyedEvent.class, event -> {
// Called when no specific handler matched
});
Async Global and Unhandled
For async events with global or unhandled registration:
// 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:
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:
// 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
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