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 serverSoulFireInstanceEvent: events that belong to one instanceSoulFireBotEvent: 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:
| Event | Scope | When it fires |
|---|---|---|
CommandManagerInitEvent | global | after built-in commands are registered and before you add your own |
ServerSettingsRegistryInitEvent | global | while the server settings registry is being assembled |
InstanceSettingsRegistryInitEvent | instance | while an instance settings registry is being assembled |
InstanceInitEvent | instance | immediately after an InstanceManager is created |
Bot events
These are the current bot-facing events exposed in com.soulfiremc.server.api.event.bot:
| Event | What it is for |
|---|---|
PreBotConnectEvent | async pre-connect hook before the bot joins |
BotConnectionInitEvent | right after the BotConnection object exists |
BotConnectionRemovedEvent | after a disconnected bot is removed from its instance |
BotPreTickEvent | bot tick hook before the tick body finishes |
BotPostTickEvent | bot tick hook after the tick body finishes |
BotPreEntityTickEvent | before entity tracking logic ticks |
BotPostEntityTickEvent | after entity tracking logic ticks |
ChatMessageReceiveEvent | incoming chat messages |
BotPacketPreReceiveEvent | inspect or replace inbound packets before handling |
BotPacketPreSendEvent | inspect or replace outbound packets before send |
BotDamageEvent | health went down |
BotOpenContainerEvent | a container or screen opened |
BotClientBrandEvent | outgoing client brand can be changed |
BotClientSettingsEvent | outgoing client settings can be changed |
BotShouldRespawnEvent | control automatic respawn behavior |
BotDisconnectedEvent | the 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 tonullto suppress handlingBotPacketPreSendEvent: replace the packet, or set it tonullto suppress sendingBotClientBrandEvent: change the brand stringBotClientSettingsEvent: change theClientInformationBotShouldRespawnEvent: setshouldRespawn
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:
PreBotConnectEventruns before the player is ready and can do blocking workBotConnectionInitEventhas a fully constructedBotConnection, but the bot is not connected yetBotPreTickEventandBotPostTickEventrun in the live bot tick loopBotPreEntityTickEventandBotPostEntityTickEventare 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
