Events
How to listen to, react to, and reason about Identica events.
Identica has its own event system for addon plugins, integrations, and internal components.
Basic listener pattern
To consume Identica events from an addon plugin:
- implement
EventListener - annotate handler methods with
@IdenticEvent - register the listener through
identica.getEventManager()
Listener example
import me.whereareiam.identica.IdenticaAPI;
import me.whereareiam.identica.event.EventListener;
import me.whereareiam.identica.event.base.IdenticEvent;
import me.whereareiam.identica.event.scenario.registration.RegistrationRequiredEvent;
import java.time.Instant;
public final class ExampleRegistrationListener implements EventListener {
@IdenticEvent
public void onRegistrationRequired(RegistrationRequiredEvent event) {
System.out.println(
"Registration is now required until " + Instant.ofEpochMilli(event.getExpiresAt())
);
}
public void register() {
IdenticaAPI.whenReady(identica -> identica.getEventManager().register(this));
}
}Event order
If you need a specific execution order, pass it to @IdenticEvent.
import me.whereareiam.identica.event.EventListener;
import me.whereareiam.identica.event.base.IdenticEvent;
import me.whereareiam.identica.event.session.SessionOpenedEvent;
import me.whereareiam.identica.type.event.EventOrder;
public final class ExampleSessionListener implements EventListener {
@IdenticEvent(EventOrder.LOW)
public void onSessionOpened(SessionOpenedEvent event) {
System.out.println("Opened session for: " + event.getSession().getEffectiveUsername());
}
}Startup and shutdown
Identica starts in phases, each one after the previous has finished: the configuration, the log, the stores (the database and replication), the core subsystems, the features, the providers, the integrations such as usage statistics, and finally player connections. Three lifecycle events mark that sequence:
IdenticaBootstrappedEventis called once the features have started, before the providers are loaded.IdenticaReadyEventis called at the end of startup. The providers are loaded with their commands and documents, Identica handles player connections, and the actions given toIdenticaAPI.whenReady(...)have run.IdenticaShutdownEventis called when Identica stops. AtEventOrder.LOWIdentica stops the phases in reverse, so a listener withEventOrder.LOWESTstill runs while the providers and the database are open, and later listeners run after they are closed.
Messages to a joining player
A player's client shows chat, titles and dialogs only once it is in the world of a server. While it still configures or switches servers, it drops chat and closes dialogs. ServerJoinedEvent (in me.whereareiam.identica.event.connection) is called at that moment: on the first join and again after every server switch, with the player's Identity and the server's name. Identica delivers its own queued messages, such as the first prompt and the completion message, from this event.
Show your own messages or dialogs to a joining player from it rather than from a platform's join or connect event:
import me.whereareiam.identica.event.EventListener;
import me.whereareiam.identica.event.base.IdenticEvent;
import me.whereareiam.identica.event.connection.ServerJoinedEvent;
import net.kyori.adventure.text.Component;
public final class WelcomeListener implements EventListener {
@IdenticEvent
public void onServerJoined(ServerJoinedEvent event) {
event.getIdentity().sendMessage(Component.text("Welcome to " + event.getServer()));
}
}On BungeeCord the event is called from Identica's scheduler shortly after the join, because BungeeCord has no event once the client is in the world.
Common event categories
The current API exposes event groups such as:
- lifecycle events
- scenario and pipeline events
- routing events
- session events
- provider events
- feature-owned events such as verification enrollment (when that feature is selected)
- account and identity events
That means addon plugins can react at different levels, from high-level scenario start events down to specific verification or routing decisions.
Cancellable and mutable events
Some events are only informational. Others can be modified or cancelled.
For example, VerificationEnrollEvent is cancellable and lets listeners rewrite the provider or method before enrollment begins. It belongs to the verification feature API. Depend on that API at compile time and register the listener after Identica initializes. Verification behavior follows the effective settings of the provider involved.
import me.whereareiam.identica.event.EventListener;
import me.whereareiam.identica.event.base.IdenticEvent;
import me.whereareiam.identica.feature.verification.event.enroll.VerificationEnrollEvent;
public final class ExampleVerificationListener implements EventListener {
@IdenticEvent
public void onEnroll(VerificationEnrollEvent event) {
if ("totp".equalsIgnoreCase(event.getMethodId())) {
event.setProviderId("credential");
}
}
}Another example is AccountPrepareEvent, where listeners can influence the final preparation decision.
import me.whereareiam.identica.event.EventListener;
import me.whereareiam.identica.event.account.AccountPrepareEvent;
import me.whereareiam.identica.event.base.IdenticEvent;
import me.whereareiam.identica.model.identity.AccountDecision;
public final class ExampleAccountPrepareListener implements EventListener {
@IdenticEvent
public void onPrepare(AccountPrepareEvent event) {
if (event.getDecision() == null) {
event.setDecision(AccountDecision.allow());
}
}
}Always check the specific event type before assuming it is cancellable or mutable.
Synchronous vs asynchronous
Identica distinguishes between synchronous events and non-synchronous events through the event type hierarchy.
If an event implements SynchronousEvent, it is processed synchronously on the current thread.
For addon authors, that means you should treat event handlers as part of the active runtime flow and avoid unnecessary blocking work inside them.
Replicated events
Some Identica events also implement ReplicatedEvent.
Those are intended to cross the replication channel and be replayed on other runtimes.
Important properties of replicated events:
- they carry a replication event id for deduplication
- they carry an origin server id
- only registered replicated event types are allowed onto the wire
- they should represent facts that are safe for every instance to handle locally
That last point is important: not every event should become a replicated event. One-time global operations are usually better expressed as commands or service calls rather than replayed events.
Built-in replicated events
The current replicated event bridge registers these built-in replicated event types:
SessionClosedEventAccountClearEventAccountDeleteEvent
Those are propagated because they represent state changes that other runtimes can safely observe and apply locally.
When to care about replicated events
As an addon author, you care about replicated events when:
- your integration runs in a replicated multi-instance environment
- you need to react consistently to account or session changes across runtimes
- you are considering defining an event that may need cross-instance replay semantics
If your addon only reacts locally and does not need cross-instance replay, normal Identica events are usually enough.
