Skip to main content

Client Architecture Overview

This document provides a comprehensive overview of the R-Type client architecture, design patterns, and component interactions.


πŸ—οΈ High-Level Architecture​

The R-Type client follows a layered architecture with clear separation of concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Application Layer β”‚
β”‚ (main.cpp - Game Loop) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
β”‚ UI Systems β”‚ β”‚ Network β”‚ β”‚ Game β”‚
β”‚ (Menus) β”‚ β”‚ Client β”‚ β”‚ Renderer β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
β”‚ Wrappers β”‚ β”‚ Asio β”‚ β”‚ SFML β”‚
β”‚ (Interfaces) β”‚ β”‚ β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🎯 Core Components​

1. Main Game Loop (client/main.cpp)​

The entry point that orchestrates all systems:

int main() {
// 1. Initialize configuration
Config::getInstance().load();

// 2. Create window and wrappers
auto window = std::make_unique<WindowSFML>(width, height, "R-Type");
auto graphics = std::make_unique<GraphicsSFML>(*window);
auto input = std::make_unique<InputSFML>(*window);
auto audio = std::make_unique<AudioSFML>();

// 3. Initialize systems
auto networkClient = std::make_unique<NetworkClientAsio>();
SoundManager::getInstance().loadAll();

// 4. Create UI screens
auto menu = std::make_unique<Menu>(*window, *graphics, *input);
auto settingsMenu = std::make_unique<SettingsMenu>(*window, *graphics, *input);
// ... other screens

// 5. Main game loop
GameState state = GameState::Menu;
while (window->isOpen()) {
float deltaTime = clock.restart();

// Update current state
switch (state) {
case GameState::Menu:
state = handleMenuState(...);
break;
case GameState::Playing:
state = handlePlayingState(...);
break;
// ... other states
}

// Render current state
window->clear();
renderCurrentState(state, ...);
window->display();
}
}

Responsibilities:

  • Initialize all subsystems
  • Manage game state transitions
  • Run the main game loop
  • Handle input and rendering

2. Wrapper Layer​

Abstract interfaces that decouple game logic from SFML:

Graphics Wrapper (wrapper/graphics/)​

  • IGraphics - Rendering interface
  • ISprite - Sprite management
  • GraphicsSFML - SFML implementation

Window Wrapper (wrapper/window/)​

  • IWindow - Window management
  • WindowSFML - SFML implementation

Input Wrapper (wrapper/input/)​

  • IInput - Input handling
  • InputSFML - SFML implementation

Audio Wrapper (wrapper/audio/)​

  • IAudio - Audio playback
  • AudioSFML - SFML implementation

Benefits:

  • βœ… Easy to swap SFML for another library (SDL, Raylib, etc.)
  • βœ… Testable game logic (mock interfaces)
  • βœ… Clean separation of concerns

3. UI Systems​

Main menu with navigation:

  • Play
  • Settings
  • Replays
  • Exit

Lobby System​

  • LobbyMenu.cpp - Create or join lobby
  • LobbyConfigMenu.cpp - Configure game rules
  • LobbyWaitingRoom.cpp - Wait for players
  • JoinLobbyDialog.cpp - Join existing lobby

Settings System (client/SettingsMenu.cpp)​

Configuration UI:

  • Resolution selection
  • Fullscreen toggle
  • Volume sliders (SFX, Music)
  • Key bindings
  • Color blind filters

Game Over Screen (client/GameOverScreen.cpp)​

Post-game UI:

  • Final score display
  • Return to menu
  • Play again option

4. Network Client​

Purpose: Handle all client-server communication

Implementation: client/network/NetworkClientAsio.cpp

Key Features:

  • UDP socket communication via Boost.Asio
  • Asynchronous I/O with dedicated network thread
  • Callback-based event system
  • Automatic reconnection handling

Message Flow:

Client ──sendLogin()──> Server
Client <─LoginResponse─ Server
Client ──sendInput()──> Server
Client <─EntityUpdate─ Server
Client <─PositionSync─ Server

See Network Architecture for details.


5. Game Renderer​

Purpose: Render active gameplay

Implementation: client/src/Game.cpp

Rendering Pipeline:

1. Clear window
2. Draw background (parallax scrolling)
3. Draw entities (sprites)
4. Draw UI overlays (score, health)
5. Apply color blind filter (if enabled)
6. Display frame

Entity Rendering:

  • Player ships
  • Enemies
  • Projectiles
  • Power-ups
  • Explosions (particles)

6. Configuration System​

Purpose: Persist user settings

Implementation: client/Config.cpp

Storage: config.json in working directory

Managed Settings:

{
"resolutionWidth": 1920,
"resolutionHeight": 1080,
"fullscreen": 0,
"sfxVolume": 100.0,
"musicVolume": 100.0,
"colorBlindMode": "None",
"keyBindings": {
"moveUp": "Z",
"moveDown": "S",
"moveLeft": "Q",
"moveRight": "D",
"shoot": "Space"
}
}

See Configuration System for details.


7. Audio System​

Purpose: Manage sound effects and music

Implementation: client/src/SoundManager.cpp

Singleton Pattern:

SoundManager& soundManager = SoundManager::getInstance();
soundManager.playSound("SHOOT");
soundManager.playMusic();
soundManager.setVolume(75.0f);

Audio Assets:

  • Background music (looping)
  • Sound effects (shoot, explosion, hit, UI)

See Audio System for details.


8. Replay System​

Purpose: Record and playback game sessions

Components:

  • ReplayRecorder - Records game events
  • ReplayPlayer - Plays back recorded sessions
  • ReplayBrowser - UI for selecting replays
  • ReplayControls - Playback controls (pause, seek, speed)

File Format: .rtr (R-Type Replay)

  • Binary format
  • Stores timestamped game events
  • Frame-perfect reproduction

See Replay System for details.


πŸ”„ State Management​

The client uses a finite state machine to manage different screens:

enum class GameState {
Menu, // Main menu
Lobby, // Lobby menu (create/join)
LobbyConfig, // Configure game rules
LobbyWaiting, // Waiting room
JoinLobbyDialog, // Join lobby dialog
Settings, // Settings menu
Playing, // Active gameplay
ReplayBrowser, // Replay selection
WatchingReplay, // Watching a replay
GameOver // Game over screen
};

State Transitions:

Menu ──> Lobby ──> LobbyConfig ──> LobbyWaiting ──> Playing ──> GameOver
β”‚ β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
└──> JoinLobbyDialog ──> LobbyWaiting β”€β”€β”€β”€β”€β”˜
β”‚
└──> Settings β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
└──> ReplayBrowser ──> WatchingReplay β”€β”€β”€β”€β”€β”˜

See Game State Management for detailed state flow.


🎨 Design Patterns Used​

1. Singleton Pattern​

Used for global managers:

  • Config::getInstance()
  • SoundManager::getInstance()
  • ColorBlindFilter::getInstance()

2. Interface/Implementation Pattern​

All wrapper components:

  • IWindow / WindowSFML
  • IGraphics / GraphicsSFML
  • IInput / InputSFML

3. Observer Pattern​

Network callbacks:

networkClient->setOnEntitySpawnCallback([](const EntitySpawnPacket& packet) {
// Handle entity spawn
});

4. State Pattern​

Game state machine with state-specific logic


πŸ“Š Threading Model​

The client uses 2 threads:

Main Thread (Game Loop)​

  • Input processing
  • Game logic updates
  • Rendering
  • UI updates

Network Thread (Asio I/O)​

  • Receiving UDP packets
  • Sending UDP packets
  • Message queue processing

Synchronization:

  • std::mutex protects shared message queue
  • Callbacks executed on main thread via polling

πŸ” Component Interactions​

Example: Player Input Flow​

1. User presses "Space" key
↓
2. InputSFML detects key press
↓
3. Main loop reads input state
↓
4. NetworkClient sends C2S_INPUT packet
↓
5. Server processes input
↓
6. Server broadcasts entity updates
↓
7. NetworkClient receives S2C_ENTITY_POS
↓
8. Callback updates game state
↓
9. Renderer draws updated entities

πŸ“¦ File Structure​

client/
β”œβ”€β”€ main.cpp # Entry point
β”œβ”€β”€ CMakeLists.txt # Build configuration
β”‚
β”œβ”€β”€ Menu.hpp/cpp # Main menu
β”œβ”€β”€ SettingsMenu.hpp/cpp # Settings screen
β”œβ”€β”€ GameOverScreen.hpp/cpp # Game over screen
β”‚
β”œβ”€β”€ LobbyMenu.hpp/cpp # Lobby menu
β”œβ”€β”€ LobbyConfigMenu.hpp/cpp # Game rules config
β”œβ”€β”€ LobbyWaitingRoom.hpp/cpp # Waiting room
β”œβ”€β”€ JoinLobbyDialog.hpp/cpp # Join dialog
β”‚
β”œβ”€β”€ ReplayBrowser.hpp/cpp # Replay browser
β”œβ”€β”€ ReplayControls.hpp/cpp # Playback controls
β”‚
β”œβ”€β”€ Config.hpp/cpp # Configuration system
β”œβ”€β”€ Button.hpp/cpp # Button UI component
β”œβ”€β”€ InputField.hpp/cpp # Text input component
β”œβ”€β”€ Slider.hpp/cpp # Slider UI component
β”œβ”€β”€ ToggleButton.hpp/cpp # Toggle UI component
β”œβ”€β”€ ColorBlindFilter.hpp/cpp # Accessibility filters
β”œβ”€β”€ KeyBinding.hpp/cpp # Key binding system
β”‚
β”œβ”€β”€ network/
β”‚ β”œβ”€β”€ NetworkClientAsio.hpp/cpp # Network client
β”‚ └── ClientGameState.hpp/cpp # Game state sync
β”‚
β”œβ”€β”€ src/
β”‚ β”œβ”€β”€ Game.hpp/cpp # Game renderer
β”‚ β”œβ”€β”€ Background.hpp/cpp # Background rendering
β”‚ β”œβ”€β”€ SoundManager.hpp/cpp # Audio manager
β”‚ β”œβ”€β”€ ReplayRecorder.hpp/cpp # Replay recording
β”‚ └── ReplayViewer.hpp/cpp # Replay playback
β”‚
└── wrapper/
β”œβ”€β”€ graphics/
β”‚ β”œβ”€β”€ Graphics.hpp # IGraphics interface
β”‚ β”œβ”€β”€ GraphicsSFML.hpp/cpp # SFML graphics
β”‚ β”œβ”€β”€ Sprite.hpp # ISprite interface
β”‚ └── SpriteSFML.hpp/cpp # SFML sprite
β”œβ”€β”€ window/
β”‚ β”œβ”€β”€ Window.hpp # IWindow interface
β”‚ └── WindowSFML.hpp/cpp # SFML window
β”œβ”€β”€ input/
β”‚ β”œβ”€β”€ Input.hpp # IInput interface
β”‚ └── InputSFML.hpp/cpp # SFML input
└── audio/
β”œβ”€β”€ Audio.hpp # IAudio interface
└── AudioSFML.hpp/cpp # SFML audio

πŸš€ Performance Considerations​

Rendering​

  • Target: 60 FPS
  • VSync: Enabled by default
  • Frame limiting: window->setFramerateLimit(60)

Networking​

  • Protocol: UDP (low latency)
  • Update rate: Server sends ~20 updates/sec
  • Client prediction: Smooth interpolation between updates

Memory​

  • Texture caching: Sprites reuse loaded textures
  • Object pooling: Reuse projectile/enemy entities
  • Smart pointers: std::unique_ptr for RAII

πŸ”§ Build Configuration​

# client/CMakeLists.txt
add_executable(r-type_client
main.cpp
Menu.cpp
SettingsMenu.cpp
# ... all client sources
)

target_link_libraries(r-type_client PRIVATE
sfml-graphics
sfml-window
sfml-system
sfml-audio
Boost::asio
common # Shared network protocol
)

πŸ“š Further Reading​


πŸ€” Common Questions​

Q: Why use wrappers instead of SFML directly? A: Wrappers provide library independence, easier testing, and cleaner interfaces.

Q: How does the client handle disconnections? A: The network client detects timeouts and attempts automatic reconnection.

Q: Can I add new menu screens? A: Yes! See the Tutorials for a step-by-step guide.

Q: Where are textures and sounds loaded? A: Assets are loaded on-demand when screens are created or during game initialization.