Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
715a11e
Add AGENTS.md with guidance for coding agents
rp- Oct 25, 2026
8621d45
Test and declare support only for maintained Python versions (3.11-3.15)
rp- Oct 25, 2026
9810281
Port packaging from setup.py to pyproject.toml and build with uv
rp- Oct 25, 2026
632ecb0
Fix dcs_random and dcs_oil_convoy scripts and city graph loading
rp- Oct 25, 2026
667443f
Fix livery scanning on non-Windows platforms and run CI on Linux too
rp- Oct 25, 2026
71a4bec
Fix Lua number and string handling in parser and serializer
rp- Oct 25, 2026
4acb372
Load all known tasks and keep unknown ones instead of failing
rp- Oct 25, 2026
4bafbc8
Fix lost goals, missing neutral countries and leftover breakpoint
rp- Oct 25, 2026
a7dd712
Fix trigger actions that produced broken or unloadable missions
rp- Oct 25, 2026
e421d02
Fix halo preset handling and hang in random_distant_points
rp- Oct 25, 2026
4f6e99f
Keep unit, group and task data on load/save
rp- Oct 25, 2026
62e89b2
Keep options, forced options, neutral task and nav points on load/save
rp- Oct 25, 2026
12d2dd3
Keep trigger data on load/save
rp- Oct 25, 2026
00fa9ea
Keep weather and drawing data on load/save
rp- Oct 25, 2026
1cc950a
Fix wrong formation values and vee formation crash
rp- Oct 25, 2026
7a39bb8
Give each airport its own unit_zones list
rp- Oct 25, 2026
59178ff
Don't hand out EPLRS ids that are already in use
rp- Oct 25, 2026
4bb6656
Keep resource files with the same file name apart
rp- Oct 25, 2026
3cd3ed0
Read non-ASCII resource file names of missions correctly
rp- Oct 25, 2026
8530398
Don't keep state of a previous mission in load_file()
rp- Oct 25, 2026
91bbeb3
Don't fail to load missions using an airport pydcs doesn't know
rp- Oct 25, 2026
de0613d
Fix broken vehicle templates
rp- Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ on:
jobs:
build:

runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.10", "3.11"]
os: [windows-latest, ubuntu-latest]
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install deps
Expand All @@ -41,3 +42,23 @@ jobs:
run: |
pip install pytest
pytest

package:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v10.3.2
- name: Build sdist and wheel
run: |
uv build
- name: Check package metadata
run: |
uvx twine check --strict dist/*
- name: Smoke test installed wheel
run: |
uv venv /tmp/wheel-test
uv pip install --python /tmp/wheel-test dist/*.whl
cd /tmp && /tmp/wheel-test/bin/python -c "import dcs; assert dcs.terrain.Caucasus().city_graph.nodes"
101 changes: 101 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# AGENTS.md

Guidance for AI coding agents (and humans) working in this repository.

## Project

**pydcs** (`dcs` package): a Python framework to create, load, edit and save mission files (`.miz`) for
the flight simulator *DCS World*. A `.miz` is a zip archive with Lua tables (`mission`, `options`,
`warehouses`, `l10n/DEFAULT/dictionary`, `mapResource`, …) plus resources. License: LGPLv3.
Package metadata and version live in `pyproject.toml` (setuptools backend, no `setup.py`).

## Setup & commands

Only runtime dependency is `pyproj` (`requirements.txt`). Use a virtualenv (`venv/` is gitignored):

```sh
python3 -m venv venv
./venv/bin/pip install -r requirements.txt mypy flake8 pytest
```

CI (`.github/workflows/pythonpackage.yml`, runs on **Windows and Linux**, Python 3.11–3.15) does exactly this —
run all three before considering a change done:

```sh
mypy dcs # type check (must be clean)
flake8 . # lint, config in setup.cfg
flake8 . --isolated --count --select=E9,F63,F7,F82 --show-source --statistics # syntax check of excluded files
pytest # tests/ and dcs/lua/test_parse.py
```

- Single test: `pytest tests/test_mission.py::BasicTests::test_basic_mission`
- Tests use paths relative to the repo root (e.g. `tests/missions/*.miz` as input, `missions/` as output
dir — gitignored), so run pytest from the repo root.
- The suite must pass on Windows and Linux. Use `os.path` / `pathlib` instead of hard-coded `\\`
separators. Tests for genuinely Windows-only behavior (e.g. the `ansi` codec, Steam library parsing)
are gated with `@pytest.mark.skipif(sys.platform != "win32", ...)`.
- Packaging: `make build` (`uv build`, sdist + wheel into `dist/`; `python -m build` works too),
`make upload` (twine, credentials from `~/.pypirc`). Packages are auto-discovered (`dcs*`); non-Python
files to ship must be listed under `[tool.setuptools.package-data]` in `pyproject.toml`. A separate CI
job (`package`, Linux) runs `uv build`, `twine check --strict` and a smoke test of the installed wheel.

## Code style

- flake8 with max line length **127**, `max-complexity` 10 (C901 ignored). Generated data files, `tests/`,
`tools/` and `doc/` are excluded from flake8 (see `setup.cfg`).
- The package ships `py.typed`; new/changed code in `dcs/` should be type annotated and pass `mypy dcs`.
- Commit messages: short imperative summary line (e.g. "Fix TriggerZoneQuadPoint argument forwarding to
parent __init__"); data refreshes are titled "DCS <version> export".

## Architecture

- `dcs/mission.py` — `Mission` is the entry point: `Mission(terrain)`, `load_file()`, `save()`, and
helpers to add groups/flights (`flight_group_inflight`, `awacs_flight`, `refuel_flight`, …).
Holds coalitions, countries, triggers, weather, translations, drawings, warehouses, etc.
- **Load/save pattern**: most model classes implement `load_from_dict(d)` / `create_from_dict(...)` to
read the parsed Lua table and `dict()` to produce it again. Round-tripping a mission without changes
must not lose data — when adding fields, implement both directions and add a load/save test.
- `dcs/lua/` — own Lua table parser (`parse.loads`) and serializer (`serialize.dumps`) used for all
`.miz` content. Serialization key order matters (ints numerically, then strings alphabetically).
- `dcs/translation.py` — `Translation`/`String`/`ResourceKey`: localized strings (`DictKey_*`) and
resource files embedded in the `.miz`.
- `dcs/country.py`, `dcs/coalition.py`, `dcs/unitgroup.py`, `dcs/unit.py`, `dcs/flyingunit.py`,
`dcs/point.py` — the object model of countries → groups → units / waypoints.
- `dcs/task.py`, `dcs/action.py`, `dcs/condition.py`, `dcs/triggers.py`, `dcs/goals.py` — waypoint
tasks/commands/options and the trigger system (rules, conditions, actions, trigger zones).
- `dcs/unittype.py` — base classes (`FlyingType`, `VehicleType`, `ShipType`, `StaticType`) for unit
type definitions; `dcs/payloads.py` and `dcs/liveries/` read payloads and liveries from the user's
DCS installation (`dcs/installation.py`, `dcs/winreg.py` — degrade gracefully off Windows).
- `dcs/terrain/` — `terrain.py` defines `Terrain`, `Airport`, `Runway`, `ParkingSlot`, `Warehouse`,
`Graph`; one subpackage per map (`caucasus`, `nevada`, `normandy`, `persiangulf`, `thechannel`,
`syria`, `marianaislands`, `falklands`, `sinai`, `kola`, `germany`), each with `<map>.py` (the
`Terrain` subclass incl. bounds, projection, `utc_offset`), `airports.py` (generated airport data)
and `projection.py`. `dcs/mapping.py` handles points, vectors and lat/lon conversion via `pyproj`.
- `dcs/drawing/` — mission editor drawings (layers, lines, polygons, icons, text boxes).
- `dcs/scripts/` — example random-mission generators installed as console scripts (`dcs_random`,
`dcs_dogfight_wwii`, `dcs_oil_convoy`).

## Generated files — don't hand-edit

These are produced from a running DCS installation and get overwritten on the next export:

- `dcs/planes.py`, `dcs/helicopters.py`, `dcs/vehicles.py`, `dcs/ships.py`, `dcs/statics.py`,
`dcs/weapons_data.py`, `dcs/countries.py` — generated by `tools/pydcs_export.lua` (run inside the DCS
mission editor). Fix the generator instead, and keep generated diffs out of unrelated changes.
- `dcs/terrain/*/airports.py` — generated via `tools/airport_import.py` (instructions in its docstring).
- `dcs/cloud_presets.py` — from `tools/weather_import.py`; projections from
`tools/export_map_projection.py`.

These files are huge (`planes.py`, `countries.py` are ~40k lines) — search them, don't read them whole.

## Adding a new terrain

Add the subpackage (with `__init__.py`) under `dcs/terrain/` and export it in `dcs/terrain/__init__.py`;
packaging picks it up automatically. If the generated airport file needs it, add an `E126`
per-file-ignore in `setup.cfg`. Add a test in `tests/test_terrain.py`.

## Other directories

- `tests/` — pytest/unittest tests; sample missions in `tests/missions/` and `tests/*.miz`.
- `tools/` — data-export/import helpers and dev utilities (`missiondiff.py` diffs two `.miz` files).
- `doc/` — Sphinx docs (published on readthedocs).
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
.PHONY: build upload clean

build:
python3 setup.py sdist bdist_wheel
uv build

upload:
python3 -m twine upload dist/*
uvx twine upload dist/*

clean:
rm -Rf dist/

2 changes: 0 additions & 2 deletions Manifest.in

This file was deleted.

16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,9 @@ pydcs comes with 2 proof of concept scripts:

### dcs_random

This script can generate 3 different mission types

* refuel

Generates a refuel mission for A-10C or M-2000C aircrafts, search your tanker and refuel.

* CAS

Support ground troops.

* CAP

Take care of your tanker and AWACS.
This script generates a random training mission on the Nevada map: player flight(s) for the
A-10C, M-2000C, Ka-50, AV-8B or F/A-18C at Nellis, Creech or Groom Lake, with AWACS, tankers and
civil air traffic around them.

For options see the script help with `dcs_random --help`

Expand Down
Loading
Loading