Skip to content

infinity-MSFS/infinity-rs

Repository files navigation

infinity-rs

Safe, idiomatic Rust bindings for the Microsoft Flight Simulator 2024 (MSFS 2024) WASM SDK.

Write MSFS gauges and systems in Rust with safe, idiomatic wrappers over the entire WASM SDK surface — SimVars, Comm Bus, async HTTP, file I/O, NanoVG rendering, simulation events, the flow lifecycle, map view, airport charts, VFX, and the planned route.

📖 Full docs: https://infinity-simulations.com/docs/developer/getting-started


Workspace Layout

This repo publishes four crates to crates.io. As a user you only ever depend on the first one and install the last one — the other two are pulled in automatically.

Crate crates.io Role
infinity-rs infinity-rs Main bindings crate — what you put in your Cargo.toml
infinity_rs_derive infinity_rs_derive Proc-macros (VarStruct, …) — re-exported from infinity_rs under the derive feature
infinity_rs_sdk infinity_rs_sdk Build-script helper that locates the MSFS 2024 SDK on disk
infinity-msfs infinity-msfs CLI: SDK installer + WASM build/packaging — cargo install infinity-msfs

The lib crate is named infinity-rs (Rust path: infinity_rs). It used to be msfs, which collided with the FlyByWire crate of the same name; the rename in 0.2.0 is intentionally breaking. Update imports from use msfs::... to use infinity_rs::..., and msfs::export_gauge! / msfs::export_system! to infinity_rs::export_gauge! / infinity_rs::export_system!.


Prerequisites

Requirement Notes
Rust 1.85+ with wasm32-wasip1 target rustup target add wasm32-wasip1
clang + llvm-ar Required for WASM compilation; install via LLVM or your system package manager
wasm-opt Part of Binaryen; only required when wasm_opt.enabled = true in your config

The MSFS 2024 SDK itself is fetched on demand by the infinity-msfs CLI (see below) — works on Windows, macOS, and Linux. You don't need to install the SDK manually unless you want to point at an existing install via MSFS2024_SDK.


Getting Started

The walkthrough below is the short version. For tutorials, configuration reference, and end-to-end project setup, see the full developer docs at https://infinity-simulations.com/docs/developer/getting-started.

1. Install the build CLI

cargo install infinity-msfs

This gives you infinity-msfs build, infinity-msfs sdk install, infinity-msfs doctor, and the project commands.

2. Add the bindings crate

[dependencies]
infinity-rs = "0.2"

That pulls everything you need by default — the WASM-runtime APIs and the #[derive(VarStruct)] macro are re-exported under infinity_rs::*.

3. Build

infinity-msfs build

On the first build, if no SDK is installed, the CLI prompts to download it from sdk.flightsimulator.com. Re-run infinity-msfs sdk install --force later to upgrade. To use an existing SDK install instead, set MSFS2024_SDK to its root path.

Feature flags

Feature Default What it enables
wasm yes WASM-host runtime APIs (NVG, comm bus, IO, network, vars, gauge/system entry points). Disable for native-only builds.
derive yes Re-exports the infinity_rs_derive proc-macros (VarStruct, etc.).
simconnect no SimConnect bindings. Works on both WASM and native Windows.

Common combinations:

# WASM gauge or system (the default)
infinity-rs = "0.2"

# Native SimConnect tool, no WASM target
infinity-rs = { version = "0.2", default-features = false, features = ["simconnect"] }

# WASM module that also uses SimConnect
infinity-rs = { version = "0.2", features = ["simconnect"] }

In code:

use infinity_rs::prelude::*;

Core Concepts

Build Tools

The build tools supplied with the project compile Rust WASM targets, optionally run wasm-opt, and can list the configured projects before building. Copy infinity-msfs.toml.example to infinity-msfs.toml in your project root and adjust the package names and copy rules for your own workspace.

[build]
target = "wasm32-wasip1"
out_dir = "build/msfs"

copy = [
  { from = "manifest.json", to = "build/msfs/manifest.json" },
  { from = "layout.json", to = "build/msfs/layout.json" }
]

[[packages]]
package = "demo-gauge"
bin = "demo_gauge"
out_name = "demo-gauge.wasm"

[[packages]]
package = "demo-system"
bin = "demo_system"
out_name = "demo-system.wasm"
out_dir = "build/msfs/systems"

