Astral Realms Documentation Help

Developer API

Other plugins emit telemetry by calling the AnalyticsService interface — the same interface used internally for join, command, and LuckPerms events. There is no AstralAnalytics-specific dependency to add; the interface lives in AstralCore.

Maven coordinates

The interface is part of core-commons:

<dependency> <groupId>com.astralrealms</groupId> <artifactId>core-commons</artifactId> <version>1.1-SNAPSHOT</version> <scope>provided</scope> </dependency>

AstralAnalytics registers the implementation at runtime with AstralCore's service repository, so your plugin only depends on the interface.

Looking up the service

import com.astralrealms.core.paper.AstralPaperAPI; import com.astralrealms.core.service.impl.AnalyticsService; AstralPaperAPI.getService(AnalyticsService.class).ifPresent(analytics -> { analytics.log(player.getUniqueId(), "shop_purchase", Map.of( "item", "diamond_sword", "price", 1500, "currency", "coins" )); });

On Velocity the lookup is AstralVelocityAPI.getService(AnalyticsService.class). On MageHic it is MageHicAPI.services().get(AnalyticsService.class).

The service is Optional<>-typed: when AstralAnalytics isn't installed your plugin keeps working — it just doesn't emit telemetry.

AnalyticsService

Three overloads, all fire-and-forget:

void log(UUID playerId, String type, JsonElement payload); void log(UUID playerId, String type, String payload); // already-serialised JSON void log(UUID playerId, String type, Map<String, Object> map); // serialised via Gson

Parameter

Description

playerId

The subject of the event. Pass null for player-less events (server-level metrics).

type

Discriminator stored in the events.type column. snake_case by convention.

payload

Anything that round-trips through Gson. The result is stored as a JSONB blob.

The implementation never throws into the caller — RabbitMQ failures are logged at WARN.

Add an ip field to your payload to trigger GeoIP enrichment: the master appends a country ISO code before persisting. See Architecture.

The track-event action

For YAML-driven instrumentation (menus, dialogs, filters), AstralAnalytics registers a single AstralCore action:

- "[track-event] shop_purchase {\"item\":\"diamond_sword\",\"price\":1500}"

Argument format: <type> <json> — the first token is the event type, the rest is the JSON payload. Both parts go through AstralCore placeholder substitution, so you can mix in %player_*%, %parameters_*%, and any other placeholder.

items: buy-button: actions: LEFT: - "[message] <green>Purchased!" - "[track-event] shop_purchase {\"item\":\"%parameters_item%\",\"price\":%parameters_price%}"

The action calls AnalyticsService.log(player.uniqueId, type, payload) with the player who clicked, so the event is automatically attributed to them.

Conventions

When you add a new event type from your plugin:

Convention

Why

snake_case for type.

Matches the built-in types and reads better in dashboards.

Stable field names.

Renaming breaks every dashboard / query that already filters on the field.

Include username when you have it.

The player_uuid column has the UUID; the denormalised name keeps dashboards self-contained.

Don't include ip unless you want GeoIP enrichment and are OK persisting the address.

The master appends country whenever it sees ip.

Numbers as numbers, durations in milliseconds.

Matches the built-in playtime/session_duration convention so queries can mix and match.

When to skip the API

The master exposes a MHAnalyticsService that bypasses RabbitMQ for MageHic-side plugins running in the master JVM. Same AnalyticsService interface — same overloads — so your code doesn't change. The only difference is the resulting rows carry server_name = server_group = "magehic" instead of the local server tags. See Architecture for the rationale.

Last modified: 25 July 2026