Developer Guide¶
This project delivers two things: Lua scripts that run inside DCS World missions, and Python tools that prepare those missions before launch. The two layers are independent — you can contribute to one without touching the other.
The two layers¶
Runtime layer — Lua inside DCS¶
src/scripts/veaf/ contains the Lua modules loaded inside DCS World missions while the mission is running. These scripts do not execute on your PC — they execute inside the simulator, on the server hosting the mission. They handle features such as QRAs, combat zones, dynamic weather, radio menus, and more.
Lua 5.1 is the scripting language embedded in DCS World. There are no external libraries, no package manager — just plain .lua files.
Build layer — Python¶
src/python/veaf-tools/ is a CLI tool that manipulates .miz files (DCS missions, which are ZIP archives). It injects Lua scripts, configuration, and weather data into a mission before it is launched.
veaf_build/ is the orchestrator that concatenates the Lua modules into a single file, compiles Windows .exe files, and publishes GitHub releases.
How the two layers connect¶
The Python tools produce a published.zip. That ZIP is consumed by mission makers to embed VEAF scripts into their own DCS missions.
flowchart LR
subgraph DT["On your PC — build"]
lua["src/scripts/veaf/*.lua<br/>(Lua modules)"]
py["veaf-tools.exe<br/>(Python CLI)"]
end
subgraph zip["published.zip"]
bundle["veaf-scripts.lua<br/>(concatenated modules)"]
end
subgraph RT["On the DCS server — runtime"]
mission[".miz\n(DCS mission)"]
dcs["DCS World"]
end
lua --> bundle
py -->|injects into| mission
bundle -->|copied into| mission
mission -->|loaded by| dcs
Where to start?¶
You want to modify a Lua script (add a feature, fix a bug in a module):
- Read the Lua Runtime Scripts section of the guide
- Edit the file in
src/scripts/veaf/ - Run
poetry run test-luato check nothing is broken
You want to modify the Python tools (veaf-tools.exe CLI, build pipeline):
- Read the Python Tools section of the guide
- Edit the code in
src/python/veaf-tools/ - Run
poetry run pytestto check
You are starting from scratch and have nothing installed:
→ The Development Environment section walks through every installation step (Python, Poetry, Lua, StyLua).
Initial environment check¶
Do this once after following the setup guide, to confirm everything is correctly installed:
git clone https://github.com/VEAF/VEAF-Mission-Creation-Tools.git
cd VEAF-Mission-Creation-Tools
git checkout develop
poetry install
poetry run test-lua # should show "OK" for every suite
poetry run pytest # should show "passed" with no failures; in parallel, one worker per core
pytest spreads the tests over one pytest-xdist worker per core; poetry run pytest -n0 runs them serially, which --pdb needs.
Quality Gates — before each commit¶
These commands must pass without errors before committing. CI also runs them automatically — a failing job blocks the merge.
| What was changed | Commands to run |
|---|---|
Lua files (src/scripts/veaf/, test/lua/) |
stylua --check src/scripts/veaf/ test/lua/ then poetry run test-lua |
Python code (src/python/, test/python/, veaf_build/) |
poetry run ruff check src/python/ test/python/ veaf_build/ then poetry run ruff format --check src/python/ test/python/ veaf_build/ then poetry run mypy src/python/veaf-tools then poetry run pytest |
On Windows,
styluais installed at~/.local/bin/stylua.exeby default (see setup guide).
Full reference¶
- Complete Developer Guide — repository layout, coding conventions, build pipeline, contributing workflow
- Testing Guide — Lua test infrastructure in detail; the Python side (pytest, shared helpers) is covered in the developer guide
exportJSON contract —veaf-tools mission exportoutput format consumed by the BFRdcs-mission-toolsplugin- Per-type radio-preset projection — how the build projects
channel_listsonto each aircraft's radios (channel 0, reserved slots, fusion — AJS-37, OH-58D, Mi-24P…) - Mission-editing MCP server —
veaf-mission-mcp, editor-parity actions (direct.mizmutation) vs VMCT actions, v1 catalog (describe_mission,add_group)
External references — DCS API¶
- DCS World Schema (YoloWingPixie, MIT) — a complete YAML schema of the DCS World mission-scripting API, exported as JSON Schema, EmmyLua annotations (
dcs-world-api.lua) and TypeScript/Go/Python types. A reference for DCS API signatures; useful for LuaLS linting and to extend ourtest/lua/dcs_mocks.luastubs.
Vendored DCS schema¶
A frozen copy (release v0.5.0) is vendored under src/python/veaf-tools/veaf_libs/data/dcs-schema/ (upstream MIT LICENSE + a NOTICE recording the tag, URL and fetch date). It serves two purposes:
- Mock-coverage audit —
poetry run audit-dcs-mockscross-references the schema's DCS functions, the calls actually made bysrc/scripts/veaf/*.luaand the stubs intest/lua/dcs_mocks.lua, then lists the DCS calls used by VEAF but not mocked (the gap we find too late today, when a test fails). Use--format json/markdownfor machine-readable output. A non-blocking CI job publishes the report to the run summary. - IDE help (optional) —
.luarc.jsonwires LuaLS to the vendoreddcs-world-api.luaEmmyLua annotations, for autocomplete and signature diagnostics in VSCode while writing VEAF Lua.
Updating this copy is an explicit bump commit (re-download the artifacts from a newer release). Drift-watch is handled by the VENDORED-DRIFT-WATCH lot.
Country ids come from our own table. The source of truth for country ids is
src/python/veaf-tools/veaf_libs/data/dcs-countries.yaml— it is what every tool uses, throughveaf_libs.dcs_countries. Up to releasev0.3.5, the schema'stypes["country.name"]table was wrong: DCS has no country at id 14, and that table renumbered its names across the hole, so 78 of the 92 countries carried the name of the one below them and New Zealand (92) was missing. Reported upstream and fixed inv0.4.0(measured 2026-10-03).test/python/test_vendored_schema_country_ids.pylocks both schema tables against our own, so a future bump cannot break them in silence.
Vendored third-party artifacts — drift watch¶
We freeze (commit a copy of) several third-party artifacts: community Lua (mist, CTLD, CSAR, AIEN, TheUniversalMission, Skynet, Hercules_Cargo, DCS-SimpleTextToSpeech), the Python luadata lib, sounds, and the DCS schema above. The vendored.yaml manifest (repo root) is the single source of truth for every pin: per artifact it records the real source (established by content comparison, never by assuming a VEAF fork is the origin), the upstream, the vendoring mode (verbatim / adapted / fork / compiled), and the manual_steps to update it (a plain re-copy vs a fork-rebase / recompile).
poetry run check-vendoredcompares each pin against upstream via the GitHub API only (no artifact download) — latest release tag or latest file commit — and reportsdrifted/up-to-date/manual(--format table|json|markdown, non-zero exit on anything actionable).- The scheduled
vendored-drift-watch.ymlworkflow (weekly cron +workflow_dispatch) runs it and opens or updates a single recap issue listing the drifts + the manual re-check reminders, each with itsmanual_steps. Notify only — never auto-update (that is the COMMUNITY-AUTOUPDATE vision). - A repo that publishes nothing but pre-releases: GitHub's
/releases/latestskips them by design, so such a repo answers 404 and reads as an error for ever — CTLD sat 17 days behind that way. Setprereleases: trueon the watch and the releases are listed instead. Do not set it on a repo that cuts stable releases: a beta would then read as drift. Addtag_pattern(a regular expression, e.g.^published-v) wherever the repo also republishes a moving tag — CTLD rewritesdevon everymasterbuild, and without a pattern that tag is always the newest thing there.