Architecture overview¶
Aegis2D is split into small libraries with one job each. A game links only what it needs: the server never loads rendering, audio or windowing code, and game rules never live inside the engine.
Status
This page describes the target architecture. The module boundaries, build targets and dependencies already exist; the module contents are being implemented ticket by ticket (see the changelog).
Layers¶
flowchart TB
subgraph game["Game (project-rtype)"]
server["r-type_server<br/>authoritative simulation"]
client["r-type_client<br/>input, prediction, display"]
end
subgraph engine["Aegis2D engine"]
direction TB
render["render<br/>OpenGL renderer"]
audio["audio<br/>sounds, music"]
platform["platform<br/>window, GL context, input"]
ecs["ecs<br/>entities, components, systems"]
net["net<br/>serialization, UDP/TCP, net thread"]
core["core<br/>log, time, loop, events, config"]
end
subgraph deps["Third-party (CPM)"]
sdl["SDL3"]
gl["glad · GLM · stb_image"]
asio["Asio"]
json["nlohmann-json"]
end
server --> ecs & net & core
client --> render & audio & platform & ecs & net & core
render --> platform
render --> gl
platform --> core
platform --> sdl
audio --> core
audio --> sdl
ecs --> core
net --> core
net --> asio
core --> json
| Module | Library | Depends on | Built for |
|---|---|---|---|
| core | aegis2d::core |
nlohmann-json | server, client |
| ecs | aegis2d::ecs |
core | server, client |
| net | aegis2d::net |
core, Asio | server, client |
| platform | aegis2d::platform |
core, SDL3 | client |
| render | aegis2d::render |
platform, glad, GLM, stb_image, SDL3 | client |
| audio | aegis2d::audio |
core, SDL3 | client |
Rules that keep the layers apart:
- Dependencies point down.
coredepends on no other module; nothing in the engine depends on the game. - Third-party libraries stay private. Modules link SDL3, glad, Asio and the others privately, so their headers never leak into game code: the game sees engine types only.
- Server builds stop at
net. WithAEGIS2D_BUILD_CLIENT=OFF,platform,renderandaudioare not even configured, and SDL3 and glad are not downloaded.
How the subsystems talk¶
flowchart LR
subgraph clientproc["Client process"]
input["platform<br/>input actions"] --> cgame["game systems"]
cgame --> cecs[("ECS world")]
cecs --> renderer["render"]
cgame -. events .-> sound["audio"]
cnet["net thread"] <-->|SPSC queues| cgame
end
subgraph serverproc["Server process"]
snet["net thread"] <-->|SPSC queues| sim["simulation<br/>60 Hz fixed step"]
sim --> secs[("ECS world")]
end
cnet <-->|"UDP: inputs, snapshots, events<br/>TCP: login"| snet
- ECS as the shared world model. Gameplay, networking and rendering read and write
components (
Transform,Velocity,Sprite,NetworkId...). Rendering only needs a position and a sprite; it never sees health or damage. - Event bus for decoupling. Subsystems publish typed events (
EntityDestroyed,PlayerFired...) on thecoreevent bus; audio plays sounds by listening to them, so gameplay code never calls audio. - Network thread and queues. Sockets run on their own thread (Asio). Messages cross to the game thread through lock-free single-producer/single-consumer queues, so the simulation never blocks on the network.
- Fixed time step.
coredrives the simulation at a fixed 60 Hz with an accumulator, independent of the frame rate; rendering interpolates between steps. Nothing depends on CPU speed. - Authoritative server. Clients send inputs; the server simulates, then broadcasts snapshots and reliable events. Clients display the server's world, interpolating remote entities between snapshots.
Build structure¶
- Every module is declared with
aegis2d_module(<name> <sources>)(cmake/Modules.cmake): a shared library with hidden symbol visibility, a generatedaegis2d/<name>/export.hpp, public headers inpublic/aegis2d/<name>/and theaegis2d::<name>alias. aegis2d::warnings(strict warnings, optionally as errors) andaegis2d::sanitizers(ASan and UBSan whenAEGIS2D_SANITIZE=ON) are attached to every module.aegis2d::enginelinks every built module, for games that want all of them.- Dependencies are declared once in
cmake/Dependencies.cmake, pinned, and fetched by CPM.
Repositories and delivery¶
flowchart LR
engine["Aegis2D-Dev/aegis2d<br/>engine"] -->|FetchContent| game["Aegis2D-Dev/project-rtype<br/>game"]
engine -->|"push to master"| mirror["Epitech mirror workflow"]
game -->|"push to master<br/>(dispatch)"| mirror
mirror -->|"build + validated sources"| epitech["Epitech delivery repository"]
The game fetches the engine with CMake FetchContent. On delivery, the
mirror workflow assembles both repositories, builds them, and
publishes the validated sources to the Epitech repository.