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
@@ -0,0 +1,64 @@
package com.mythlane.example.leaderboard
import com.hypixel.hytale.server.core.entity.entities.Player
import com.hypixel.hytale.server.core.plugin.JavaPlugin
import com.hypixel.hytale.server.core.plugin.JavaPluginInit
import com.mythlane.async.Async
import com.mythlane.async.ecs.ComponentRegistry
import com.mythlane.async.ecs.read
import com.mythlane.async.binding.handle
import com.mythlane.async.binding.installAsync
import com.mythlane.async.binding.pluginScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
/**
* Demonstrates: `pluginScope(this).launch { while (isActive) { delay(…); … } }`
* for a periodic task, plus parallel `read<T>` via `async { … }.awaitAll()`.
*
* SDK-side wiring of `onlinePlayers()` and the component type is left as `TODO`.
*/
class LeaderboardPlugin(init: JavaPluginInit) : JavaPlugin(init) {
override fun start() {
installAsync()
ComponentRegistry.register<PlayerStats>(PlayerStatsBinding.componentType())
pluginScope(this).launch {
while (isActive) {
delay(60.seconds)
runCatching { recompute() }
}
}
}
override fun shutdown() {
Async.shutdown()
}
private suspend fun recompute() = coroutineScope {
val players: List<Player> = onlinePlayers()
players.map { p ->
async {
p to read<PlayerStats, Int>(p.handle()) { level }
}
}.awaitAll()
.sortedByDescending { it.second }
.take(5)
// .also { broadcast(...) } — wire to your messaging API
}
private fun onlinePlayers(): List<Player> = TODO("Return Players from your dev server (e.g. HytaleServer.get().worlds...).")
}
/** Stub component shape — replace with your real `Component<EntityStore>` subclass. */
class PlayerStats { var level: Int = 1 }
object PlayerStatsBinding {
fun componentType(): Any = TODO("Return EntityStore.REGISTRY.register(PlayerStats::class.java, ...) result here.")
}
@@ -0,0 +1,16 @@
{
"Group": "Mythlane",
"Name": "PeriodicLeaderboard",
"Version": "${version}",
"Description": "${description}",
"Authors": [
{ "Name": "Mythlane", "Email": "contact@mythlane.com", "Url": "https://mythlane.com" }
],
"Website": "https://mythlane.com",
"Main": "com.mythlane.example.leaderboard.LeaderboardPlugin",
"ServerVersion": "${hytaleServerVersion}",
"Dependencies": {},
"OptionalDependencies": {},
"DisabledByDefault": false,
"IncludesAssetPack": false
}