Files
spigot-event-producer/README.md
T
dmg ccac20fa2b
Release / release (push) Successful in 3m33s
CI / build (push) Successful in 2m48s
ci: configure release authentication
2026-08-01 11:51:55 -04:00

130 lines
5.9 KiB
Markdown

# 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:<server UUID>`
- `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.