Physically accurate environment simulation for Arma 3. The mod is standalone and requires only CBA A3. It models atmosphere, thermal and optical effects, physiology, mobility, environmental conditions, maritime state, radio propagation, wildlife, and map symbology. Each model uses published physics and public standards.
Author: lErrorl404l. Licence: GPL-2.0-or-later with a PBO-distribution exception.
- Arma 3
- CBA A3 (latest version)
The AEE addons load as one set: aee_core calls into the module addons
(atmosphere, thermal, environmental, and the rest), so do not load aee_core
on its own. The set requires only CBA A3. ACE3 is optional.
- Subscribe to the mod on Steam Workshop.
- Launch Arma 3. AEE loads automatically.
- Download the latest release from Releases.
- Extract the zip into your Arma 3
@AEEfolder (or any name you prefer). - Launch with
-mod=@CBA_A3;@AEEin your launch parameters, or enable both in the Arma 3 launcher.
Place the AEE Environment Config module in the editor to customise settings.
- Elevation lapse rates and diurnal temperature cycles.
- Terrain microclimate, including urban heat islands and water influence.
- True air density from temperature, pressure, and humidity.
- Koppen biome classification with seasonal variation.
- Atmospheric events: lightning (ice-phase gated), sandstorms, dust devils, microbursts (NWS tiers), ICAO eddy-dissipation-rate turbulence, airframe icing.
- Thermal crossover, mirage, and solar glare.
- Ice-crystal halos and parhelia, and the green flash.
- Cn2-based atmospheric seeing and physical smoke dispersal.
- Rain visibility (Atlas extinction), dew and frost on optics, snow blindness.
- Wet-bulb globe temperature (ISO 7243 heat-stress categories), NWS heat index, wind chill (JAG/TTI).
- Altitude acclimatisation with time-of-useful-consciousness hypoxia.
- ISO 17166 UV index with ozone absorption, hypothermia and dehydration risk.
- SAE J1349 engine derating by air density, slip-curve traction, mud accretion.
- Nash-cascade river water level, NRMM cone-index route degradation.
- Momentum-theory helicopter lift.
- CBRN persistence from the published hydrolysis activation energy where one exists, and a labelled latent-heat slope otherwise. Rothermel fire spread, avalanche risk.
- Stefan freeze/thaw, flash-flood prediction, surface wetness, scent dispersion.
- Harmonic tidal prediction (M2/S2/K1/O1 constituents).
- WMO Beaufort sea state, Pierson-Moskowitz wave height, magnetic compass deviation.
- Friis free-space path loss with atmospheric ducting.
- ITU-R P.531 ionospheric absorption for VHF/UHF/HF.
- Biome, time and weather aware ambient soundscape from vanilla media only.
- Client-local animals by biome, terrain and water, with hunger and thirst.
- Reactive spooks and a decaying disturbance field from movement and gunfire.
- NATO APP-6(C) unit and equipment markers, drawn from the standard's taxonomy.
- ONE option-driven AEE symbol entry with an in-game selector: the marker picker shows one
AEE Symbolentry, and a CBA keybind opens a selector that changes the active affiliation, battle dimension, function, echelon and palette live. The concrete variants are hidden from the picker but stay script-resolvable. - Terrain and map-feature symbols from the topographic standard (STANAG 3675 and the DGIWG Symbol Register, with FM 21-31 and the USGS sheet as the public-domain drawings): hills, rock, woodland, vineyards, churches, lighthouses, radio masts and the rest.
- The map colour palette follows the same standard: relief brown, water blue, vegetation green, roads red and white.
- One load-time config re-declare reaches the main map, the briefing, the GPS, the minimap, the airborne minimap, Eden and Zeus. Eden and Zeus also carry the real NATO side symbols.
- The layer is a re-texture of the engine's own location and object classes, so the engine draws it and no overlay is stacked on the marker layer. The engine owns the contour, road, rail and satellite geometry.
- A unit symbol keeps its real-world ground size at every map zoom. A pure kernel scales the drawn pixel size with the map scale.
- A destroyed unit stays on the map as a last-known contact at its death position, instead of vanishing.
- An optional blue-force-tracker gate draws the unit symbols only while the player carries a GPS or a tracker device. The gate ships off, so the default does not change.
- The map zooms out twice as far as vanilla. A scripted legend draws the palette and the terrain symbol groups from the same table the map config uses.
Five compat addons load only when their host mod is present. Install the ones you need:
| Addon | Host Mod | Purpose |
|---|---|---|
compat_ace3 |
ACE3 | Maps AEE state onto ACE medical and weather |
compat_acre2 |
ACRE2 | Custom radio signal-strength callback |
compat_tfar |
TFAR | Scales transmission range |
compat_kat |
KAT | Drives KAT circulation from AEE dehydration |
compat_acm |
ACM | Drives CBRN contamination from AEE persistence |
AEE writes engine wind, fog, overcast and rain from its own model, and reads engine overcast and rain as inputs. AEE owns the engine weather and writes overcast on the server.
Core atmospheric state is deterministic. It is a function of position, mission time, and engine weather. Every machine computes identical values. No publicVariable is needed.
Event FX (lightning, dust, sound) vary cosmetically per machine.
The environment model is a physical layer over the engine, not a replacement for it. Five limits follow from that.
Projectile trajectory. The engine reads CfgAmmo airFriction and CfgAmmo coefGravity when it creates a projectile. No script command changes either value after firing. Therefore air density and wind do not change the engine's projectile path. AEE corrects the muzzle velocity from propellant temperature, computes a real drag kernel at the muzzle, and hands per-projectile drag to ACE3 advanced ballistics when that mod is present. A bullet that flies under air density alone needs a solver that owns the projectile, which AEE does not ship.
Engine power. A ground vehicle's power band is PhysX configuration. The engine exposes no command to change it at run time, so a static configuration value cannot carry an environment-dependent derate.
Ground and sky. The engine has no sub-surface terrain model and no radiative sky interface. AEE emulates both from surface classification and published sky models, and labels them as emulation.
Thermal imaging. The engine renders thermal from a per-object simulated temperature model. AEE drives the render heat keys and the AI infrared keys as load-time config, and generates the per-optic thermal arrays. Terrain, vegetation and rock heat are engine-baked, so only the existing second sun reaches them. The MWIR clear-air transmission has no sourced closed form, so AEE returns a clear-air transmission of 1 and states the ceiling. A headless probe cannot read pixels, so the rendered look is an operator-only observation.
Configuration overrides. The aee_mobility addon overrides engine configuration at load. A configuration value cannot change at run time, so the PBO is the on-off switch: remove it to disable every override.
AEE develops in three phases. The project board tracks the 36 issues across them.
- Phase 1 Baseline: core physics models, unit tests, dedicated-server integration test.
- Phase 2 Expansion: more models, more compat addons, wider test coverage.
- Phase 3 Frontier: advanced phenomena, edge-case coverage, performance optimisation.
44 addons under addons/.
Core addons (39): actions, ai, altitude, ambience, armour, atmos, ballistics, blast, cartography, clothing, core, diagnostics, dive, eye, flight, hud, hydrology, lib, lighting, ltm, magnetism, maritime, material, mobility, nightvision, optics, particles, persistence, physiology, radio, strain, symbology, thermal, thermal_display, vehicles, vision, weather, weatherfx, wildlife.
Compat addons (5): compat_ace3, compat_acm, compat_acre2, compat_kat, compat_tfar.
Build PBO names are aee_<component>. For example, the core addon builds to aee_core.pbo.
Functions use the name aee_<component>_fnc_<name>. For example, aee_core_fnc_init.
Shared simulation state lives in aee_core_* mission variables. Any mod or mission script can read them.
Each function file is functions/fnc_<name>.sqf. The files are registered in XEH_PREP.hpp and compiled through CBA XEH (Extended_PreInit_EventHandlers). This is the standard ACE3/CBA pattern.
AEE registers its settings at mission start with CBA_fnc_addSetting. Each addon registers its own settings from addons/<component>/initSettings.inc.sqf, included from that addon's XEH_preInit.sqf. The framework settings appear under the AEE category. Each module registers under an AEE category, for example AEE Optics and AEE Thermal.
Localisation uses a per-addon stringtable.xml. Keys use the form STR_AEE_<Component>_<Key>.
The dev console, the visual workbench and its four keybinds are documented in the operator guide.
hemtt check -p -e # Pedantic lint (warnings are errors)
hemtt build # Build PBOs
hemtt release # Sign PBOs, make bikey and zip
hemtt launch <profile> # Launch game with mod loadedProfiles are default, ace3, acre2, tfar, kat, acm, and full. Workshop IDs are configured in .hemtt/project.toml.
python3 tools/run_tests.py # Unit tests and physics validationThe unit tests read the real SQF kernels through the sqf_lite harness where a kernel is pure, and mirror the rest in Python. They cover reference values, edge cases, and clamps. A pre-commit hook runs them on every commit. tools/tests/test_suite_registration.py fails when a suite on disk is not in the runner.
The headless dedicated-server test in Docker runs the mod against real Arma 3 and verifies ten phases (see docs/wiki/chapters/testing.qmd).
tools/docker_test.sh --load is the fast pre-flight gate. It boots the server on the load-gate mission and fails when a matcher function does not compile, the log holds an error line, or a matcher rung answers weaker than its case expects.
tools/report_match_coverage.py measures how much of the installed content each matcher can name. It splits the classes by the PBO that declares them, so the base-game and modded numbers stay separate. It writes the gap lists and the baseline under data/coverage/, and tools/tests/test_match_coverage.py locks the measurement.
The engine's config mass and getMass are engine tuning values, never a real-world mass, so a mass override must be a calibrated scale of a held real mass. The probe aee_p72_mass_probe.sqf measures the engine's own mass for every class in data/vehicle/class_bindings.json. It measures only and applies nothing. Run the census, then build the calibration:
tools/docker_test.sh # writes tests/docker/run.log
python3 tools/validation/build_mass_calibration.py # reads that logThe tool writes data/physics/mass_calibration.json. It fails closed when the
log is absent or misses a bound class. The tool writes approved: false: the
operator approves the calibration, and the approval records the approver and
the date. The validator tools/validation/validate_mass_calibration.py gates
the artefact.
When the calibration is approved, tools/validation/gen_physics_config.py
derives one mass per bound class as real_analogue_mass_kg / fit.scale and
emits it into the same CfgVehicles block as maxSpeed. The generator refuses
an unapproved calibration and a class with no held mass or no resolved parent.
aee_clothing_fnc_getItemMass states one grade with every answer, in this
order: sourced (the classname names a researched family), family (a
published family mass the class does not name), engine_mass (the engine
config mass, converted to kilograms and never presented as a published
figure), type (the slot names the kind and the mass is 0), and zero
(nothing is known and the mass is 0). ADR-046 records the decision.
The repository publishes the extracted values as JSON with their citations
under data/*/sources/. It never publishes the captured third-party page,
drawing or manual, because those belong to their publisher. The retrieved
bytes stay in the local source cache outside the working tree, and
tools/tests/test_source_capture_policy.py fails on any other tracked file
under a sources directory.
GitHub Actions runs on every push:
hemtt check -p -ehemtt build- Unit tests via
tools/run_tests.py - Headless Docker server test (on demand)
- SBOM on release tags
- Release-drafter
The project uses trunk-based development. All commits must be signed.
A pre-commit hook runs:
hemtt check -p -este-lintaudit
Make logical commits. Link each commit to its change rationale. Report vulnerabilities through SECURITY.md.
- GitHub Repository
- Project Board
- Documentation
- Engine field guide - the per-feature engine reference for a modder.
- Issues