5.9 KiB
Spigot Event Producer
A Spigot 26.2 plugin that records Minecraft activity as CloudEvents 1.0, persists it in a local SQLite outbox, and sends newline-delimited batches to game-ingest-server.
Requirements
- Spigot 26.2
- Java 17 or newer
- Network access to the ingest endpoint
Build and install
./gradlew clean test jar
cp build/libs/spigot-event-producer-0.1.0-SNAPSHOT.jar /path/to/server/plugins/
Start the Minecraft server once to create plugins/SpigotEventProducer/config.yml. Set a meaningful, stable server-name. When server-id is empty, the plugin generates and persists a UUID. Do not reuse that generated UUID on another server.
The default ingest endpoint is:
https://events.dmg.games/events
Events
Every event is a structured CloudEvent 1.0 JSON object with these common attributes:
source:urn:minecraft-server:<server UUID>serveridandservername: CloudEvent extensions identifying the servergameversion: the Bukkit game versionplayerid: player UUID for player eventsdata.player_idanddata.player_name: stable UUID and current player name
Produced event types:
| Type | Trigger |
|---|---|
games.dmg.minecraft.server.started |
Plugin/server startup |
games.dmg.minecraft.server.stopped |
Plugin/server shutdown |
games.dmg.minecraft.player.joined |
Player joins |
games.dmg.minecraft.player.quit |
Player quits |
games.dmg.minecraft.chat |
Non-cancelled public player chat |
games.dmg.minecraft.private_message |
A recognized private-message command |
games.dmg.minecraft.player.location |
Location snapshot |
games.dmg.minecraft.player.statistics |
Partitioned statistic snapshot |
Chat and private messages
Public chat events contain the complete message text.
Private-message events recognize /msg, /message, /tell, /w, /whisper, /pm, /reply, and /r, including namespaced variants such as /minecraft:tell. Explicit commands contain the destination token. Reply commands set reply: true and omit the recipient so downstream processing can infer it from earlier ordered events. The event also reports whether the command was cancelled.
Spigot has no universal private-message event. Commands implemented with unrelated aliases or entirely custom plugin logic will not be recognized until their syntax is added. A recognized command event reports observation of the command, not guaranteed delivery to its recipient.
Location snapshots
Each online player is sampled every 60 seconds by default. Events include world UUID/name, coordinates, yaw, pitch, game mode, and biome.
Statistic snapshots
Every Bukkit statistic is read, including untyped, block, item, and entity statistics. Zero values are retained. Invalid statistic/material/entity combinations rejected by Bukkit are skipped.
Large snapshots are divided by category and size. All parts share a snapshot_id, capture start/completion timestamps, and include part and parts fields. Statistic reads are spread across ticks using a configurable operation budget; a player remains queued until all combinations have been read. Empty categories still emit an explicit part.
Delivery and durability
The outbox is stored at:
plugins/SpigotEventProducer/event-outbox.sqlite3
SQLite writes and queue-size decisions run on a dedicated persistence worker. NDJSON construction and HTTP requests use a separate delivery worker, so a slow ingest request cannot prevent newly observed events from reaching SQLite. Bukkit state is only read on Bukkit-managed threads.
The worker sends every five minutes by default. It may send earlier when the configured event or byte threshold is reached. Requests remain below the ingest service's default 1 MiB and 1,000-event limits. Events are removed only after HTTP 202 Accepted; failures remain in SQLite and are retried.
Delivery is at least once. If the ingest server accepts a request but its response is lost, the plugin retries the same stable CloudEvent IDs. Consumers should deduplicate by CloudEvent id when necessary.
A shutdown event is persisted before the plugin closes its outbox. If it is not uploaded during shutdown, it is delivered after the next startup.
Configuration
server-name: "minecraft-server"
server-id: ""
ingest-url: "https://events.dmg.games/events"
send-interval-seconds: 300
snapshot-interval-seconds: 60
http-timeout-seconds: 15
batch:
max-events: 1000
max-bytes: 900000
dispatch-threshold:
events: 1000
bytes: 900000
statistics:
chunk-json-bytes: 200000
operations-per-tick: 1000
All intervals and limits must be positive. Keep batch.max-bytes below the ingest server's configured body limit and batch.max-events at or below its maximum events per request.
Releases
Gitea Actions runs the Gradle checks for every push and pull request and stores a development JAR as a workflow artifact. Pushes to main also run semantic-release using conventional commits:
fix:creates a patch release;feat:creates a minor release;- a breaking-change footer or
!creates a major release.
A successful release creates a vX.Y.Z tag and attaches spigot-event-producer-X.Y.Z.jar permanently to the corresponding Gitea Release. The release workflow requires a repository secret named GITEA_TOKEN with repository contents write permission.
For a local versioned build:
./gradlew clean check jar -PreleaseVersion=1.2.3
Privacy and operations
This plugin deliberately records complete public and private message text, player identifiers, precise locations, and activity statistics. Server operators should disclose that collection, restrict access to the plugin data directory and ingest database, use HTTPS, and configure retention appropriate to their jurisdiction.
The outbox has no automatic retention limit because dropping unsent events would violate durability. Monitor disk usage if the ingest service is unavailable for an extended period.