Skip to content

Listening to Events

This guide covers how to register event listeners in your Hytale plugins.

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

Use register() for events with Void key type, or when you want to listen for a specific key.

// basic registration
events.register(BootEvent.class, this::onBoot);
// with priority
events.register(EventPriority.EARLY, PlayerConnectEvent.class, this::onConnect);
// with custom numeric priority
events.register((short)-5000, SomeEvent.class, this::onSomeEvent);
// lambda handler
events.register(ShutdownEvent.class, event -> {
getLogger().info("Server shutting down!");
});
// only receive events for "lobby" world
events.register(AddPlayerToWorldEvent.class, "lobby", this::onLobbyJoin);
// only receive LoadedAssetsEvent for CraftingRecipe assets
events.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 worlds
events.registerGlobal(AddPlayerToWorldEvent.class, this::onPlayerAddToWorld);
// with priority
events.registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, this::onChunkLoad);
// lambda
events.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 matched
events.registerUnhandled(AddPlayerToWorldEvent.class, this::onUnhandledWorldJoin);
// with priority
events.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 handler
events.registerAsync(PlayerChatEvent.class, future ->
future.thenApply(event -> {
// modify the message asynchronously
event.setContent(filterBadWords(event.getContent()));
return event;
})
);
// async with specific key
events.registerAsync(SomeAsyncEvent.class, "channelName", future ->
future.thenAccept(event -> {
// handle asynchronously
})
);
// global async
events.registerAsyncGlobal(PlayerChatEvent.class, future ->
future.thenApply(event -> {
logChat(event);
return event;
})
);
// unhandled async
events.registerAsyncUnhandled(SomeAsyncEvent.class, future ->
future.thenApply(this::handleUnmatched)
);
events.register(PlayerConnectEvent.class, this::onPlayerConnect);
private void onPlayerConnect(PlayerConnectEvent event) {
PlayerRef player = event.getPlayerRef();
getLogger().info("Player connected: " + player.getUsername());
}
events.register(BootEvent.class, event -> {
getLogger().info("Server booted!");
});
events.register(EventPriority.FIRST, PlayerConnectEvent.class, event -> {
// this runs before NORMAL priority handlers
logConnection(event);
});

All registration methods return an EventRegistration object that can be used to manage the listener.

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;
}
}
}
if (chatRegistration != null && chatRegistration.isRegistered()) {
// handler is still active
}

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 once
combined.unregister();

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 needed
events.register(PlayerConnectEvent.class, this::onConnect);
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
}
}
// global registration for chunk events (all worlds)
eventRegistry.registerGlobal(ChunkPreLoadProcessEvent.class, this::onChunkPreLoadProcess);
// global for world events
eventRegistry.registerGlobal(AddWorldEvent.class, event ->
event.getWorld().getWorldMapManager().addMarkerProvider("warps", WarpMarkerProvider.INSTANCE));
eventRegistry.registerGlobal(AllWorldsLoadedEvent.class, event -> this.loadWarps());
// keyed by asset class - only receive CraftingRecipe asset events
this.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);
// FIRST priority - run before other handlers
this.getEventRegistry().registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, FluidPlugin::onChunkPreProcess);
// LAST priority - ensure other handlers process first
this.getEventRegistry().registerGlobal(EventPriority.LAST, ChunkPreLoadProcessEvent.class, FarmingPlugin::preventSpreadOnNew);
ScenarioMethod
Global event (Void key)register()
Want ALL events of a keyed typeregisterGlobal()
Want events for specific key onlyregister(..., key, ...)
Fallback when no key matchesregisterUnhandled()
Async event handlingregisterAsync() / registerAsyncGlobal()
Run before other handlersUse EventPriority.FIRST or EARLY
Run after other handlersUse EventPriority.LATE or LAST