[wasm_opt]
enabled = true
args = [
  "-O1",
  "--signext-lowering",
  "--enable-bulk-memory",
  "--enable-nontrapping-float-to-int",
]

[scripts]
pre_build = [
  "cargo fmt --check"
]

post_build = [
  "python ./tools/fix_layout.py"
]

some example build commands:

infinity-msfs projects
infinity-msfs list-projects
infinity-msfs build
infinity-msfs build --release
infinity-msfs build --release --no-wasm-opt
infinity-msfs build --only demo-gauge --release
infinity-msfs build --verbose

Systems and Gauges

Everything in MSFS runs as either a System or a Gauge. Implement the corresponding trait and export it with the matching macro.

System — logic-only, no rendering:

use infinity_rs::prelude::*;

pub struct MySystem { /* ... */ }

impl System for MySystem {
    fn init(&mut self, ctx: &Context, install: &SystemInstall) -> bool { true }
    fn update(&mut self, ctx: &Context, dt: f32) -> bool { true }
    fn kill(&mut self, ctx: &Context) -> bool { true }
}

infinity_rs::export_system!(
    name  = my_system,
    state = MySystem,
    ctor  = MySystem::new(),
);

Gauge — rendered panel element with optional mouse input:

use infinity_rs::prelude::*;

pub struct MyGauge { /* ... */ }

impl Gauge for MyGauge {
    fn init(&mut self, ctx: &Context, install: &mut GaugeInstall) -> bool { true }
    fn update(&mut self, ctx: &Context, dt: f32) -> bool { true }
    fn draw(&mut self, ctx: &Context, draw: &mut GaugeDraw) -> bool { true }
    fn kill(&mut self, ctx: &Context) -> bool { true }

    fn mouse(&mut self, _ctx: &Context, x: f32, y: f32, flags: i32) { /* optional */ }
}

infinity_rs::export_gauge!(
    name  = my_gauge,
    state = MyGauge,
    ctor  = MyGauge::new(),
);

The macros emit the correctly named extern "C" entry points expected by the simulator.


Features

SimVars — infinity_rs::vars

Read and write simulation variables via AVar (A-vars) and LVar (L-vars).

use infinity_rs::vars::{AVar, LVar};

// A-var: read-only aircraft variable
let airspeed = AVar::new("AIRSPEED INDICATED", "Knots")?;
let kts: f64 = airspeed.get()?;

// L-var: readable and writable local variable
let mut flag = LVar::new("L:MY_GAUGE_ACTIVE", "Bool")?;
flag.set(1.0)?;
let val = flag.get()?;

Indexed A-vars (e.g. per-engine data):

use infinity_rs::vars::{AVar, VarParamArray1};

let eng_rpm = AVar::new("GENERAL ENG RPM", "RPM")?;
let rpm = eng_rpm.get_with(VarParamArray1::new(1), Default::default())?; // engine 1

#[derive(VarStruct)]

Bundle multiple vars into a single struct and snapshot them all at once:

use infinity_rs::VarStruct;

#[derive(Debug, Clone, Copy, VarStruct)]
struct FlightData {
    #[var(name = "A:PLANE ALTITUDE",               unit = "Feet",    kind = "A")]
    altitude_ft: f64,

    #[var(name = "A:PLANE HEADING DEGREES TRUE",   unit = "Degrees", kind = "A")]
    heading_deg: f64,

    #[var(name = "A:GENERAL ENG RPM",              unit = "RPM",     kind = "A", index = 1)]
    eng1_rpm: f64,

    #[var(name = "L:MY_CUSTOM_VALUE",              unit = "Number",  kind = "L")]
    custom: f64,
}

let snapshot = FlightData::read()?;
println!("Alt: {} ft  Hdg: {}°  ENG1: {} RPM", snapshot.altitude_ft, snapshot.heading_deg, snapshot.eng1_rpm);

Comm Bus — infinity_rs::comm_bus

Send and receive binary messages between WASM modules, JavaScript, and the sim.

use infinity_rs::prelude::*;

// Subscribe to a named event
let _sub = Subscription::subscribe("my.module/event", |bytes| {
    println!("Received {} bytes", bytes.len());
})?;

// Broadcast a message
let payload = 42u32.to_le_bytes();
commbus_call("my.module/event", &payload, BroadcastFlags::JS | BroadcastFlags::WASM);

