ExportVM#

The ExportVM is a lightweight, isolated Lua state dedicated to running Export.lua scripts. It is separate from both the GuiVM and the MissionVM, and exists specifically to let external tools, such as cockpit builder hardware, streaming overlays, telemetry loggers, and similar third-party software to read live simulation data without needing to interact with the mission or GUI environments directly.

Execution#

Unlike the MissionVM, ExportVM scripts are not triggered by Mission Editor triggers or AI tasking actions. Instead, DCS calls a fixed set of callback functions, if defined, at specific points in the simulation loop:

  • LuaExportStart() — called once when the export script is loaded (e.g. mission start).

  • LuaExportStop() — called once when the export script is unloaded (e.g. mission end).

  • LuaExportBeforeNextFrame() — called once per simulation frame, before the frame is processed.

  • LuaExportAfterNextFrame() — called once per simulation frame, after the frame is processed.

  • LuaExportActivityNextEvent(t) — called to poll for the next in-game event; used for event-driven export logic.

These callbacks are defined by the user inside Export.lua (or a script it dofile’s) located in the Scripts folder of the DCS Saved Games directory.

API Hierarchy#

The ExportVM does not expose singleton or class objects the way the MissionVM does. Instead, it exposes a flat table of global Lo*-prefixed functions for reading simulation state, most commonly the state of the player’s own aircraft, for example:

  • LoGetSelfData() — returns basic state data (position, heading, etc.) for the player-controlled unit.

  • LoGetModelTime() — returns the current simulation model time.

  • LoGetAltitudeAboveSeaLevel() / LoGetAltitudeAboveGroundLevel() — altitude queries.

  • LoGetIndicatedAirSpeed() / LoGetTrueAirSpeed() — airspeed queries.

  • LoGetVectorVelocity() — velocity vector queries.

  • LoGetADIPitchBankYaw() — attitude data for instrument export.

Sandboxing#

Because ExportVM code runs outside the mission’s sandbox, it has access to a broader set of standard Lua libraries (including file I/O) than MissionVM scripts, since it is intended to write telemetry out to files, network sockets, or shared memory for consumption by external applications.

Installation#

Export.lua is loaded from the Scripts folder of your DCS Saved Games directory, not the DCS installation directory:

%USERPROFILE%\Saved Games\DCS\Scripts\Export.lua

If the Scripts folder does not already exist, create it. DCS automatically dofile’s Export.lua from this location on startup, so no other registration step is required.

Warning

Only one Export.lua can be active at a time. If you need to combine multiple export tools (e.g. a cockpit builder driver alongside a telemetry logger), have your Export.lua dofile each tool’s script and chain their callbacks together, rather than overwriting one tool’s file with another’s. See the example below for a simple way to chain callbacks.

Example script#

The following minimal Export.lua writes the player’s aircraft position and indicated airspeed, once per second, to a plain text file in the Saved Games folder. It illustrates the basic shape every export script follows: declare the four LuaExport* callbacks, use the Lo* functions to read simulation state, and use standard Lua file I/O (available here, unlike in the MissionVM) to persist or forward that data.

-- Export.lua
-- Writes basic telemetry to Logs/telemetry.log once per second.

local telemetryFile = nil
local lastWriteTime = 0
local writeInterval = 1.0 -- seconds

function LuaExportStart()
    -- lfs is available here (unlike in the MissionVM) for locating Saved Games.
    local logDir = lfs.writedir() .. 'Logs\\'
    telemetryFile = io.open(logDir .. 'telemetry.log', 'w')
end

function LuaExportStop()
    if telemetryFile then
        telemetryFile:close()
        telemetryFile = nil
    end
end

function LuaExportBeforeNextFrame()
    -- Intentionally left blank; work is done in LuaExportAfterNextFrame below.
end

function LuaExportAfterNextFrame()
    if not telemetryFile then
        return
    end

    local now = LoGetModelTime()
    if now - lastWriteTime < writeInterval then
        return
    end
    lastWriteTime = now

    local selfData = LoGetSelfData()
    if not selfData then
        -- No player-controlled unit (e.g. spectating, or at the main menu).
        return
    end

    local ias = LoGetIndicatedAirSpeed()
    telemetryFile:write(string.format(
        '%.1f\tlat=%.6f\tlon=%.6f\talt=%.1f\tias=%.1f\n',
        now, selfData.LatLongAlt.Lat, selfData.LatLongAlt.Long,
        selfData.LatLongAlt.Alt, ias
    ))
    telemetryFile:flush()
end

function LuaExportActivityNextEvent(t)
    -- Return the next time (in model time) this function should be called
    -- again; returning t + a large number effectively disables polling
    -- for event-driven logic this script doesn't use.
    return t + 600
end

Place this file at Saved Games\DCS\Scripts\Export.lua (adjusting for your DCS variant per Installation above), then start or rejoin a mission. telemetry.log will appear under Saved Games\DCS\Logs and update once per second while a unit is occupied.

Chaining multiple export scripts#

Since only one Export.lua is loaded at a time, combine multiple tools by dofile-ing each one from a single top-level Export.lua and composing their callbacks:

-- Export.lua
-- Chains callbacks from two independent export scripts.

local callbacks = {}

local function loadExporter(path)
    local env = {}
    setmetatable(env, {__index = _G})
    local chunk = assert(loadfile(path))
    setfenv(chunk, env)
    chunk()
    table.insert(callbacks, env)
end

loadExporter(lfs.writedir() .. 'Scripts\\MyTelemetryExporter.lua')
loadExporter(lfs.writedir() .. 'Scripts\\MyCockpitExporter.lua')

function LuaExportStart()
    for _, cb in ipairs(callbacks) do
        if cb.LuaExportStart then cb.LuaExportStart() end
    end
end

function LuaExportStop()
    for _, cb in ipairs(callbacks) do
        if cb.LuaExportStop then cb.LuaExportStop() end
    end
end

function LuaExportBeforeNextFrame()
    for _, cb in ipairs(callbacks) do
        if cb.LuaExportBeforeNextFrame then cb.LuaExportBeforeNextFrame() end
    end
end

function LuaExportAfterNextFrame()
    for _, cb in ipairs(callbacks) do
        if cb.LuaExportAfterNextFrame then cb.LuaExportAfterNextFrame() end
    end
end

Note

setfenv is a Lua 5.1 API, matching the Lua version embedded in DCS. Each chained script should define its callbacks as ordinary global functions, exactly as if it were the only script installed.

Footnotes#

  • ExportVM runs independently of whether a mission is loaded in a way visible to the player; it is primarily concerned with the local client’s own aircraft state.

  • Multiple Lo* functions are only meaningful (or only return valid data) while the player is in control of a unit.

  • lfs (LuaFileSystem) and standard Lua io are both available in the ExportVM, letting scripts locate the Saved Games directory (lfs.writedir()) and read/write files directly – neither is available in the sandboxed MissionVM.