Events
Events avoid polling. The plugin declares the event names it needs with
events.subscribe; the Host samples public state and sends notifications to
the plugin's connection(s).
Current events (API 1.0)
| Event | Data | Produced when |
|---|---|---|
scene.changed |
{scene, previous} |
the public scene id changes |
player.position |
{x, y, z, scene} |
the local player moved > 0.75 m; sampled at ~4 Hz |
plugin.started |
plugin id string | a plugin handshake completed with all permissions approved |
plugin.stopped |
plugin id string | the plugin process/connection stopped cleanly |
plugin.error |
plugin id string | the plugin process exited with a non-zero code |
collect.progress |
{target, live} |
auto-collect target/live counts changed |
unlock.progress |
{running, done, sent, total, skipped} |
waypoint unlock progress changed |
chests.progress |
{running, current, total, found, confirmed} |
chest collection progress changed |
The Core is sampled at approximately 4 Hz and only when at least one plugin has an event subscription. This keeps an idle Host from touching game state.
Subscribe
{"jsonrpc":"2.0","id":10,"method":"events.subscribe",
"params":{"events":["player.position","scene.changed"]}}
The result is the accepted list. Unknown events are ignored. Call
events.unsubscribe with the same shape to stop delivery.
The subscription requires events.subscribe in the manifest and the user's
approval.
Delivery
{
"jsonrpc": "2.0",
"method": "shire.event",
"params": {"event": "player.position", "data": {"x":123.5,"y":200,"z":-45,"scene":3}}
}
Each event is an independent notification; it has no id and must not be
answered. If the plugin is slow, the Host keeps at most 256 pending lines per
connection and drops the oldest. Subscriptions are per plugin, not per
connection: both the request and event connections receive the same event
stream.
Python example
plugin.subscribe("player.position", "scene.changed")
def on_position(data):
plugin.set_ui([
plugin.text("Last position", "title"),
plugin.text(f"{data['x']:.1f}, {data['z']:.1f}", "pos"),
])
def on_scene(data):
plugin.notify("Scene", f"{data['previous']} -> {data['scene']}")
plugin.on_event("player.position", on_position)
plugin.on_event("scene.changed", on_scene)
plugin.run()
UI events
UI interactions are separate notifications (shire.uiEvent); see UI.
Backpressure and limits
- Events are sampled and queued by the Host on the game/render thread.
- Outbound queues are bounded; old events are dropped before the connection is allowed to grow without bound.
- A plugin that never reads its pipe eventually fills the pipe and is disconnected on write timeout; it does not block the Shire renderer because the writer runs on a separate connection thread with a timeout.
- Event filters and priorities are not in API 1.0. Filter in the plugin or
subscribe only to the names you need. New event names are additive within a
minor API version; check
api.info/the handshake before subscribing.