Skip to content
On this page

R-Type Lobby System - Documentation Technique

Vue d'ensemble

Le système de lobby implémente une architecture client/serveur TCP complète permettant la gestion de rooms multijoueurs avant de rejoindre la partie UDP.


Architecture

Composants principaux

┌─────────────────┐         TCP (Lobby)          ┌─────────────────┐
│   ClientHub     │ ◄───────────────────────────► │   ServerHub     │
│                 │                                │                 │
│ - Callbacks     │   LOGIN, LIST_ROOMS,          │ - RoomSlots     │
│ - PacketsManager│   CREATE_ROOM, JOIN_ROOM      │ - RoomManager   │
└─────────────────┘                                └─────────────────┘
        │                                                   │
        │                                                   │
        ▼                                                   ▼
┌─────────────────┐         UDP (Game)            ┌─────────────────┐
│  ClientRoom     │ ◄───────────────────────────► │  ServerRoom     │
│                 │                                │                 │
│ - Game packets  │   INPUT, GAME_STATE,          │ - GameServer    │
│ - Player state  │   SPAWN, DESTROY              │ - ECS           │
└─────────────────┘                                └─────────────────┘
┌─────────────────┐         TCP (Lobby)          ┌─────────────────┐
│   ClientHub     │ ◄───────────────────────────► │   ServerHub     │
│                 │                                │                 │
│ - Callbacks     │   LOGIN, LIST_ROOMS,          │ - RoomSlots     │
│ - PacketsManager│   CREATE_ROOM, JOIN_ROOM      │ - RoomManager   │
└─────────────────┘                                └─────────────────┘
        │                                                   │
        │                                                   │
        ▼                                                   ▼
┌─────────────────┐         UDP (Game)            ┌─────────────────┐
│  ClientRoom     │ ◄───────────────────────────► │  ServerRoom     │
│                 │                                │                 │
│ - Game packets  │   INPUT, GAME_STATE,          │ - GameServer    │
│ - Player state  │   SPAWN, DESTROY              │ - ECS           │
└─────────────────┘                                └─────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Flux de communication

CLIENT                                    SERVER
  │                                          │
  ├─► LOGIN (username, password, game) ───► │
  │ ◄── ROOMCONNECTION (port UDP) ─────────┤
  │                                          │
  ├─► LIST_ROOMS ──────────────────────────►│
  │ ◄── RoomListResponse (rooms[]) ────────┤
  │                                          │
  ├─► CREATE_ROOM (roomName) ──────────────►│
  │ ◄── RoomCreatedResponse (id, port) ────┤
  │                                          │
  ├─► JOIN_ROOM (roomId) ───────────────────►│
  │ ◄── RoomJoinedResponse (id, port) ─────┤
  │                                          │
  └─► [Connexion UDP sur port reçu] ───────► ServerRoom
CLIENT                                    SERVER
  │                                          │
  ├─► LOGIN (username, password, game) ───► │
  │ ◄── ROOMCONNECTION (port UDP) ─────────┤
  │                                          │
  ├─► LIST_ROOMS ──────────────────────────►│
  │ ◄── RoomListResponse (rooms[]) ────────┤
  │                                          │
  ├─► CREATE_ROOM (roomName) ──────────────►│
  │ ◄── RoomCreatedResponse (id, port) ────┤
  │                                          │
  ├─► JOIN_ROOM (roomId) ───────────────────►│
  │ ◄── RoomJoinedResponse (id, port) ─────┤
  │                                          │
  └─► [Connexion UDP sur port reçu] ───────► ServerRoom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Composants techniques

1. ClientHub (Networking/client/ClientHub.hpp/cpp)

Responsabilités :

  • Gestion connexion TCP avec ServerHub
  • Envoi/réception de packets lobby
  • Système de callbacks pour réponses asynchrones
  • Intégration PacketsManager (sérialisation/désérialisation)

API publique :

cpp
bool connect(const std::string& host, short port);
void login(const std::string& username, const std::string& password);
void requestRoomList();
void createRoom(const std::string& roomName);
void joinRoom(uint32_t roomId);
void update();  // Poll packets
void disconnect();

