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.
This commit is contained in:
2026-04-28 16:30:39 +02:00
parent ad1379c267
commit 5fc3bda1c5
22 changed files with 1069 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# Examples
Four runnable plugin sketches built as Gradle composite builds — they
consume the in-development library directly via `includeBuild("../..")`,
so editing the lib and rebuilding an example picks up the change without
publishing.
| Example | Pattern |
|---|---|
| [`player-load/`](player-load/) | Load player JSON on `PlayerReadyEvent`, mutate a component on the world thread. |
| [`async-moderation/`](async-moderation/) | `IAsyncEvent` (`PlayerChatEvent`) bridged to coroutines, off-thread check, conditional `modify` + event cancel. |
| [`periodic-leaderboard/`](periodic-leaderboard/) | Plugin-scoped periodic loop with parallel `read` over players. |
| [`bounty-board/`](bounty-board/) | The kitchen sink — exercises every public Async primitive. |
## Build any of them
```bash
cd examples/<name>
./gradlew shadowJar
# → build/libs/<name>.jar
```
Drop the JAR in your dev server's `mods/`.
## How the composite wiring works
Each example's `settings.gradle.kts` does:
```kotlin
includeBuild("../..") {
dependencySubstitution {
substitute(module("com.mythlane:async"))
.using(project(":dist"))
}
}
```
So `implementation("com.mythlane:async:0.1.0-SNAPSHOT")` resolves to the
local `:dist` project — no Maven publication required during dev.
## What's stubbed
Each example contains `TODO()` markers in two places:
1. **Custom `Component<EntityStore>` registration.** Declaring `Wallet`,
`PlayerStats`, etc. on `EntityStore.REGISTRY` is plugin-specific and not
part of what Async does. Async only consumes the resulting `ComponentType`
via `ComponentRegistry.register<T>(...)`.
2. **Online-player accessors** like `onlinePlayerRefs()` and
`broadcastToAllWorlds(...)`. These iterate `Universe.get()` /
`world.players` in whatever shape your plugin needs.
Wire those to your dev server and the examples become fully runnable. The
Async-side calls (`installAsync()`, `playerScope`, `modify<T>`,
`PlayerRef.toEntityHandle()`, etc.) are accurate as-shipped.