# 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 ```bash ./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: ```text https://events.dmg.games/events ``` ## Events Every event is a structured CloudEvent 1.0 JSON object with these common attributes: - `source`: `urn:minecraft-server:` - `serverid` and `servername`: CloudEvent extensions identifying the server - `gameversion`: the Bukkit game version - `playerid`: player UUID for player events - `data.player_id` and `data.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: ```text 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 ```yaml 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 `RELEASE_TOKEN` with repository contents write permission. For a local versioned build: ```bash ./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.