// Callbacks
void setLoginCallback(LoginCallback cb);
void setRoomListCallback(RoomListCallback cb);
void setRoomCreatedCallback(RoomCreatedCallback cb);
void setRoomJoinedCallback(RoomJoinedCallback cb);
void setErrorCallback(ErrorCallback cb);
bool connect(const std::string& host, short port);
void login(const std::string& username, const std::string& password);
void requestRoomList();
void createRoom(const std::string& roomName);
void joinRoom(uint32_t roomId);
void update();  // Poll packets
void disconnect();

// Callbacks
void setLoginCallback(LoginCallback cb);
void setRoomListCallback(RoomListCallback cb);
void setRoomCreatedCallback(RoomCreatedCallback cb);
void setRoomJoinedCallback(RoomJoinedCallback cb);
void setErrorCallback(ErrorCallback cb);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Exemple d'utilisation :

cpp
boost::asio::io_context io;
ClientHub hub(io);

// Setup callbacks
hub.setLoginCallback([](bool success, const std::string& msg) {
    std::cout << "Login: " << (success ? "OK" : "FAIL") << std::endl;
});

hub.setRoomListCallback([](const std::vector<RoomInfo>& rooms) {
    for (const auto& room : rooms) {
        std::cout << "Room " << room.roomId << ": " << room.roomName 
                  << " (" << (int)room.currentPlayers << "/" << (int)room.maxPlayers << ")"
                  << " Port: " << room.port << std::endl;
    }
});

// Connect and login
if (hub.connect("127.0.0.1", 4242)) {
    hub.login("Player1", "password123");
    
    // Update loop
    while (true) {
        hub.update();
        io.poll();
    }
}
boost::asio::io_context io;
ClientHub hub(io);

// Setup callbacks
hub.setLoginCallback([](bool success, const std::string& msg) {
    std::cout << "Login: " << (success ? "OK" : "FAIL") << std::endl;
});

hub.setRoomListCallback([](const std::vector<RoomInfo>& rooms) {
    for (const auto& room : rooms) {
        std::cout << "Room " << room.roomId << ": " << room.roomName 
                  << " (" << (int)room.currentPlayers << "/" << (int)room.maxPlayers << ")"
                  << " Port: " << room.port << std::endl;
    }
});

