r-type  0.0.0
R-Type main
Loading...
Searching...
No Matches
asioClient.cpp
Go to the documentation of this file.
1#include <cstring>
2#include <iostream>
3
4#include <asio.hpp>
5
7
8using asio::ip::udp;
9
10eng::AsioClient::AsioClient() : m_socket(m_ioContext) {}
11
12void eng::AsioClient::connect(const std::string &host, uint16_t port)
13{
14 try
15 {
16 const asio::ip::address addr = asio::ip::make_address(host);
17 m_serverEndpoint = udp::endpoint(addr, port);
18
19 m_socket.open(udp::v4());
20
21 m_workGuard = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type>>(
22 asio::make_work_guard(m_ioContext));
23 m_ioThread = std::thread(
24 [this]()
25 {
26 try
27 {
28 m_ioContext.run();
29 }
30 catch (const std::exception &e)
31 {
32 std::cerr << "[AsioClient] IO context exception: " << e.what() << "\n";
33 }
34 });
35
36 startReceive();
37 m_connected = true;
38
39 std::cout << "[AsioClient] Connecté au serveur " << host << ":" << port << "\n";
40 }
41 catch (const std::exception &e)
42 {
43 std::cerr << "[AsioClient] Erreur de connexion: " << e.what() << "\n";
44 }
45}
46
48{
49 if (m_connected)
50 {
51 sendDisconnect();
52
53 if (m_workGuard)
54 {
55 asio::post(m_ioContext,
56 [this]()
57 {
58 asio::error_code ec;
59 m_socket.close(ec);
60 });
61 m_workGuard.reset();
62 }
63
64 m_ioContext.stop();
65
66 if (m_ioThread.joinable())
67 {
68 m_ioThread.join();
69 }
70
71 m_connected = false;
72 std::cout << "[AsioClient] Déconnecté du serveur\n";
73 }
74}
75
76void eng::AsioClient::sendConnect(const std::string &playerName) { sendConnectWithCaps(playerName, 0); }
77
78void eng::AsioClient::sendConnectWithCaps(const std::string &playerName, std::uint32_t clientCaps)
79{
80 rnp::PacketHeader header;
81 header.type = static_cast<std::uint8_t>(rnp::PacketType::CONNECT);
82
83 // Payload: name_len(1) | player_name[name_len] | client_caps(4, BE)
84 std::vector<uint8_t> payload;
85 std::uint8_t nameLen = std::min(static_cast<std::uint8_t>(playerName.size()), static_cast<std::uint8_t>(31));
86 payload.push_back(nameLen);
87 payload.insert(payload.end(), playerName.begin(), playerName.begin() + nameLen);
88
89 // client_caps (4 bytes, big endian)
90 payload.push_back(static_cast<uint8_t>((clientCaps >> 24) & 0xFF));
91 payload.push_back(static_cast<uint8_t>((clientCaps >> 16) & 0xFF));
92 payload.push_back(static_cast<uint8_t>((clientCaps >> 8) & 0xFF));
93 payload.push_back(static_cast<uint8_t>(clientCaps & 0xFF));
94
95 header.length = payload.size();
96 header.flags =
97 static_cast<std::uint16_t>(rnp::PacketFlags::RELIABLE) | static_cast<std::uint16_t>(rnp::PacketFlags::ACK_REQ);
98 header.reserved = 0;
99 header.sequence = ++m_sequenceNumber;
100 header.sessionId = 0; // Pas encore de session
101
102 m_clientCaps = clientCaps;
103 std::vector<uint8_t> buffer = rnp::serialize(header, payload.data());
104
105 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
106 [this](const asio::error_code &error, std::size_t bytesTransferred)
107 { handleSend(error, bytesTransferred); });
108}
109
111
113{
114 rnp::PacketHeader header;
115 header.type = static_cast<std::uint8_t>(rnp::PacketType::DISCONNECT);
116
117 // Payload: reason_code(2, BE)
118 std::vector<uint8_t> payload;
119 std::uint16_t reasonCode = static_cast<std::uint16_t>(reason);
120 payload.push_back(static_cast<uint8_t>((reasonCode >> 8) & 0xFF));
121 payload.push_back(static_cast<uint8_t>(reasonCode & 0xFF));
122
123 header.length = payload.size();
124 header.flags = 0;
125 header.reserved = 0;
126 header.sequence = ++m_sequenceNumber;
127 header.sessionId = m_sessionId;
128
129 std::vector<uint8_t> buffer = rnp::serialize(header, payload.data());
130
131 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
132 [this](const asio::error_code &error, std::size_t bytesTransferred)
133 { handleSend(error, bytesTransferred); });
134}
135
136void eng::AsioClient::sendPlayerInput(uint8_t direction, uint8_t shooting)
137{
138 rnp::PacketHeader header;
139 header.type = static_cast<std::uint8_t>(rnp::PacketType::PLAYER_INPUT);
140 header.length = 2;
141 header.flags = 0;
142 header.reserved = 0;
143 header.sequence = ++m_sequenceNumber;
144 header.sessionId = m_sessionId;
145
146 uint8_t payload[2] = {direction, shooting};
147 std::vector<uint8_t> buffer = rnp::serialize(header, payload);
148
149 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
150 [this](const asio::error_code &error, std::size_t bytesTransferred)
151 { handleSend(error, bytesTransferred); });
152}
153
154void eng::AsioClient::sendPlayerInputAsEvent(std::uint16_t playerId, uint8_t direction, uint8_t shooting,
155 uint32_t clientTimeMs)
156{
159 ev.entityId = playerId;
160
161 // Data: buttons(2, BE) | direction(1) | shooting(1) | client_time_ms(4, BE)
162 std::uint16_t buttons = 0; // TODO: map from direction/shooting to buttons
163 ev.data.push_back(static_cast<uint8_t>((buttons >> 8) & 0xFF));
164 ev.data.push_back(static_cast<uint8_t>(buttons & 0xFF));
165 ev.data.push_back(direction);
166 ev.data.push_back(shooting);
167 ev.data.push_back(static_cast<uint8_t>((clientTimeMs >> 24) & 0xFF));
168 ev.data.push_back(static_cast<uint8_t>((clientTimeMs >> 16) & 0xFF));
169 ev.data.push_back(static_cast<uint8_t>((clientTimeMs >> 8) & 0xFF));
170 ev.data.push_back(static_cast<uint8_t>(clientTimeMs & 0xFF));
171
172 std::vector<rnp::EventRecord> events;
173 events.push_back(ev);
174
175 const std::vector<uint8_t> eventsPayload = rnp::serializeEvents(events);
176
177 rnp::PacketHeader header;
178 header.type = static_cast<std::uint8_t>(rnp::PacketType::ENTITY_EVENT);
179 header.length = static_cast<std::uint16_t>(eventsPayload.size());
180 header.flags = 0;
181 header.reserved = 0;
182 header.sequence = ++m_sequenceNumber;
183 header.sessionId = m_sessionId;
184
185 std::vector<uint8_t> buffer = rnp::serialize(header, eventsPayload.data());
186
187 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
188 [this](const asio::error_code &error, std::size_t bytesTransferred)
189 { handleSend(error, bytesTransferred); });
190}
191
193{
194 rnp::PacketHeader header;
195 header.type = static_cast<std::uint8_t>(rnp::PacketType::PING);
196 header.length = 0;
197 header.flags = 0;
198 header.reserved = 0;
199 header.sequence = ++m_sequenceNumber;
200 header.sessionId = m_sessionId;
201
202 std::vector<uint8_t> buffer = rnp::serialize(header);
203
204 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
205 [this](const asio::error_code &error, std::size_t bytesTransferred)
206 { handleSend(error, bytesTransferred); });
207}
208
209void eng::AsioClient::sendPing(std::uint32_t nonce, std::uint32_t sendTimeMs)
210{
211 rnp::PacketHeader header;
212 header.type = static_cast<std::uint8_t>(rnp::PacketType::PING);
213
214 // Payload: nonce(4, BE) | send_time_ms(4, BE)
215 std::vector<uint8_t> payload;
216
217 // nonce (4 bytes, big endian)
218 payload.push_back(static_cast<uint8_t>((nonce >> 24) & 0xFF));
219 payload.push_back(static_cast<uint8_t>((nonce >> 16) & 0xFF));
220 payload.push_back(static_cast<uint8_t>((nonce >> 8) & 0xFF));
221 payload.push_back(static_cast<uint8_t>(nonce & 0xFF));
222
223 // send_time_ms (4 bytes, big endian)
224 payload.push_back(static_cast<uint8_t>((sendTimeMs >> 24) & 0xFF));
225 payload.push_back(static_cast<uint8_t>((sendTimeMs >> 16) & 0xFF));
226 payload.push_back(static_cast<uint8_t>((sendTimeMs >> 8) & 0xFF));
227 payload.push_back(static_cast<uint8_t>(sendTimeMs & 0xFF));
228
229 header.length = payload.size();
230 header.flags = 0;
231 header.reserved = 0;
232 header.sequence = ++m_sequenceNumber;
233 header.sessionId = m_sessionId;
234
235 std::vector<uint8_t> buffer = rnp::serialize(header, payload.data());
236
237 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
238 [this](const asio::error_code &error, std::size_t bytesTransferred)
239 { handleSend(error, bytesTransferred); });
240}
241
242void eng::AsioClient::sendAck(std::uint32_t cumulative, std::uint32_t ackBits)
243{
244 rnp::PacketHeader header;
245 header.type = static_cast<std::uint8_t>(rnp::PacketType::ACK);
246
247 // Payload: cumulative_ack(4, BE) | ack_bits(4, BE)
248 std::vector<uint8_t> payload;
249
250 // cumulative_ack (4 bytes, big endian)
251 payload.push_back(static_cast<uint8_t>((cumulative >> 24) & 0xFF));
252 payload.push_back(static_cast<uint8_t>((cumulative >> 16) & 0xFF));
253 payload.push_back(static_cast<uint8_t>((cumulative >> 8) & 0xFF));
254 payload.push_back(static_cast<uint8_t>(cumulative & 0xFF));
255
256 // ack_bits (4 bytes, big endian)
257 payload.push_back(static_cast<uint8_t>((ackBits >> 24) & 0xFF));
258 payload.push_back(static_cast<uint8_t>((ackBits >> 16) & 0xFF));
259 payload.push_back(static_cast<uint8_t>((ackBits >> 8) & 0xFF));
260 payload.push_back(static_cast<uint8_t>(ackBits & 0xFF));
261
262 header.length = payload.size();
263 header.flags = 0;
264 header.reserved = 0;
265 header.sequence = ++m_sequenceNumber;
266 header.sessionId = m_sessionId;
267
268 m_lastAckSent = cumulative;
269 std::vector<uint8_t> buffer = rnp::serialize(header, payload.data());
270
271 m_socket.async_send_to(asio::buffer(buffer), m_serverEndpoint,
272 [this](const asio::error_code &error, std::size_t bytesTransferred)
273 { handleSend(error, bytesTransferred); });
274}
275
277{
278 m_packetHandlers[type] = handler;
279}
280
281void eng::AsioClient::setEventsHandler(std::function<void(const std::vector<rnp::EventRecord> &)> handler)
282{
283 m_eventsHandler = std::move(handler);
284}
285
286void eng::AsioClient::handleConnectAccept(const std::vector<uint8_t> &payload)
287{
288 if (payload.size() < 12)
289 {
290 std::cerr << "[AsioClient] Invalid CONNECT_ACCEPT payload\n";
291 return;
292 }
293
294 // session_id(4, BE)
295 m_sessionId = (static_cast<std::uint32_t>(payload[0]) << 24) | (static_cast<std::uint32_t>(payload[1]) << 16) |
296 (static_cast<std::uint32_t>(payload[2]) << 8) | static_cast<std::uint32_t>(payload[3]);
297
298 // tick_rate_hz(2, BE)
299 m_serverTickRate = (static_cast<std::uint16_t>(payload[4]) << 8) | static_cast<std::uint16_t>(payload[5]);
300
301 // mtu_payload_bytes(2, BE)
302 m_serverMtu = (static_cast<std::uint16_t>(payload[6]) << 8) | static_cast<std::uint16_t>(payload[7]);
303
304 // server_caps(4, BE)
305 m_serverCaps = (static_cast<std::uint32_t>(payload[8]) << 24) | (static_cast<std::uint32_t>(payload[9]) << 16) |
306 (static_cast<std::uint32_t>(payload[10]) << 8) | static_cast<std::uint32_t>(payload[11]);
307
308 std::cout << "[AsioClient] Connection accepted - Session ID: " << m_sessionId << ", Tick Rate: " << m_serverTickRate
309 << " Hz" << ", MTU: " << m_serverMtu << " bytes\n";
310}
311
313{
314 // Track reliable packets (simplified implementation)
315 // In production, implement proper acknowledgment tracking
316}
317
318void eng::AsioClient::processAck(const std::vector<uint8_t> &payload)
319{
320 if (payload.size() < 8)
321 {
322 return;
323 }
324
325 // cumulative_ack (4 bytes, big endian)
326 std::uint32_t cumulative = (static_cast<std::uint32_t>(payload[0]) << 24) |
327 (static_cast<std::uint32_t>(payload[1]) << 16) |
328 (static_cast<std::uint32_t>(payload[2]) << 8) | static_cast<std::uint32_t>(payload[3]);
329
330 // ack_bits (4 bytes, big endian)
331 std::uint32_t ackBits = (static_cast<std::uint32_t>(payload[4]) << 24) |
332 (static_cast<std::uint32_t>(payload[5]) << 16) |
333 (static_cast<std::uint32_t>(payload[6]) << 8) | static_cast<std::uint32_t>(payload[7]);
334
335 // Remove acknowledged packets from pending reliable
336 m_pendingReliable.erase(cumulative);
337
338 // Process ack bits for selective acknowledgment
339 for (int i = 0; i < 32; ++i)
340 {
341 if (ackBits & (1 << i))
342 {
343 m_pendingReliable.erase(cumulative - i - 1);
344 }
345 }
346}
347
348void eng::AsioClient::processWorldState(const std::vector<uint8_t> &payload)
349{
350 if (payload.size() < 6)
351 {
352 return;
353 }
354
355 // server_tick (4 bytes, big endian)
356 std::uint32_t serverTick = (static_cast<std::uint32_t>(payload[0]) << 24) |
357 (static_cast<std::uint32_t>(payload[1]) << 16) |
358 (static_cast<std::uint32_t>(payload[2]) << 8) | static_cast<std::uint32_t>(payload[3]);
359
360 // entity_count (2 bytes, big endian)
361 std::uint16_t entityCount = (static_cast<std::uint16_t>(payload[4]) << 8) | static_cast<std::uint16_t>(payload[5]);
362
363 std::cout << "[AsioClient] World state received - Tick: " << serverTick << ", Entities: " << entityCount << "\n";
364
365 // TODO: Parse entities and call appropriate handler
366}
367
368void eng::AsioClient::processEntityEvent(const std::vector<uint8_t> &payload)
369{
370 try
371 {
372 if (payload.size() < 6)
373 {
374 // Legacy format without server_tick
375 const std::vector<rnp::EventRecord> events = rnp::deserializeEvents(payload.data(), payload.size());
376 if (m_eventsHandler)
377 {
378 m_eventsHandler(events);
379 }
380 return;
381 }
382
383 // New format with server_tick
384 // server_tick (4 bytes, big endian)
385 std::uint32_t serverTick =
386 (static_cast<std::uint32_t>(payload[0]) << 24) | (static_cast<std::uint32_t>(payload[1]) << 16) |
387 (static_cast<std::uint32_t>(payload[2]) << 8) | static_cast<std::uint32_t>(payload[3]);
388
389 // event_count (2 bytes, big endian)
390 std::uint16_t eventCount =
391 (static_cast<std::uint16_t>(payload[4]) << 8) | static_cast<std::uint16_t>(payload[5]);
392
393 // Events serialized
394 const std::vector<rnp::EventRecord> events = rnp::deserializeEvents(payload.data() + 6, payload.size() - 6);
395
396 std::cout << "[AsioClient] Entity events received - Tick: " << serverTick << ", Events: " << eventCount << "\n";
397
398 if (m_eventsHandler)
399 {
400 m_eventsHandler(events);
401 }
402 }
403 catch (const std::exception &e)
404 {
405 std::cerr << "[AsioClient] Erreur de parsing ENTITY_EVENT: " << e.what() << "\n";
406 }
407}
408
410{
411 // Simplified retransmission logic
412 // In production, track timestamps and implement exponential backoff
413 for (const auto &[seq, data] : m_pendingReliable)
414 {
415 m_socket.async_send_to(asio::buffer(data), m_serverEndpoint,
416 [this](const asio::error_code &error, std::size_t bytesTransferred)
417 { handleSend(error, bytesTransferred); });
418 }
419}
420
422{
423 m_socket.async_receive_from(asio::buffer(m_recvBuffer), m_serverEndpoint,
424 [this](const asio::error_code &error, std::size_t bytesTransferred)
425 { handleReceive(error, bytesTransferred); });
426}
427
428void eng::AsioClient::handleReceive(const asio::error_code &error, std::size_t bytesTransferred)
429{
430 if (!error)
431 {
432 std::vector<uint8_t> data(m_recvBuffer.begin(), m_recvBuffer.begin() + bytesTransferred);
433 processPacket(data);
434 startReceive();
435 }
436 else if (error != asio::error::operation_aborted)
437 {
438 std::cerr << "[AsioClient] Erreur de réception: " << error.message() << "\n";
439 startReceive();
440 }
441}
442
443void eng::AsioClient::handleSend(const asio::error_code &error, std::size_t bytesTransferred)
444{
445 if (error)
446 {
447 std::cerr << "[AsioClient] Erreur d'envoi: " << error.message() << "\n";
448 }
449}
450
451void eng::AsioClient::processPacket(const std::vector<uint8_t> &data)
452{
453 try
454 {
455 rnp::PacketHeader header = rnp::deserializeHeader(data.data(), data.size());
456
457 std::vector<uint8_t> payload;
458 if (header.length > 0 && data.size() >= 16 + header.length)
459 {
460 payload.assign(data.begin() + 16, data.begin() + 16 + header.length);
461 }
462
463 // Vérifier la session ID (sauf pour CONNECT_ACCEPT)
464 if (static_cast<rnp::PacketType>(header.type) != rnp::PacketType::CONNECT_ACCEPT && m_sessionId != 0 &&
465 header.sessionId != m_sessionId)
466 {
467 std::cerr << "[AsioClient] Invalid session ID in packet\n";
468 return;
469 }
470
471 // Gérer les flags de fiabilité
472 if (header.flags & static_cast<std::uint16_t>(rnp::PacketFlags::RELIABLE))
473 {
474 handleReliablePacket(header);
475 }
476 if (header.flags & static_cast<std::uint16_t>(rnp::PacketFlags::ACK_REQ))
477 {
478 sendAck(header.sequence, 0);
479 }
480
481 switch (static_cast<rnp::PacketType>(header.type))
482 {
484 {
485 handleConnectAccept(payload);
486 break;
487 }
489 {
490 processWorldState(payload);
491 break;
492 }
494 {
495 processEntityEvent(payload);
496 break;
497 }
499 {
500 processAck(payload);
501 break;
502 }
504 {
505 if (payload.size() >= 4)
506 {
507 std::uint16_t errorCode =
508 (static_cast<std::uint16_t>(payload[0]) << 8) | static_cast<std::uint16_t>(payload[1]);
509 std::uint16_t msgLen =
510 (static_cast<std::uint16_t>(payload[2]) << 8) | static_cast<std::uint16_t>(payload[3]);
511 if (payload.size() >= 4 + msgLen)
512 {
513 std::string errorMsg(payload.begin() + 4, payload.begin() + 4 + msgLen);
514 std::cerr << "[AsioClient] Error " << errorCode << ": " << errorMsg << "\n";
515 }
516 }
517 break;
518 }
520 {
521 if (payload.size() >= 8)
522 {
523 std::uint32_t nonce = (static_cast<std::uint32_t>(payload[0]) << 24) |
524 (static_cast<std::uint32_t>(payload[1]) << 16) |
525 (static_cast<std::uint32_t>(payload[2]) << 8) |
526 static_cast<std::uint32_t>(payload[3]);
527 std::uint32_t sendTime = (static_cast<std::uint32_t>(payload[4]) << 24) |
528 (static_cast<std::uint32_t>(payload[5]) << 16) |
529 (static_cast<std::uint32_t>(payload[6]) << 8) |
530 static_cast<std::uint32_t>(payload[7]);
531 std::cout << "[AsioClient] PONG received - nonce: " << nonce << ", time: " << sendTime << "\n";
532 }
533 break;
534 }
535 default:
536 break;
537 }
538
539 // Appeler les handlers personnalisés
540 auto it = m_packetHandlers.find(static_cast<rnp::PacketType>(header.type));
541 if (it != m_packetHandlers.end())
542 {
543 it->second(header, payload);
544 }
545 }
546 catch (const std::exception &e)
547 {
548 std::cerr << "[AsioClient] Erreur de traitement du paquet: " << e.what() << "\n";
549 }
550}
This file contains the client network implementation for Asio.
void processPacket(const std::vector< uint8_t > &data)
void handleReliablePacket(const rnp::PacketHeader &header)
void handleReceive(const asio::error_code &error, std::size_t bytesTransferred)
void connect(const std::string &host, uint16_t port) override
void sendPlayerInput(uint8_t direction, uint8_t shooting)
void sendConnect(const std::string &playerName)
void processWorldState(const std::vector< uint8_t > &payload)
void sendPlayerInputAsEvent(std::uint16_t playerId, uint8_t direction, uint8_t shooting, uint32_t clientTimeMs)
void disconnect() override
void handleSend(const asio::error_code &error, std::size_t bytesTransferred)
void processAck(const std::vector< uint8_t > &payload)
std::function< void(const rnp::PacketHeader &, const std::vector< uint8_t > &)> PacketHandler
void processEntityEvent(const std::vector< uint8_t > &payload)
void setEventsHandler(std::function< void(const std::vector< rnp::EventRecord > &)> handler)
void setPacketHandler(rnp::PacketType type, PacketHandler handler)
void retransmitReliable()
void sendAck(std::uint32_t cumulative, std::uint32_t ackBits)
void sendConnectWithCaps(const std::string &playerName, std::uint32_t clientCaps)
void handleConnectAccept(const std::vector< uint8_t > &payload)
DisconnectReason
Disconnect reason codes.
Definition Protocol.hpp:53
PacketType
Packet types according to RNP specification.
Definition Protocol.hpp:24
std::vector< uint8_t > serialize(const PacketHeader &header, const uint8_t *payload=nullptr)
Serialize packet with header and optional payload (Big Endian)
Definition Protocol.hpp:330
PacketHeader deserializeHeader(const uint8_t *data, const std::size_t size)
Deserialize packet header (Big Endian)
Definition Protocol.hpp:345
std::vector< EventRecord > deserializeEvents(const std::uint8_t *payload, const std::size_t length)
Deserialize ENTITY_EVENT payload into event records Format per event: type(1) | entity_id(4,...
Definition Protocol.hpp:253
std::vector< std::uint8_t > serializeEvents(const std::vector< EventRecord > &events)
Serialize events in ENTITY_EVENT format (TLV with entity_id) Format per event: type(1) | entity_id(4,...
Definition Protocol.hpp:217
Event record for ENTITY_EVENT packets (TLV format)
Definition Protocol.hpp:104
std::uint32_t entityId
Definition Protocol.hpp:106
std::vector< std::uint8_t > data
Definition Protocol.hpp:107
Packet header according to RNP specification (Big Endian) Total size: 16 bytes.
Definition Protocol.hpp:115
std::uint32_t sessionId
Definition Protocol.hpp:121
std::uint8_t type
Definition Protocol.hpp:116
std::uint16_t length
Definition Protocol.hpp:117
std::uint32_t sequence
Definition Protocol.hpp:120
std::uint16_t flags
Definition Protocol.hpp:118
std::uint16_t reserved
Definition Protocol.hpp:119