Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ikeepcalm/coi-client/llms.txt

Use this file to discover all available pages before exploring further.

COI Client communicates with the server-side Paper plugin exclusively through Fabric custom payloads — Minecraft’s built-in plugin messaging system carried over the play connection. Every channel is prefixed coi-client:, and there is no REST API, WebSocket, or external transport involved. All messages travel over the same TCP connection the player uses to play, making the integration zero-configuration from a network standpoint.

Channel Summary

All seven channels are listed below. Two are sent Client → Server (player actions), and five are sent Server → Client (server-authoritative state updates).
DirectionChannelPayload ClassPurpose
C→Scoi-client:useAbilityUsePayloadActivate an ability by ID
C→Scoi-client:requestAbilityRequestPayloadRequest the available abilities list
S→Ccoi-client:abilitiesAbilitiesPayloadDeliver the full abilities list to the client
S→Ccoi-client:cooldownCooldownPayloadSet an ability on cooldown for N ticks
S→Ccoi-client:effectVisualEffectPayloadTrigger or stop a client-side visual effect
S→Ccoi-client:mythicalMythicalFormPayloadApply or remove a mythical creature form
S→Ccoi-client:conditionsConditionsPayloadSync the player’s beyonder condition state
All S→C receivers are registered globally via ClientPlayNetworking.registerGlobalReceiver during mod initialization. It is safe to send any S→C payload at any point after the player completes the login handshake — you do not need to wait for a client-side ready signal.

Registering Channels in Paper

On the Paper side, register the channels your plugin intends to send in your onEnable() method using the Messenger API. You must register each outgoing channel before sending on it.
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.Messenger;

public class CirclePlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        Messenger messenger = getServer().getMessenger();

        // S→C channels — register as outgoing
        messenger.registerOutgoingPluginChannel(this, "coi-client:abilities");
        messenger.registerOutgoingPluginChannel(this, "coi-client:cooldown");
        messenger.registerOutgoingPluginChannel(this, "coi-client:effect");
        messenger.registerOutgoingPluginChannel(this, "coi-client:mythical");
        messenger.registerOutgoingPluginChannel(this, "coi-client:conditions");

        // C→S channels — register as incoming so the server can receive them
        messenger.registerIncomingPluginChannel(this, "coi-client:use", this::handleAbilityUse);
        messenger.registerIncomingPluginChannel(this, "coi-client:request", this::handleAbilityRequest);
    }

    /**
     * Convenience helper — serialize fields into a raw byte array and send.
     * For each payload, write fields in the exact order documented in the
     * Payloads reference, using DataOutputStream for type-safe encoding.
     */
    private void sendPayload(Player player, String channel, byte[] data) {
        player.sendPluginMessage(this, channel, data);
    }
}

Message Flow

The canonical interaction between a joining player and the server follows these steps:
1

Player joins the server

The Minecraft client completes the login sequence and the play connection is established.
2

Client sends coi-client:request

CircleOfImaginationClient automatically fires requestAbilitiesFromServer() on the ClientPlayConnectionEvents.JOIN event, sending an empty AbilityRequestPayload to the server. The same request is also fired when the player opens the Ability Binding screen (default key: K).
3

Server responds with coi-client:abilities

Your Paper plugin receives the request, looks up the player’s pathway and unlocked abilities, builds a semicolon-separated abilities string, and sends back an AbilitiesPayload.
4

HUD updates

The client parses the abilities data, populates the in-memory ability list, validates any previously bound slots, and refreshes the HUD overlay with the player’s available abilities.
After this initial exchange, the server can push cooldowns (coi-client:cooldown), trigger visual effects (coi-client:effect), apply mythical forms (coi-client:mythical), and sync beyonder conditions (coi-client:conditions) at any time in response to gameplay events.

Wire Format

All payloads use a sequential codec over RegistryFriendlyByteBuf. There are only two field types used across the entire protocol:
TypeWrite methodRead methodNotes
Stringbuf.writeUtf(value)buf.readUtf()UTF-8, length-prefixed varint
intbuf.writeInt(value)buf.readInt()Big-endian 32-bit signed integer
Fields are written and read in the exact order they are declared in each payload record. There are no field delimiters, version headers, or trailing bytes — the codec is strictly positional. Refer to the Payloads reference for the field order of each channel.

Build docs developers (and LLMs) love