- ra3.client::display: shared interactive loops; SDL and Vulkan backends implement only the primitives (init/present/poll_event/window_size/ key_down/present_terrain). ra3.display picks the backend. - Menu: maps by localized name (gamestrings.csf), red/gold theme, hover highlight, mouse + keyboard, wheel scroll, fullscreen and FPS/vsync options, loading progress bar. - Terrain: continuous tile sampling via a texture array (REPEAT, uv = cell/(2*cellSize)) removes per-cell grid seams; SAGE blend ramp for material transitions; FPS label + top-right minimap overlays. - Skip the skirmish sim for map views; reuse the Vulkan texture; no idle terrain redraw.
OpenRA3
A from-scratch, portable re-implementation of Command & Conquer: Red Alert 3
in C++26 using C++ modules and import std;, built with Clang for
both Linux and Windows.
Red Alert 3 runs SAGE 2.0. EA never released that engine's C++ source, but it
did open-source the closely related SAGE 1.0 engine as
electronicarts/CnC_Generals_Zero_Hour
(GPLv3). OpenRA3 uses that tree as the architectural reference and reconstructs
the RA3-specific behaviour by decompiling the retail ra3_1.12.game binary with
Ghidra.
OpenRA3 ships no game assets or binaries. You must own Red Alert 3. The engine reads your own local installation at runtime; nothing from it is ever copied into this repository. Command & Conquer and Red Alert are trademarks of Electronic Arts; this project is unaffiliated with and unsupported by EA.
Status
OpenRA3 is at v0.3.0. The engine compiles and runs headless, and a minimal
skirmish is playable: it reads a real multiplayer map out of your install,
recovers the player start waypoints, and simulates two sides building a base,
extracting ore and fighting until one side is wiped out. The balance is the
retail Red Alert 3 balance, pinned in ra3.data from EA's open RA3 XML:
damage types, ArmorTemplate percentages, weapon target masks, build costs and
the ore economy. Presentation is a Vulkan backend (ra3.vulkan) with a null
fallback; the software renderer still produces headless images. Terrain tiles
cross-fade the way the retail Terrain.fx does (a per-cell blend ramp plus a
gutter-padded atlas), so material boundaries are smooth instead of a grid of
hard lines. An in-window menu lists the maps by their localized name and
exposes every render/skirmish option for tweaking before launch. The whole tree
builds for Linux (clang + libc++) and cross-compiles to Windows
(openra3.exe + SDL3.dll) with llvm-mingw — both using C++26 modules and
import std;.
$ openra3 skirmish --game-dir "/game" --map map_mp_2_feasel4
OpenRA3 skirmish map=map_mp_2_feasel4 source=archive:map_mp_2_feasel4 seed=1
start positions:
P0 (1338, 1940)
P1 (1291, 1404)
result: decided winner=0 frames=9293 (309.8 s)
P0 Commander Allied money= 700 units=15 kills=21 losses=15
P1 AI Soviet money= 376 units= 0 kills=15 losses=21
Offline only
OpenRA3 implements offline skirmish and, eventually, LAN/single-player. There is deliberately no online mode: no EA account, no online service, no matchmaking, no GameSpy/Steam integration, and no networking in the simulation. The match loop is deterministic and self-contained so it can be replayed and tested without any external service.
Assets
The runtime reads a portable assets/ folder next to the executable — there
is no --game-dir at run time. The build extracts it from your install:
openra3_assets(build target) runscmake/extract_assets.cmake, which- extracts every map (double-unwrapped
CkMp) and terrain TGA intoassets/via the engine's ownopenra3 extract; - with
-DOPENRA3_EXTRACT_ALL=ON(default) also dumps every.bigentry plus models, textures (.png), sound effects/voice and movie audio via thera3-headless/ra3toolsscripts.
- extracts every map (double-unwrapped
- CMake cache vars:
RA3_GAME_DIR(defaultC:/Red Alert 3),RA3TOOLS_DIR(thera3toolsscripts),PYTHON_EXECUTABLE. - The step is skipped when the exe cannot run on the build host (a Windows
cross-build in a Linux container); the exe then extracts maps/terrain on first
launch, and the full dump can be run explicitly:
cmake --build <build> --target openra3_assetson a native host, oropenra3 extract --game-dir DIR --out DIR.
No game data is committed or distributed. reference/, assets/ and the
ra3tools checkout are git-ignored. Without an install the engine falls back to a
built-in test map so the project still builds and runs in CI.
Layout
| Path | Purpose |
|---|---|
src/core/ra3.core.cppm |
fundamental types, math, strings, random, message stream |
src/logic/ra3.logic.cppm |
objects, players, teams, spatial partition, game loop |
src/client/ra3.client.cppm |
display abstraction + shared interactive loops + client facade |
src/display/ra3.display.cppm |
picks the backend (Vulkan, then SDL) for the app |
src/game/ra3.game.cppm |
RA3 sides, player templates, skirmish defaults |
src/data/ra3.data.cppm |
retail RA3 balance: damage types, armour, weapons, units, economy |
src/fs/ra3.fs.cppm |
BIG4 archives, RefPack codec, local install locator |
src/map/ra3.map.cppm |
map catalog, EAR/RefPack unwrap, start waypoints, gamestrings.csf display names |
src/skirmish/ra3.skirmish.cppm |
base building, economy, AI, combat, win condition |
src/terrain/ra3.terrain.cppm |
CkMp terrain: HeightMapData, BlendTileData (tiles + blends), Terrain.big tiles |
src/render/ra3.render.cppm |
ARGB framebuffer, TGA decode, BMP encode, map compositing, bitmap-font text |
src/ui/ra3.ui.*.cppm |
SDL3 window viewer and menu (null backend when SDL3 is absent) |
src/vulkan/ra3.vulkan.*.cppm |
Vulkan presentation backend and menu (null fallback without a loader) |
src/ra3.cppm |
umbrella module re-exporting the SDK |
apps/openra3/main.cpp |
menu / maps / skirmish / render CLI |
tests/ra3_tests.cpp |
smoke tests (run via ctest) |
tools/ |
reference fetch + Ghidra-driven reconstruction helpers |
docs/ |
architecture, reverse-engineering notes, roadmap |
Dockerfile |
Linux build image (dev toolchain + deploy runtime) |
Dockerfile.win |
isolated Windows cross-build image (llvm-mingw + SDL3 MinGW) |
cmake/toolchains/ |
llvm-mingw-x86_64.cmake cross toolchain |
scripts/ |
build-linux.sh / build-windows.sh one-shot builders |
.gitlab-ci.yml |
build both targets → test → package pipeline |
Why Clang + import std;
The engine never #includes the standard library: every module does
import std;. CMake's support for that (CXX_MODULE_STD) works today with
Clang + libc++. Linux uses the distro clang (LLVM 21); Windows cross-compiles
with llvm-mingw (clang 23 + libc++ +
the libc++ std module). The two toolchains live in separate images so
their compilers and standard libraries never interfere.
Build
The host needs no toolchain: each target builds inside its own image.
# Linux -> build/linux/bin/openra3
scripts/build-linux.sh
# Windows -> build/windows/bin/openra3.exe (+ SDL3.dll)
scripts/build-windows.sh
Or drive Docker directly:
docker build --target dev -t openra3-linux:local .
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local \
cmake -S . -B build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local cmake --build build/linux -j
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local ctest --test-dir build/linux --output-on-failure
docker build -f Dockerfile.win --target dev -t openra3-windows:local .
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local \
cmake -S . -B build/windows -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake \
-DCMAKE_BUILD_TYPE=Release
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local cmake --build build/windows -j
Behind a slow or blocked mirror, pass --build-arg APT_MIRROR=<url>.
The two images are deliberately separate so the Linux (clang + libc++) and
Windows (llvm-mingw + MinGW SDL3) toolchains never interfere. The Windows demo
is build/windows/bin/openra3.exe next to SDL3.dll:
# show a real map in a Vulkan window
openra3.exe render --game-dir "C:\Red Alert 3" --vulkan
Vulkan is provided by vendored volk + headers (third_party/), resolved at
runtime, so neither image needs a Vulkan SDK.
Running a skirmish
# list the maps in your install
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
/work/build/linux/bin/openra3 maps --game-dir /game
# play a headless skirmish on a real map
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
/work/build/linux/bin/openra3 skirmish --game-dir /game --map map_mp_2_feasel4 --seed 7
The Windows build is a native openra3.exe — copy it next to SDL3.dll and run
it from cmd/PowerShell, e.g. openra3.exe skirmish --game-dir "C:\Red Alert 3".
--frames N caps the simulation length (default 15 minutes of game time at
30 Hz). The result is deterministic for a given map and seed.
Menu
Run openra3 with no arguments (or openra3 menu) to open a window listing the
maps by their localized display name (read from the install's
gamestrings.csf, e.g. map_mp_2_feasel4 → "Battlebase Beta") with their id
below. The Options column exposes every render/skirmish parameter — mode
(3D terrain / top-down / skirmish), window size, camera pitch/yaw/height, FOV,
zoom, terrain scale, terrain pitch, world size, seed, frame cap, the overview
thumbnail toggle and a BMP output path. Up/Down selects, Left/Right
changes a value (or moves the text caret on the BMP field), Tab switches
between the map list and the options, Enter starts and Esc quits. Starting a
3D or top-down view opens the viewer; closing it returns to the menu. When no
window backend is available the same flow falls back to a console picker, and
openra3 menu-preview renders one menu frame to a BMP for inspection.
Rendering the map
render draws the real terrain: it parses the map's HeightMapData
(elevation grid) and BlendTileData (per-cell tile index plus the per-cell
Blends/ThreeWayBlends and their BlendDescriptions), loads the tile
textures from Data\Terrain.big (RefPack + TGA), and rasterises the map with an
elevation shade. Each cell cross-fades into its blend neighbour with the same
linear ramp the retail Terrain.fx uses, so material transitions are smooth; on
the GPU path the tile atlas is padded with a replicated gutter so filtering
never bleeds between tiles. Match state is overlaid (start markers in yellow,
player 0 in blue, player 1 in red). --thumbnail uses the old <map>_art.tga
overview instead.
Offscreen image (works anywhere, no display needed):
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -v "$PWD/out:/out" openra3-linux:local \
/work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4 --out /out/map.bmp
On Windows the same command runs natively: openra3.exe render --game-dir "C:\Red Alert 3" --map map_mp_2_feasel4 opens an SDL3 window.
Interactive window (Vulkan with --vulkan, otherwise SDL3). The camera
follows the retail tactical view: it opens centred on the first player's start,
the wheel zooms, pushing the cursor against a screen edge scrolls, dragging with
the left button pans, and Esc quits. --zoom Z sets the initial zoom (1 fits
the whole map). In a container you need an X server on the host — on Windows run
VcXsrv and launch it with "Disable
access control", then:
docker run --rm -v "/path/to/Red Alert 3:/game:ro" -e DISPLAY=host.docker.internal:0.0 \
openra3-linux:local /work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4
If no display is available the viewer falls back to writing openra3_view.bmp.
The unit overlay uses an approximate world scale (--world-size, default
5120); exact calibration from the map's heightmap is on the roadmap.
Reverse engineering
The reconstruction is driven by Ghidra against the retail binary. See
docs/REVERSE_ENGINEERING.md for the workflow and
the recovered symbol map, and tools/ for the helpers.
License
Project code: GPLv3, matching the CnC_Generals_Zero_Hour reference (see
LICENSE).