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 Luaioare 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.