Event System Tasks
Event subscription, cancellation, and completion notifications.
Overview
The Yeow event system registers listeners with event.subscribe. Registration has two modes: observe (mode: "watch", read-only, not awaited by the runtime) and intercept (default: writable, released with event.complete).
event.subscribe
Subscribes to a game event.
- Request:
{ "pluginName": "<name>", "eventType": "<eventType>", "callbackId": "<cbId>", "mode": "watch"? } - Return:
true
| Field | Required | Description |
|---|---|---|
pluginName | Yes | Owning plugin name |
eventType | Yes | Event name (camelCase, e.g. playerJoin), see the event name table |
callbackId | Yes | Event handler callback ID (must be registered with persistent: true) |
mode | No | "watch" = observer (read-only, not awaited); omitted = intercept (writable, waits for event.complete) |
Trigger timing: When a game event occurs, the runtime delivers event data to callbackId (via the cb channel, with r being the event data object). The data includes the _cancellable boolean field marking whether it can be cancelled. Intercept deliveries additionally carry _eventId (a unique id per dispatch, see below) and a top-level eventId with the same value; observer deliveries do not generate _eventId.
event.unsubscribe
Unsubscribes from an event.
- Request:
{ "pluginName": "<name>", "eventType": "<eventType>" }|{ "pluginName": "<name>", "callbackId": "<cbId>" } - Return:
true
The implementation should also remove the corresponding event listener, to avoid events continuing to be sent to JS callbacks that no longer respond.
Observer mode (mode: "watch")
Observers serve read-only use cases (logging, statistics, forwarding). The runtime delivers the event data and continues immediately: it registers no pending wait, generates no _eventId, and applies no write-back; an observer neither needs to nor can send event.complete. Because it does not participate in waiting, an observer neither extends the event's stay on the main thread nor triggers event.timeout.
event.complete
Intercept handler completion signal — the plugin informs the runtime that event handling has finished and subsequent steps can be scheduled. This task has no meaning for observers.
- Request:
{ "eventId": "<eventId>", "mods": { "cancelled": <bool> } } - Return:
true
| Field | Required | Description |
|---|---|---|
eventId | Yes | The _eventId carried when the runtime delivers event data (top-level eventId), returned as-is — the unique id per dispatch |
mods | No | Event modification object. When omitted or null, treated as {} |
mods.cancelled | Yes* | If true, the runtime must cancel the event (when mods is present) |
mods can carry arbitrary write-back fields, and the runtime applies the commonly stable fields according to the event type (for each event's writable fields, see the fields marked "writable" in the Event Data Specification; unmarked fields are ignored). Besides
cancelled, the following are currently supported:joinMessage/quitMessage/message(chat, command) /format/to/respawnLocation/deathMessage/newFoodLevel/damage/amount/target/clickedItem/cursorItem/ serverPing'smotd/maxPlayers/numPlayers/icon.
Event Handling Flow
1. A game event triggers
2. The runtime finds the plugins subscribed to this event and their callbackIds (observers and interceptors are handled separately)
3. Interceptors: event data is delivered through the cb channel → $hm(t="cb", p=callbackId, r={...event data, _cancellable:bool, _eventId});
observers: the same data is delivered (without _eventId) and not awaited
4. The JS-side event handler executes
5. The interceptor sends event.complete (containing eventId) → received by the runtime
6. The runtime matches this dispatch by eventId and applies mods (e.g. cancelled)Multiple Plugin Concurrency
When multiple plugins subscribe to the same event, the implementation can choose:
- Serial: sends the event to each plugin one by one, waiting for one plugin to complete (
event.complete) before sending to the next - Concurrent: sends to all subscribed plugins at once, waiting for the slowest plugin to finish
In both modes, the merge strategy for each plugin's modification of cancelled is: if any plugin sets it to cancelled, it is cancelled.