R-Type  2
Doom but in better
Loading...
Searching...
No Matches
NetworkManager.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2024
3** NetworkManager.cpp
4** File description:
5** Handle UDP sockets & Send/Receive messages between server and client
6*/
7
13#include <iostream>
15
16GUI::Network::NetworkManager::NetworkManager(const std::uint32_t entityId) : EntityNode(entityId) {}
17
19{
20 if (isConnected()) {
21 _disconnect();
22 }
23}
24
26{
27 try {
28 _connect();
29 PRETTY_INFO << "UDP Socket initialized on port " << _port << std::endl;
30 }
31 catch (const std::exception &e) {
32 PRETTY_CRITICAL << "Error initializing UDP socket: " << e.what() << std::endl;
33 }
34}
35
37{
38 try {
39 std::vector<uint8_t> buffer(1024);
40 asio::ip::udp::endpoint senderEndpoint;
41
42 size_t bytesReceived = _socket.receive_from(asio::buffer(buffer), senderEndpoint);
43 if (bytesReceived > 0) {
44 std::vector<uint8_t> receivedData(buffer.begin(), buffer.begin() + bytesReceived);
45 Packet packet = Packet::deserialize(receivedData);
46 /*// Debug part \\*/
47 std::cerr << packet.print() << std::endl;
48 /*// Debug part \\*/
49 }
50 }
51 catch (const std::exception &e) {
52 PRETTY_ERROR << "Error handling messages: " << e.what() << std::endl;
53 }
54}
55
57{
58 if (!isConnected()) {
59 std::cerr << "Cannot send message: No active connection." << std::endl;
60 return;
61 }
62
63 // if (message.size() > 65507) {
64 // std::cerr << "Message too large for UDP datagram." << std::endl;
65 // return;
66 // }
67
68 std::string serializedMessage = convertMessageToString(message);
69
70 try {
71 asio::ip::udp::endpoint remoteEndpoint(asio::ip::make_address(_ip), _port);
72 std::vector<uint8_t> data(serializedMessage.begin(), serializedMessage.end());
73
74 _socket.send_to(asio::buffer(data), remoteEndpoint);
75 PRETTY_DEBUG << "Message : " << std::hex << serializedMessage << " sent to " << _ip << ":" << _port << std::endl;
76 }
77 catch (const std::exception &e) {
78 PRETTY_CRITICAL << "Error sending message: " << e.what() << std::endl;
79 }
80}
81
83{
84 //core dump on this
85 MessageNode msg;
87 msg.id = 0;
88 std::memcpy(msg.info.username, _playerName.c_str(), 8);
89 msg.info.username[8] = '\0';
90 sendMessage(msg);
91}
92
94{
95 //std::cerr << "In is connected" << std::endl;
96 //std::cerr << "status: " << Recoded::myToString(_socket.is_open()) << std::endl;
97 return _socket.is_open();
98}
99
101{
102 float value;
103 std::memcpy(&value, bytes, sizeof(float));
104 return value;
105}
106
107std::string GUI::Network::NetworkManager::bytesToHex(const std::vector<uint8_t> &bytes)
108{
109 std::ostringstream oss;
110 for (uint8_t byte : bytes) {
111 oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte) << " ";
112 }
113 return oss.str();
114}
115
117{
118 // if (message.empty()) {
119 // return "[Error] Empty message received.";
120 // }
121
123 result.type = static_cast<MessageType>(message[0]);
124
125 switch (result.type) {
126 case MessageType::P_CONNECT: { // CONNECT
127 std::memcpy(result.info.username, message.data() + 1, 8);
128 result.info.username[8] = '\0';
129 break;
130 }
131 case MessageType::P_DISCONNECT: { // DISCONNECT
132 std::size_t id;
133 std::memcpy(&id, &message[1], sizeof(std::size_t));
134 PRECISE_DEBUG << "DISCONNECT Message\nEntity ID: " << id << "\n";
135 result.id = id;
136 break;
137 }
138 case MessageType::P_MOVE: { // MOVE
139 std::size_t id;
140 float x, y;
141 std::memcpy(&id, &message[1], sizeof(std::size_t));
142 x = bytesToFloat(&message[1 + sizeof(std::size_t)]);
143 y = bytesToFloat(&message[1 + sizeof(std::size_t) + sizeof(float)]);
144 PRETTY_DEBUG << "MOVE Message\nEntity ID: " << id << "\nPosition: (" << x << ", " << y << ")\n";
145 result.id = id;
146 result.info.coords.first = x;
147 result.info.coords.second = y;
148 break;
149 }
150 case MessageType::P_SHOOT: { // SHOOT
151 //std::cout << "Shoot" << std::endl;
152 std::size_t id;
153 std::memcpy(&id, &message[1], sizeof(std::size_t));
154 PRETTY_DEBUG << "SHOOT Message\nEntity ID: " << id << "\n";
155 result.id = id;
156 break;
157 }
158 case MessageType::P_SPAWN: { // SPAWN
159 //std::cout << "Spawn" << std::endl;
160 std::size_t id;
161 int asset;
162 float x, y;
163 std::memcpy(&id, &message[1], sizeof(std::size_t));
164 std::memcpy(&asset, &message[1 + sizeof(std::size_t)], sizeof(int));
165 x = bytesToFloat(&message[1 + sizeof(std::size_t) + sizeof(int)]);
166 y = bytesToFloat(&message[1 + sizeof(std::size_t) + sizeof(int) + sizeof(float)]);
167 PRETTY_CRITICAL << "SPAWN Message\nEntity ID: " << id << "\nAsset ID: " << asset << "\nPosition: (" << x << ", " << y << ")\n";
168 result.id = id;
169 result.info.assetId = asset;
170 result.info.coords.first = x;
171 result.info.coords.second = y;
172 break;
173 }
174 case MessageType::P_KILL: { // KILL
175 //std::cout << "Kill" << std::endl;
176 std::size_t id;
177 std::memcpy(&id, &message[1], sizeof(std::size_t));
178 PRETTY_DEBUG << "KILL Message\nEntity ID: " << id << "\n";
179 result.id = id;
180 break;
181 }
182 case MessageType::P_DAMAGE: { // DAMAGE
183 //std::cout << "Damage" << std::endl;
184 std::size_t id;
185 PRETTY_DEBUG << "DAMAGE Message\nEntity ID: " << id << "\n";
186 std::memcpy(&id, &message[1], sizeof(std::size_t));
187 result.id = id;
188 break;
189 }
190 case MessageType::P_STATUS: { // STATUS
191 //std::cout << "Status" << std::endl;
192 uint8_t status = message[1];
193 PRETTY_DEBUG << "STATUS Message\nStatus: ";
194 if (status == 0x00) {
195 PRETTY_DEBUG << "On going\n";
196 } else if (status == 0x01) {
197 PRETTY_DEBUG << "Victory\n";
198 } else if (status == 0xFF) {
199 PRETTY_DEBUG << "Defeat\n";
200 } else {
201 PRETTY_DEBUG << "Unknown\n";
202 }
203 break;
204 }
206 //std::cout << "Handshake" << std::endl;
207 PRETTY_DEBUG << "Handshake OK\n";
208 }
209 case MessageType::P_ERROR: { // ERROR
210 //std::cout << "Error" << std::endl;
211 uint8_t errorCode = message[1];
212 break;
213 }
214 default:
215 PRETTY_DEBUG << "Unknown Message Type\n";
216 break;
217 }
218
219 return result;
220}
221
223{
224 // if (message.empty()) {
225 // return "[Error] Empty message received.";
226 // }
227
228 std::vector<uint8_t> serializedMessage(1);
229 serializedMessage[0] = static_cast<uint8_t>(message.type);
230
231 switch (message.type) {
232 case MessageType::P_CONNECT: { // CONNECT
233 //std::cout << "Connect" << std::endl;
234 serializedMessage.resize(10);
235 std::memcpy(serializedMessage.data() + 1, message.info.username, 8);
236 serializedMessage[9] = 0;
237 break;
238 }
239 case MessageType::P_DISCONNECT: { // DISCONNECT
240 //std::cout << "Disconnect" << std::endl;
241 serializedMessage.resize(1 + sizeof(std::size_t));
242 std::memcpy(serializedMessage.data() + 1, &message.id, sizeof(std::size_t));
243 break;
244 }
245 case MessageType::P_MOVE: { // MOVE
246 //std::cout << "Move" << std::endl;
247 serializedMessage.resize(1 + sizeof(std::size_t) + sizeof(float) * 2);
248 std::memcpy(serializedMessage.data() + 1, &message.id, sizeof(std::size_t));
249 std::memcpy(serializedMessage.data() + 1 + sizeof(std::size_t), &message.info.coords.first, sizeof(float));
250 std::memcpy(serializedMessage.data() + 1 + sizeof(std::size_t) + sizeof(float), &message.info.coords.second, sizeof(float));
251 break;
252 }
253 case MessageType::P_SHOOT: { // SHOOT
254 //std::cout << "Shoot" << std::endl;
255 serializedMessage.resize(1 + sizeof(std::size_t));
256 std::memcpy(serializedMessage.data() + 1, &message.id, sizeof(std::size_t));
257 break;
258 }
259 default:
260 PRETTY_DEBUG << "Unknown Message Type\n";
261 break;
262 }
263
264 std::string result(serializedMessage.begin(), serializedMessage.end());
265 return result;
266}
267
268void GUI::Network::NetworkManager::setPort(const unsigned int port)
269{
270 PRETTY_DEBUG << "In the set Port" << std::endl;
271 if (_port == port) {
272 return;
273 };
274 PRETTY_SUCCESS << "Setting Port" << std::endl;
275 _port = port;
276 _connect();
277}
278
279void GUI::Network::NetworkManager::setIp(const std::string &ip)
280{
281 PRETTY_DEBUG << "In the set ip" << std::endl;
282 if (_ip == ip) {
283 return;
284 };
285 PRETTY_SUCCESS << "Setting ip" << std::endl;
286 _ip = ip;
287 _connect();
288}
289
290void GUI::Network::NetworkManager::setPlayerName(const std::string &playerName)
291{
292 if (_playerName == playerName) {
293 return;
294 }
295 PRETTY_SUCCESS << "Setting player name" << std::endl;
296 _playerName = playerName;
297}
298
299void GUI::Network::NetworkManager::setAddress(const std::string &ip, const unsigned int port)
300{
301 if (_ip == ip && _port == port) {
302 return;
303 }
304 PRETTY_SUCCESS << "Setting address" << std::endl;
305 _ip = ip;
306 _port = port;
307 _connect();
308}
309
311{
312 // if (!isConnected()) {
313 // std::cerr << "Cannot receive message: No active connection." << std::endl;
314 // return;
315 // }
316
317 try {
318 std::array<char, 2048> recvBuffer;
319 asio::ip::udp::endpoint remoteEndpoint;
320
321 while (_continueListening) {
322 std::error_code ec;
323 size_t bytesRecv = _socket.receive_from(
324 asio::buffer(recvBuffer), remoteEndpoint, 0, ec
325 );
326
327 if (ec) {
328 PRETTY_ERROR << "[Client] Receive error: " << ec.message() << "\n";
329 break;
330 }
331
332 if (bytesRecv > 0) {
333 std::vector<uint8_t> message(recvBuffer.begin(), recvBuffer.begin() + bytesRecv);
334 GUI::Network::MessageNode translatedMessage = translateMessage(message);
335 _bufferedMessages.push_back(translatedMessage);
336 PRETTY_DEBUG << "[Client] Translated Message from " << remoteEndpoint << ":\n" << Recoded::myToString(translatedMessage) << "\n";
337 }
338 }
339
340 }
341 catch (const std::exception &e) {
342 PRETTY_CRITICAL << "[Client] Exception in receiveMessage: " << e.what() << "\n";
343 }
344}
345
347{
348 _continueListening = true;
349}
350
352{
353 _continueListening = false;
354}
355
356std::vector<GUI::Network::MessageNode> GUI::Network::NetworkManager::getBufferedMessages()
357{
358 std::vector<GUI::Network::MessageNode> list = this->_bufferedMessages;
359 _bufferedMessages.clear();
360 return list;
361}
362
363void GUI::Network::NetworkManager::_connect()
364{
365 //std::cerr << "In the connect function " << std::endl;
366 PRETTY_DEBUG << "Connecting" << std::endl;
367 try {
368 if (isConnected()) {
369 _disconnect();
370 }
371
372 if (_ip.empty() || _port <= 0 || _port > 65535) {
373 throw std::invalid_argument("Invalid IP or port for connection.");
374 }
375
376 asio::ip::udp::endpoint remoteEndpoint(asio::ip::make_address(_ip), _port);
377
378 asio::error_code ec;
379 _socket.open(asio::ip::udp::v4(), ec);
380 if (ec) {
381 std::cerr << "[CLIENT] can't open socket bc: " << ec.message() << std::endl;
382 }
383 asio::ip::udp::endpoint localEndpoint(asio::ip::udp::v4(), 0);
384 _socket.bind(localEndpoint);
385
386 PRETTY_SUCCESS << "Connected to server at " << _ip << ":" << _port << std::endl;
387
388 Packet connectPacket(MessageType::P_HANDSHAKE);
389 std::vector<uint8_t> serializedData = Packet::serialize(connectPacket);
390
391 try {
392 asio::ip::udp::endpoint remoteEndpoint(asio::ip::make_address(_ip), _port);
393 _socket.send_to(asio::buffer(serializedData), remoteEndpoint);
394 _connectionActive = true;
395 }
396 catch (const std::exception &e) {
397 PRETTY_ERROR << "Error sending CONNECT packet: " << e.what() << std::endl;
398 }
399 }
400 catch (const std::exception &e) {
401 PRETTY_CRITICAL << "Error connecting to server: " << e.what() << std::endl;
402 _connectionActive = false;
403 }
404}
405
406void GUI::Network::NetworkManager::_disconnect()
407{
408 try {
409 if (_socket.is_open()) {
410 _socket.close();
411 PRECISE_DEBUG << "Disconnected from server at " << _ip << ":" << _port << std::endl;
412 } else {
413 PRETTY_ERROR << "Socket is already closed." << std::endl;
414 }
415 _connectionActive = false;
416 }
417 catch (const std::exception &e) {
418 PRETTY_CRITICAL << "Error disconnecting from server: " << e.what() << std::endl;
419 }
420}
#define PRETTY_ERROR
Error log with details and colour.
#define PRECISE_DEBUG
Debug log with precise details.
#define PRETTY_DEBUG
Debug log with details and colour.
#define PRETTY_INFO
Info log with details and colour.
#define PRETTY_CRITICAL
Critical log with details and colour.
#define PRETTY_SUCCESS
Success log with details and colour.
This file defines the NetworkManager class, which handles UDP socket communication for sending and re...
const bool isConnected() const
Checks if the connection is active.
void setAddress(const std::string &ip, const unsigned int port)
Sets both the IP address and port for communication.
~NetworkManager()
Destroy the Network Manager object.
std::vector< GUI::Network::MessageNode > getBufferedMessages()
NetworkManager(const std::uint32_t entityId=0)
Constructs a NetworkManager instance with an optional entity ID.
void startGame()
starts a game by sending CONNECT TO server.
void sendMessage(const MessageNode &message)
Sends a message to the server.
void initialize()
Initializes the UDP socket and binds it to a local endpoint. This function opens the socket and binds...
std::string bytesToHex(const std::vector< uint8_t > &bytes)
Converts a byte array to a hexadecimal string representation.
void setPlayerName(const std::string &name)
Sets the player's name for identification.
void handleMessages()
Handles incoming messages on the UDP socket.
void setIp(const std::string &ip)
Sets the IP address for communication.
void receiveMessage()
Receives messages continuously from the server.
std::string convertMessageToString(const MessageNode &message)
Translates a MessageNode into a string.
void setPort(const unsigned int port)
Sets the port for communication.
MessageNode translateMessage(const std::vector< uint8_t > &message)
Translates a message into a MessageNode.
float bytesToFloat(const uint8_t *bytes)
Converts a byte array to a floating-point value.
Represents a packet for the custom binary UDP protocol.
const std::string print() const
Prints a string representation of the packet's contents, for debugging purposes.
static std::vector< uint8_t > serialize(const Packet &packet)
Serializes a Packet object into a vector of bytes suitable for network transmission.
static Packet deserialize(const std::vector< uint8_t > &data)
Deserializes a packet from a vector of bytes received over the network.
MessageType
Enum for the different message types in the UDP protocol.
const std::string myToString(const Rect< RectType > &rectangle)
Converts a Rect<T> object to its string representation.
Definition Rect.hpp:223
std::pair< float, float > coords
Coordinates associated with the event, such as the position of an entity.
char username[9]
Username of player connecting.
int assetId
ID representing the game asset related to the event (e.g., entity or object).