134 lines
4.9 KiB
C++
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;
|
|
};
|
|
} |