Event System
Event System
Section titled “Event System”Hytale’s event system allows plugins to react to game events, from player connections to block breaks. The system supports both synchronous and asynchronous events, with features like priority ordering, keyed events, and cancellation.
Package Location
Section titled “Package Location”- Event interfaces:
com.hypixel.hytale.event - Event registry:
com.hypixel.hytale.event.EventRegistry - Built-in events:
com.hypixel.hytale.server.core.event.events
Core Concepts
Section titled “Core Concepts”Event Types
Section titled “Event Types”The event system has two primary event interfaces:
| Interface | Description | Handler Type |
|---|---|---|
IEvent<KeyType> | Synchronous events processed immediately | Consumer<EventType> |
IAsyncEvent<KeyType> | Asynchronous events processed with futures | Function<CompletableFuture<EventType>, CompletableFuture<EventType>> |
Both interfaces are generic over a KeyType:
Voidfor non-keyed events (most common)- A specific type for keyed events (e.g.,
Stringfor chat channels)
Event Interfaces
Section titled “Event Interfaces”// Synchronous event - most commonpublic interface IEvent<KeyType> extends IBaseEvent<KeyType> {}
// Asynchronous event - for operations that may need async processingpublic interface IAsyncEvent<KeyType> extends IBaseEvent<KeyType> {}
// Cancellable mixin - allows handlers to prevent default behaviorpublic interface ICancellable { boolean isCancelled(); void setCancelled(boolean cancelled);}Registering Event Handlers
Section titled “Registering Event Handlers”Basic Registration
Section titled “Basic Registration”@Overrideprotected void setup() { // Simple event registration (default priority: NORMAL) getEventRegistry().register( PlayerConnectEvent.class, this::onPlayerConnect );}
private void onPlayerConnect(PlayerConnectEvent event) { // Handle the event World world = event.getWorld(); PlayerRef player = event.getPlayerRef();}Registration with Priority
Section titled “Registration with Priority”@Overrideprotected void setup() { // Register with specific priority getEventRegistry().register( EventPriority.FIRST, // Process early PlayerChatEvent.class, this::onChatFirst );
getEventRegistry().register( EventPriority.LAST, // Process late (monitoring) PlayerChatEvent.class, this::onChatLast );}Event Priorities
Section titled “Event Priorities”Events are processed in priority order from lowest to highest:
| Priority | Value | Typical Use |
|---|---|---|
FIRST | -21844 | Modify event data before others see it |
EARLY | -10922 | Early processing, setup |
NORMAL | 0 | Default, general handling |
LATE | 10922 | React after modifications |
LAST | 21844 | Final processing, logging, monitoring |
You can also use raw short values for fine-grained control:
getEventRegistry().register( (short) 5000, // Custom priority between NORMAL and LATE SomeEvent.class, this::onEvent);Keyed Events
Section titled “Keyed Events”Some events are “keyed” - they have an associated key that handlers can filter on.
Registering for a Specific Key
Section titled “Registering for a Specific Key”// Only receive events for a specific keygetEventRegistry().register( SomeKeyedEvent.class, "my-channel", // The key to filter on this::onMyChannelEvent);Registering Globally (All Keys)
Section titled “Registering Globally (All Keys)”// Receive all keyed events regardless of keygetEventRegistry().registerGlobal( SomeKeyedEvent.class, this::onAnyKeyedEvent);Unhandled Events
Section titled “Unhandled Events”Register a handler that only fires if no keyed handler processed the event:
getEventRegistry().registerUnhandled( SomeKeyedEvent.class, this::onUnhandledEvent // Only called if no key-specific handler matched);Async Events
Section titled “Async Events”Async events support asynchronous processing chains:
@Overrideprotected void setup() { getEventRegistry().registerAsync( PlayerChatEvent.class, future -> future.thenApply(event -> { // Process asynchronously if (containsBannedWord(event.getContent())) { event.setCancelled(true); } return event; }) );}Async with Priority
Section titled “Async with Priority”getEventRegistry().registerAsync( EventPriority.EARLY, PlayerChatEvent.class, future -> future.thenApply(this::processChat));Async Global and Unhandled
Section titled “Async Global and Unhandled”// Async global listenergetEventRegistry().registerAsyncGlobal( SomeAsyncKeyedEvent.class, future -> future.thenApply(this::handleGlobally));
// Async unhandled listenergetEventRegistry().registerAsyncUnhandled( SomeAsyncKeyedEvent.class, future -> future.thenApply(this::handleUnhandled));Cancellable Events
Section titled “Cancellable Events”Events implementing ICancellable can be cancelled to prevent their default behavior:
private void onPlayerChat(PlayerChatEvent event) { if (event.getContent().contains("badword")) { event.setCancelled(true); // Message won't be sent }}Checking Cancellation
Section titled “Checking Cancellation”private void onPlayerChat(PlayerChatEvent event) { if (event.isCancelled()) { return; // Skip if already cancelled by another handler }
// Process the event}Note: LAST priority handlers can still observe cancelled events for logging purposes.
Built-in Events
Section titled “Built-in Events”Player Events
Section titled “Player Events”Located in com.hypixel.hytale.server.core.event.events.player:
| Event | Async | Cancellable | Description |
|---|---|---|---|
PlayerConnectEvent | No | No | Player connected to server |
PlayerDisconnectEvent | No | No | Player disconnected |
PlayerReadyEvent | No | No | Player fully loaded |
PlayerChatEvent | Yes | Yes | Player sent chat message |
PlayerInteractEvent | No | No | Player interaction |
PlayerCraftEvent | No | No | Player crafted item |
PlayerMouseMotionEvent | No | No | Player mouse movement |
PlayerMouseButtonEvent | No | No | Player mouse click |
PlayerSetupConnectEvent | No | No | Pre-connection setup |
PlayerSetupDisconnectEvent | No | No | Pre-disconnect setup |
DrainPlayerFromWorldEvent | No | No | Player leaving world |
AddPlayerToWorldEvent | No | No | Player entering world |
Entity Events
Section titled “Entity Events”Located in com.hypixel.hytale.server.core.event.events.entity:
| Event | Description |
|---|---|
EntityEvent | Base entity event |
EntityRemoveEvent | Entity removed from world |
LivingEntityInventoryChangeEvent | Entity inventory changed |
LivingEntityUseBlockEvent | Entity used a block |
ECS Events
Section titled “ECS Events”Located in com.hypixel.hytale.server.core.event.events.ecs:
| Event | Description |
|---|---|
BreakBlockEvent | Block broken |
PlaceBlockEvent | Block placed |
DamageBlockEvent | Block damaged |
UseBlockEvent | Block used/interacted |
DropItemEvent | Item dropped |
InteractivelyPickupItemEvent | Item picked up |
CraftRecipeEvent | Recipe crafted |
ChangeGameModeEvent | Game mode changed |
SwitchActiveSlotEvent | Active inventory slot changed |
DiscoverZoneEvent | Zone discovered |
Permission Events
Section titled “Permission Events”Located in com.hypixel.hytale.server.core.event.events.permissions:
| Event | Description |
|---|---|
PlayerPermissionChangeEvent | Player permissions changed |
GroupPermissionChangeEvent | Permission group changed |
PlayerGroupEvent | Player added/removed from group |
Server Events
Section titled “Server Events”| Event | Description |
|---|---|
ShutdownEvent | Server shutting down |
PrepareUniverseEvent | Universe being prepared |
BootEvent | Server booting up |
Working with Events
Section titled “Working with Events”Modifying Event Data
Section titled “Modifying Event Data”Many events allow modification:
private void onPlayerChat(PlayerChatEvent event) { // Modify the message String filtered = filterProfanity(event.getContent()); event.setContent(filtered);
// Change message targets List<PlayerRef> newTargets = event.getTargets().stream() .filter(this::canReceive) .toList(); event.setTargets(newTargets);
// Custom formatting event.setFormatter((player, msg) -> Message.translation("custom.chat.format") .param("name", player.getUsername()) .param("msg", msg) );}Accessing Event Context
Section titled “Accessing Event Context”Events provide context about what happened:
private void onPlayerConnect(PlayerConnectEvent event) { // Get the player reference PlayerRef playerRef = event.getPlayerRef();
// Get the ECS holder for components Holder<EntityStore> holder = event.getHolder();
// Get/set the target world World world = event.getWorld(); event.setWorld(differentWorld); // Change spawn world
// Access player component (deprecated, prefer holder) Player player = event.getPlayer();}Creating Custom Events
Section titled “Creating Custom Events”// Synchronous eventpublic class MyCustomEvent implements IEvent<Void> { private final String data;
public MyCustomEvent(String data) { this.data = data; }
public String getData() { return data; }}
// Cancellable async eventpublic class MyAsyncEvent implements IAsyncEvent<Void>, ICancellable { private boolean cancelled = false;
@Override public boolean isCancelled() { return cancelled; }
@Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; }}Dispatching Custom Events
Section titled “Dispatching Custom Events”// Get the event bus from the serverIEventBus eventBus = HytaleServer.get().getEventBus();
// Dispatch a sync eventIEventDispatcher<MyEvent, MyEvent> dispatcher = eventBus.dispatchFor(MyEvent.class, null);MyEvent result = dispatcher.dispatch(new MyEvent("data"));
// Dispatch an async eventIEventDispatcher<MyAsyncEvent, CompletableFuture<MyAsyncEvent>> asyncDispatcher = eventBus.dispatchForAsync(MyAsyncEvent.class, null);CompletableFuture<MyAsyncEvent> future = asyncDispatcher.dispatch(new MyAsyncEvent());Best Practices
Section titled “Best Practices”- Choose the right priority: Use
FIRSTfor modifications,LASTfor monitoring - Check cancellation: Early-exit if the event is already cancelled (unless monitoring)
- Don’t block: Keep handlers fast; use async events for slow operations
- Handle exceptions: Wrap risky code in try-catch to avoid breaking other handlers
- Use appropriate granularity: Register for specific keys when possible
private void onEvent(SomeEvent event) { if (event instanceof ICancellable c && c.isCancelled()) { return; // Skip cancelled events }
try { // Handler logic } catch (Exception e) { getLogger().at(Level.WARNING).withCause(e).log("Error handling event"); }}Automatic Cleanup
Section titled “Automatic Cleanup”All event registrations made through getEventRegistry() are automatically unregistered when your plugin shuts down. You don’t need to manually unregister handlers.
Related
Section titled “Related”- Plugin Registries - All available registries
- Plugin Lifecycle - Registration timing
- Built-in Events Catalog - Complete event reference