SoulFire LogoSoulFire

Event System

Listen for SoulFire events and change behavior through event data.

Use events before you use Mixins. Events cover server lifecycle, bots, chat, packets, and settings. They are easier to maintain than Mixins.

How listener registration works

Every Plugin registers itself in two ways when it is constructed:

  • it registers static listeners declared on the class
  • it registers instance listeners declared on the plugin object

Both patterns work:

@EventHandler
public static void onBotInit(BotConnectionInitEvent event) {
  // stateless handler
}
@EventHandler
public void onSettingsRegistryInit(InstanceSettingsRegistryInitEvent event) {
  // instance handler
}

The annotation comes from LambdaEvents:

import net.lenni0451.lambdaevents.EventHandler;

Event hierarchy

The public event model is split into three scopes:

  • SoulFireGlobalEvent: events that belong to the whole SoulFire server
  • SoulFireInstanceEvent: events that belong to one instance
  • SoulFireBotEvent: events that belong to one bot and also imply instance access

A bot event provides a BotConnection. From it, you can access the InstanceManager and SoulFireServer.

Lifecycle events

These are the main lifecycle hooks currently exposed:

EventScopeWhen it fires
CommandManagerInitEventglobalafter built-in commands are registered and before you add your own
ServerSettingsRegistryInitEventglobalwhile the server settings registry is being assembled
InstanceSettingsRegistryInitEventinstancewhile an instance settings registry is being assembled
InstanceInitEventinstanceimmediately after an InstanceManager is created

Bot events

These are the current bot-facing events exposed in com.soulfiremc.server.api.event.bot:

EventWhat it is for
PreBotConnectEventasync pre-connect hook before the bot joins
BotConnectionInitEventright after the BotConnection object exists
BotConnectionRemovedEventafter a disconnected bot is removed from its instance
BotPreTickEventbot tick hook before the tick body finishes
BotPostTickEventbot tick hook after the tick body finishes
BotPreEntityTickEventbefore entity tracking logic ticks
BotPostEntityTickEventafter entity tracking logic ticks
ChatMessageReceiveEventincoming chat messages
BotPacketPreReceiveEventinspect or replace inbound packets before handling
BotPacketPreSendEventinspect or replace outbound packets before send
BotDamageEventhealth went down
BotOpenContainerEventa container or screen opened
BotClientBrandEventoutgoing client brand can be changed
BotClientSettingsEventoutgoing client settings can be changed
BotShouldRespawnEventcontrol automatic respawn behavior
BotDisconnectedEventthe bot disconnected

Mutable hooks

Some events are plain immutable records. Some are deliberately mutable and are the lowest-friction way to change behavior without a Mixin.

Use these when they match your need:

  • BotPacketPreReceiveEvent: replace the packet, or set it to null to suppress handling
  • BotPacketPreSendEvent: replace the packet, or set it to null to suppress sending
  • BotClientBrandEvent: change the brand string
  • BotClientSettingsEvent: change the ClientInformation
  • BotShouldRespawnEvent: set shouldRespawn

SoulFire provides AbstractCancellable, but most public events use data that a listener can change. For many low-level hooks, replacing a packet or mutating the outgoing value is the intended extension model.

Timing and thread context

The event name alone is not enough. You also need to care about timing:

  • PreBotConnectEvent runs before the player is ready and can do blocking work
  • BotConnectionInitEvent has a fully constructed BotConnection, but the bot is not connected yet
  • BotPreTickEvent and BotPostTickEvent run in the live bot tick loop
  • BotPreEntityTickEvent and BotPostEntityTickEvent are even closer to the entity update path

In early lifecycle hooks, minecraft().player can still be null.

Custom commands

If your plugin needs terminal commands, listen for CommandManagerInitEvent and add Brigadier commands there. SoulFire posts this event after its built-in commands are already registered.

When to stop and use a Mixin instead

Use a Mixin only when:

  • there is no event at the right timing point
  • you need data that the event API does not provide
  • you need to alter control flow inside Minecraft or SoulFire internals

Before you write a Mixin, make sure that an existing packet or client-setting event cannot do the work.

Reference code to study

The built-in plugins and SoulFire's own event-emitting mixins are the best reference:

Next steps

How is this page?

Last updated on

On this page