Skip to content

Event Specification ​

Overview ​

The Yeow event system is the mechanism by which the runtime delivers game events to plugins. Plugins subscribe to events via event.subscribe, and the runtime delivers event data through the cb channel.


Registration and Unregistration ​

Plugins manage event subscriptions through the event.subscribe and event.unsubscribe methods on the task channel. See the Event System task specification for details.

Two subscription modes (the mode field of event.subscribe) ​

modeSemanticsRuntime behaviour
absentinterceptAfter delivery it waits for event.complete; r carries _eventId, write-back fields (mods) are applied on completion; 5s timeout as backstop
"watch"observeDelivery continues immediately — no _eventId, no waiting, no write-back applied; observers never spin the game thread

Observers are the default offered to beginners: yeow-api's eventWatch() sends mode: "watch", while eventIntercept() omits the field. permissionCheck was extracted into its own request/response API (the handler must return { allowed }), so implementations must route it through the intercept path even when the caller used eventWatch.

Observers and interceptors may coexist for the same event: implementations should deliver observers first (without waiting) and then process interceptors (waiting).

Field Adaptation Rules ​

In event data:

  • The player field holds the player's UUID string; the caller can look up the Player by UUID
  • The from, to, and respawnLocation fields hold location objects of the form { "x": <double>, "y": <double>, "z": <double>, "yaw": <double>, "pitch": <double>, "world": "<name>" }
  • Events use the _cancellable field to indicate whether they can be cancelled

Event Name Table ​

All event names use camelCase format. Complete list:

Event nameCategoryCancellableDescription
playerJoinPlayerNoPlayer joined
playerQuitPlayerNoPlayer quit
playerChatPlayerYesPlayer chatted
playerMovePlayerYesPlayer moved
playerInteractPlayerYesPlayer interacted
playerCommandPlayerYesPlayer ran a command
playerDeathPlayerNoPlayer died
playerRespawnPlayerNoPlayer respawned
playerTeleportPlayerYesPlayer teleported
playerItemConsumePlayerYesPlayer consumed an item
playerDropItemPlayerYesPlayer dropped an item
playerPickupItemPlayerYesPlayer picked up an item
playerBucketFillPlayerYesPlayer filled a bucket with liquid
playerBucketEmptyPlayerYesPlayer emptied a bucket
playerExpChangePlayerNoPlayer experience changed
playerLevelChangePlayerNoPlayer level changed
playerGameModeChangePlayerNoPlayer game mode changed
playerAdvancementDonePlayerNoPlayer completed an advancement
playerToggleSneakPlayerNoPlayer toggled sneaking
playerToggleFlightPlayerNoPlayer toggled flying
foodLevelChangePlayerYesPlayer hunger changed
entityDamageEntityYesEntity damaged
entityDeathEntityNoEntity died
entitySpawnEntityNoEntity spawned
entityExplodeEntityYesEntity exploded
entityRegainHealthEntityNoEntity regained health
entityTargetEntityNoEntity changed target
projectileLaunchEntityNoProjectile launched
projectileHitEntityNoProjectile hit
blockBreakBlockYesBlock broken
blockPlaceBlockYesBlock placed
blockFadeBlockYesBlock faded
blockGrowBlockNoBlock grew
blockSpreadBlockNoBlock spread
blockExplodeBlockYesBlock exploded
inventoryOpenInventoryYesInventory opened
inventoryCloseInventoryNoInventory closed
inventoryClickInventoryYesInventory clicked
serverPingServerNoServer pinged
serverCommandServerYesConsole command
playerResourcePackStatusResource PackNoResource pack status changed

Permission Check (separate channel, not an event) ​

Ordinary plugins should not register a checker: it is asked on every Yeow permission check and the main thread spins while waiting — this is an extension point reserved for permission management plugins.

The Yeow ecosystem permission check is request/response: no event object, nothing to cancel, no observer mode — so it does not belong to the event system. It uses dedicated task types on the task channel:

Task typePayloadMeaning
permission.subscribeCheck{ pluginName, callbackId }Register a checker (results are delivered to callbackId as a cb frame)
permission.unsubscribeCheck{ pluginName }Remove every checker of that plugin
permission.complete{ checkId, allowed }Answer one check (no reply; omitting allowed means "not handled")

The runtime delivers:

json
$hm(t="cb", p="<callbackId>", r={ "target": "<uuid|CONSOLE>", "node": "myplugin.home",
    "permission": { "node": "myplugin.home", "default": "all" }, "_checkId": "perm#1" })

_checkId must be passed back to permission.complete. While waiting, the implementation must drain the scheduler (same spin as event dispatch); the timeout follows profile.callback-timeout-event-ms (5s default, treated as "not handled").

  • Trigger scope (Yeow ecosystem only): the player.hasPermission task and the execution-time check of Yeow-registered commands; other Java plugins' hasPermission / command execution never triggers it
  • Priority: an answer overrides Paper-side hasPermission; otherwise it falls back to Paper; with several checkers the last answer wins
  • Node fusion: nodes are still registered into the Paper permission system (classic plugins / permission plugins can manage them)
  • API wrapper: onPermissionCheck(handler) (yeow-api, see Permission)
FieldTypeMeaning
targetstringSubject: a player UUID or "CONSOLE"
nodestringPermission node (e.g. myplugin.home)
permissionobject{ node, default? } (default is the registered default, omitted when unregistered)
_checkIdstringUnique id of this check; pass it back in the answer

Module Documentation ​

ModuleDescription
player-events21 player events
entity-events6 entity events + 2 projectile events
block-events6 block events
inventory-events3 inventory events
server-events3 server events