Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commite8beb5604c

README.md

probe.shared

The probe registry, and the one path a hit takes to reach you.

The three arm verbs attach by three different mechanisms — a wrapped function, a patched line, a value read each frame — and then all hand the hit to M.fire. The condition, the limit, the cooldown, the body and the notice live there and nowhere else, so a hit reads the same whichever verb armed it and a change to how probes report is a change in one place.

Surface

FunctionAnswers
M.register(kind, source, opts)A new probe record, or nil, error. Refuses without opts.notice.
M.fire(probe, ctx)Takes one hit. Returns whether the world should freeze here.
M.cancel(id) / M.cancelAll()Detach and forget, by whatever mechanism attached.
M.all() / M.get(id)The registry, sorted by id / one probe.
M.row(probe)One probe as a probe.list row.
M.drain(probe)The recorded hits, emptying the ring.

What a mechanism owes the registry

Two things, and the registry handles the rest.

  1. A source string at registration: what this probe is attached to, in the probe's own words. It is written here rather than by the agent, because which of several armed probes fired is exactly what the agent cannot supply about its own probe.
  2. A detach function on the probe record, so cancel is one path no matter how the probe attached.
local shared = require(".shared")

local probe, err = shared.register("line", "Ticker:9", opts)
if probe == nil then return { ok = false, error = err } end

probe.detach = function() debugger.removeBreakpoint(bpId) end
-- ...and at the moment of the hit:
shared.fire(probe, { locals = debugger.getLocals(1) })

What fire guarantees

  • when is tested first, so a filtered hit costs nothing but the test.
  • The cooldown suppresses rather than drops silently: suppressed hits are counted and reported in probe.list.
  • The body runs inside a protected call. Three consecutive errors disarm the probe and say so, because a body that throws every time is not reporting anything, it is only costing the frame.
  • Whatever the body returns is what the notice carries. A table becomes the values, a string becomes the line, nothing still reports the hit.
  • Reaching the limit disarms the probe and posts a final notice naming that it stopped. A probe that goes quiet without saying so is worse than one that never fired, because silence reads as "it never happened".

Compiling

when and run are compiled once, at arm time, so a bad snippet is an error the agent sees while arming rather than a silence it discovers much later. Both see the hit's context unpacked as bare names — args, ret, locals, upvalues, value, previous, entity, component, frame, time, hit, probe — so a condition reads ret.killed rather than ctx.ret.killed.