CommandBridge Logo About Docs
GitHub Discord Download

Receiving

To receive messages on a channel, register a MessageListener with channel.listen(). The listener is called for every incoming message on that channel.

The easiest way to explain it is with an example:

import dev.objz.commandbridge.api.channel.MessageChannel;
import dev.objz.commandbridge.api.channel.command.CommandPayload;
import dev.objz.commandbridge.api.message.Subscription;

MessageChannel<CommandPayload> channel = api.channel(CommandPayload.class);

Subscription sub = channel.listen((ctx, payload) -> {
    String from = ctx.from().id();
    String command = payload.command();
});

First, we call channel.listen() with a lambda that takes a MessageContext and the payload. The context carries metadata about the message: which server sent it and when.

Then, we store the returned Subscription. We need it to unregister the listener during plugin shutdown.

The listener is invoked on the thread that processes the incoming network message. If you need to dispatch to a specific thread (Bukkit main thread, Folia region, Velocity event thread), schedule that explicitly inside the listener.

Multiple listeners can be registered on the same channel and all of them are called for each incoming message.


MessageContext

MessageContext<T> is a record with three components:

Component Type Description
channel() Class<T> The payload class token, identifying which channel the message arrived on
from() Platform.ServerTarget The server that sent the message
timestamp() long Unix epoch milliseconds when the message was sent

ctx.from() returns a ServerTarget with .id() (the server's configured identifier) and .type() (Platform.BACKEND or Platform.VELOCITY).

On backends, ctx.from().type() is always BACKEND. On Velocity, the type is inferred from the session and can be BACKEND for connected backends or VELOCITY for other proxy instances in a multi-proxy setup.


Canceling a subscription

Subscription.cancel() unregisters the listener. It is idempotent, so calling it more than once is safe.

Use a single Subscription field when you register one listener. Use List<Subscription> when you register multiple listeners that need to be canceled together (as shown on the Events page for onServerConnected and onServerDisconnected).

import dev.objz.commandbridge.api.message.Subscription;

private Subscription commandListener;

@Subscribe
public void onProxyInitialize(ProxyInitializeEvent event) {
    commandListener = channel.listen((ctx, payload) -> {
        String from = ctx.from().id();
        String command = payload.command();
    });
}

@Subscribe
public void onProxyShutdown(ProxyShutdownEvent event) {
    commandListener.cancel();
}
import dev.objz.commandbridge.api.message.Subscription;

private Subscription commandListener;

@Override
public void onEnable() {
    commandListener = channel.listen((ctx, payload) -> {
        String from = ctx.from().id();
        String command = payload.command();
    });
}

@Override
public void onDisable() {
    commandListener.cancel();
}

If you don't cancel subscriptions during shutdown, the listener reference stays in the channel's internal list and prevents garbage collection.

Always cancel subscriptions during shutdown. Store every Subscription returned by listen() and cancel them all on disable.


Receiving methods

Subscription listen(MessageListener<P> listener)

Registers a listener for incoming messages on this channel. Returns a Subscription. Multiple listeners can be registered on the same channel and all are called for each incoming message.

void cancel()

Unregisters the listener. Called on the Subscription returned by listen(). Idempotent.