Skip to content

Variables

Joprebond edited this page Sep 15, 2026 · 4 revisions

Variables

Source package: com.jopre.streamlink.variable

A Variable is the runtime representation of a single named value that flows through StreamLink's event → condition → action pipeline — things like event.user-name, stream.hype-train-active, or a user-defined counter. Every Condition check, every {{...}}/#...#-style placeholder substitution in an Action Item, and every persisted "global" value is ultimately backed by an instance of com.jopre.streamlink.variable.Variable.

This page documents the variable package itself. See also:

How a Variable comes to exist

A Variable is never deserialized directly from a StreamLink config file the way an Action Item or Condition is — instead it's constructed in code from one of a few source types, all keyed by name into a shared Map<String, Variable> that's threaded through action/condition processing:

Source type Package Turns into a Variable in...
ProviderVariable com.jopre.streamlink.provider.variables ClientSideEventHandler.getVariables(...) — one Variable per matched field in the incoming Twitch/provider payload, scope PROVIDER
VariableDeclaration com.jopre.streamlink.action ActionProcessor.createActionVariables(...) — one Variable per Action-declared default, scope ACTION
Hard-coded in event/system code various e.g. event.event-id (scope EVENT), stream.hype-train-active and the logged-in player name (scope SYSTEM) — see VariableScope below
Action Items that write variables com.jopre.streamlink.action.type VariableStorageActionItem, VariableMathActionItem results, VariableValueActionItem copies

Variable

Package: com.jopre.streamlink.variable · Implements Comparable<Variable>

Fields

Field Data Type Description
variableName String The variable's key in the Map<String, Variable> it's stored in.
scope VariableScope Where/how long the variable lives — see the enum section below.
dataType VariableDataType Discriminator selecting which one of value/intVal/decimalVal/booleanVal actually holds the data.
value String Populated when dataType == STRING.
intVal Integer Populated when dataType == INTEGER.
decimalVal Double Populated when dataType == DECIMAL.
booleanVal Boolean Populated when dataType == BOOLEAN.
dirty boolean Set true by most constructors/setters to flag the variable as changed. Note: the (scope, name, value, dataType) constructor — the one used when parsing a stored string value, e.g. from VariableStorageActionItem — explicitly sets dirty = false instead, unlike every other constructor. Worth confirming whether that's intentional.
valid boolean Whether the variable currently holds a usable, correctly-typed value. Set true on a successful typed construction/set; the (scope, name, value, dataType) constructor sets it false if setTypedValue throws IllegalArgumentException (e.g. a non-numeric string handed to an INTEGER/DECIMAL variable) rather than propagating the error.

Constructors

Constructor Sets dataType to Notes
Variable(VariableScope, String) (none) Bare variable — dirty = true, no value/type set yet.
Variable(VariableScope, String, Double) DECIMAL
Variable(VariableScope, String, Integer) INTEGER
Variable(VariableScope, String, Boolean) BOOLEAN
Variable(VariableScope, String, String, VariableDataType) as given Parses value via setTypedValue; the constructor most often used when hydrating a variable from a JSON/config/payload string (see setTypedValue below). Logs an error and sets valid = false on parse failure instead of throwing.
Variable(Variable) copy of source Copy constructor — copies every field including scope, dirty, and valid.

Key methods

  • setTypedValue(VariableDataType, String) / setTypedValue(String) — parses a raw string into the correct typed field for the given VariableDataType via a switch. For STRING it's a plain assignment; for INTEGER/DECIMAL/BOOLEAN it only parses when the input is non-blank (a blank input silently leaves the existing typed field untouched rather than clearing it).
  • setValue(String) / setValue(Double) / setValue(Integer) / setValue(Boolean) — type-checked setters; each throws IllegalArgumentException if the variable's dataType doesn't match the setter being called. Likely bug: setValue(Boolean) checks VariableDataType.INTEGER.equals(this.dataType) instead of BOOLEAN — as written it rejects setting a boolean on a BOOLEAN-typed variable and would instead accept it on an INTEGER-typed one.
  • setValue(Variable) — copies every field from another Variable (scope included). Used by VariableValueActionItemProcessor to copy one variable's value into another by reference.
  • compareTo(Variable) and the compareTo(Integer) / compareTo(Double) / compareTo(Boolean) / compareTo(String) overloads — back the numeric/string/boolean Condition checks. compareTo(Variable) throws IllegalArgumentException if either side is missing data, invalid, or the two dataTypes don't match. Likely bug: the compareTo(String) overload builds a temporary STRING-typed Variable but calls tmpVar.setValue(booleanVal) (using the outer variable's own, almost always-null booleanVal field) instead of setValue(strVal) — as written, string comparisons through this overload look broken. (StringVariableConditionProcessor's GREATER case avoids this by calling compareVar.getValue().compareTo(val) directly instead of going through this overload; its other branches — GREATER_OR_EQUAL, EQUAL, LESS_OR_EQUAL, LESS — do call compareVar.compareTo(val), i.e. this overload.)
  • toString() — returns the string form of whichever typed field is populated for the current dataType. This is what backs placeholder substitution — see below.
  • hasData() / validRequest(Variable) — private helpers backing compareTo.

Variable Placeholder Syntax

com.jopre.streamlink.action.processor.BaseProcessor.replacePlaceholders(String, Map<String, Variable>) is the method every Action Item processor runs message/text fields through (e.g. ChatActionItem.chatMessage, ToastActionItem.title/message, CommandActionItem.commandName) to substitute in variable values via Variable.toString().

Discrepancy worth flagging: replacePlaceholders only recognizes tokens delimited by a single # on each side — e.g. #event.user-name# — via its hand-rolled scanner. It does not recognize the {{event.user-name}}-style Mustache placeholders used throughout this wiki's existing sample JSON (see Action Items). Either the sample JSON on this wiki predates a syntax change, or {{...}} is handled by some other code path not covered by BaseProcessor — worth confirming against actual runtime behavior before relying on either syntax. The #...# form is what BaseProcessor implements today.


Enums

VariableDataType

Package: com.jopre.streamlink.variable · Used by: Variable.dataType, and (per the Action Item Enums page) VariableStorageActionItem.dataType.

Constant JSON Value Description
STRING string Value lives in Variable.value.
INTEGER integer Value lives in Variable.intVal.
DECIMAL decimal Value lives in Variable.decimalVal.
BOOLEAN boolean Value lives in Variable.booleanVal.

VariableScope

Package: com.jopre.streamlink.variable · Used by: Variable.scope, and (per the Action Item Enums page) VariableStorageActionItem.variableScope.

Constant JSON Value Observed usage in current source
PROVIDER provider Assigned by ClientSideEventHandler to every variable bound from an incoming provider payload via a ProviderVariable — e.g. all the TwitchVariableMap fields end up as PROVIDER-scoped variables at runtime.
ACTION action The default scope: ActionProcessor.createActionVariables() uses it for Action-declared VariableDeclaration defaults, and it's also the fallback VariableStorageActionItem uses when its own variableScope field is omitted, plus the scope given to intermediate/result variables inside VariableMathActionItemProcessor.
EVENT event Used specifically for the event.event-id variable that ClientSideEventHandler attaches to every fired event — not observed elsewhere in the reviewed source.
GLOBAL global Special-cased by BaseProcessor.createOrUpdateVariable: a GLOBAL-scoped variable is stored in EventDataStore's process-wide globalVariables map instead of the per-event variable map, so it outlives any single event/action run. VariablePersistenceActionItem (variable-persistence) can additionally serialize every GLOBAL variable to/from stream-link-variables.json in the mod's config directory (StreamLinkConstants.VARIABLE_NAME_FILE), so this is also the only scope with disk persistence.
SYSTEM system Used by StreamLink's own internal state, set directly in code rather than from a config file: the hype-train-active flag (StreamLinkConstants.HYPE_TRAIN_VAR_NAME, set/cleared in ClientSideEventHandler and StreamLinkClientMod) and the logged-in player name default (StreamLinkConstants.DEFAULT_USER_VARIABLE, set in StreamLinkClientMod). Both are stored via EventDataStore.setGlobalVariable(...), so — like GLOBAL — SYSTEM variables also live in the global variable map at runtime, just originating from mod code instead of a variable-storage action.

Note: GLOBAL and SYSTEM both end up in EventDataStore's global variable map at runtime — the enum constant only distinguishes where the variable came from (a config-driven variable-storage action vs. StreamLink's own internal bookkeeping), not where it's stored.

Clone this wiki locally