1
0
Fork 0
Univerxel/src/core/net/Client.hpp

134 lines
4.9 KiB
C++

#pragma once
#include "data.hpp"
#include "PacketView.hpp"
#include <Tracy.hpp>
namespace net {
class Client {
public:
static constexpr auto HEADER_SIZE = sizeof(client_packet_type) + sizeof(salt_t);
Client(const connection& ct) {
auto addr = [&] {
if(auto addr = ct.toAddress())
return addr.value();
FATAL("Invalid server address format");
}();
host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0);
if(host == nullptr) {
FATAL("Network client creation failed");
}
LOG_D("Connecting to " << ct);
salt = std::rand();
peer = enet_host_connect(host, &addr, CHANNEL_COUNT, salt);
if(peer == nullptr) {
FATAL("Cannot contact server " << ct);
}
if(ENetEvent event; enet_host_service(host, &event, TIMEOUT) <= 0 || event.type != ENET_EVENT_TYPE_CONNECT) {
enet_peer_reset(peer);
FATAL("Connection to server " << ct << " failed");
}
LOG_I("Connected to server " << ct);
}
~Client() {
LOG_D("Leaving server");
enet_peer_disconnect_now(peer, (enet_uint32)disconnect_reason::QUIT);
enet_host_destroy(host);
}
template<typename D, typename R>
void pull(R onData, D onDisconnect, int delay = 0) {
ENetEvent event;
while(enet_host_service(host, &event, delay) > 0) {
switch(event.type) {
case ENET_EVENT_TYPE_CONNECT:
LOG_D("Client reconnected");
break;
case ENET_EVENT_TYPE_DISCONNECT:
LOG_D("Client disconnected with reason " << event.data);
ready = false;
onDisconnect((disconnect_reason)event.data);
break;
case ENET_EVENT_TYPE_RECEIVE: {
if(event.packet->dataLength < sizeof(server_packet_type)) {
LOG_D("Empty packet from server");
break;
}
const server_packet_type type = static_cast<server_packet_type>(*event.packet->data);
if(type < server_packet_type::BROADCASTED) {
if(event.packet->dataLength < sizeof(server_packet_type) + sizeof(salt)) {
LOG_D("Wrong salted packet size");
break;
}
if(memcmp(&salt, event.packet->data + sizeof(server_packet_type), sizeof(salt)) != 0) {
LOG_D("Wrong server salt");
break;
}
}
if(type == server_packet_type::CHALLENGE) {
if(event.packet->dataLength != sizeof(server_packet_type) + 2 * sizeof(salt)) {
LOG_D("Wrong challenge packet size");
break;
}
salt_t l;
PacketReader(event.packet).read(l);
salt ^= l;
LOG_D("Handshake done");
ready = true;
break;
}
onData(event.packet, (channel_type)event.channelID);
enet_packet_destroy(event.packet);
break;
}
case ENET_EVENT_TYPE_NONE:
break;
}
}
TracyPlot("CltNetUp", (int64_t)host->outgoingBandwidth);
TracyPlot("CltNetDown", (int64_t)host->incomingBandwidth);
TracyPlot("CltNetRTT", (int64_t)peer->roundTripTime);
}
bool send(packet_t* packet, channel_type channel) {
return enet_peer_send(peer, (enet_uint8)channel, packet) == 0;
}
bool send(client_packet_type type, const void *data, size_t size, channel_type channel, std::optional<enet_uint32> flags = {}) {
return send(makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0), salt).get(), channel);
}
template<typename D>
bool send(client_packet_type type, const D& data, channel_type channel, std::optional<enet_uint32> flags = {}) {
return send(type, &data, sizeof(data), channel, flags);
}
static PacketWriter makePacket(client_packet_type type, const void* data, size_t size, enet_uint32 flags, salt_t salt) {
auto packet = PacketWriter(sizeof(client_packet_type) + sizeof(salt_t) + size, flags);
packet.write(type);
packet.write(salt);
if (data != nullptr)
packet.write(data, size);
return packet;
}
constexpr bool isReady() const { return ready; }
constexpr salt_t getSalt() const { return salt; }
protected:
ENetHost *host;
ENetPeer *peer;
salt_t salt;
bool ready = false;
};
}