- 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.
7.0 KiB
Architecture
OpenRA3 is organised as a stack of C++26 modules. Each layer may import the ones
below it, never the ones above. Every module uses import std; for the standard
library; the engine is built with Clang + libc++ on Linux and cross-compiled
with llvm-mingw for Windows.
┌───────────────────────────────────────┐
applications │ openra3 (apps/openra3) │
└───────────────────┬───────────────────┘
│ import ra3
┌───────────────────▼───────────────────┐
umbrella │ ra3 (re-exports everything) │
└───────────────────┬───────────────────┘
┌──────────────┬───────────────┼───────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
ra3.display ra3.render ra3.skirmish ra3.client ra3.game
backend pick framebuffer match rules display+loops RA3 sides
│ │ │ │ │
┌──┴───┐ │ └───────┬───────┘ │
▼ ▼ ▼ ▼ │
ra3.ui ra3.vulkan ra3.terrain ra3.logic │
SDL Vulkan heightmap/blends simulation │
┌──────────────────────┘
▼
ra3.map / ra3.fs
catalog / BIG4 + RefPack
Presentation layer
ra3.client::display is the single seam between the engine and the windowing
stack. A backend implements only the low-level primitives (init, present,
poll_event, window_size, key_down, shutdown) and, optionally,
present_terrain; the interactive loops (menu, image viewer, camera viewer,
GPU terrain) and the native-to-ui_event mapping live once in the base class,
so the SDL and Vulkan paths cannot drift. SDL3 remains the platform layer (it
owns the window, input and the low-cost blit backend); Vulkan is the accelerated
backend. ra3.display selects the first backend that starts, and the app never
references a backend by name.
The Vulkan backend currently hand-rolls its pipelines. As materials, skybox and
HUD arrive, a thin RHI + render-graph belongs between ra3.client and the
Vulkan/SDL backends; present_terrain is the placeholder for that step.
Module responsibilities
ra3.core
The vocabulary every other module shares, mirroring SAGE's GameEngine/Common:
real/int32/uint32, coord3d/coord2d/rgb_color, ascii_string +
make_name_key, the deterministic random stream, and the message_stream
command bus (node layout derived from retail MessageStream::appendMessage,
0x0060c4a0).
ra3.logic
The deterministic simulation, mirroring SAGE's GameLogic: thing → object
with pluggable update_modules, player/player_list, the partition_manager
spatial grid, and the 30 Hz game_logic driver (prepare_new_game /
start_new_game / update).
ra3.client
The presentation boundary: the abstract display (low-level primitives plus the
shared interactive loops and ui_event mapping), a headless_display for
CPU-only runs, and game_client, the facade that owns the simulation and drives
the frame loop.
ra3.display
Backend selection: tries Vulkan, then SDL, then reports failure so the caller can fall back to an offscreen image. The only module that names a concrete backend.
ra3.terrain
The real map terrain: parses CkMp's HeightMapData and BlendTileData
(tiles, Blends/ThreeWayBlends and their BlendDescriptions), loads the
Terrain.big tile textures, and rasterises the map (software render/render3d
and the GPU gpu_terrain for present_terrain).
ra3.game
Red Alert 3 data that SAGE keeps in PlayerTemplate: the three sides
(faction flags Empire=2, Allied=4, Soviet=8, Random=7, recovered from
the retail skirmish setup) and skirmish defaults.
ra3.fs
Reading the user's installation. Implements the BIG4 archive container and
EA's RefPack codec, plus find_game_dir (--game-dir / $RA3_GAME_DIR /
C:\Red Alert 3). Only the archive index is held in memory; payloads are read
on demand.
ra3.map
Map discovery and loading: scans MapsMultiplayer.big for main map entries,
unwraps the two compression layers (BIG4 RefPack → EAR\0 wrapper → RefPack →
CkMp), recovers Player_N_Start waypoint coordinates, and decodes the
localized display names from the install's gamestrings.csf. Degenerate
extractions are rejected so the caller can fall back.
ra3.skirmish
The minimal match: two players, unit classes (harvester/infantry/tank/base), passive + harvester income, a simple build AI, movement and combat on a fixed 30 Hz step, and a base-destruction win condition. Fully deterministic.
ra3.render
A dependency-free software renderer: an ARGB8888 image framebuffer with
blit/line/circle/text primitives (an embedded 8x8 bitmap font), a TGA decoder for
the game's map art, a 24-bit BMP encoder for headless output, and compose which
overlays a world grid and markers (start positions, live units) onto the map art.
ra3.ui
The SDL3 backend: sdl_display implements the ra3.client::display primitives
using an SDL_Renderer streaming texture. Cheap and dependency-light, it is the
fallback when Vulkan is unavailable. A null backend returns false so the build
still runs without SDL3.
ra3.vulkan
The accelerated backend: vulkan_display implements the same primitives and
adds present_terrain, a GPU heightfield raymarcher (mipmapped tile atlas with a
replicated gutter and the retail SAGE blend ramp). A null backend reports failure
when no Vulkan loader is present.
Design rules
- No raw owning pointers. Ownership is
std::unique_ptr; the partition manager holds non-owningthing *views only. - Portable simulation. No platform APIs in
ra3.core/ra3.logic. All platform concerns live behinddisplay. - Determinism. Anything that can diverge between runs (random, iteration order) is explicit and seedable.
- RE-traceable. Where a structure or constant comes from the retail binary, the address is cited in the comment.
- No bundled assets, no online. Game data is read from the user's install and never committed; there is no networking or online service.