Sign In

Plugin Messaging

Send analytics events from backend servers through your proxy. This allows Paper/Spigot servers to track events without configuring API keys on each server.

Architecture

1

Your Plugin calls AnalyseMessaging.event(...)

2

Message sent via player.sendPluginMessage()

3

Proxy receives on "analyse:events" channel

4

Analyse plugin forwards to API

5

Event appears in your dashboard

When to Use

Use plugin messaging when:

  • Your proxy has the Analyse plugin with API key configured
  • Backend servers don't have (or need) their own API keys
  • You want centralized event routing through the proxy

Setup

1. Add the SDK to Your Backend Plugin

<dependency>
<groupId>net.analyse</groupId>
<artifactId>analyse-api</artifactId>
<version>0.4.0</version>
<scope>provided</scope>
</dependency>

2. Register the Channel

Register the plugin messaging channel on your backend server:

import net.analyse.api.messaging.AnalyseMessaging;
@Override
public void onEnable() {
// Register the outgoing channel
getServer().getMessenger().registerOutgoingPluginChannel(
this,
AnalyseMessaging.CHANNEL
);
}
@Override
public void onDisable() {
// Unregister on disable
getServer().getMessenger().unregisterOutgoingPluginChannel(this);
}

Sending Events

Basic Event

import net.analyse.api.messaging.AnalyseMessaging;
public void trackCrateOpen(Player player, String crateType) {
try {
byte[] message = AnalyseMessaging.createEventMessage(
"crate_opened",
player.getUniqueId(),
player.getName(),
Map.of("crate_type", crateType),
null // no numeric value
);
player.sendPluginMessage(plugin, AnalyseMessaging.CHANNEL, message);
} catch (IOException e) {
plugin.getLogger().warning("Failed to send event: " + e.getMessage());
}
}

Using the Builder

import net.analyse.api.messaging.AnalyseMessaging;
public void trackPurchase(Player player, String item, double price) {
try {
byte[] message = AnalyseMessaging.event("shop_purchase")
.withPlayer(player.getUniqueId(), player.getName())
.withData("item", item)
.withData("category", "weapons")
.withValue(price)
.build();
player.sendPluginMessage(plugin, AnalyseMessaging.CHANNEL, message);
} catch (IOException e) {
plugin.getLogger().warning("Failed to send event: " + e.getMessage());
}
}

Tracking Conversions

Send A/B test conversion events:

import net.analyse.api.messaging.AnalyseMessaging;
public void trackConversion(Player player, String testKey, String event) {
try {
byte[] message = AnalyseMessaging.createConversionMessage(
player.getUniqueId(),
player.getName(),
testKey,
event
);
player.sendPluginMessage(plugin, AnalyseMessaging.CHANNEL, message);
} catch (IOException e) {
plugin.getLogger().warning("Failed to send conversion: " + e.getMessage());
}
}

Complete Example

public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
// Register the channel
getServer().getMessenger().registerOutgoingPluginChannel(
this,
AnalyseMessaging.CHANNEL
);
// Register listeners
getServer().getPluginManager().registerEvents(new GameListener(this), this);
}
@Override
public void onDisable() {
getServer().getMessenger().unregisterOutgoingPluginChannel(this);
}
/**
* Send an event through the proxy
*/
public void sendEvent(Player player, String eventName, Map<String, Object> data) {
try {
byte[] message = AnalyseMessaging.event(eventName)
.withPlayer(player.getUniqueId(), player.getName())
.withData(data)
.build();
player.sendPluginMessage(this, AnalyseMessaging.CHANNEL, message);
} catch (IOException e) {
getLogger().warning("Failed to send event: " + e.getMessage());
}
}
}
public class GameListener implements Listener {
private final MyPlugin plugin;
public GameListener(MyPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onQuestComplete(QuestCompleteEvent event) {
Player player = event.getPlayer();
Quest quest = event.getQuest();
plugin.sendEvent(player, "quest_completed", Map.of(
"quest_id", quest.getId(),
"quest_name", quest.getName(),
"difficulty", quest.getDifficulty().name(),
"time_minutes", event.getCompletionTimeMinutes()
));
}
}

API Reference

AnalyseMessaging

MethodDescription
CHANNELThe channel name: analyse:events
createEventMessage(...)Create an event message
createConversionMessage(...)Create a conversion message
event(name)Start building an event message

EventMessageBuilder

MethodDescription
withPlayer(uuid, username)Set the player
withData(key, value)Add a data field
withValue(double)Set numeric value
build()Build the message bytes

How It Works

  1. Your backend plugin creates a message using AnalyseMessaging
  2. Message is sent via player.sendPluginMessage()
  3. Proxy receives on analyse:events channel
  4. Proxy parses and forwards to Analyse API
  5. Event appears in your dashboard

The proxy automatically adds _source_server to event data so you can see which backend server sent each event.

Requirements

  • Analyse plugin installed on your proxy (Velocity or BungeeCord)
  • API key configured on the proxy
  • Backend servers do not need their own API key

Troubleshooting

Events not appearing

  1. Check proxy console for debug messages (enable debug: true)
  2. Verify the channel is registered on your backend plugin
  3. Ensure a player is online (plugin messages require a player connection)

IOException when building message

  • Ensure all required fields are set (player UUID is required)
  • Check that data values are serializable (primitives, strings, maps, lists)