Skip to main content

Server Architecture Overview

Executive Summaryโ€‹

The R-Type server is a high-performance multiplayer game server built with modern C++ (C++17) that handles game logic, network synchronization, and player management for 1-4 concurrent players. The architecture leverages an Entity Component System (ECS) pattern combined with multithreading to achieve optimal performance and scalability.

Key Featuresโ€‹

  • ๐ŸŽฎ 60 FPS game simulation with deterministic logic
  • ๐ŸŒ UDP-based networking with reliable packet delivery
  • ๐Ÿงต Multithreaded architecture (network thread + game thread)
  • ๐Ÿ”ง Entity Component System (ECS) for flexible game logic
  • ๐Ÿ”„ Thread-safe communication via lock-free queues
  • ๐Ÿ›ก๏ธ Graceful error handling and automatic client timeout

Architectural Layersโ€‹

The server follows a layered architecture common in multiplayer game development:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Application Layer โ”‚
โ”‚ (GameServer) โ”‚
โ”‚ - Lobby management โ”‚
โ”‚ - Player lifecycle โ”‚
โ”‚ - Game state coordination โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Network Layer โ”‚ โ”‚ Game Layer โ”‚
โ”‚ (NetworkServer) โ”‚ โ”‚ (GameLoop) โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ - UDP Socket โ”‚ โ”‚ - ECS Engine โ”‚
โ”‚ - Client Mgmt โ”‚ โ”‚ - Systems โ”‚
โ”‚ - Protocol โ”‚ โ”‚ - Entities โ”‚
โ”‚ - Reliability โ”‚ โ”‚ - Components โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Common Services โ”‚
โ”‚ โ”‚
โ”‚ - Threading โ”‚
โ”‚ - Logging โ”‚
โ”‚ - Protocol โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Core Componentsโ€‹

1. GameServer (Orchestrator)โ€‹

Location: server/GameServer.{hpp,cpp}

The GameServer class is the main entry point that orchestrates all server operations.

Key Responsibilities:

  • Initialize and coordinate network and game threads
  • Manage the player lobby (waiting for 1-4 players)
  • Handle player connection lifecycle
  • Bridge network events to game logic

Architecture Pattern: Facade Pattern - provides a unified interface to the complex subsystems.

class GameServer {
NetworkServer _networkServer; // Network thread
engine::GameLoop _gameLoop; // Game thread

std::atomic<bool> _gameStarted;
std::atomic<int> _playerCount;
std::unordered_map<uint32_t, bool> _playersReady;
};

2. NetworkServer (Network Layer)โ€‹

Location: server/network/NetworkServer.{hpp,cpp}

Handles all network communication using Boost.Asio for asynchronous UDP I/O.

Key Responsibilities:

  • Asynchronous packet reception and transmission
  • Client session management and authentication
  • Protocol encoding/decoding
  • Reliable packet delivery (ACK/retry mechanism)
  • Client timeout detection

Architecture Pattern: Reactor Pattern (via Boost.Asio) - handles I/O events asynchronously.

Thread Safety: All network I/O runs on a dedicated thread. Events are queued for the main thread.

class NetworkServer {
boost::asio::io_context _ioContext;
boost::asio::ip::udp::socket _socket;
std::thread _networkThread;

std::map<uint32_t, ClientSession> _clients;
ThreadSafeQueue<NetworkEvent> _eventQueue;
};

3. GameLoop (Game Logic Layer)โ€‹

Location: server/engine/system/GameLoop.{hpp,cpp}

The heart of the game simulation, running at 60 FPS in a separate thread.

Key Responsibilities:

  • Manage the Entity Component System (ECS)
  • Execute game systems in priority order
  • Process player input commands
  • Generate entity state updates for network broadcast
  • Handle entity lifecycle (spawn, update, destroy)

Architecture Pattern: Game Loop Pattern with ECS Architecture.

class GameLoop {
EntityManager _entityManager;
std::vector<std::unique_ptr<ISystem>> _systems;

std::thread _gameThread;
ThreadSafeQueue<NetworkInputCommand> _inputQueue;
ThreadSafeQueue<EntityStateUpdate> _outputQueue;
};

