16 const asio::ip::address addr = asio::ip::make_address(host);
17 m_serverEndpoint = udp::endpoint(addr, port);
19 m_socket.open(udp::v4());
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(
30 catch (
const std::exception &e)
32 std::cerr <<
"[AsioClient] IO context exception: " << e.what() <<
"\n";
39 std::cout <<
"[AsioClient] Connecté au serveur " << host <<
":" << port <<
"\n";
41 catch (
const std::exception &e)
43 std::cerr <<
"[AsioClient] Erreur de connexion: " << e.what() <<
"\n";
55 asio::post(m_ioContext,
66 if (m_ioThread.joinable())
72 std::cout <<
"[AsioClient] Déconnecté du serveur\n";
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);
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));
95 header.
length = payload.size();
99 header.
sequence = ++m_sequenceNumber;
102 m_clientCaps = clientCaps;
103 std::vector<uint8_t> buffer =
rnp::serialize(header, payload.data());
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); });
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));
123 header.
length = payload.size();
126 header.
sequence = ++m_sequenceNumber;
129 std::vector<uint8_t> buffer =
rnp::serialize(header, payload.data());
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); });
143 header.
sequence = ++m_sequenceNumber;
146 uint8_t payload[2] = {direction, shooting};
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); });
155 uint32_t clientTimeMs)
162 std::uint16_t buttons = 0;
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));
172 std::vector<rnp::EventRecord> events;
173 events.push_back(ev);
179 header.
length =
static_cast<std::uint16_t
>(eventsPayload.size());
182 header.
sequence = ++m_sequenceNumber;
185 std::vector<uint8_t> buffer =
rnp::serialize(header, eventsPayload.data());
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); });
199 header.
sequence = ++m_sequenceNumber;
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); });
215 std::vector<uint8_t> payload;
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));
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));
229 header.
length = payload.size();
232 header.
sequence = ++m_sequenceNumber;
235 std::vector<uint8_t> buffer =
rnp::serialize(header, payload.data());
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); });
248 std::vector<uint8_t> payload;
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));
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));
262 header.
length = payload.size();
265 header.
sequence = ++m_sequenceNumber;
268 m_lastAckSent = cumulative;
269 std::vector<uint8_t> buffer =
rnp::serialize(header, payload.data());
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); });
278 m_packetHandlers[type] = handler;
283 m_eventsHandler = std::move(handler);
288 if (payload.size() < 12)
290 std::cerr <<
"[AsioClient] Invalid CONNECT_ACCEPT payload\n";
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]);
299 m_serverTickRate = (
static_cast<std::uint16_t
>(payload[4]) << 8) |
static_cast<std::uint16_t
>(payload[5]);
302 m_serverMtu = (
static_cast<std::uint16_t
>(payload[6]) << 8) |
static_cast<std::uint16_t
>(payload[7]);
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]);
308 std::cout <<
"[AsioClient] Connection accepted - Session ID: " << m_sessionId <<
", Tick Rate: " << m_serverTickRate
309 <<
" Hz" <<
", MTU: " << m_serverMtu <<
" bytes\n";
320 if (payload.size() < 8)
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]);
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]);
336 m_pendingReliable.erase(cumulative);
339 for (
int i = 0; i < 32; ++i)
341 if (ackBits & (1 << i))
343 m_pendingReliable.erase(cumulative - i - 1);
350 if (payload.size() < 6)
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]);
361 std::uint16_t entityCount = (
static_cast<std::uint16_t
>(payload[4]) << 8) |
static_cast<std::uint16_t
>(payload[5]);
363 std::cout <<
"[AsioClient] World state received - Tick: " << serverTick <<
", Entities: " << entityCount <<
"\n";
372 if (payload.size() < 6)
378 m_eventsHandler(events);
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]);
390 std::uint16_t eventCount =
391 (
static_cast<std::uint16_t
>(payload[4]) << 8) |
static_cast<std::uint16_t
>(payload[5]);
394 const std::vector<rnp::EventRecord> events =
rnp::deserializeEvents(payload.data() + 6, payload.size() - 6);
396 std::cout <<
"[AsioClient] Entity events received - Tick: " << serverTick <<
", Events: " << eventCount <<
"\n";
400 m_eventsHandler(events);
403 catch (
const std::exception &e)
405 std::cerr <<
"[AsioClient] Erreur de parsing ENTITY_EVENT: " << e.what() <<
"\n";
413 for (
const auto &[seq, data] : m_pendingReliable)
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); });
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); });
432 std::vector<uint8_t> data(m_recvBuffer.begin(), m_recvBuffer.begin() + bytesTransferred);
436 else if (error != asio::error::operation_aborted)
438 std::cerr <<
"[AsioClient] Erreur de réception: " << error.message() <<
"\n";
447 std::cerr <<
"[AsioClient] Erreur d'envoi: " << error.message() <<
"\n";
457 std::vector<uint8_t> payload;
458 if (header.
length > 0 && data.size() >= 16 + header.
length)
460 payload.assign(data.begin() + 16, data.begin() + 16 + header.
length);
467 std::cerr <<
"[AsioClient] Invalid session ID in packet\n";
474 handleReliablePacket(header);
485 handleConnectAccept(payload);
490 processWorldState(payload);
495 processEntityEvent(payload);
505 if (payload.size() >= 4)
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)
513 std::string errorMsg(payload.begin() + 4, payload.begin() + 4 + msgLen);
514 std::cerr <<
"[AsioClient] Error " << errorCode <<
": " << errorMsg <<
"\n";
521 if (payload.size() >= 8)
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";
541 if (it != m_packetHandlers.end())
543 it->second(header, payload);
546 catch (
const std::exception &e)
548 std::cerr <<
"[AsioClient] Erreur de traitement du paquet: " << e.what() <<
"\n";
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.
PacketType
Packet types according to RNP specification.
std::vector< uint8_t > serialize(const PacketHeader &header, const uint8_t *payload=nullptr)
Serialize packet with header and optional payload (Big Endian)
PacketHeader deserializeHeader(const uint8_t *data, const std::size_t size)
Deserialize packet header (Big Endian)
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,...
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,...
Event record for ENTITY_EVENT packets (TLV format)
std::vector< std::uint8_t > data