Listening to Events
This guide covers how to register event listeners in your Hytale plugins.
Getting the EventRegistry
Section titled “Getting the EventRegistry”In your plugin class (extending JavaPlugin), access the event registry via getEventRegistry():
public class MyPlugin extends JavaPlugin {
public MyPlugin(JavaPluginInit init) { super(init); }
@Override protected void setup() { EventRegistry events = this.getEventRegistry(); // register listeners here }}Registration Methods
Section titled “Registration Methods”register() - Standard Registration
Section titled “register() - Standard Registration”Use register() for events with Void key type, or when you want to listen for a specific key.
Void-Keyed Events (Global)
Section titled “Void-Keyed Events (Global)”// basic registrationevents.register(BootEvent.class, this::onBoot);
// with priorityevents.register(EventPriority.EARLY, PlayerConnectEvent.class, this::onConnect);
// with custom numeric priorityevents.register((short)-5000, SomeEvent.class, this::onSomeEvent);
// lambda handlerevents.register(ShutdownEvent.class, event -> { getLogger().info("Server shutting down!");});Keyed Events (Specific Key)
Section titled “Keyed Events (Specific Key)”// only receive events for "lobby" worldevents.register(AddPlayerToWorldEvent.class, "lobby", this::onLobbyJoin);
// only receive LoadedAssetsEvent for CraftingRecipe assetsevents.register(LoadedAssetsEvent.class, CraftingRecipe.class, this::onRecipeLoad);registerGlobal() - Global Registration for Keyed Events
Section titled “registerGlobal() - Global Registration for Keyed Events”Use registerGlobal() when you want to receive ALL events of a keyed type, regardless of the key value. This is essential for events like AddPlayerToWorldEvent where you want to handle all worlds.
// receive player add events for ALL worldsevents.registerGlobal(AddPlayerToWorldEvent.class, this::onPlayerAddToWorld);
// with priorityevents.registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, this::onChunkLoad);
// lambdaevents.registerGlobal(AddWorldEvent.class, event -> { getLogger().info("World added: " + event.getWorld().getName());});registerUnhandled() - Fallback Registration
Section titled “registerUnhandled() - Fallback Registration”Use registerUnhandled() for handlers that should only be called when NO keyed handlers matched the event. Useful for default/fallback behavior.
// only called if no world-specific handler matchedevents.registerUnhandled(AddPlayerToWorldEvent.class, this::onUnhandledWorldJoin);
// with priorityevents.registerUnhandled(EventPriority.NORMAL, SomeKeyedEvent.class, this::fallbackHandler);registerAsync() - Async Event Registration
Section titled “registerAsync() - Async Event Registration”Use registerAsync() for events implementing IAsyncEvent. The handler receives a CompletableFuture to enable async processing chains.
// async handlerevents.registerAsync(PlayerChatEvent.class, future -> future.thenApply(event -> { // modify the message asynchronously event.setContent(filterBadWords(event.getContent())); return event; }));
// async with specific keyevents.registerAsync(SomeAsyncEvent.class, "channelName", future -> future.thenAccept(event -> { // handle asynchronously }));
// global asyncevents.registerAsyncGlobal(PlayerChatEvent.class, future -> future.thenApply(event -> { logChat(event); return event; }));
// unhandled asyncevents.registerAsyncUnhandled(SomeAsyncEvent.class, future -> future.thenApply(this::handleUnmatched));Handler Patterns
Section titled “Handler Patterns”Method Reference
Section titled “Method Reference”events.register(PlayerConnectEvent.class, this::onPlayerConnect);
private void onPlayerConnect(PlayerConnectEvent event) { PlayerRef player = event.getPlayerRef(); getLogger().info("Player connected: " + player.getUsername());}Lambda Expression
Section titled “Lambda Expression”events.register(BootEvent.class, event -> { getLogger().info("Server booted!");});Inline with Priority
Section titled “Inline with Priority”events.register(EventPriority.FIRST, PlayerConnectEvent.class, event -> { // this runs before NORMAL priority handlers logConnection(event);});EventRegistration Object
Section titled “EventRegistration Object”All registration methods return an EventRegistration object that can be used to manage the listener.
Storing Registrations
Section titled “Storing Registrations”public class MyPlugin extends JavaPlugin { private EventRegistration<?, ?> chatRegistration;
@Override protected void setup() { chatRegistration = getEventRegistry() .registerGlobal(PlayerChatEvent.class, this::onChat); }
// manually unregister when needed public void disableChatHandler() { if (chatRegistration != null) { chatRegistration.unregister(); chatRegistration = null; } }}Checking Registration Status
Section titled “Checking Registration Status”if (chatRegistration != null && chatRegistration.isRegistered()) { // handler is still active}Combining Registrations
Section titled “Combining Registrations”Group multiple registrations for bulk unregistration:
EventRegistration<?, ?> combined = EventRegistration.combine( events.register(EventA.class, this::onA), events.register(EventB.class, this::onB), events.register(EventC.class, this::onC));
// later: unregister all at oncecombined.unregister();Automatic Cleanup
Section titled “Automatic Cleanup”All event registrations made through getEventRegistry() are automatically cleaned up when your plugin is disabled or the server shuts down. You only need to manually unregister if you want to stop listening before plugin shutdown.
// automatically cleaned up - no manual unregister neededevents.register(PlayerConnectEvent.class, this::onConnect);Complete Plugin Example
Section titled “Complete Plugin Example”package com.example.myplugin;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;import com.hypixel.hytale.server.core.plugin.JavaPluginInit;import com.hypixel.hytale.event.EventRegistry;import com.hypixel.hytale.event.EventRegistration;import com.hypixel.hytale.event.EventPriority;import com.hypixel.hytale.server.core.event.events.*;import com.hypixel.hytale.server.core.event.events.player.*;import com.hypixel.hytale.server.core.universe.world.events.*;
public class MyPlugin extends JavaPlugin {
private EventRegistration<?, ?> chatRegistration;
public MyPlugin(JavaPluginInit init) { super(init); }
@Override protected void setup() { EventRegistry events = this.getEventRegistry();
// 1. simple void-keyed event events.register(BootEvent.class, this::onBoot);
// 2. global registration for keyed events events.registerGlobal(PlayerConnectEvent.class, this::onPlayerConnect); events.registerGlobal(AddWorldEvent.class, this::onWorldAdd);
// 3. with priority - run early events.registerGlobal(EventPriority.EARLY, PlayerReadyEvent.class, this::onPlayerReady);
// 4. specific key - only "spawn" world events.register(AddPlayerToWorldEvent.class, "spawn", this::onSpawnJoin);
// 5. async event - store registration for later control chatRegistration = events.registerAsyncGlobal(PlayerChatEvent.class, future -> future.thenApply(event -> { if (containsBadWords(event.getContent())) { event.setCancelled(true); } return event; }) );
// 6. lambda for simple handlers events.register(ShutdownEvent.class, event -> { getLogger().info("Goodbye!"); }); }
private void onBoot(BootEvent event) { getLogger().info("Server booted, MyPlugin ready!"); }
private void onPlayerConnect(PlayerConnectEvent event) { getLogger().info("Player connecting: " + event.getPlayerRef().getUsername()); // optionally set spawn world // event.setWorld(mySpawnWorld); }
private void onWorldAdd(AddWorldEvent event) { getLogger().info("World added: " + event.getWorld().getName()); }
private void onPlayerReady(PlayerReadyEvent event) { // player is fully loaded and ready sendWelcomeMessage(event.getPlayer()); }
private void onSpawnJoin(AddPlayerToWorldEvent event) { // only called for "spawn" world getLogger().info("Player joined spawn world"); }
// public method to disable chat filtering public void disableChatFilter() { if (chatRegistration != null) { chatRegistration.unregister(); chatRegistration = null; getLogger().info("Chat filter disabled"); } }
private boolean containsBadWords(String content) { // implementation return false; }
private void sendWelcomeMessage(Player player) { // implementation }}Common Patterns from Built-in Plugins
Section titled “Common Patterns from Built-in Plugins”TeleportPlugin Pattern
Section titled “TeleportPlugin Pattern”// global registration for chunk events (all worlds)eventRegistry.registerGlobal(ChunkPreLoadProcessEvent.class, this::onChunkPreLoadProcess);
// global for world eventseventRegistry.registerGlobal(AddWorldEvent.class, event -> event.getWorld().getWorldMapManager().addMarkerProvider("warps", WarpMarkerProvider.INSTANCE));
eventRegistry.registerGlobal(AllWorldsLoadedEvent.class, event -> this.loadWarps());CraftingPlugin Pattern
Section titled “CraftingPlugin Pattern”// keyed by asset class - only receive CraftingRecipe asset eventsthis.getEventRegistry().register(LoadedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeLoad);this.getEventRegistry().register(RemovedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeRemove);this.getEventRegistry().register(LoadedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetLoad);FluidPlugin Pattern
Section titled “FluidPlugin Pattern”// FIRST priority - run before other handlersthis.getEventRegistry().registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, FluidPlugin::onChunkPreProcess);FarmingPlugin Pattern
Section titled “FarmingPlugin Pattern”// LAST priority - ensure other handlers process firstthis.getEventRegistry().registerGlobal(EventPriority.LAST, ChunkPreLoadProcessEvent.class, FarmingPlugin::preventSpreadOnNew);When to Use Which Method
Section titled “When to Use Which Method”| Scenario | Method |
|---|---|
| Global event (Void key) | register() |
| Want ALL events of a keyed type | registerGlobal() |
| Want events for specific key only | register(..., key, ...) |
| Fallback when no key matches | registerUnhandled() |
| Async event handling | registerAsync() / registerAsyncGlobal() |
| Run before other handlers | Use EventPriority.FIRST or EARLY |
| Run after other handlers | Use EventPriority.LATE or LAST |