Broadcast flags:

Flag Target
BroadcastFlags::JS JavaScript gauges
BroadcastFlags::WASM Other WASM modules
BroadcastFlags::WASM_SELF This WASM module itself
BroadcastFlags::ALL Everyone
BroadcastFlags::DEFAULT SDK default

Subscriptions automatically unsubscribe when dropped.


HTTP Networking — infinity_rs::network

Make asynchronous HTTP GET and POST requests from within a WASM module.

use infinity_rs::prelude::*;

let params = HttpParams {
    headers: vec!["Accept: application/json".to_string()],
    post_field: None,
    body: vec![],
};

http_request(Method::Get, "https://example.com/data.json", params, |resp| {
    if resp.error_code == 0 {
        let body = String::from_utf8_lossy(&resp.data);
        println!("Got: {body}");
    }
})?;

Callbacks are invoked on the next simulator update tick after the response arrives.


File I/O — infinity_rs::io

High-level API (infinity_rs::io::fs)

use infinity_rs::io::fs;

// Async read — callback fires when data is ready
let req = fs::read("\\work/config.json", |data| {
    println!("Read {} bytes", data.len());
})?;

// Async write
let req = fs::write("\\work/output.bin", &my_bytes)?;

// Poll completion in your update loop
if req.is_done() { /* ... */ }
if req.has_error() { eprintln!("{:?}", req.last_error()); }

Low-level API (infinity_rs::io)

Full control via OpenFile, IoRequest, and OpenFlags for advanced use cases.

Supported open flags: RDONLY, WRONLY, RDWR, CREAT, TRUNC, HIDDEN


NanoVG Rendering — infinity_rs::nvg

Vector graphics rendering inside a Gauge using the NanoVG API.

use infinity_rs::nvg::*;
use infinity_rs::prelude::*;

// In Gauge::init
let nvg = NvgContext::new(ctx).expect("NVG init failed");
let font = nvg.create_font("sans", "./data/Roboto-Regular.ttf");

// In Gauge::draw
nvg.frame(win_w, win_h, px_ratio, |nvg| {
    // Shapes
    Shape::rect(10.0, 10.0, 200.0, 100.0)
        .fill(Color::rgb(0, 120, 255))
        .draw(nvg);

    Shape::circle(150.0, 150.0, 40.0)
        .stroke(Color::rgba(255, 255, 255, 200), 2.0)
        .draw(nvg);

    // Text
    nvg.text(font_id, 16.0, 50.0, 50.0, "Hello MSFS!");

    // Transforms
    nvg.scoped(|nvg| {
        nvg.translate(cx, cy);
        nvg.rotate(angle_rad);
        // ... draw rotated content ...
    });
});

NvgContext is automatically cleaned up via Drop.


Flow API — infinity_rs::flow

Subscribe to simulation lifecycle events (flight load, teleport start/done, replay boundaries, plane crash, …). Useful for resetting state on flight reload or pausing logic across slew/back-on-track windows. Subscriptions auto-unregister on Drop.

Event API — infinity_rs::events

Subscribe to and trigger named simulation events (KEY_TOGGLE_MASTER_BATTERY, etc.) with typed parameter arguments via FsParamArg.

Map Views — infinity_rs::map_view

MapView wraps fsMapView*: spawn a host-side map texture, configure aerial / altitude shading, follow mode, isolines, and an integrated weather radar (top / horizontal / vertical with custom rain-rate color ramps and cone angle). MapView::image_pattern(...) hands the texture straight to NanoVG so you can paint it through any gauge.

Charts — infinity_rs::charts

Async access to the fsCharts* API. get_index → get_pages → get_page_image returns a ChartImage whose host id can be sampled by NanoVG via nvg_pattern(...). ChartIndex and ChartPages are owned wrappers that release the host allocations on Drop; ref-views (ChartCategoryRef, ChartMetadataRef, ChartPageRef) decode strings as &str borrowed from the owner.

VFX — infinity_rs::vfx

Spawn particle effects in the world (spawn_in_world) or attached to a sim object node (spawn_on_sim_object) with optional VfxParam { name, rpn } graph bindings. VfxInstance owns the host id and destroys it on Drop; leak() detaches if the host should keep emitting after the handle goes away.

