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