// Connect and login
if (hub.connect("127.0.0.1", 4242)) {
    hub.login("Player1", "password123");
    
    // Update loop
    while (true) {
        hub.update();
        io.poll();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

2. ServerHub (Networking/server/ServerHub.hpp/cpp)

Responsabilités :

  • Acceptation connexions TCP multiples
  • Dispatch packets vers handlers appropriés
  • Gestion des RoomSlots (tracking rooms actives)
  • Création automatique de rooms au LOGIN
  • Gestion du cycle de vie des ServerRooms

Fonctionnalités clés :

  • Acceptation asynchrone : startAccept()handleAccept()handleRead() en boucle
  • Gestion LOGIN : Crée ou réutilise une room pour le game demandé
  • Gestion LOBBY : Délègue à handleLobbyPacket()
  • RoomSlots : Structure trackant idx, port, assigned players, game name

Structure RoomSlot :

cpp
struct RoomSlot {
    std::size_t roomIdx;      // Index dans RoomManager
    unsigned short port;      // Port UDP de la ServerRoom
    std::size_t assigned;     // Nombre de joueurs assignés
    std::string game;         // Nom du jeu (pour ségrégation)
};
struct RoomSlot {
    std::size_t roomIdx;      // Index dans RoomManager
    unsigned short port;      // Port UDP de la ServerRoom
    std::size_t assigned;     // Nombre de joueurs assignés
    std::string game;         // Nom du jeu (pour ségrégation)
};
1
2
3
4
5
6

3. LobbyPacket (RType-Game/Packets/LobbyPacket.hpp/cpp)

Structure :

cpp
enum class LobbyAction : uint8_t {
    LIST_ROOMS = 0,
    LIST_ROOMS_RESPONSE = 1,
    CREATE_ROOM = 2,
    CREATE_ROOM_RESPONSE = 3,
    JOIN_ROOM = 4,
    JOIN_ROOM_RESPONSE = 5,
    LEAVE_ROOM = 6,
    ERROR_RESPONSE = 7
};

struct RoomInfo {
    uint32_t roomId;
    std::string roomName;
    uint16_t port;
    uint8_t currentPlayers;
    uint8_t maxPlayers;
    bool inGame;
};

class LobbyPacket : public IPacket {
public:
    LobbyAction action;
    std::string roomName;
    uint32_t roomId;
    uint16_t port;
    std::vector<RoomInfo> rooms;
    std::string errorMessage;
    
    // Factory methods
    static LobbyPacket createListRequest();
    static LobbyPacket createRoomListResponse(const std::vector<RoomInfo>& rooms);
    static LobbyPacket createCreateRoomRequest(const std::string& name);
    static LobbyPacket createRoomCreatedResponse(uint32_t id, uint16_t port);
    static LobbyPacket createJoinRoomRequest(uint32_t id);
    static LobbyPacket createRoomJoinedResponse(uint32_t id, uint16_t port);
    static LobbyPacket createErrorResponse(const std::string& error);
};
enum class LobbyAction : uint8_t {
    LIST_ROOMS = 0,
    LIST_ROOMS_RESPONSE = 1,
    CREATE_ROOM = 2,
    CREATE_ROOM_RESPONSE = 3,
    JOIN_ROOM = 4,
    JOIN_ROOM_RESPONSE = 5,
    LEAVE_ROOM = 6,
    ERROR_RESPONSE = 7
};

struct RoomInfo {
    uint32_t roomId;
    std::string roomName;
    uint16_t port;
    uint8_t currentPlayers;
    uint8_t maxPlayers;
    bool inGame;
};

class LobbyPacket : public IPacket {
public:
    LobbyAction action;
    std::string roomName;
    uint32_t roomId;
    uint16_t port;
    std::vector<RoomInfo> rooms;
    std::string errorMessage;
    
    // Factory methods
    static LobbyPacket createListRequest();
    static LobbyPacket createRoomListResponse(const std::vector<RoomInfo>& rooms);
    static LobbyPacket createCreateRoomRequest(const std::string& name);
    static LobbyPacket createRoomCreatedResponse(uint32_t id, uint16_t port);
    static LobbyPacket createJoinRoomRequest(uint32_t id);
    static LobbyPacket createRoomJoinedResponse(uint32_t id, uint16_t port);
    static LobbyPacket createErrorResponse(const std::string& error);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

4. LobbyState (RType-Game/States/LobbyState.hpp/cpp)

Responsabilités :

  • Interface utilisateur du lobby
  • Gestion des modes : LOADING, ROOM_LIST, CREATE_ROOM, ERROR
  • Intégration avec IGraphicsBackend pour le rendu
  • Navigation clavier (↑/↓, Enter, Backspace)

Modes d'interface :

  • LOADING : Attente réponse serveur
  • ROOM_LIST : Liste des rooms disponibles avec navigation
  • CREATE_ROOM : Saisie du nom de la nouvelle room
  • ERROR : Affichage message d'erreur

Contrôles :

  • / : Navigation dans la liste
  • Enter : Sélectionner room / Créer room / Valider
  • Backspace : Supprimer caractère (mode CREATE_ROOM)
  • C : Mode création de room
  • R : Rafraîchir liste

Protocole réseau

PacketType (IPacket.hpp)

cpp
enum class PacketType : uint8_t {
    LOGIN = 0x01,
    LOGIN_RESPONSE = 0x02,
    LOGOUT = 0x03,
    ROOMCONNECTION = 0x04,
    LOBBY = 0x30,
    // ... game packets ...
};
enum class PacketType : uint8_t {
    LOGIN = 0x01,
    LOGIN_RESPONSE = 0x02,
    LOGOUT = 0x03,
    ROOMCONNECTION = 0x04,
    LOBBY = 0x30,
    // ... game packets ...
};
1
2
3
4
5
6
7
8

Format des packets

Structure binaire :

┌──────────────┬──────────────┬────────────────────┐
│ Type (1 byte)│ Size (2 bytes)│ Payload (variable) │
└──────────────┴──────────────┴────────────────────┘
     │              │                   │
     │              │                   └─► Données sérialisées
     │              └──────────────────────► Taille du payload
     └──────────────────────────────────────► PacketType

Puis: Compression LZ4 + Encryption XOR (seed 0xAA)
┌──────────────┬──────────────┬────────────────────┐
│ Type (1 byte)│ Size (2 bytes)│ Payload (variable) │
└──────────────┴──────────────┴────────────────────┘
     │              │                   │
     │              │                   └─► Données sérialisées
     │              └──────────────────────► Taille du payload
     └──────────────────────────────────────► PacketType

Puis: Compression LZ4 + Encryption XOR (seed 0xAA)
1
2
3
4
5
6
7
8
9

Exemple LOGIN packet :

Original:
[0x01][len_username][username][len_password][password][len_game][game]

Après PacketsManager.buildPacket():
[0x01][0x32 0x00][...50 bytes payload...]
   │      │          └─► payload (username+password+game)
   │      └───────────────► size = 50
   └──────────────────────► type = LOGIN

Puis: LZ4 compress → XOR encrypt → envoi TCP
Original:
[0x01][len_username][username][len_password][password][len_game][game]

Après PacketsManager.buildPacket():
[0x01][0x32 0x00][...50 bytes payload...]
   │      │          └─► payload (username+password+game)
   │      └───────────────► size = 50
   └──────────────────────► type = LOGIN

Puis: LZ4 compress → XOR encrypt → envoi TCP
1
2
3
4
5
6
7
8
9
10

Tests

Test en ligne de commande

Fichier : Networking/client/main_lobby_test.cpp

Compilation :

bash
./compile_lobby_client.sh
./compile_lobby_client.sh
1

Exécution :

bash
# Terminal 1
./r-type_server

# Terminal 2 (noter le port affiché par le serveur)
./lobby_client_test 37837
# Terminal 1
./r-type_server

# Terminal 2 (noter le port affiché par le serveur)
./lobby_client_test 37837
1
2
3
4
5

Sortie attendue :

=== LOBBY CLIENT TEST ===
[1] Connecting to 127.0.0.1:37837...
[ClientHub] Connected to 127.0.0.1:-27699
[2] Logging in as TestPlayer...
✅ Login: SUCCESS
[3] Requesting room list...
✅ Room list received: 1 rooms
  - Room 1000: Room 1000 (1/4) Port: 36313
[4] Creating room 'TestRoom'...
✅ Room created! ID: 1000 Port: 5242
[5] Joining room 1000...
✅ Room joined! ID: 1000 Port: 36313
[6] Disconnecting...
=== TEST COMPLETED ===
=== LOBBY CLIENT TEST ===
[1] Connecting to 127.0.0.1:37837...
[ClientHub] Connected to 127.0.0.1:-27699
[2] Logging in as TestPlayer...
✅ Login: SUCCESS
[3] Requesting room list...
✅ Room list received: 1 rooms
  - Room 1000: Room 1000 (1/4) Port: 36313
[4] Creating room 'TestRoom'...
✅ Room created! ID: 1000 Port: 5242
[5] Joining room 1000...
✅ Room joined! ID: 1000 Port: 36313
[6] Disconnecting...
=== TEST COMPLETED ===
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Logs serveur attendus

[SERVER HUB]: Creation (Port = 37837).
[SERVER HUB]: login -> User: TestPlayer Password: password123 Game: rtype.
[SERVER HUB]: Created room idx=0 on port=36313
[ServerHub] Client requests room list
[ServerHub] Creating room: TestRoom
[ServerHub] Client joins room: 1000
[SERVER HUB]: Client disconnected properly.
[SERVER HUB]: Creation (Port = 37837).
[SERVER HUB]: login -> User: TestPlayer Password: password123 Game: rtype.
[SERVER HUB]: Created room idx=0 on port=36313
[ServerHub] Client requests room list
[ServerHub] Creating room: TestRoom
[ServerHub] Client joins room: 1000
[SERVER HUB]: Client disconnected properly.
1
2
3
4
5
6
7

✅ Fonctionnalités implémentées

Track #2 - Networking (10/10 points)

  • Architecture Client/Server TCP (+2 pts)

    • ClientHub avec callbacks asynchrones
    • ServerHub avec gestion multi-clients
    • Communication bidirectionnelle persistante
  • Système de Lobby complet (+3 pts)

    • Authentification (username/password/game)
    • Liste des rooms avec informations temps réel
    • Création de rooms nommées
    • Jointure de rooms existantes
  • PacketsManager (+2 pts)

    • Sérialisation/Désérialisation automatique
    • Compression LZ4 des packets
    • Encryption XOR basique
    • Factory pattern pour création de packets
  • Données correctes (+2 pts)

    • Ports UDP réels des ServerRooms
    • Nombre de joueurs assignés (tracking)
    • IDs de rooms cohérents (1000+)
    • Gestion d'erreurs (Room not found)
  • Qualité du code (+1 pt)

    • Logs détaillés client/serveur
    • Callbacks pour programmation réactive
    • Architecture modulaire (Hub/Room séparés)
    • Documentation inline

🚀 Utilisation

Lancer le serveur

bash
make r-type_server
./r-type_server
# Note le port affiché : [SERVER HUB]: Creation (Port = XXXXX).
make r-type_server
./r-type_server
# Note le port affiché : [SERVER HUB]: Creation (Port = XXXXX).
1
2
3

Lancer un client de test

bash
./compile_lobby_client.sh
./lobby_client_test <PORT>
./compile_lobby_client.sh
./lobby_client_test <PORT>
1
2

Scénario typique

  1. Connexion : Le client se connecte au ServerHub sur le port TCP
  2. Login : Envoi username/password/game → Serveur crée ou assigne une room
  3. Liste rooms : Client demande LIST_ROOMS → Reçoit RoomInfo[] avec ports UDP
  4. Création room : Client crée "MyRoom" → Serveur démarre ServerRoom sur port UDP dynamique
  5. Jointure : Client rejoint room 1000 → Serveur retourne port UDP 36313
  6. Transition UDP : Client se connecte au port UDP pour commencer la partie

📁 Fichiers clés

Client

  • Networking/client/ClientHub.hpp/cpp : Gestion connexion TCP lobby
  • Networking/client/ClientRoom.hpp/cpp : Gestion connexion UDP game
  • Networking/client/main_lobby_test.cpp : Test en ligne de commande
  • RType-Game/States/LobbyState.hpp/cpp : Interface utilisateur lobby

Serveur

  • Networking/server/ServerHub.hpp/cpp : Hub TCP central
  • Networking/server/ServerRoom.hpp/cpp : Room UDP pour partie
  • Networking/server/RoomManager.hpp/cpp : Gestion des rooms actives
  • Networking/server/Room.hpp/cpp : Processus room (fork/thread)

Packets

  • Networking/LoginPackets.hpp : LOGIN, LOGIN_RESPONSE
  • Networking/RoomPackets.hpp : ROOMCONNECTION
  • RType-Game/Packets/LobbyPacket.hpp/cpp : LOBBY actions
  • Networking/PacketsManager.hpp/cpp : Sérialisation/compression

Utilitaires

  • compile_lobby_client.sh : Script de compilation client test
  • README_LOBBY_SYSTEM.md : Cette documentation

🐛 Problèmes connus et limitations

Cosmétiques

  • Affichage du port en signed int (affiche -20815 au lieu de 44721) → Conversion uint16 nécessaire

Fonctionnelles

  • Pas de gestion de déconnexion propre des rooms
  • Pas de heartbeat pour détecter clients morts
  • Pas de limite de temps pour création de room
  • Pas de vérification de username unique

À implémenter

  • Interface graphique SFML complète (LobbyState + backend)
  • Network metrics UI (RTT, bandwidth, packet loss)
  • Gestion du chat dans le lobby
  • Spectateur mode
  • Reconnexion automatique

📚 Références

  • Architecture ECS : Architecture/ECS.drawio
  • Architecture Server/Client : Architecture/SERVER_CLIENT.drawio
  • Guide Track #2 : GUIDE_LOBBY_UI_CORRECTED.md
  • Status Track #2 : DEV_C_STATUS_ACTUEL.md

👥 Auteurs

Projet R-Type - Epitech 2025 Team: G-CPP-500-COT-5-1-rtype-2

Date de dernière mise à jour : 9 Novembre 2025