This document describes the architecture of the R-Type Clone game engine.
- High-Level Architecture
- Module System
- Inter-Module Communication
- Threading Model
- ECS Architecture
- Network Architecture
- Module Reference
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Application Host โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ModulesManager โ โ
โ โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ โ
โ โ โ Window โ โ Renderer โ โ ECS โ โ Sound โ ... โ โ
โ โ โ Manager โ โ (GLEW) โ โ (Lua) โ โ Manager โ โ โ
โ โ โโโโโโฌโโโโโโ โโโโโโฌโโโโโโ โโโโโโฌโโโโโโ โโโโโโฌโโโโโโ โ โ
โ โ โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โโโโโโโโโโโดโโโโโโโโโโ โ โ
โ โ โ ZeroMQ Bus โ โ โ
โ โ โ (Pub/Sub) โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The engine uses a dynamic module architecture where each subsystem is:
- Loaded at runtime as a shared library (
.so/.dll) - Isolated in its own thread
- Communicating via message passing
- Hot-swappable without recompiling the host
All modules implement the IModule interface:
namespace rtypeEngine {
class IModule {
public:
virtual ~IModule() = default;
// Lifecycle
virtual void init() = 0;
virtual void loop() = 0;
virtual void stop() = 0;
virtual void cleanup() = 0;
virtual void release() = 0;
// Messaging
virtual void subscribe(const std::string& topic, MessageHandler handler) = 0;
virtual void sendMessage(const std::string& topic, const std::string& message) = 0;
// Threading
virtual void start() = 0;
virtual void processMessages() = 0;
};
}
Modules are loaded via a C-style factory function to avoid ABI issues:
// In each module's .cpp file
extern "C" {
rtypeEngine::IModule* createModule(
const std::string& pubEndpoint,
const std::string& subEndpoint
);
}
The ModulesManager handles:
- Opening shared libraries (
dlopen/LoadLibrary) - Retrieving the
createModulesymbol - Instantiating modules with messaging endpoints
- Managing module lifecycle
Modules communicate via a topic-based publish/subscribe system using ZeroMQ:
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Module A โ โ ZMQ Proxy โ โ Module B โ
โ โ โ โ โ โ
โ Publisher โโผโโโโโโโโโบโ XSUB XPUB โโโโโโโโโโโผโ Subscriber โ
โ Subscriberโโผโโโโโโโโโโ โโโโโโโโโโโผโบ Publisher โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
[ Topic (string) ] [ Payload (string/binary) ]
For binary messages:
[ Topic Length (4 bytes) ] [ Topic (N bytes) ] [ MsgPack Payload (M bytes) ]
LuaECS Renderer WindowManager
โ โ โ
โ RenderEntityCommand โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโบโ โ
โ โ โ
โ โ (renders frame) โ
โ โ โ
โ โ ImageRendered โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโบโ
โ โ โ
โ โ (displays)
Each module runs in its own thread:
class AModule : public IModule {
protected:
std::thread _moduleThread;
std::atomic<bool> _running{false};
public:
void start() override {
_running = true;
_moduleThread = std::thread([this]() {
while (_running) {
processMessages();
loop();
}
});
}
};
- Isolation: Slow modules don't block others
- Parallelism: Utilize multi-core CPUs
- Resilience: Module crashes are contained
- Message queues are thread-safe (ZeroMQ handles this)
- Shared state is minimized
- Critical sections use mutexes when necessary
The Entity Component System is implemented in Lua using Sol2:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LuaECSManager โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Entities โ โ Components โ โ Systems โ โ
โ โ โ โ โ โ โ โ
โ โ ID: 1 โโโโโโโผโโโค Transform โ โ InputSystem โ โ
โ โ ID: 2 โโโโโโโผโโโค Physic โ โ PlayerSystem โ โ
โ โ ID: 3 โโโโโโโผโโโค Mesh โ โ RenderSystem โ โ
โ โ ... โ โ ... โ โ ... โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Components are Lua tables created by factory functions:
function Transform(x, y, z, rx, ry, rz, sx, sy, sz)
return {
x = x or 0, y = y or 0, z = z or 0,
rx = rx or 0, ry = ry or 0, rz = rz or 0,
sx = sx or 1, sy = sy or 1, sz = sz or 1
}
end
Systems process entities with specific components:
local RenderSystem = {}
function RenderSystem.update(dt)
local entities = ECS.getEntitiesWith({"Transform", "Mesh"})
for _, id in ipairs(entities) do
local transform = ECS.getComponent(id, "Transform")
local mesh = ECS.getComponent(id, "Mesh")
-- Update rendering...
end
end
ECS.registerSystem(RenderSystem)
The ECS supports different runtime modes:
ECS.capabilities = {
hasAuthority = true, -- Can modify game state
hasRendering = true, -- Has graphics output
hasLocalInput = true, -- Has local input
hasNetworkSync = false, -- Network synchronization
isClientMode = false, -- Running as client
isServerMode = false -- Running as server
}
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Server โ โ Client โ
โ โ โ โ
โ โโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโ โ
โ โ ECS โ โ ENTITY_POS โ โ ECS โ โ
โ โ (Authority)โ โโโโโโโโโโโโโโโโโโโโโบโ โ (Predicted)โ โ
โ โโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโโโ โ INPUT โ โโโโโโโโโโโโโโ โ
โ โ Physics โ โโโโโโโโโโโโโโโโโโโโโโค โ Input โ โ
โ โ Simulation โ โ โ โ Handler โ โ
โ โโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
- Client sends input โ
INPUTmessage to server - Server processes โ Physics simulation, game logic
- Server broadcasts โ
ENTITY_POSto all clients - Client interpolates โ Smooth visual updates
- Transport: UDP via ASIO
- Serialization: MsgPack
- Update Rate: 20Hz (50ms intervals)
See Network Protocol for details.
Purpose: Window creation and input handling
Subscribes to:
ImageRendered- Display rendered frames
Publishes:
KeyPressed- Key press eventsKeyReleased- Key release eventsMousePressed- Mouse click eventsMouseMoved- Mouse movement eventsWindowResized- Window resize events
Purpose: 3D rendering with OpenGL
Subscribes to:
RenderEntityCommand- Entity rendering commands
Publishes:
ImageRendered- Frame ready for display
Purpose: Entity Component System with Lua scripting
Subscribes to:
- Various game events (forwarded to Lua systems)
Publishes:
RenderEntityCommand- Rendering instructionsPhysicCommand- Physics instructions- Sound/Music commands
Purpose: Physics simulation
Subscribes to:
PhysicCommand- Physics instructions
Publishes:
Collision- Collision events
Purpose: Audio playback
Subscribes to:
SoundPlay- Play sound effectSoundStopAll- Stop all soundsMusicPlay- Play musicMusicStop- Stop musicMusicPause/MusicResume- Control music
Purpose: Client-server communication
Handles:
- UDP socket management
- Client connections
- Message routing
- Binary protocol encoding/decoding