Data Flow Diagramโ€‹

Understanding how data flows through the system is crucial:

Client Input โ†’ NetworkServer โ†’ Input Queue โ†’ GameLoop โ†’ ECS Systems
โ–ฒ โ”‚
โ”‚ โ–ผ
โ”‚ Entity Updates
โ”‚ โ”‚
โ”‚ โ–ผ
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Output Queue โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ State Sync
โ”‚
โ–ผ
Broadcast to Clients

Step-by-Step Flowโ€‹

  1. Input Reception

    • Client sends input packet (UDP)
    • NetworkServer receives and validates
    • Input queued to thread-safe input queue
  2. Game Simulation

    • GameLoop dequeues inputs from queue
    • Each system processes entities in order:
      • MovementSystem: Updates positions
      • CollisionSystem: Detects hits
      • EnemySpawnerSystem: Spawns enemies
      • LifetimeSystem: Removes expired entities
    • State changes queued to output queue
  3. Network Synchronization

    • NetworkServer dequeues entity updates
    • Broadcasts to all connected clients
    • Reliable packets await ACK with retry logic

Threading Modelโ€‹

The server uses two primary threads plus one I/O thread:

Thread Architectureโ€‹

ThreadPurposeFrequencyComponents
Main ThreadServer lifecycle, lobby managementEvent-drivenGameServer
Network ThreadAsynchronous I/O, packet handlingEvent-driven (Boost.Asio)NetworkServer, io_context
Game ThreadGame simulation loop60 FPS (16.67ms)GameLoop, ECS Systems

Thread Communicationโ€‹

Problem: Threads must share data safely without race conditions.

Solution: Thread-safe queues with mutex protection.

template <typename T>
class ThreadSafeQueue {
std::queue<T> _queue;
std::mutex _mutex;
std::condition_variable _condVar;

void push(T item);
std::optional<T> tryPop();
};

Benefits:

  • โœ… No data races
  • โœ… Lock-free reads (tryPop is non-blocking)
  • โœ… Minimal contention
  • โœ… Clear ownership semantics

Entity Component System (ECS)โ€‹

The server uses a data-oriented ECS architecture for game logic.

ECS Principlesโ€‹

  1. Entities: Just an ID (uint32_t)
  2. Components: Pure data structures (Position, Velocity, Health)
  3. Systems: Logic that operates on components
  4. Factory: Centralized entity creation (GameEntityFactory)

Why ECS?โ€‹

BenefitExplanation
PerformanceCache-friendly, data-oriented design
FlexibilityEasy to add new entity types
MaintainabilityClear separation of data and logic
ScalabilitySystems can be parallelized (future)

GameEntityFactory & Event-Driven Spawningโ€‹

Location: server/engine/entity/GameEntityFactory.{hpp,cpp}

The GameEntityFactory centralizes all entity creation logic. Systems do not create entities directly - instead, they emit spawn events that the GameLoop processes using the factory. This follows pure ECS architecture where systems only contain logic, not entity creation.

Key Responsibilities:

  • Create all game entities (players, enemies, bullets)
  • Manage entity IDs (network synchronization)
  • Ensure consistent component configuration
  • Provide a single source of truth for entity templates

Architecture Pattern: Factory Pattern + Event-Driven Architecture

class GameEntityFactory {
private:
EntityManager& _entityManager;
uint32_t _nextEnemyId;
uint32_t _nextBulletId;

public:
// Creates a player with all required components
Entity createPlayer(uint32_t clientId, uint32_t playerId, float x, float y);

// Creates an enemy with type-specific stats
Entity createEnemy(Enemy::Type type, float x, float y);

// Creates player/enemy bullets
Entity createPlayerBullet(EntityId ownerId, const Position& pos);
Entity createEnemyBullet(EntityId ownerId, const Position& pos);
};

Benefits:

  • โœ… Single Responsibility: Entity creation isolated in factory
  • โœ… Consistency: All entities created the same way
  • โœ… Maintainability: Change entity structure in one place
  • โœ… Testability: Easy to mock spawn events and factory
  • โœ… Pure ECS: Systems only process logic, never create entities
  • โœ… Minimal Coupling: Systems only know about spawn event queue

Event-Driven Spawning Flow:

// 1. Define spawn events (server/engine/events/SpawnEvents.hpp)
struct SpawnEnemyEvent { Enemy::Type type; float x, y; };
struct SpawnPlayerBulletEvent { EntityId ownerId; Position position; };
struct SpawnEnemyBulletEvent { EntityId ownerId; Position position; };

using SpawnEvent = std::variant<SpawnEnemyEvent,
SpawnPlayerBulletEvent,
SpawnEnemyBulletEvent>;

// 2. Systems receive spawn queue reference at construction
class EnemySpawnerSystem : public ISystem {
std::vector<SpawnEvent>& _spawnQueue;

public:
EnemySpawnerSystem(std::vector<SpawnEvent>& spawnQueue)
: _spawnQueue(spawnQueue) {}

void spawnEnemy() {
// Emit spawn event instead of creating entity directly
_spawnQueue.push_back(SpawnEnemyEvent{Enemy::Type::BASIC, x, y});
}
};

// 3. GameLoop processes events each frame
void GameLoop::processSpawnEvents() {
for (const auto& event : _spawnEvents) {
std::visit([this](const auto& e) {
processSpawnEvent(e); // Compile-time dispatch
}, event);
}
_spawnEvents.clear();
}

// Overloaded handlers for each event type
void processSpawnEvent(const SpawnEnemyEvent& e) {
_entityFactory.createEnemy(e.type, e.x, e.y);
}

Entity Creation Architectureโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ GameLoop โ”‚
โ”‚ - Owns GameEntityFactory โ”‚
โ”‚ - Owns spawn event queue (vector<SpawnEvent>) โ”‚
โ”‚ - Processes spawn events each frame โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”œโ”€โ–บ Creates systems with spawn queue ref
โ”‚ system = new EnemySpawnerSystem(_spawnEvents)
โ”‚
โ”œโ”€โ–บ Systems emit spawn events
โ”‚ _spawnQueue.push_back(SpawnEnemyEvent{...})
โ”‚
โ””โ”€โ–บ GameLoop processes events via factory
processSpawnEvent() โ†’ _entityFactory.createEnemy()

Why Event-Driven Spawning?

  • ๐ŸŽฏ Decoupling: Systems don't need to know about GameLoop or Factory
  • ๐Ÿงช Testability: Easy to mock a vector<SpawnEvent> for testing
  • ๐Ÿ“ˆ Extensibility: Add new entity types without changing systems
  • ๐Ÿ”ง Maintainability: Single spawn processing location in GameLoop
  • โšก Performance: Batch entity creation, better cache locality

Systems (logic) automatically process entities with required components:

  • MovementSystem: operates on Position + Velocity
  • CollisionSystem: operates on Position + Hitbox + Health

Design Patterns Usedโ€‹

1. Entity Component System (ECS)โ€‹

  • Where: Game logic layer
  • Why: Performance, flexibility, maintainability
  • Implementation: EntityManager + ComponentManager + Systems

2. Factory Pattern + Event-Driven Architectureโ€‹

  • Where: Entity creation (GameEntityFactory + SpawnEvents)
  • Why: Centralize entity creation, decouple systems from factory, pure ECS
  • Implementation: Systems emit spawn events, GameLoop processes via factory
  • Benefits: Pure ECS, minimal coupling, testability, extensibility

3. Reactor Pattern (Asio)โ€‹

  • Where: Network layer
  • Why: Asynchronous I/O without threads-per-connection
  • Implementation: Boost.Asio io_context

4. Producer-Consumer Patternโ€‹

  • Where: Thread communication
  • Why: Safe data exchange between threads
  • Implementation: ThreadSafeQueue

5. Facade Patternโ€‹

  • Where: GameServer
  • Why: Simplify complex subsystem interactions
  • Implementation: GameServer provides simple start()/run()/stop()