Planned Route — infinity_rs::planned_route

EFB / route-broadcast integration. current_efb_route() borrows the route the EFB currently holds; subscribe_broadcast(...) listens for pushes; subscribe_requests(...) receives RouteRequest handles when other modules ask for a route, and you reply via request.respond(route). Both subscriptions auto-unregister on Drop.

Examples

All examples are in msfs/examples/.

Example Description
io_system_simple.rs Read and copy a file using the high-level io::fs API
io_system.rs Full low-level file I/O API
vars_full_api.rs A-vars, L-vars, indexed vars, VarStruct snapshot
comm_bus_gauge.rs Gauge that publishes Comm Bus events on mouse click
comm_bus_sytem.rs System that receives Comm Bus commands and broadcasts state
network_fetch_system.rs Fetch JSON over HTTP on a Comm Bus trigger
network_post_system.rs HTTP POST with a request body
nvg_render.rs Attitude indicator rendered with NanoVG
flow_system.rs Adjust logic based on sim events using the Flow API
event_api.rs Subscribe to and trigger simulation events
map_view_weather_radar.rs Texture-backed weather radar gauge with rain-rate color ramp + range overlay
charts_gauge.rs Async charts pipeline (index → pages → image) painted through NanoVG
vfx_system.rs Sim-object-attached particle effect driven by N1 via an RPN-bound graph param
planned_route_system.rs Subscribe to EFB route broadcasts and respond to route requests

Crate Structure

msfs/src/
├── lib.rs            — top-level re-exports
├── prelude.rs        — convenient glob import
├── modules.rs        — System / Gauge traits
├── exports.rs        — export_system! / export_gauge! macros
├── context.rs        — FsContext wrapper
├── types.rs          — GaugeDraw, GaugeInstall, SystemInstall
├── sys.rs            — raw bindgen bindings
├── abi.rs            — ABI shims for host entry points
├── vars/             — AVar, LVar, VarKind, VarStruct
├── comm_bus/         — Subscription, BroadcastFlags, commbus_call
├── network/          — http_request, HttpParams, Method, HttpResponse
├── io/               — File I/O (low-level + fs high-level)
├── nvg/              — NanoVG: NvgContext, Shape, Color, Transform, …
├── events/           — Sim event helpers (subscribe / trigger / FsParamArg)
├── flow/             — Flow lifecycle subscription
├── map_view.rs       — MapView (fsMapView*) — weather radar / terrain / 3D
├── charts.rs         — Async charts API (fsCharts*) with owned wrappers
├── vfx.rs            — VfxInstance (fsVfx*) with RAII destroy
├── planned_route.rs  — EFB planned-route broadcast + request channels
├── host/             — Native testing shims (non-wasm targets)
├── utils/            — Internal utilities
└── bindgen_support/  — Headers consumed by the build script

Projects Using infinity-rs

Project Developer
T-38 Talon Aero Dynamics
DC-10 Aero Dynamics

SDK Coverage

Every public header in MSFS 2024 SDK/WASM/include/MSFS/ is wrapped:

Header Module
MSFS.h, MSFS_Core.h, MSFS_Utils.h infinity_rs::sys, internal helpers
MSFS_Vars.h infinity_rs::vars
MSFS_CommBus.h infinity_rs::comm_bus
MSFS_Network.h infinity_rs::network
MSFS_IO.h infinity_rs::io
MSFS_Render.h, Render/nanovg.h infinity_rs::nvg
MSFS_GaugeContext.h, MSFS_SystemContext.h infinity_rs::context, infinity_rs::modules
MSFS_Events.h, Types/MSFS_EventsEnum.h infinity_rs::events
MSFS_Flow.h infinity_rs::flow
MSFS_MapView.h infinity_rs::map_view
MSFS_Charts.h infinity_rs::charts
MSFS_Vfx.h infinity_rs::vfx
MSFS_PlannedRoute.h, MSFS_FlightPlan.h infinity_rs::planned_route (FlightPlan types re-exposed via sys)
MSFS_Weather.h re-exported via infinity_rs::sys (typed wrapper TBD)
Legacy/gauges.h infinity_rs::sys (legacy gauge ABI)
SimConnect.h infinity_rs::sys under the simconnect feature (native only)

About

Rust bindings, abstractions and build tools for FS2024 WASM

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors