Compétence 13 : Code Opérationnel¶
Rédiger le code à l'aide du langage informatique adapté au logiciel en implémentant les solutions techniques précédemment identifiées, afin de concrétiser la vision et la valeur du produit par le client.
Observable 13.1 : Code Opérationnel Répondant aux Exigences¶
Exigences Fonctionnelles et Implémentation¶
| Exigence | Implémentation | Fichier Principal |
|---|---|---|
| Jeu multijoueur 4 joueurs | UDPServer + GameWorld | UDPServer.cpp, GameWorld.cpp |
| Authentification sécurisée | TCPAuthServer + TLS | TCPAuthServer.cpp |
| Classements persistants | MongoDB + Leaderboard | MongoDBLeaderboardRepository.cpp |
| Voice chat | Opus + PortAudio | VoiceChatManager.cpp |
| Système social | Friends + Messages | MongoDBFriendshipRepository.cpp |
| Cross-platform | SFML/SDL2 plugins | IWindow.hpp, SFMLWindow.cpp |
Code du Serveur de Jeu¶
UDPServer - Boucle de Jeu¶
Fichier : src/server/infrastructure/adapters/in/network/UDPServer.cpp
void UDPServer::startBroadcastLoop() {
_broadcastTimer.expires_after(std::chrono::milliseconds(50)); // 20 Hz
_broadcastTimer.async_wait([this](boost::system::error_code ec) {
if (ec) return;
// Broadcast snapshot pour chaque room active
for (auto& [roomCode, instance] : _instanceManager.getInstances()) {
auto gameWorld = instance->getGameWorld();
if (gameWorld) {
broadcastSnapshotForRoom(roomCode, gameWorld);
}
}
startBroadcastLoop(); // Reschedule
});
}
void UDPServer::broadcastSnapshotForRoom(
const std::string& roomCode,
const std::shared_ptr<game::GameWorld>& gameWorld
) {
protocol::GameSnapshot snapshot = gameWorld->getSnapshot();
// Sérialisation
std::vector<uint8_t> payload(snapshot.serializedSize());
snapshot.to_bytes(payload.data());
// Compression si >= 128 bytes
auto compressed = compression::compress(payload.data(), payload.size());
// ...
// Envoi à tous les joueurs de la room
for (const auto& endpoint : roomEndpoints) {
_socket.async_send_to(buffer, endpoint, ...);
}
}
GameWorld - État de Jeu¶
Fichier : src/server/infrastructure/game/GameWorld.cpp
void GameWorld::update(float deltaTime) {
if (!_isRunning) return;
float scaledDt = deltaTime * _gameSpeedMultiplier;
// 1. Mettre à jour les positions des entités
updatePlayerPositions(scaledDt);
updateMissiles(scaledDt);
updateEnemies(scaledDt);
updateEnemyMissiles(scaledDt);
// 2. Mettre à jour les systèmes
updateForcePods(scaledDt);
updateBitDevices(scaledDt);
updatePowerUps(scaledDt);
// 3. Vérifier les collisions
checkCollisions();
// 4. Mettre à jour le boss si présent
if (_boss.has_value()) {
updateBoss(scaledDt);
}
// 5. Gérer les vagues
updateWaveSpawning(scaledDt);
// 6. Mettre à jour les combos
updateComboTimers(scaledDt);
}
protocol::GameSnapshot GameWorld::getSnapshot() const {
protocol::GameSnapshot snap;
// Joueurs
snap.player_count = static_cast<uint8_t>(_players.size());
for (const auto& [id, player] : _players) {
snap.players[snap.player_count++] = player.toState();
}
// Missiles
snap.missile_count = 0;
for (const auto& [id, missile] : _missiles) {
snap.missiles[snap.missile_count++] = missile.toState();
}
// Ennemis, boss, etc.
// ...
return snap;
}
Code du Client¶
GameScene - Rendu et Input¶
Fichier : src/client/src/scenes/GameScene.cpp
void GameScene::handleEvent(const events::Event& event) {
auto& config = AccessibilityConfig::getInstance();
// Gestion inputs avec remapping
if (auto* keyPressed = std::get_if<events::KeyPressed>(&event)) {
if (config.isActionKey(GameAction::Shoot, keyPressed->key)) {
shootMissile();
}
if (config.isActionKey(GameAction::Pause, keyPressed->key)) {
togglePause();
}
if (config.isActionKey(GameAction::OpenChat, keyPressed->key)) {
openChatInput();
}
// Mouvement
if (config.isActionKey(GameAction::MoveUp, keyPressed->key)) {
_inputState.up = true;
}
// ...
}
}
void GameScene::update(float deltaTime) {
// Appliquer prédiction client
if (_inputState.up) _localY -= MOVE_SPEED * deltaTime;
if (_inputState.down) _localY += MOVE_SPEED * deltaTime;
if (_inputState.left) _localX -= MOVE_SPEED * deltaTime;
if (_inputState.right) _localX += MOVE_SPEED * deltaTime;
// Envoyer inputs au serveur
sendInputToServer();
// Appliquer snapshot serveur (réconciliation)
applyServerSnapshot();
// Mettre à jour animations
updateAnimations(deltaTime);
// Mettre à jour chat
updateChatMessages(deltaTime);
}
void GameScene::render() {
renderBackground();
renderEnemies();
renderMissiles();
renderPlayers();
renderPowerUps();
renderForcePods();
if (_boss.has_value()) {
renderBoss();
renderBossHealthBar();
}
renderHUD();
renderChatOverlay();
if (_paused) {
renderPauseOverlay();
}
}
Environnement d'Exécution¶
Langages Utilisés¶
| Composant | Langage | Standard | Justification |
|---|---|---|---|
| Serveur | C++ | C++23 | Performance, contrôle mémoire |
| Client | C++ | C++23 | Performance graphique |
| Discord Bots | TypeScript | ES2022 | Écosystème Discord.js |
| Scripts | Bash | POSIX | Automatisation build/deploy |
Configuration Build (CMake)¶
cmake_minimum_required(VERSION 3.30)
project(rtype CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Dépendances vcpkg
find_package(Boost REQUIRED COMPONENTS system)
find_package(OpenSSL REQUIRED)
find_package(mongocxx REQUIRED)
find_package(SFML REQUIRED COMPONENTS graphics window system audio)
Observable 13.2 : Conventions de Formatage et de Nommage¶
Conventions de Nommage¶
| Type | Convention | Exemples |
|---|---|---|
| Classes | PascalCase | GameWorld, UDPServer, Button |
| Interfaces | I + PascalCase | IWindow, IScene, IUIElement |
| Méthodes | camelCase | handleEvent(), getSnapshot() |
| Membres privés | _camelCase | _players, _missiles |
| Constantes | SCREAMING_SNAKE_CASE | MAX_PLAYERS, MOVE_SPEED |
| Namespaces | lowercase | domain::entities, events |
| Enums | PascalCase | enum class State { Waiting, InGame } |
| Fichiers | PascalCase.hpp/.cpp | GameWorld.hpp, UDPServer.cpp |
Exemples de Code Conforme¶
Structure des Headers¶
/*
** EPITECH PROJECT, 2025
** rtype
** File description:
** GameWorld
*/
#ifndef GAMEWORLD_HPP_
#define GAMEWORLD_HPP_
#include <unordered_map>
#include <vector>
#include <optional>
#include "../../common/protocol/Protocol.hpp"
#include "../../common/collision/AABB.hpp"
namespace infrastructure::game {
class GameWorld {
public:
void update(float deltaTime);
protocol::GameSnapshot getSnapshot() const;
private:
std::unordered_map<uint8_t, ConnectedPlayer> _players;
std::optional<Boss> _boss;
static constexpr float MOVE_SPEED = 200.0f;
static constexpr uint8_t MAX_PLAYERS = 4;
};
} // namespace infrastructure::game
#endif /* !GAMEWORLD_HPP_ */
Constructeur avec Initializer List¶
Fichier : src/client/src/ui/Button.cpp
Button::Button(const Vec2f& pos, const Vec2f& size,
const std::string& text, const std::string& fontKey)
: _pos(pos) // Alignement vertical
, _size(size) // Virgule en début de ligne
, _text(text)
, _fontKey(fontKey)
, _state(State::Normal)
, _focused(false)
{
}
Gestion des Événements avec variant¶
void Button::handleEvent(const events::Event& event) {
if (!_enabled) {
_state = State::Disabled;
return;
}
// Pattern matching avec std::get_if
if (auto* moved = std::get_if<events::MouseMoved>(&event)) {
bool isHovered = contains(static_cast<float>(moved->x),
static_cast<float>(moved->y));
_state = isHovered ? State::Hovered : State::Normal;
}
if (auto* pressed = std::get_if<events::MouseButtonPressed>(&event)) {
if (pressed->button == events::MouseButton::Left &&
contains(static_cast<float>(pressed->x),
static_cast<float>(pressed->y))) {
_state = State::Pressed;
if (_onClick) {
_onClick();
}
}
}
}
Constantes Nommées¶
Fichier : src/client/include/scenes/GameScene.hpp
// Constantes de gameplay
static constexpr float MOVE_SPEED = 200.0f;
static constexpr float SHIP_WIDTH = 64.0f;
static constexpr float SHIP_HEIGHT = 30.0f;
static constexpr float SHOOT_COOLDOWN_TIME = 0.3f;
static constexpr uint8_t MAX_HEALTH = 100;
// Constantes UI
static constexpr float HUD_HEALTH_BAR_WIDTH = 200.0f;
static constexpr float HUD_MARGIN = 20.0f;
static constexpr unsigned int LABEL_FONT_SIZE = 14;
// Constantes écran
static constexpr float SCREEN_WIDTH = 1920.0f;
static constexpr float SCREEN_HEIGHT = 1080.0f;
// Clés de ressources
static constexpr const char* SHIP_TEXTURE_KEY = "ship";
static constexpr const char* FONT_KEY = "main";
Formatage du Code¶
Indentation et Espacement¶
// Accolades sur ligne suivante pour classes/fonctions
class GameWorld
{
public:
void update(float deltaTime);
private:
std::unordered_map<uint8_t, Player> _players;
};
// Espaces autour des opérateurs
float scaledDt = deltaTime * _gameSpeedMultiplier;
// Pas d'espace après nom de fonction
void handleEvent(const events::Event& event);
// Espace après keywords
if (condition) {
// ...
}
for (const auto& item : collection) {
// ...
}
Commentaires¶
// Commentaire sur une ligne pour explications courtes
/*
* Commentaire multi-lignes pour
* explications plus longues
*/
/// Documentation Doxygen pour API publique
/// @param deltaTime Temps écoulé depuis la dernière frame
/// @return Snapshot de l'état de jeu
protocol::GameSnapshot getSnapshot() const;
Tableau Récapitulatif¶
| Aspect | Convention | Exemple |
|---|---|---|
| Indentation | 4 espaces | Standard projet |
| Accolades | Nouvelle ligne (classes) | class Foo\n{ |
| Pointeurs/Refs | Collés au type | const std::string& name |
| Include guards | FILENAME_HPP_ | #ifndef GAMEWORLD_HPP_ |
| Namespaces | Pas d'indentation | namespace foo {\nclass Bar; |
| Lignes max | ~100 caractères | Lisibilité |
Conclusion¶
Le code R-Type respecte :
- Exigences fonctionnelles : Multijoueur, auth, classements, voice, social
- Langage adapté : C++23 pour performance et modernité
- Conventions strictes : PascalCase classes, _camelCase membres, SCREAMING_SNAKE constantes
- Lisibilité : Indentation cohérente, commentaires pertinents, noms explicites
Ces pratiques garantissent un code maintenable, lisible et professionnel.