Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

elevator-core is an engine-agnostic elevator simulation library written in pure Rust. You define stops at arbitrary positions on a 1-D axis, spawn riders, and step the tick loop — the engine handles dispatch, trapezoidal motion, doors, boarding, and metrics. Your code reacts to a typed event stream.

▶ Try it live: the in-browser playground runs the full simulation. Race two dispatch strategies on the same traffic, side-by-side, no install.

Who this is for

  • Game developers dropping a ready-made elevator into Bevy, macroquad, Godot, Unity, or a homebrew engine.
  • Algorithm researchers prototyping dispatch strategies (SCAN, LOOK, ETD, RSR, or your own) on a deterministic, snapshot-able simulation.
  • Educators teaching scheduling, real-time systems, or queueing through a visual, interactive testbed.
  • Hobbyists who think elevators are neat. (You’re correct.)

What you can build

The library models stops at arbitrary distances along a shaft axis, not uniform floors. Position is a plain f64; the bundled assets/config/space_elevator.ron stretches the same engine to 1,000 distance units between two stops as a stress test. A few examples:

ScenarioHow
Office building with 5 floorsStops at 0, 4, 8, 12, 16
Skyscraper with sky lobbiesMulti-group dispatch with express zones
Space elevator / orbital tetherStops at 0 and 1,000 — same engine
Player-controlled carServiceMode::Manual + direct velocity commands
Custom AI dispatchImplement DispatchStrategy::rank()
VIP passengers, cargo, robotsExtension storage — attach any typed data

The core crate provides primitives, not opinions. A rider is anything that rides; you decide whether they are tenants, hotel guests, hospital patients, miners, or freight pallets, and attach the semantics through the extension storage system.

What it isn’t

  • Not a renderer. No graphics, no windowing, no audio. The core crate is headless; see Bevy Integration for a 2-D visual wrapper.
  • Not real-time. The tick loop runs as fast as you drive it. There is no wall-clock coupling — a tick is whatever ticks_per_second says it is. Games layer real-time scheduling on top.
  • Not an ECS framework. It uses an ECS-inspired internal layout but exposes a focused simulation API, not a general-purpose ECS.
  • Not networked or multi-building. One simulation per process. Federation, multiplayer, and cross-building routing are out of scope.
  • Not an optimizer. Built-in dispatch strategies are reference implementations — fast enough for most consumers, but not tuned for any specific building. Bring your own algorithm if you need optimal performance.

Workspace at a glance

The simulation core sits at the centre. Host crates wrap it for a target engine; supporting crates exist only to keep the hosts in sync.

flowchart TB
    core["<b>elevator-core</b><br/>simulation library<br/>(pure Rust, no engine deps)"]

    subgraph hosts ["Hosts"]
        bevy["elevator-bevy<br/>2-D visual frontend"]
        wasm["elevator-wasm<br/>browser playground"]
        ffi["elevator-ffi<br/>C ABI: Unity, .NET, GameMaker"]
        gdext["elevator-gdext<br/>Godot extension"]
        tui["elevator-tui<br/>terminal viewer / smoke runner"]
    end

    subgraph supporting ["Supporting"]
        contract["elevator-contract<br/>cross-host determinism harness"]
        layout["elevator-layout-*<br/>repr(C) codegen for hosts"]
    end

    core --> bevy
    core --> wasm
    core --> ffi
    core --> gdext
    core --> tui

    contract -.validates.-> ffi
    contract -.validates.-> wasm
    layout -.codegens for.-> ffi

elevator-core is the only crate published to crates.io as a library; the hosts ship through their target ecosystems. See Supporting Crates for what each supporting crate does and Using the Bindings for host integration walkthroughs.

Determinism in one paragraph

Given the same initial config and the same sequence of API calls, the simulation is byte-for-byte deterministic. The core loop contains no internal randomness; every tick phase is pure over the world state. The built-in PoissonSource traffic generator uses an OS-seeded RNG and is not deterministic — for reproducible traffic, plug in a seeded TrafficSource. See Snapshots and Determinism for the contract, save/load, and replay patterns.

Stability and MSRV

  • MSRV: Rust 1.88 (uses let-chains, stabilised in 1.88; a CI job pinned to the exact MSRV keeps this honest).
  • Versioning: Semver. Breaking changes bump the major version. Adding variants to #[non_exhaustive] enums (events, errors) is not breaking.
  • Release cadence: managed via release-please; per-crate changelogs live under crates/*/CHANGELOG.md.
  • Per-item classification: see Stability and Versioning.

Next steps