Revision & CadenceGate
Two small tools for change and timing: a Revision marks a change OptiPine cannot see for itself, and a CadenceGate limits how often deliberately delayed work may run.
Revision
A Watch can compare values, arrays and rings. Some state it cannot observe at all, such as a set of objects your script updates in several places. A Revision is a counter for that case: the producer calls bump() after a meaningful change, and each consumer keeps the value it last saw and compares it with changedSince(). Nobody keeps a snapshot, and every operation is O(1).
// Pseudocode: updateZones() and rebuildSummary() belong to the script.
var op.Revision zonesRevision = op.revision()
var int summarySeen = na
// Producer: after any change to the zones.
if updateZones()
zonesRevision.bump()
// Consumer: rebuild only when the zones moved on since it last looked.
if zonesRevision.changedSince(summarySeen)
rebuildSummary()
summarySeen := zonesRevision.current()- A snapshot of
naalways reports a change, so a consumer that has never looked rebuilds once. - Several consumers can share one Revision, each with its own saved value.
- Row rings and typed stores keep their own content revision, available through
revision(), so they need no separate token. - The counter wraps to 0 after 2,000,000,000 bumps.
CadenceGate
Some work may run late on purpose, such as refitting a model or recomputing a summary that does not need every bar. A CadenceGate lets it run at most once every so many ticks. A tick is any number that only grows; bar_index is the usual choice.
var op.CadenceGate refit = op.cadenceGate()
if refit.due(bar_index, 20)
// Runs on the first check, then again once 20 bars have passed.
refitModel()- A new gate is due on its first check.
due()records the tick only when it returns true, so a gate that stays closed does not drift.force = trueruns regardless of the cadence, for example onbarstate.islast.runs()counts the runs, andreset()returns the gate to its first-check state.
Only when something changed
dueWhenChanged() adds a second condition: the producer's revision must differ from the one recorded at the last run. A change that arrives while the gate is closed is remembered until the cadence opens.
var op.FloatRowRing samples = op.floatRowRing(500, 4)
var op.CadenceGate refit = op.cadenceGate()
if refit.dueWhenChanged(bar_index, samples.revision(), 20)
refitModel(samples)A revision of na means the producer has not published one yet; the gate then follows the cadence alone, so it never waits forever.
Asking without running
due() and dueWhenChanged() consume the gate when they return true. peek() answers the same cadence question without recording anything, which is useful when another condition decides whether the work actually runs.
Calls
| Call | What it does |
|---|---|
op.revision(initialValue = 0) | Creates a revision token. Negative values become 0. |
revision.bump() | Advances the revision and returns the new value. |
revision.current() | Reads the revision without changing it. |
revision.changedSince(snapshot) | True when the snapshot is stale. na always reports stale. |
revision.set(value), revision.reset() | Assign an explicit value, or return to 0. |
op.cadenceGate() | Creates a gate that is due on its first check. |
gate.due(tick, interval = 1, force = false) | Consumes the gate and returns true when work is due. |
gate.dueWhenChanged(tick, revision, interval = 1, force = false) | Due only when the cadence is open and the revision changed. |
gate.peek(tick, interval = 1, force = false) | True when due() with the same arguments would run. Records nothing. |
gate.runs(), gate.reset() | Count runs, or return to the first-check state. |
An interval below 1 counts as 1. Every signature is in the API Reference.