R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
NetworkMessages.hpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** Created by mael on 08/12/2025.
4** File description:
5** NetworkMessages.hpp
6*/
7
8#pragma once
9
10#include <cstdint>
11#include <stdexcept>
12#include <string>
13#include <vector>
14
31namespace NetworkMessages {
32
52 enum class MessageType : uint16_t {
53 // Connection messages (0x00xx)
54 HANDSHAKE_REQUEST = 0x0001,
55 HANDSHAKE_RESPONSE = 0x0002,
56 REGISTER_REQUEST = 0x0007,
57 REGISTER_RESPONSE = 0x0008,
58 LOGIN_REQUEST = 0x0009,
59 LOGIN_RESPONSE = 0x000A,
60 DISCONNECT = 0x0003,
61 KICK = 0x0004,
62 PING = 0x0005,
63 PONG = 0x0006,
64
65 // Client to Server gameplay messages (0x01xx)
66 C2S_PLAYER_INPUT = 0x0100,
67 C2S_JOIN_GAME = 0x0101,
68
69 // Client to Server lobby messages (0x03xx)
70 C2S_LIST_ROOMS = 0x0300,
71 C2S_CREATE_ROOM = 0x0301,
72 C2S_JOIN_ROOM = 0x0302,
73 C2S_LEAVE_ROOM = 0x0306,
74 C2S_AUTO_MATCHMAKING = 0x0308, // Auto-matchmaking request (trigger matchmaking)
75 C2S_UPDATE_AUTO_MM_PREF = 0x0309, // Update auto-matchmaking preference only
76 C2S_START_MATCHMAKING = 0x0303,
78 C2S_START_GAME = 0x0305,
79 C2S_CHAT_MESSAGE = 0x0307,
80
81 // Server to Client gameplay messages (0x02xx)
82 S2C_GAME_STATE = 0x0200,
83 S2C_GAME_START = 0x0201,
84 S2C_ENTITY_DESTROYED = 0x0202,
85 S2C_GAME_OVER = 0x0203,
86 S2C_GAMERULE_UPDATE = 0x0204,
87
88 // Server to Client lobby messages (0x04xx)
89 S2C_ROOM_LIST = 0x0400,
90 S2C_ROOM_CREATED = 0x0401,
91 S2C_JOINED_ROOM = 0x0402,
93 S2C_MATCH_FOUND = 0x0404,
94 S2C_ROOM_STATE = 0x0405,
95 S2C_CHAT_MESSAGE = 0x0406,
96 S2C_LEFT_ROOM = 0x0407,
97
98 UNKNOWN = 0xFFFF
99 };
100
101 // ============================================================================
102 // LOW-LEVEL PROTOCOL FUNCTIONS (Generic)
103 // ============================================================================
104
111 inline std::vector<uint8_t> createMessage(MessageType type, const std::vector<uint8_t> &payload) {
112 std::vector<uint8_t> packet;
113
114 // Write message type (2 bytes, little endian)
115 uint16_t typeValue = static_cast<uint16_t>(type);
116 packet.push_back(static_cast<uint8_t>(typeValue & 0xFF));
117 packet.push_back(static_cast<uint8_t>((typeValue >> 8) & 0xFF));
118
119 // Write payload length (4 bytes, little endian)
120 uint32_t length = static_cast<uint32_t>(payload.size());
121 packet.push_back(static_cast<uint8_t>(length & 0xFF));
122 packet.push_back(static_cast<uint8_t>((length >> 8) & 0xFF));
123 packet.push_back(static_cast<uint8_t>((length >> 16) & 0xFF));
124 packet.push_back(static_cast<uint8_t>((length >> 24) & 0xFF));
125
126 // Write payload
127 packet.insert(packet.end(), payload.begin(), payload.end());
128
129 return packet;
130 }
131
137 inline MessageType getMessageType(const std::vector<uint8_t> &packet) {
138 if (packet.size() < 6) {
139 // Minimum: 2 (type) + 4 (length)
141 }
142
143 uint16_t typeValue = packet[0] | (packet[1] << 8);
144 return static_cast<MessageType>(typeValue);
145 }
146
152 inline std::vector<uint8_t> getPayload(const std::vector<uint8_t> &packet) {
153 if (packet.size() < 6) {
154 return {};
155 }
156
157 // Read payload length
158 uint32_t length = packet[2] | (packet[3] << 8) | (packet[4] << 16) | (packet[5] << 24);
159
160 // Validate length is reasonable (max 10MB to prevent DoS attacks)
161 constexpr uint32_t MAX_PAYLOAD_SIZE = 10 * 1024 * 1024;
162 if (length > MAX_PAYLOAD_SIZE) {
163 return {};
164 }
165
166 // Validate the declared length doesn't exceed actual packet size
167 if (packet.size() < 6 + length) {
168 return {};
169 }
170
171 // Additional sanity check: ensure no integer overflow when adding 6 + length
172 if (length > packet.size() - 6) {
173 return {};
174 }
175
176 return std::vector<uint8_t>(packet.begin() + 6, packet.begin() + 6 + length);
177 }
178
179 // ============================================================================
180 // HIGH-LEVEL HELPER FUNCTIONS (Specific messages)
181 // ============================================================================
182
186 inline std::vector<uint8_t> serializeString(const std::string &str) {
187 std::vector<uint8_t> bytes;
188 uint32_t length = static_cast<uint32_t>(str.size());
189
190 // Write length
191 bytes.push_back(static_cast<uint8_t>(length & 0xFF));
192 bytes.push_back(static_cast<uint8_t>((length >> 8) & 0xFF));
193 bytes.push_back(static_cast<uint8_t>((length >> 16) & 0xFF));
194 bytes.push_back(static_cast<uint8_t>((length >> 24) & 0xFF));
195
196 // Write string data
197 bytes.insert(bytes.end(), str.begin(), str.end());
198
199 return bytes;
200 }
201
205 inline std::string deserializeString(const std::vector<uint8_t> &bytes, size_t &offset) {
206 if (bytes.size() < offset + 4) {
207 return "";
208 }
209
210 // Read length
211 uint32_t length =
212 bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24);
213
214 // Validate length is reasonable (max 1MB for strings to prevent DoS)
215 constexpr uint32_t MAX_STRING_SIZE = 1024 * 1024;
216 if (length > MAX_STRING_SIZE) {
217 return "";
218 }
219
220 offset += 4;
221
222 // Validate the declared length doesn't exceed actual buffer size
223 if (bytes.size() < offset + length) {
224 return "";
225 }
226
227 // Additional sanity check: ensure no integer overflow
228 if (length > bytes.size() - offset) {
229 return "";
230 }
231
232 std::string result(bytes.begin() + offset, bytes.begin() + offset + length);
233 offset += length;
234
235 return result;
236 }
237
238 // ============================================================================
239 // CONNECTION MESSAGES
240 // ============================================================================
241
249 inline std::vector<uint8_t> createConnectRequest(const std::string &playerName,
250 bool isSpectator = false) {
251 std::vector<uint8_t> payload = serializeString(playerName);
252
253 // Add spectator flag (1 byte)
254 payload.push_back(isSpectator ? 1 : 0);
255
257 }
258
267 inline std::string parseConnectRequest(const std::vector<uint8_t> &packet, bool &isSpectator) {
269 isSpectator = false;
270 return "";
271 }
272
273 auto payload = getPayload(packet);
274 size_t offset = 0;
275 std::string playerName = deserializeString(payload, offset);
276
277 // Read spectator flag if present (backwards compatible)
278 if (offset < payload.size()) {
279 isSpectator = (payload[offset] != 0);
280 } else {
281 isSpectator = false;
282 }
283
284 return playerName;
285 }
286
294 inline std::string parseConnectRequest(const std::vector<uint8_t> &packet) {
295 bool isSpectator = false;
296 return parseConnectRequest(packet, isSpectator);
297 }
298
305 inline std::vector<uint8_t> createConnectResponse(const std::string &message) {
306 auto payload = serializeString(message);
308 }
309
317 inline std::string parseConnectResponse(const std::vector<uint8_t> &packet) {
319 return "";
320 }
321
322 auto payload = getPayload(packet);
323 size_t offset = 0;
324 return deserializeString(payload, offset);
325 }
326
327} // namespace NetworkMessages
Network protocol with unified message format.
Definition Server.hpp:38
std::vector< uint8_t > createConnectRequest(const std::string &playerName, bool isSpectator=false)
Create HANDSHAKE_REQUEST message.
MessageType
All message types in the R-Type protocol.
std::string parseConnectResponse(const std::vector< uint8_t > &packet)
Parse HANDSHAKE_RESPONSE message.
std::string parseConnectRequest(const std::vector< uint8_t > &packet, bool &isSpectator)
Parse HANDSHAKE_REQUEST message.
std::vector< uint8_t > createConnectResponse(const std::string &message)
Create HANDSHAKE_RESPONSE message.
std::string deserializeString(const std::vector< uint8_t > &bytes, size_t &offset)
Deserialize bytes to string.
MessageType getMessageType(const std::vector< uint8_t > &packet)
Get message type from packet.
std::vector< uint8_t > createMessage(MessageType type, const std::vector< uint8_t > &payload)
Create a message with type and payload.
std::vector< uint8_t > serializeString(const std::string &str)
Serialize a string to bytes.
std::vector< uint8_t > getPayload(const std::vector< uint8_t > &packet)
Get payload from packet (without header)