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

134 lines
4.8 KiB
C++
Raw Normal View History

2020-09-22 20:37:09 +00:00
#pragma once
#include "data.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 ip 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, 1);
enet_host_destroy(host);
}
void pull(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:
onDisconnect(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::CHUNK) {
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;
memcpy(&l, event.packet->data + sizeof(server_packet_type) + sizeof(salt), sizeof(l));
salt ^= l;
LOG_D("Handshake done");
ready = true;
break;
}
onData(event.channelID, event.packet);
enet_packet_destroy(event.packet);
break;
}
case ENET_EVENT_TYPE_NONE:
break;
}
}
}
virtual void onDisconnect(enet_uint32 reason) {
LOG_D("Client disconnected with reason " << reason);
ready = false;
}
virtual void onData(enet_uint8 channel, ENetPacket* packet) {
LOG_D("Data from server " << packet->dataLength << " on " << channel);
}
bool send(client_packet_type type, ENetPacket* packet, channel_type channel) {
assert(packet->dataLength >= HEADER_SIZE);
if(!ready) {
LOG_W("Contacting server before handshake");
enet_packet_destroy(packet);
return false;
}
memcpy(packet->data, &type, sizeof(client_packet_type));
memcpy(packet->data + sizeof(client_packet_type), &salt, sizeof(salt));
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 = {}) {
auto packet = enet_packet_create(NULL, HEADER_SIZE + size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0));
memcpy(packet->data + HEADER_SIZE, data, size);
return send(type, packet, channel);
}
template<typename D>
bool send(client_packet_type type, const D& payload, channel_type channel, std::optional<enet_uint32> flags = {}) {
return send(type, &payload, sizeof(payload), channel, flags);
}
constexpr bool isReady() const { return ready; }
protected:
ENetHost *host;
ENetPeer *peer;
salt_t salt;
bool ready = false;
};
}