Add-ons/SotA Event System (SES)/Developer API
More actions
Developer API
The SotA Event System exposes a global SES table for subscribing, publishing, ordering, replaying, and removing callbacks. Add sota-event-system-ses-addon to the package's dependencies before using it.[1]
Subscribe and unsubscribe
local function onGoldChanged(currentGold, previousGold)
-- update this add-on's own state
end
local subscription = SES.on("ShroudOnGoldChanged", onGoldChanged)
SES.off("ShroudOnGoldChanged", subscription)
SES.on(eventName, callback, opts) returns a subscription index. SES.off accepts that index, the original function reference, or no second argument to remove all callbacks for the event. SES.once has the same options but removes its wrapper before the first callback runs, so recursively invoking the event cannot fire it twice.
SES.hasSubscribers(eventName) reports whether at least one live callback remains. SES.mute(eventName) and SES.unmute(eventName) suppress and restore dispatch for that event.
Publish custom events
SES.invoke("ExampleAddon.onReady", packageId, version)
Any add-on can publish a custom name with arbitrary Lua arguments. Namespace custom events with the producer's package or project name. A subscriber failure is protected by pcall and logged without stopping the other subscribers.[1]
Custom events are in-process communication, not network messages. They disappear when the Lua session ends unless a producer publishes them again or a subscriber uses a replayed payload still cached in the current session.
Replay late announcements
SES.on("ExampleAddon.onReady", receiveReady, { replayLast = true })
replayLast immediately delivers the most recent payload when it exists. For discovery events with several independent producers, use replayLastPerSender; SES keys the cache by the string form of the first event argument and retains 32 senders by default:
SES.on("ExampleAddon.onLibrary", receiveLibrary,
{ replayLastPerSender = true })
An event can request a larger cache with replayLastPerSenderCap. Choose a stable unique first argument, as POIS and PQS libraries do. Reusing it replaces that sender's cached announcement.[1]
Ordering callbacks
Handlers dispatch through early, normal, then late tiers. Registration order applies within a tier unless named after or before constraints say otherwise.
SES.on("ExampleAddon.onUpdate", prepare, {
tier = "early", name = "Prepare"
})
SES.on("ExampleAddon.onUpdate", render, {
tier = "early", name = "Render", after = "Prepare"
})
Names and ordering constraints apply within the same event and tier. after and before may be one name or an array. SES warns about dependency cycles and falls back safely rather than dropping callbacks.
Key and sound helpers
Subscribe to ShroudOnKeyDown_<key> or ShroudOnKeyUp_<key>. For a chord, put held keys before the triggering key, such as ShroudOnKeyDown_LeftControl+Space. SES watches only keys that have subscribers.
SES.sound.watch(instanceId, soundName, channel) observes a channel returned by the add-on's own sound playback and emits ShroudOnSoundStarted and ShroudOnSoundStopped. unwatch stops observation without stopping the sound; isPlaying returns SES's last known state. SES itself never plays or stops the sound.[1]
Runtime configuration
Polling intervals and beacon-arrival distance are exposed through SES.config. Changing them affects every consumer in the shared session, so a library should not retune global values casually. Prefer the defaults unless the add-on family controls and documents the whole deployment.
References
- ↑ 1.0 1.1 1.2 1.3 Community Addons: approved SES 1.45.1 package, reviewed October 13, 2026.