kerb
Rust crate for reading real-time telemetry from racing simulators via Windows Shared Memory.
Add a single dependency, enable feature flags for the simulators you want, and call SimConnection::connect().
Supported Simulators
| Simulator | Feature flag | Notes |
|---|---|---|
| iRacing | iracing |
Event-based sync, 0% CPU idle |
| Assetto Corsa Evo | ac-evo |
Windows Shared Memory |
| Le Mans Ultimate | lmu |
Requires rF2 plugin DLL |
Quick Start
All simulators are enabled by default. Add to Cargo.toml:
[]
= "0.1"
To include only specific simulators, disable defaults:
[]
= { = "0.1", = false, = ["iracing"] }
Call SimConnection::connect() — it auto-detects whichever sim is running and returns it wrapped in a Connection enum. Match on the variant to access each sim's full API:
use ;
[!IMPORTANT] The variants present in
Connectiondepend on which features are enabled in yourCargo.toml. Withdefault-features = false, features = ["iracing"]onlyConnection::IRacingexists — add_ => {}to handle any variants you don't care about.
Threading
All connection types (IRsdkConnection, AcEvoConnection, LmuConnection — and therefore Connection) are not Send. This is a deliberate API contract: they hold raw shared-memory pointers, Win32 handles, and interior-mutability caches. Create and use a connection on a single thread.
The standard pattern for GUI apps and overlays is a dedicated telemetry thread that owns the connection and forwards normalized data via channels or events:
spawn;
Connection Loop
For overlays that need to reconnect automatically:
use ;
use ;
Multi-Sim Support
If multiple sims are running simultaneously and connect() picks the wrong one, use connect_to():
use ;
let conn = connect_to?;
Feature Flags
All features are enabled by default. Use default-features = false to opt in selectively.
| Feature | Module | Connection variant |
Default |
|---|---|---|---|
iracing |
kerb::iracing |
Connection::IRacing |
yes |
ac-evo |
kerb::ac_evo |
Connection::AcEvo |
yes |
lmu |
kerb::lmu |
Connection::Lmu |
yes |
Per-Simulator API
iRacing
Connection: IRsdkConnection (via Connection::IRacing)
| Method | Returns | Scope | Notes |
|---|---|---|---|
frame() |
IracingFrame |
player's car | ~90 typed fields; IDE autocomplete works |
session_info() |
Option<IracingSession> |
whole session | Parsed YAML; cached until iRacing reports a change |
session_yaml() |
Option<String> |
whole session | Raw YAML string for manual parsing |
telemetry_snapshot() |
HashMap<String, TelemetryValue> |
player's car | Dynamic access by iRacing variable name |
var_list_snapshot() |
Vec<VarMeta> |
— | All variable names, types, units, and descriptions |
wait_for_data(ms) |
bool |
— | Blocks until new data or timeout; uses Win32 event (0% CPU) |
is_connected() |
bool |
— | true when iRacing is broadcasting telemetry |
IracingFrame is a typed struct with one pub field per variable — your IDE autocomplete shows all ~90 available fields directly. Fields use snake_case (SteeringWheelAngle → steering_wheel_angle).
IRacing =>
To save the raw session YAML to disk:
save_session?;
Assetto Corsa Evo
Connection: AcEvoConnection (via Connection::AcEvo)
| Method | Returns | Scope | Notes |
|---|---|---|---|
frame() |
Result<AcEvoFrame> |
player's car | Plain struct with physics, graphics, static_data |
telemetry_snapshot() |
HashMap<String, TelemetryValue> |
player's car | Keys are field names from the physics/graphics/static structs |
var_list_snapshot() |
Vec<VarMeta> |
— | All available field names |
is_connected() |
bool |
— | true when status == AC_STATUS_LIVE (not paused, not replay) |
wait_for_data(ms) |
— | — | Sleep up to 16 ms; AC Evo has no data-ready event |
AcEvoFrame contents by page:
| Field | Struct | Update rate | What it contains |
|---|---|---|---|
physics |
AcPhysicsData |
Every sim tick | Inputs, RPM, speed, tyres, suspension, aero, damage, G-forces, brake bias |
graphics |
AcGraphicsData |
Every render frame | Lap times, position, flags, fuel, pit state, electronics, session/timing state |
static_data |
AcStaticData |
Once at session load | Car model, track name, player name, session type |
AcEvo =>
To discover all available fields, save a snapshot:
save_telemetry_snapshot?;
Le Mans Ultimate
Connection: LmuConnection (via Connection::Lmu)
| Method | Returns | Scope | Notes |
|---|---|---|---|
frame() |
Box<LmuFrame> |
all cars | ~500 KB struct; boxed to avoid stack overflow; allocates per call |
frame_into(&mut LmuFrame) |
Result<(), SimError> |
all cars | Allocation-free read into a reused buffer (LmuFrame::new_boxed()) |
frame.player_telemetry() |
Option<&LmuVehicleTelemetry> |
player only | Cross-references scoring + telemetry by vehicle ID; returns None if not found |
frame.player_scoring_idx() |
Option<usize> |
player only | Index into frame.vehicles_scoring for the player's entry |
telemetry_snapshot() |
HashMap<String, TelemetryValue> |
player only | Field names from LmuVehicleTelemetry |
var_list_snapshot() |
Vec<VarMeta> |
— | All field names from LmuVehicleTelemetry |
is_connected() |
bool |
— | true when plugin is loaded and session has started |
wait_for_data(ms) |
— | — | Sleep up to 16 ms; LMU has no data-ready event |
Lmu =>
For hot paths (e.g. 60 Hz overlay polling) reuse one buffer instead of allocating ~500 KB per frame:
let mut frame = new_boxed;
while conn.is_connected
[!IMPORTANT] Plugin required. Install
rFactor2SharedMemoryMapPlugin64.dll— see LMU Plugin Setup.
Save Utilities
use ;
// All sims — accepts &Connection
save_telemetry_snapshot?;
save_var_list_snapshot?;
// iRacing only — accepts &IRsdkConnection
save_session?;
Using from GitHub
# All simulators (default)
= { = "https://github.com/mvoof/kerb" }
# iRacing only
= { = "https://github.com/mvoof/kerb", = false, = ["iracing"] }
Character Encoding
iRacing uses Windows-1252 for all strings. The crate decodes them automatically. Use decode_cp1252(bytes) if you need to decode raw bytes yourself.
Le Mans Ultimate — Plugin Setup
LMU does not expose telemetry by default. Install the rF2SharedMemoryMapPlugin:
- Download
rFactor2SharedMemoryMapPlugin64.dllfrom the releases page - Copy to
<Steam>\steamapps\common\Le Mans Ultimate\Plugins\ - In-game: Settings → Gameplay → Enable Plugins: ON
- Restart LMU
If the plugin is missing, SimConnection::connect() skips LMU and tries the next enabled sim.
Codegen — iRacing Typed Frame (for crate developers only)
[!IMPORTANT] End users of the crate do not need this.
src/iracing/types.rsis already committed to the repository with all current iRacing variables. Re-run codegen only if iRacing adds or changes variables after an SDK update.
IracingFrame is a struct with one pub field per iRacing variable. Field names are snake_case of the iRacing variable name (SteeringWheelAngle → steering_wheel_angle).
How to regenerate
- Start iRacing and enter a session (practice, qualifying, or race)
- Run codegen — it connects to the live session, reads all variables, and writes
types.rs:
- Commit the updated
src/iracing/types.rs
Benchmarks
Covers CP-1252 decoding, frame copies, snapshot HashMap allocation, and iRacing session cache behavior.
Examples
Simulator SDK References
| Simulator | Documentation |
|---|---|
| iRacing | iRacing SDK (login required). Community reference: irsdkdocs |
| Assetto Corsa Evo | Shared Memory API Documentation — official Kunos thread; struct reference — Google Doc |
| Le Mans Ultimate | Uses rF2SharedMemoryMapPlugin — community plugin built on ISI/S397 internals sample |
License
MIT — see LICENSE