bounty-board
The kitchen-sink example. A PvP bounty system that exercises every public Async v0.1 primitive in one realistic plugin.
Players post bounties on each other (paying gold to put a price on a head). On payout, the killer collects. A periodic public broadcast lists the top 5 active bounties; an optional Discord webhook fires on placements and payouts; in-memory state writes to disk on shutdown (load on boot left to your plugin).
What it shows
| Primitive | Where |
|---|---|
installAsync() |
start() |
Async.shutdown() |
shutdown() |
ComponentRegistry.register<T>(...) |
start(), twice |
playerScope(player) |
registerJoinHook |
pluginScope(this) |
chat handlers, broadcast loop, shutdown save |
worldScope(world) |
startWorldDecayForKnownWorlds |
WorldScopes.cancel(uuid) |
onWorldUnload |
Player.handle() / PlayerRef.toEntityHandle() |
every modify / read |
withContext(AsyncDispatchers.HytaleIO) |
webhook + persistence |
delay(...) (HytaleScheduled) |
broadcast and decay loops |
read<T, R> strict |
handlePayout |
readOrNull<T, R> |
join, broadcast, wallet query |
modify<T> Unit |
bounty append, decay, payout |
modify<T, R> returning Boolean |
atomic gold deduction |
withTimeout(...) |
guards the world-death race in the broadcast loop |
ComponentNotFoundException handling |
handlePayout |
WorldClosedException handling |
notifyDiscord |
pluginScope.future { } |
every chat command |
Parallel read via async/awaitAll |
broadcastTop5 |
Commands
| Chat | Effect |
|---|---|
!bounty? |
Show your wallet. |
!bounty <player> <amount> |
Pay gold to put a bounty on someone. |
!payout <player> |
Admin demo: collect bounties from a target (stand-in for a kill hook — Hytale's v0.1 SDK doesn't expose PlayerDeathEvent). |
The ! prefix matters: Hytale's CommandManager swallows every /-prefixed
message before PlayerChatEvent fires, so this example uses ! to flow
through chat normally.
Build
./gradlew shadowJar
# → build/libs/bounty-board.jar
Run
- Drop the JAR in
mods/. - Wire the SDK stubs in
BountyPlugin.kt:onlinePlayerRefs(),onlinePlayerRefsIn(world),knownWorlds(),broadcastToAllWorlds(text)— your dev server's accessors.WalletBinding.componentType(),BountyStateBinding.componentType()— registerWalletandBountyStateonEntityStore.REGISTRYand return the result.
- Optionally set
BOUNTY_WEBHOOK_URLin the server's env for Discord notifications. - In game:
!bounty <other-player> 100places,!payout <other-player>collects, the top-5 broadcast appears every 60s.
The persistence layer (BountyRepo) is intentionally a one-line JSON
serializer for the demo. Real plugins should use kotlinx.serialization or
a database — the load-bearing pattern is
withContext(HytaleIO) { Files.writeString(...) }, not the JSON shape.