Tutorial — your first mission¶
One thread, from an empty folder to a mission that runs. You know the DCS Mission Editor; you have never opened VMCT.
Every step says what to type, what should happen, and how to tell it worked. The background is not here: each concept links to its card at the moment it is needed. If you want the overview first, read Discover VMCT in ten minutes.
Allow an hour, half of it inside DCS.
You have Claude, Gemini or another AI? It can walk this whole path for you: you describe the mission in plain language, it creates the folder, places the groups, validates and builds. This tutorial is still useful to understand what it does and to check what it produces. To wire it to VMCT — Claude Desktop, Claude Code, Codex, Antigravity, Gemini CLI, OpenCode, another MCP-capable AI, or a chat AI that can only advise — see Install the AI assistant: once the tools are installed, one command,
.\veaf-tools.exe ai-setup, is enough.
Step 0 — Install the tools¶
Create an empty folder for your mission. Download
veaf-tools-updater.exe from the
latest release,
drop it in, and run it:
.\veaf-tools-updater.exe
The
.\is not decoration. The default Windows terminal is PowerShell, and PowerShell does not search the current directory — on purpose, so that an executable dropped there cannot take the place of a real command. Without the.\, it answers thatveaf-tools-updater.exe"is not recognized", naming the very file you are looking at. Command Prompt (cmd.exe) accepts both forms, so.\works everywhere: it is the form this documentation writes throughout. Details and the other differences between the two shells: PowerShell or Command Prompt?.Windows sometimes blocks a downloaded
.exe: right-click → Properties → tick Unblock.
What should happen: a published/ folder appears, and veaf-tools.exe next to the updater.
How to tell:
.\veaf-tools.exe about
prints the installed version.
Which language the tool answers in. It follows the language of Windows. To see the one it picked, and where it got it from:
.\veaf-tools.exe user-config
Effective settings
Language: en (from: OS/default)
This tutorial quotes the tool's messages in English. If yours come out in another language, set it once and for all:
.\veaf-tools.exe user-config --set lang=en
The setting is written to veafmct.yaml, at the root of your user folder (C:\Users\<you>\veafmct.yaml), and applies to every mission folder on this machine.
user-config now names that file as the language's source.
The other settings it can hold: global user configuration.
The day you ask for help
.\veaf-tools.exe doctor shows the tool's version, DCS's, the system and the tool's latest errors, then repeats them in a block ready to paste into a Discord message or a GitHub issue.
Your Windows user name is already masked in it.
Paste that block as it is: it is the first thing anyone would ask you for.
→ Running doctor
Step 1 — Create the mission folder¶
.\veaf-tools.exe prepare --template standard --theatre Caucasus
What should happen: twelve files are laid down, plus ctld-config.yaml, and the message ends
with
Mission folder ready: … Next: place/extract your .miz into src/mission, then
veaf-tools validateandveaf-tools build.
--theatre Caucasus lays down a blank Caucasus mission in src/mission: you can build right away,
with no DCS round-trip.
--template standard picks this tutorial's module set: the modules you will need are already
written into mission.yaml, the ones that require configuration being shipped commented out,
ready to enable. minimal lays down fewer — too few for step 8, where you would have a block to
write from scratch; full lays down more.
Re-running prepare later
On a folder that is already prepared, prepare asks file by file whether to replace or
keep — with a "replace all" and a "keep all". Answer keep for mission.yaml: it is the
file you edit by hand, and the only one whose content is yours. The template is then not
applied, and the tool says so.
How to tell: src/mission/mission exists, and mission.yaml is at the root.
Step 2 — Look at what was generated¶
Open mission.yaml. The substance is two blocks:
mission:
name: "My-Mission"
modules:
# ── Infrastructure ──
UNITS:
TIME:
CACHE:
EVENTS:
MARKERS:
COMMANDS:
# ── Core ──
RADIO: true # the VEAF F10 radio menu
SPAWN: true
SHORTCUTS: true # built-in aliases (-shilka, -sa2, …)
INTERPRETER: true
# ── Features ──
NAMEDPOINTS: true
MOVE: true
GRASS: true
WEATHER: true
REMOTE: true
AIRBASES: true
# ── Combat ──
GROUNDAI: true # ground units AI behaviour (required by CASMISSION)
CASMISSION: true # `_cas` marker (no config)
TRANSPORTMISSION: true # `_transport` marker (no config)
CARRIER: true # carrier-operations radio menus
# COMBATZONE:
# enabled: true
# combat_zones:
# - type: zone
# zone_name: CZ-Alpha
# friendly_name: Alpha Zone
# training: false
(A commented QRA block and a # ── Community ── section follow — step 8 and step 6 come back to
those.)
Three forms of module live side by side, and the difference matters:
| Form | What it means |
|---|---|
UNITS: — nothing after the colon |
infrastructure, always there, nothing to configure |
RADIO: true |
a switch: the module works as it is |
# COMBATZONE: — a commented block |
the module needs configuration; the example is ready to uncomment |
Change the name, and switch security off while you learn:
mission:
name: My-First-Flight
security:
disabled: true
Then, if you have no SRS server, switch text to speech off right away: find the STTS: true
line under # ── Community ── and set it to false.
STTS: false
Without SRS, STTS has nothing to do — no reason to have it on while you learn. If you do not know
what SRS is, you do not have it: set false.
The rest of the file — section headers, mission: options, blocks for modules that need
configuration — is commented out: those are examples ready to uncomment, not active configuration.
security: disabled: true is not optional here
VEAF security is on by default: most marker commands and combat-zone activations then need
an authenticated radio or a password. Offline, on your own, that shows up as commands doing
nothing — and you go looking for the bug elsewhere. The security: block goes at the root of
the file, not inside modules:. Put it back before you deploy on a server —
step 10 shows how to do it once and for all.
→ card: mission.yaml and its modules ·
veafSecurity
Step 3 — Check before building¶
.\veaf-tools.exe validate
What should happen: three warnings, zero errors.
⚠ No player slot in the mission (no unit with Client or Player skill)… ⚠ presets.yaml is configured but the mission has no player aircraft… ⚠ waypoints.yaml is configured but the mission has no aircraft group… Validation: 0 errors, 3 warnings.
That is expected: the mission is empty. The warnings describe exactly what you are about to add.
Step 4 — Build¶
.\veaf-tools.exe build My-First-Flight.miz
What should happen: the build announces the generation of veaf-config.lua, the list of
modules it read, the trigger injection, then the pipeline steps. It ends with "Processing
complete!".
Active VEAF modules (22): AIRBASES, CACHE, CARRIER, CASMISSION, COMMANDS, CSAR, CTLD, EVENTS, GRASS, GROUNDAI, INTERPRETER, MARKERS, MOVE, NAMEDPOINTS, RADIO, REMOTE, SHORTCUTS, SPAWN, TIME, TRANSPORTMISSION, UNITS, WEATHER
That line is your acknowledgement of receipt: a module you have just added and which is not on it
was not read. Modules that carry a list show how many entries they hold — you will see
COMBATZONE (1) appear at step 8.
The total depends on what you switched off: 22 here because STTS went to false at step 2, 23 if
you left it on.
How to tell: two files appeared.
| File | What it is |
|---|---|
My-First-Flight.miz |
the base mission |
missions/My-First-Flight_noon.miz |
the "noon" variant, produced by src/versions.yaml |
Not a duplicate: the weather step found its configuration file.
→ card: the build · card: weather variants
Run veaf-tools.exe from the mission folder
The scripts and the output file are resolved from the current directory. Run from elsewhere and
it looks for published/ in the wrong place.
Always spell out the .miz
The name you give is kept only if it ends in .miz. build My-First-Flight — no extension
— and a bare build both write My-First-Flight_YYYYMMDD.miz, with today's date. Handy for
archiving a version, confusing during the tutorial: you would go looking for "the one at the
root" and not recognise it. Details: card: the build.
This .miz has no player slot yet: do not try to fly it, that is what the next step is for.
What you have just checked is that the build chain works end to end, without launching DCS once.
Step 5 — Add a player slot¶
The blank mission has nobody in it. This is where DCS enters.
Do not create a new mission: open the one you have just built. In the DCS Mission Editor,
open My-First-Flight.miz, at the root of your mission folder, and add a flyable flight to it:
- an aircraft you own, blue coalition;
- on the ramp, engines cold — an air start makes the later checks awkward;
- skill Client.
Save it (same name, same place), then back to the console:
.\veaf-tools.exe extract My-First-Flight.miz
.\veaf-tools.exe validate
.\veaf-tools.exe build My-First-Flight.miz
extract replaces the blank src/mission with your real mission; build rebuilds it with the VEAF
scripts. This is the loop you will live in: editor → extract → build. It is repeatable as
often as you like — the build strips the VEAF triggers before injecting fresh ones, so nothing piles
up. That is why you reopen the built .miz rather than making another one: your editor work and the
build's work stack up without treading on each other.
How to tell: validate no longer complains about the missing player slot.
Which file to reopen in the editor
Always the one at the root of the folder: that is the one the editor writes and the one the
build rewrites. The variants under missions/ are products — do not reopen those to edit.
Step 6 — Fly it, and find the VEAF menu¶
Launch My-First-Flight.miz in DCS, take the slot, and open the radio menu: F10 "Other".
What you should see: a VEAF entry. It exists only because RADIO: true is in your
mission.yaml.
The menu holds more than that — the standard template also turns on CTLD (helicopter logistics and
transport) and CSAR (downed-pilot recovery). This tutorial uses the VEAF entry alone; you can switch
the others off the way you did STTS at step 2, by setting their line to false in mission.yaml.
Then try a marker: drop a marker on the F10 map and give it the alias
-shilka
A ZSU-23-4 Shilka appears where the marker is. That is SPAWN: true plus SHORTCUTS: true — and
the security you switched off at step 2. The long form of the same command is
_spawn unit, name ZSU-23-4 Shilka.
If the VEAF menu is not there: the scripts did not load. The DCS log says so — see reading the DCS logs.
Step 7 — Give everyone the same radios¶
Open src/presets.yaml: it ships with a complete plan for the Caucasus. To see the mechanism you
are going to replace it with the smallest file that works — so put it aside first:
Copy-Item src\presets.yaml src\presets.yaml.bak
Then replace its content with:
channels_collection:
common:
Guard:
title: Guard
freqs:
uhf: 243.0
vhf: 121.5
channel_lists:
blue:
primary_1:
01: Guard
Rebuild, and reopen the mission in game.
How to tell: channel 1 of your aircraft's first radio is on 243.0, and a "presets" kneeboard is available in the cockpit.
Now restore the shipped file — it already carries the Caucasus airfields and the usual agency frequencies, and it is the one you will want to work with:
Copy-Item src\presets.yaml.bak src\presets.yaml -Force
How real projects do this
Copying a file before changing it works, but it does not scale: after a few weeks a mission is a dozen files that evolve together, and "go back to the day before yesterday" becomes impossible by hand. Version control tools answer exactly that: they keep the folder's full history, let you compare any two states and return to any of them, and make working as a team possible.
The de facto standard is called Git. You do not need it to finish this tutorial, and it is a tool in its own right that has to be learned for itself — but the moment your mission matters, it is the next investment that pays. To start: Pro Git, free and complete.
Step 8 — An objective players activate in game¶
This is the piece that makes a VEAF mission worth the trouble: an objective that only exists when somebody asks for it.
In the DCS editor:
- create a trigger zone named
CZ-Alpha; - put a red vehicle group inside it, named
CZ-Alpha-ARMOR.
In mission.yaml, find the COMBATZONE block under the # ── Combat ── heading. It ships
commented out, with the right example already in it:
# COMBATZONE:
# enabled: true
# combat_zones:
# - type: zone
# zone_name: CZ-Alpha
# friendly_name: Alpha Zone
# training: false
Uncomment it, then set training to true:
COMBATZONE:
enabled: true
combat_zones:
- type: zone
zone_name: CZ-Alpha
friendly_name: Alpha Zone
training: true
Uncommenting means removing the # and the three spaces after it
On each line of the block, delete exactly # plus three spaces — never the spaces at the start
of the line. In YAML the indentation is the structure: COMBATZONE: at two spaces belongs
to modules:, at zero spaces it becomes a top-level block nothing reads. That is why the
shipped block is indented the way it is: the subtraction comes out right.
Then the usual loop:
.\veaf-tools.exe extract My-First-Flight.miz
.\veaf-tools.exe validate
.\veaf-tools.exe build My-First-Flight.miz
validate will tell you if CZ-Alpha does not exist in the mission — exactly the kind of mistake
it is there to catch.
How to tell, before launching DCS: two signs, in this order.
validatereports 0 errors: the zone you named really is in the mission.- The build lists your active modules, and
COMBATZONEnow carries its zone count:
Active VEAF modules (23): AIRBASES, CACHE, CARRIER, CASMISSION, COMBATZONE (1), COMMANDS, …
If you read COMBATZONE (0), the module is active but your combat_zones: list came to nothing —
an indentation mistake, almost always. If COMBATZONE does not appear at all, the block is not
inside modules:.
In game: the zone is empty at start-up, on purpose. Go to F10 "Other" → COMBAT ZONES → Alpha Zone → Activate zone.
What you should see: the message "VeafCombatZone Alpha Zone has been activated.", then your armour appearing, then the zone report.
The gotcha that costs an hour
A group is only captured by the zone if its name starts with the zone's name. Placed in the
right spot but named ARMOR-1, it is ignored. It must be CZ-Alpha-ARMOR-1.
Step 9 — Open an airfield to dynamic slots¶
This step is often already done, and that is normal: by placing your blue aircraft on an airfield's ramp at step 5, you gave that airfield to the blue coalition. Check it in the DCS editor and change nothing if that is the case — it is the only thing to do there, the rest is written by the build.
src/warehouses.yaml does not need touching either: as shipped, it enables dynamic slots on
every airfield belonging to a coalition. Under its comments, it comes down to two blocks:
blue:
defaults:
fuel: unlimited
weapons: unlimited
red:
defaults:
fuel: unlimited
weapons: unlimited
If you changed something in the editor, run extract then build again. If everything was
already in place, there is nothing to rebuild: go straight to the check.
How to tell: in DCS, at slot selection, the airfield offers dynamic-spawn aircraft.
If the list is shorter than expected: DCS only offers what the airfield's parking can actually take. An airfield with helicopter-only spots will not offer aircraft, whatever the stock says.
Step 10 — Prepare the server version¶
In step 2 you switched security off while learning. It has to come back before the mission goes to a server: without it, any player can spawn or destroy whatever they like with a marker. Putting it back by hand before every deployment means forgetting it one day; a build profile does it for you.
In mission.yaml, replace the security: block from step 2 with:
profiles:
TEST:
security:
disabled: true
The block stays at the root of the file, where security: was.
A profile only holds what changes: here, the one difference between your test version and the server's.
Without a profile the base configuration applies, and it now has security on.
To fly and test, build with the profile:
.\veaf-tools.exe build --profile TEST Mon-Premier-Vol.miz
That is the new shape of the editor → extract → build loop: only the build changes.
For the server, build without a profile, into a separate file:
.\veaf-tools.exe build Mon-Premier-Vol-serveur.miz
The file to reopen in the editor is still Mon-Premier-Vol.miz. Mon-Premier-Vol-serveur.miz is a product, like the variants in missions/: it goes to the server, you do not edit it.
How to tell: the build with a profile says so in its first lines,
Building with profile: TEST
and the build without one prints nothing of the sort.
A misspelt profile does not stop the build
--profile TSET prints "Profile 'TSET' not found in mission.yaml — using base config", then builds without a profile: security on, and marker commands that no longer do anything offline.
If -shilka stops working after this step, read the build output before looking anywhere else.
Case, on the other hand, does not matter: --profile test finds TEST.
On the server, security lets through the pilots listed in the server's veaf-pilots.txt file, according to their level; everybody else has to give a password.
Before opening the mission to the public, set your own passwords: the default ones are published with the tools.
→ veafSecurity · build profiles
Step 11 — Update the tools¶
The tools move on: fixes, new modules, new commands. To update, rerun the updater from the mission folder:
.\veaf-tools-updater.exe
What should happen: it asks for the latest release, compares it with the installed version, and only downloads when the release is newer. Already up to date, it stops there:
Found release version: X.Y.Z
Installed version X.Y.Z is already up-to-date
Work done!
Otherwise it announces "Newer version available: … → …", then downloads, verifies and installs.
The update replaces the published/ folder and both executables.
It touches neither mission.yaml, nor src/, nor your .miz files.
Then rebuild. The VEAF scripts travel inside the .miz: the build copies them in.
A mission built before the update therefore keeps the old scripts until you rebuild it:
.\veaf-tools.exe build --profile TEST Mon-Premier-Vol.miz
.\veaf-tools.exe build Mon-Premier-Vol-serveur.miz
How to tell: .\veaf-tools.exe about prints the new version, and the build writes it on its first line.
To stay on one specific version, for the length of an event for instance: updating.
Step 12 — What next¶
You have a mission that runs, versioned, rebuildable. The rest is à la carte:
| You want to | Go to |
|---|---|
| Understand one piece in particular | the cards |
| Add managed tankers, AWACS, carriers | veafAssets |
| A QRA that scrambles on intrusion | veafQraManager |
| Air combat by waves | veafAirWaves |
| Password-protect your server | veafSecurity |
| See every feature running in game | the demo mission |
| Take over a mission made with v5 of the tools | Migrating to v6 |
| Adopt a mission that was not made with VMCT | Adopt a third-party mission |
| Build or change a mission by asking an AI assistant | Install the AI assistant |
| Every configuration option | mission.yaml reference |
| Every command | CLI reference |
| The end-to-end detail | Full guide |
And keep the habit: validate before build, and the DCS log whenever something
fails to load.