Appearance
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
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
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
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
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
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
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 listeEnter: Sélectionner room / Créer room / ValiderBackspace: Supprimer caractère (mode CREATE_ROOM)C: Mode création de roomR: 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
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
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
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
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
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
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
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
2
Scénario typique
- Connexion : Le client se connecte au ServerHub sur le port TCP
- Login : Envoi username/password/game → Serveur crée ou assigne une room
- Liste rooms : Client demande LIST_ROOMS → Reçoit RoomInfo[] avec ports UDP
- Création room : Client crée "MyRoom" → Serveur démarre ServerRoom sur port UDP dynamique
- Jointure : Client rejoint room 1000 → Serveur retourne port UDP 36313
- Transition UDP : Client se connecte au port UDP pour commencer la partie
📁 Fichiers clés
Client
Networking/client/ClientHub.hpp/cpp: Gestion connexion TCP lobbyNetworking/client/ClientRoom.hpp/cpp: Gestion connexion UDP gameNetworking/client/main_lobby_test.cpp: Test en ligne de commandeRType-Game/States/LobbyState.hpp/cpp: Interface utilisateur lobby
Serveur
Networking/server/ServerHub.hpp/cpp: Hub TCP centralNetworking/server/ServerRoom.hpp/cpp: Room UDP pour partieNetworking/server/RoomManager.hpp/cpp: Gestion des rooms activesNetworking/server/Room.hpp/cpp: Processus room (fork/thread)
Packets
Networking/LoginPackets.hpp: LOGIN, LOGIN_RESPONSENetworking/RoomPackets.hpp: ROOMCONNECTIONRType-Game/Packets/LobbyPacket.hpp/cpp: LOBBY actionsNetworking/PacketsManager.hpp/cpp: Sérialisation/compression
Utilitaires
compile_lobby_client.sh: Script de compilation client testREADME_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