5. Observer Pattern (Callbacks)โ€‹

  • Where: Network events
  • Why: Decouple network layer from game logic
  • Implementation: Function callbacks (std::function)

Performance Characteristicsโ€‹

Target Metricsโ€‹

  • Game Loop: 60 FPS (16.67ms per frame)
  • Network Tick Rate: 30 Hz (updates every 2 frames)
  • Player Capacity: 1-4 concurrent players
  • Network Protocol: UDP (low latency)

Optimization Strategiesโ€‹

  1. ECS Architecture: Cache-friendly data layout
  2. Minimal Locking: Lock-free queues where possible
  3. Batch Processing: Systems process entities in batches
  4. Selective Sync: Only sync entities that changed (needsSync flag)
  5. Smart Spawning: Enemy spawner throttled by timer

Scalability Considerationsโ€‹

Current Designโ€‹

The architecture is optimized for small multiplayer sessions (1-4 players):

AspectCurrent DesignScaling Strategy
Players per game1-4Room/lobby system for multiple games
Game threadSingle threadCan partition by game instance
Network threadSingle threadCan scale with io_context thread pool
Entity limit~1000sECS handles efficiently

Future Scaling Pathsโ€‹

  1. Multiple Game Instances: Run separate GameServer instances per game room
  2. Thread Pool for Systems: Parallelize independent systems
  3. Spatial Partitioning: Optimize collision detection for large entity counts
  4. Database Integration: Persist player data and statistics

Error Handling & Resilienceโ€‹

Defensive Programmingโ€‹

The server is designed to never crash from client actions:

try {
// Risky operation
processClientPacket(packet);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
// Server continues running
}

Graceful Degradationโ€‹

  • Client timeout: Automatic disconnection after 30s of inactivity
  • Invalid packets: Logged and ignored, not crash
  • Player disconnect: Game continues if others remain
  • Signal handling: SIGINT/SIGTERM trigger graceful shutdown

System Requirementsโ€‹

Build Dependenciesโ€‹

  • C++17 Compiler: GCC 7+, Clang 5+, MSVC 2017+
  • CMake: 3.16+
  • Boost: 1.70+ (Asio, System)
  • vcpkg: For dependency management

Runtime Requirementsโ€‹

  • OS: Linux, Windows, macOS
  • RAM: ~50 MB per game instance
  • CPU: 1 core (2+ recommended)
  • Network: UDP port 8080

File Structureโ€‹

server/
โ”œโ”€โ”€ main.cpp # Entry point
โ”œโ”€โ”€ GameServer.{hpp,cpp} # Main orchestrator
โ”œโ”€โ”€ GameEvents.hpp # Event documentation
โ”œโ”€โ”€ network/
โ”‚ โ””โ”€โ”€ NetworkServer.{hpp,cpp} # Network layer
โ”œโ”€โ”€ engine/
โ”‚ โ”œโ”€โ”€ component/ # ECS components
โ”‚ โ”‚ โ”œโ”€โ”€ ComponentManager.{hpp,cpp}
โ”‚ โ”‚ โ””โ”€โ”€ GameComponents.hpp
โ”‚ โ”œโ”€โ”€ entity/ # Entity management
โ”‚ โ”‚ โ”œโ”€โ”€ EntityManager.{hpp,cpp}
โ”‚ โ”‚ โ””โ”€โ”€ Entity.hpp
โ”‚ โ”œโ”€โ”€ system/ # ECS systems
โ”‚ โ”‚ โ”œโ”€โ”€ GameLoop.{hpp,cpp}
โ”‚ โ”‚ โ”œโ”€โ”€ GameSystems.hpp
โ”‚ โ”‚ โ””โ”€โ”€ System.{hpp,tpp}
โ”‚ โ””โ”€โ”€ threading/ # Thread utilities
โ”‚ โ””โ”€โ”€ ThreadSafeQueue.hpp
โ””โ”€โ”€ tests/ # Unit tests

Next Stepsโ€‹