Skip to content

Defining Events

Events are the unit of Pack capability. Everything a viewer can do to your game is an Event, and every other feature (Rewards, composites, reveals, Chat-Plays) ultimately fires one. Declare an Event with a name, a queue, and a JSON Schema for its parameters, and the platform generates the Reward UI, validates viewer input, and routes the invocation to your Adapter, no glue code from you. This is the first thing you write in any Pack.

[events.spawn_monster]
summary = "Spawn a monster next to the player."
queue = "effects"
[events.spawn_monster.params_schema]
type = "object"
required = ["kind"]
[events.spawn_monster.params_schema.properties.kind]
type = "string"
enum = ["imp", "pinky", "cacodemon"]
"x-mobrule-viewer-override" = true

Each Event has a summary, a queue (one of the Pack’s declared [queues.<name>] entries), and a params_schema (JSON Schema). The schema is compiled and cached once per Pack load.

The Invocation payload sent to the Adapter must validate against params_schema. Payloads >16 KiB are rejected at ingress.

Use:

  • type, required, enum, pattern, minimum, maximum, maxLength
  • additionalProperties = false to lock the shape.
  • Nested objects for grouped params.
  • x-mobrule-options: an inline choice list for a picker widget, instead of a bare enum. See UI hints.
  • x-mobrule-widget: force a specific picker widget (chips, dropdown, slider, …). See UI hints.
  • x-mobrule-visible-when: show a property only while another field equals a value. See UI hints.

Viewer Input: letting the redemption carry the viewer’s text

Section titled “Viewer Input: letting the redemption carry the viewer’s text”

Sometimes you want the viewer’s own words or choice to flow into the effect: the text they typed in the redemption becomes a param on the Invocation. There are two ways to declare this, and an Event picks at most one of them.

Boolean shorthand (whole text into one param)

Section titled “Boolean shorthand (whole text into one param)”

Set "x-mobrule-viewer-override" = true on a single string property. The whole viewer text drops into that param verbatim:

[events.shout.params_schema]
type = "object"
required = ["message"]
[events.shout.params_schema.properties.message]
type = "string"
maxLength = 80
"x-mobrule-viewer-override" = true

The broadcaster’s Cue→Event mapping UI surfaces it as a viewer-controlled input, and the platform validates the value (here, the maxLength) before producing an Invocation.

Two constraints, both checked at Pack load:

  • At most one property per Event may carry x-mobrule-viewer-override.
  • That property must be type = "string" (the text injects uncoerced).

When one redemption should fill several params, declare a viewer_input block instead. The platform splits the viewer’s single text across the named fields, in order, and coerces each token to that field’s schema type:

[events.rename.params_schema]
type = "object"
required = ["slot", "name"]
[events.rename.params_schema.properties.slot]
type = "integer"
[events.rename.params_schema.properties.name]
type = "string"
[events.rename.viewer_input]
fields = ["slot", "name"]
# delimiter = "," # optional; omit for whitespace

With the above, 0 Big Boy sets slot = 0 and name = "Big Boy" (the last field swallows the remainder). delimiter, when given, must be non-empty; omit it to split on whitespace.

Split fields must name declared scalar properties (string, integer, number, or boolean); a token that won’t coerce fails schema validation and refunds the viewer.

The two forms are mutually exclusive: an Event declaring a viewer_input block must not also set x-mobrule-viewer-override, and vice versa.

Composite Events reject both forms. The container’s params are face data, and viewer text exists only on the original redemption, never on the rolled member.

  • Manifest reference: where Events sit in the TOML tree.
  • Pack Config: for non-per-Invocation, broadcaster-side config.
  • UI hints: the full x-mobrule-* vocabulary and widget set.
  • Option datasets: sourcing a large choice list from a pack-shipped file instead of inlining it.