--- 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 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` | Base interface for all events | | `IEvent` | Synchronous events | | `IAsyncEvent` | 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>> eventClasses = HytaleServer.get().getEventBus().getRegisteredEventClasses(); // Get registered event names Set 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 ```