Skip to content

Profiler

public static class Profiler

Namespace Snitch.Api

The Snitch profiler’s modder API. Reference Snitch.Api.dll OR drop this single file into your mod. Every call is a zero-overhead no-op when the Snitch profiler is not installed, and lights up automatically when it is - so you can ship this unconditionally with no hard dependency.

using Snitch.Api;
using (Profiler.Sample("MyMod.Pathfinding")) { ...expensive work... } // times a section
Profiler.RegisterCounter("MyMod.QueueLen", () => _queue.Count, "items");
Profiler.RegisterStateProvider("MyMod.Jobs", () => new StateSnapshot { Title = "Jobs" }
.Add("running", _running).Add("queued", _queued));

Tip: a class named SnitchProbe with a static Register() is auto-discovered and called on bind (see AutoRegister), so your mod’s Core doesn’t need to wire anything. Snitch also auto-times every mod’s OnUpdate etc., so basic per-mod frame cost needs no code at all.

All calls MUST be made from the Unity main thread. Counter/state delegates are invoked by the host on the main thread, so they may safely touch game objects.

public static bool Enabled { get; }

True only when the Snitch host is installed AND sampling is currently armed. Gate hot loops on this for the absolutely-free path: if (Profiler.Enabled) using (Profiler.Sample("X")) { ... }.

public static Scope Sample(string label)

Time a section. using (Profiler.Sample("MyMod.Foo")) { ... }. No heap allocation; a no-op (default scope) when the host is absent or sampling is off.

public static void Begin(string label)

Manual section begin (pair with End). Prefer Sample.

public static void End(string label)
public static void RegisterCounter(
string id,
Func<double> read,
string unit = null)

Register a numeric gauge polled by the host at a few Hz. Re-registering the same id replaces it. Load-order-proof: if Snitch loads AFTER your mod, the registration is queued and applied on bind.

public static void UnregisterCounter(string id)
public static void RegisterStateProvider(
string id,
Func<StateSnapshot> snapshot)

Register an entity/state-distribution snapshot, polled by the host at a few Hz on the main thread. Load-order-proof (queued until the host binds).

public static void UnregisterStateProvider(string id)
public static void Mark(string label)

Annotate a one-off event/spike in the timeline.

public static void RegisterAblationLever(
string name,
Action apply,
Action restore)

Register an ablation lever for your subsystem so ‘snitch ablate ’ can measure its causal frame-time cost. apply turns your subsystem OFF, restore turns it back ON. Both run on the main thread. Load-order-proof (queued until the host binds).

public static Panel RegisterPanel(string id, string title = null)

Declare a panel: a named, toggleable, movable, resizable area in the Snitch overlay + web dashboard that groups everything this mod reports. Counters/state you register with an id starting “id.” automatically appear inside it. Returns a builder so you can fluently add text/actions/toggles/log. The in-game replacement for a mod’s own debug window. Load-order-proof; a no-op (the builder still works) if Snitch is absent.

public static void RegisterAction(string panelId, string label, Action run)

A clickable button in a mod’s panel - the in-game replacement for a debug hotkey action. Runs on the main thread when clicked (overlay/dashboard) or via ‘snitch act’. Load-order-proof.

public static void RegisterToggle(
string panelId,
string label,
Func<bool> get,
Action<bool> set)

An on/off control in a mod’s panel - the in-game replacement for a toggle hotkey. get reports the current state, set applies it; both run on the main thread. Load-order-proof.

public static void RegisterSlider(
string panelId,
string label,
double min,
double max,
Func<double> get,
Action<double> set,
double step = 0d,
string unit = null)

A draggable value in a mod’s panel - the in-game replacement for typing a number into a console to find it. Appears as a real slider in the Snitch overlay and in the web dashboard, and is settable by id with snitch slider.

get reports the current value, set applies it; both run on the main thread. The host clamps to [min, max] and snaps to step (0 = continuous) BEFORE calling the setter, so a setter never sees a value outside the range it declared - wherever the write came from. Load-order-proof.

public static void RegisterText(string panelId, Func<string> provider)

A free-text, multi-line readout in a mod’s panel (anything a counter/distribution can’t express). Polled by the host on the main thread. Load-order-proof.

public static void BindPanelLog(string panelId)

Mark that a panel should display its own log channel (the lines you send via Log / Write with the same id). Load-order-proof.

public static void Log(
string channel,
string message,
LogLevel level = LogLevel.Info)

Send a log line to a channel (use your mod/panel id as the channel). It appears in that mod’s panel log AND in Snitch’s combined timeline. Keep calling your own MelonLogger too if you want it in Latest.log. Load-order-proof; a no-op if Snitch is absent.

public static void AutoRegister()

Discover a convention type named SnitchProbe with a static Register() in THIS mod’s own assembly and invoke it once - so a mod never has to wire a Register() call into its Core. Drive it from a [ModuleInitializer] in your probe file. No-op + load-order-proof: discovery is deferred until the host binds, and is a permanent no-op if Snitch is not installed.