Files
async/examples/async-moderation/README.md
T
kayjaydee 5fc3bda1c5 feat(examples): four runnable plugin examples
- player-load: load-on-join (PlayerReadyEvent → IO → modify).
- async-moderation: IAsyncEvent → coroutine bridge for chat moderation.
- periodic-leaderboard: pluginScope loop with parallel reads.
- bounty-board: kitchen-sink demo exercising every v0.1 primitive.
2026-04-28 16:30:39 +02:00

1.4 KiB

async-moderation

Listens to PlayerChatEvent (an IAsyncEvent), runs a fake 200ms moderation check off-thread, and on flagged messages bumps a warnings counter on the world thread + cancels the event so it never reaches other players.

What it shows

  • eventRegistry.registerAsyncGlobal(PlayerChatEvent::class.java) { future -> … } — the global overload. registerAsync only matches IAsyncEvent<Void>, and PlayerChatEvent isn't Void-keyed; the wrong overload compiles silently and never fires.
  • scope.future { … } from kotlinx-coroutines-jdk8 — bridges CompletableFuture ↔ coroutines without runBlocking.
  • withContext(AsyncDispatchers.HytaleIO) { … } — for the simulated HTTP call.
  • Conditional modify<T>(...) + event.isCancelled = true — the canonical shape for any moderation/filter handler.

Build

./gradlew shadowJar
# → build/libs/async-moderation.jar

Run

  1. Drop the JAR in mods/, wire ModerationStatsBinding.componentType() to your registered component.
  2. Type a message containing badword, spam, or scam in chat.
  3. The message is suppressed; the sender's ModerationStats.warnings increments on the world thread.

The example uses a private CoroutineScope to make the supervisor relationship explicit. In a real plugin, pluginScope(this) is fine and flows through Async.shutdown() cleanly.