GET /cmd?q= [args] Run a console verb synchronously. Returns plain text. URL-encode spaces in args.
Live C# console wired into the running game.
A localhost HTTP console that compiles and evals C# against the live game (Roslyn), plus scene inspection and per-instance logging. For writing mods, not playing them.
Built-in verbs: eval, belay, find, inspect, ropes, renderers, gameplay, survival, edelweiss, input, emit, help.
Features
Requires
Extract the zip into your Cairn folder, next to Cairn.exe — the mod lands in Mods/ and its compiler libraries in UserLibs/. The bare DLL alone won't run.
One instance per port, probed from 14200 up. The chosen port is logged at startup. Both GET and POST are supported. Each instance also mirrors MelonLogger errors and warnings to a per-PID log at MelonLoader/CairnDevTools/<timestamp>_pid<pid>_errors.log — safe with multiple instances open.
GET /cmd?q= [args] Run a console verb synchronously. Returns plain text. URL-encode spaces in args.
POST /cmd?q=eval Body is raw C# — no URL-encoding. Preferred for multi-line scripts. The q query param defaults to eval when omitted.
GET /events?since=&name=&timeout= Long-poll for edge events emitted by game patches or the emit verb. Blocks until the next event past since (default: now), optionally filtered by name, or until timeout ms (default 60000). Returns one-line JSON {seq,name,payload,ts} or {timeout:true,seq,oldest}.
# run a verb
curl "http://127.0.0.1:14200/cmd?q=help"
# eval multi-line C#
curl -s -X POST "http://127.0.0.1:14200/cmd?q=eval" --data-binary @script.cs
# long-poll for events
curl "http://127.0.0.1:14200/events?since=0&name=revive-resolved" Roslyn-compiled C# against the live game. All loaded assemblies are referenced. Import namespaces are pre-added: System, System.Linq, System.Collections.Generic, UnityEngine, Il2Cpp, Il2CppTheGameBakers.Cairn.Netplay, CairnDevTools — so LINQ and co-op types resolve unqualified. End with return <expr> to get a value back.
Log(message) Alias for MelonLogger.Msg, available in every script body.
message | string | Text to log. |
Bag → Dictionary Persists between evals. Store objects in one script, read them in another.
// inline — returns the value
return Il2Cpp.PawnManager.MCSpawned;
// persist across evals
Bag["pawn"] = UnityEngine.Object.FindObjectOfType<Il2Cpp.ClimbingV2PawnController>();
// retrieve later
var pawn = (Il2Cpp.ClimbingV2PawnController)Bag["pawn"]; Scripts can await Game.WaitFor — the POST blocks and returns only when the script settles. Level-triggered: if the predicate is already true it completes on the next frame, so no edge is ever missed. Timeout faults with a TimeoutException carrying the message. A single blocking POST is capped at ~28 s by the overall script deadline, so drive long sequences across multiple calls rather than one very long await.
Game.WaitFor(predicate, timeoutMs?) → Task Await the first frame on which predicate is true.
predicate | Func<bool> | Polled each frame on the main thread. |
timeoutMs opt | int | Default 30000. |
Game.WaitFor(sample, done, timeoutMs?) → Task Sample a value each frame; complete when done(value) is true. Returns the value.
sample | Func<T> | Reads game state each frame. |
done | Func<T, bool> | Predicate over the sample. |
timeoutMs opt | int | Default 30000. |
Game.Delay(ms) → Task Frame-based delay. Advances in lockstep with the game (paused when the pump is).
ms | int | Wall-clock ms to wait. |
Game.NextFrame() → Task Yield to the next frame.
Game.WaitForMenu(timeoutMs?) → Task Await the MainMenu being present.
Game.WaitForGameplay(timeoutMs?) → Task Await the climber pawn being spawned.
Game.WaitForFullyLoaded(timeoutMs?) → Task Await loadingState == GameStarted — stricter than pawn-spawned.
Game.WaitForScene(name, timeoutMs?) → Task Await the active scene having a given name.
name | string | e.g. <code>CommonBaseScene</code>. |
Game.WaitForEagleEye(timeoutMs?) → Task Await the eagle-eye warp view being open.
Game.EnterGameplay(timeoutMs?) → Task The canonical "get me into a playable game": wait for menu → continue → pawn-spawn → fully-loaded. Idempotent: returns immediately if already in gameplay.
Game.Continue() → string Continue the most-recent save from the main menu (fires the gameplay verb inline).
Game.Do(verb, args) → string Dispatch any registered console verb by name from inside a script.
verb | string | Registered verb name. |
args | string[] | Argument strings. |
// drive the game to a state in one blocking POST
return await Game.EnterGameplay();
// wait for a specific object to appear
var ui = await Game.WaitFor(
() => UnityEngine.Object.FindObjectOfType<Il2Cpp.EagleEyeUI>(true),
ui => ui != null);
// frame-based timing
await Game.Delay(500);
await Game.NextFrame();
// dispatch a registered verb from inside a script
var result = Game.Do("survival", "on"); Read-only scene queries. All run on the main thread and return plain text.
belay Complete connection graph for the local climber's securing rope: harness attach, rope identity/length/bounds, every holder, every part with endpoint pins, LineRenderer truth, climbot state, pitons, and the causal fall-distance scalars.
find Search all scene Transforms (including inactive) by name. Returns up to 30 matches with hierarchy paths and positions.
inspect Full component list for one GameObject: position, rotation, scale, per-component extras (rope length, renderer bounds), and child list.
ropes Census of every LogicalRope in the scene: hierarchy path, active/init/visible state, length, holder count, bounds.
renderers [minSpanMeters] Active renderers with bounds over the span threshold (default 300 m). Returns up to 25 entries.
minSpanMeters opt | float | Default 300. |
curl "http://127.0.0.1:14200/cmd?q=belay"
curl "http://127.0.0.1:14200/cmd?q=find Quickdraw"
curl "http://127.0.0.1:14200/cmd?q=inspect Aava/bn_Head"
curl "http://127.0.0.1:14200/cmd?q=ropes"
curl "http://127.0.0.1:14200/cmd?q=renderers 10" Utility commands registered at startup.
help List every registered verb. The default when no q is given.
gameplay Continue the most-recent save from the main menu (MainMenu → CommonBaseScene, ~12 s). Only works when the MainMenu is in the scene.
survival [on|off] Freeze hunger, thirst, and cold so the climber doesn't starve mid-investigation. Blocks the drain at the source (ClimberData.Set), not just the ratio getters. Toggles when called with no argument.
edelweiss [on|off] Make the resurrection item non-consumable so it can revive repeatedly. Useful for repro loops that drive the death→revive cycle many times. Toggles when called with no argument.
input [...] Hold a synthetic gamepad control value. Drives the real Unity InputSystem so InputAction callbacks fire (hold-to-switch, chords). Controls: lt, rt, lb, rb, lsx, lsy.
control | string | lt | rt | lb | rb | lsx | lsy |
value | float | 0..1 |
input clear Release all synthetic input and hand control back to the physical device.
input state Report current synthetic input values.
emit [payload] Push a named event onto the EventBus from outside the process. Wakes any /events long-poll watching that name.
curl "http://127.0.0.1:14200/cmd?q=gameplay"
curl "http://127.0.0.1:14200/cmd?q=survival on"
curl "http://127.0.0.1:14200/cmd?q=edelweiss on"
curl "http://127.0.0.1:14200/cmd?q=input lt 1 rt 1"
curl "http://127.0.0.1:14200/cmd?q=input clear"
curl "http://127.0.0.1:14200/cmd?q=emit revive-resolved ok"