MizEdit's modify DSL (Domain Specific Language) lets you search and modify Lua tables inside DCS .miz files.
A .miz file is a ZIP archive containing three Lua table files:
| File | Contents |
|---|---|
mission |
Units, routes, weather, triggers, coalition data — the main mission |
options |
Mission options (overrides the player's options.lua) |
warehouses |
Airport warehouse data and dynamic spawn configuration |
MizEdit unpacks the .miz, amends these Lua tables, and repacks into a new .miz file.
Every modify block follows this structure:
# config/presets.yaml
PresetName:
modify:
file: mission # which Lua file to modify: mission | options | warehouses
variables: ... # optional: extract or define variables
if: ... # optional: condition that must be true
for-each: <path> # required: path to elements to iterate
where: ... # optional: filter on the reference element
select: <path> # optional: navigate deeper to the element to change
replace: ... # modify existing values
delete: ... # remove elements
insert: ... # add new keys/values
merge: ... # merge two Lua tables
run: <python.func> # call a Python function for complex logic
debug: true # optional: verbose loggingPaths navigate the Lua table tree from root to leaf:
| Syntax | Meaning |
|---|---|
/node |
Descend into a named key |
* |
Iterate all elements in a list or table |
[x] |
Select the n-th element from a Lua list (1-based) or a specific key from a table |
[x,y] |
Select multiple elements |
$'...' |
Evaluate the content as a Python expression (returns a boolean for filtering, or a value for replacement) |
'{var}' |
Substitute a previously defined variable's value |
Important on indexing: Lua lists are 1-based, so [1] is the first element. However, when accessing data via Python
expressions (inside $'...' or reference), use Python 0-based indexing: reference[units][0].
for-each defines which elements to iterate over. The matched element becomes the reference element — the context you work with.
for-each: coalition/blue/country/*/ship/group/*/units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75']This walks the mission tree and selects every individual carrier unit matching the type list. The reference element is each unit.
where filters which reference elements to process, but does NOT change what the reference points to.
for-each: coalition/blue/country/*/ship/group/*
where: units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75']This iterates all groups, but only processes groups containing a matching carrier. The reference element is each group (not the unit). This matters when you need to modify group-level data like routes.
Rule: Use for-each alone when you want to modify the iterated elements directly. Add where when you need to filter by a child property but work on the parent.
select navigates from the reference element to the specific sub-element to modify. The reference stays the same.
for-each: coalition/blue/country/*/ship/group/*
where: units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CN_74','CVN_75']
select: route/points/*/task/params/tasks/$'{id}' == 'WrappedAction'/params/action/$'{id}' == 'ActivateBeacon'/paramsThis finds blue carrier groups, then drills into their route tasks to find TACAN beacon parameters.
Simple replacement:
replace:
frequency: 371000000
modeChannel: XConditional replacement (switch/case): When a replace value is a mapping, each key is a Python expression. The first expression that evaluates to True determines the value:
replace:
frequency:
$'{reference[units][0][type]}'[-2:] == '72': 1158000000
$'{reference[units][0][type]}'[-2:] == '73': 1160000000delete: $'{type}' == 'FA-18C_hornet'Use insert when the target key doesn't exist yet:
insert:
Radio:
- channels:
- 243Merges two parts of a mission file together, e.g., merging neutral country data into blue:
merge:
source: coalition/neutral
target: coalition/blueThe following variables are available in all modify blocks:
| Variable | Description |
|---|---|
{reference} |
The current reference element (set by for-each). Access properties like {reference[units][0][type]}. Uses Python 0-based indexing. |
{type} |
The type name of the current element (when iterating units) |
Extract values from the mission or define your own:
variables:
theatre: theatre # extract from mission
temperature: weather/season/temperature # extract nested value
speed: 40 # fixed value
rand: '$random.randint(1, 10)' # Python random
mylist: '$list(range(1, {rand}))' # use other variablesReference variables with {variablename} in paths and expressions.
Run the modification only when a condition is met:
variables:
start_time: start_time
theatre: theatre
if: ${start_time} > 20000 and '{theatre}' == 'Caucasus'For complex logic, call a Python function:
variables:
wind: weather/wind/atGround
for-each: coalition/*/country/*/ship/group/*
where: units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75', "Stennis", "LHA_Tarawa"]
run: core.utils.mizedit.relocate_carrierThe function receives three arguments:
| Parameter | Description |
|---|---|
data |
The selected element |
reference |
The reference element |
kwargs |
All defined variables |
def my_function(data: dict, reference: dict, **kwargs) -> dict:
# modify data in place or return a replacement dict
return dataSee the DCSServerBot source for available built-in functions.
Set debug: true to enable verbose logging. The log shows which elements were matched and what changes were made. Check the DCSServerBot log file for output.
Select every blue carrier unit and set its frequency based on type:
# config/presets.yaml
SetCarrierFreqs:
modify:
file: mission
for-each: coalition/blue/country/*/ship/group/*/units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75']
replace:
frequency:
"$'{type}' == 'CVN_71'": 371000000
"$'{type}' == 'CVN_72'": 372000000
"$'{type}' == 'CVN_73'": 373000000
"$'{type}' == 'CVN_74'": 374000000
"$'{type}' == 'CVN_75'": 375000000Shorter version using a Python calculation:
# config/presets.yaml
SetCarrierFreqs:
modify:
file: mission
for-each: coalition/blue/country/*/ship/group/*/units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75']
replace:
frequency: $int('3' + '{type}'[-2:] + '000000')TACAN data is nested inside the group's route, not on the unit itself. We use for-each to iterate groups, where to filter for carrier groups, and select to reach the beacon parameters:
# config/presets.yaml
SetTACAN:
modify:
file: mission
for-each: coalition/blue/country/*/ship/group/*
where: units/$'{type}' in ['CVN_71','CVN_72','CVN_73','CVN_74','CVN_75']
select: route/points/*/task/params/tasks/$'{id}' == 'WrappedAction'/params/action/$'{id}' == 'ActivateBeacon'/params
replace:
modeChannel: X
channel: $'{reference[units][0][type]}'[-2:]
frequency:
$'{reference[units][0][type]}'[-2:] == '72': 1158000000
$'{reference[units][0][type]}'[-2:] == '73': 1160000000Here {reference} points to the group (from for-each), so {reference[units][0][type]} gets the first unit's type name. The [-2:] slice extracts the carrier number (e.g. "72" from "CVN_72").
Set the first radio channel of all blue F-14Bs. Uses insert to add the Radio key if it doesn't exist:
# config/presets.yaml
ChangeRadios:
modify:
file: mission
for-each: coalition/blue/country/*/plane/group/*/units/$'{type}' in ['F-14B']
select: Radio/[1]/channels
replace:
1: 243
insert:
Radio:
- channels:
- 243# config/presets.yaml
DeleteAllHornets:
modify:
file: mission
for-each: coalition/[blue,red]/country/*/plane/group/*/units
delete: $'{type}' == 'FA-18C_hornet'# config/presets.yaml
EnableDynamicCargo:
modify:
file: warehouses
debug: true
for-each: airports/*/$'{coalition}' == 'BLUE'
replace:
dynamicCargo: trueThe path coalition/[blue,red]/country/*/ship/group/*/units/$'{type}' in ['CVN_71'..'CVN_75'] walks the tree:
|_ coalition
|_ blue
|_ country
|_ ... all countries ...
|_ ship
|_ group
|_ ... all groups ...
|_ units
|_ elements where ["type"] is one of ['CVN_71'..'CVN_75']
|_ red
|_ ... same structure ...