163 lines
6.2 KiB
C++
163 lines
6.2 KiB
C++
#pragma once
|
|
|
|
#include "data.hpp"
|
|
#include "PacketView.hpp"
|
|
|
|
namespace net {
|
|
|
|
class Server {
|
|
public:
|
|
Server(const connection& ct, int connections) {
|
|
if(connections > 0) {
|
|
auto addr = [&] {
|
|
if (auto addr = ct.toAddress())
|
|
return addr.value();
|
|
|
|
FATAL("Invalid ip address format");
|
|
}();
|
|
|
|
host = enet_host_create(&addr, connections, CHANNEL_COUNT, 0, 0);
|
|
if(host == nullptr) {
|
|
FATAL("Network server creation failed");
|
|
}
|
|
|
|
LOG_I("Listening on " << ct);
|
|
} else {
|
|
host = nullptr;
|
|
LOG_D("Local only server");
|
|
}
|
|
}
|
|
~Server() {
|
|
enet_host_destroy(host);
|
|
}
|
|
|
|
template<typename C, typename D, typename R>
|
|
void pull(C onConnect, D onDisconnect, R onData, int delay = 10, int count = 10) {
|
|
if (host == nullptr)
|
|
return;
|
|
|
|
ENetEvent event;
|
|
for(int i = 0; i < count && enet_host_service(host, &event, delay) > 0; i++) {
|
|
switch(event.type) {
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
onConnect(event.peer, event.data);
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
onDisconnect(event.peer, (disconnect_reason)event.data);
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE: {
|
|
onData(event.peer, event.packet, (channel_type)event.channelID);
|
|
enet_packet_destroy(event.packet);
|
|
break;
|
|
}
|
|
|
|
case ENET_EVENT_TYPE_NONE:
|
|
break;
|
|
}
|
|
}
|
|
#if TRACY_ENABLE
|
|
TracyPlot("SrvNetUpData", (int64_t)host->totalSentData);
|
|
host->totalSentData = 0;
|
|
TracyPlot("SrvNetUpPackets", (int64_t)host->totalSentPackets);
|
|
host->totalSentPackets = 0;
|
|
TracyPlot("SrvNetDownData", (int64_t)host->totalReceivedData);
|
|
host->totalReceivedData = 0;
|
|
TracyPlot("SrvNetDownPackets", (int64_t)host->totalReceivedPackets);
|
|
host->totalReceivedPackets = 0;
|
|
|
|
int64_t throttling = 0;
|
|
int64_t peerCount = 0;
|
|
iterPeers([&](peer_t *peer) {
|
|
throttling += ENET_PEER_PACKET_THROTTLE_SCALE - peer->packetThrottle;
|
|
peerCount++;
|
|
});
|
|
TracyPlot("SrvPeersCount", peerCount);
|
|
TracyPlot("SrvPeersThrottling", peerCount > 0 ? throttling * 100 / ENET_PEER_PACKET_THROTTLE_SCALE / peerCount : 0);
|
|
#endif
|
|
}
|
|
|
|
void disconnect(peer_t *peer, disconnect_reason reason) const {
|
|
enet_peer_disconnect(peer, (enet_uint32)reason);
|
|
}
|
|
|
|
template<typename D>
|
|
static D* GetPeerData(peer_t* peer) { return (D*)peer->data; }
|
|
|
|
template<typename Call>
|
|
void iterPeers(Call call) {
|
|
for (auto currentPeer = host->peers;
|
|
currentPeer < &host->peers[host->peerCount];
|
|
++currentPeer)
|
|
{
|
|
if (currentPeer->state == ENET_PEER_STATE_CONNECTED)
|
|
call(currentPeer);
|
|
}
|
|
}
|
|
|
|
/// Send to single peer
|
|
/// Expect salt_t at peer->data
|
|
bool sendTo(peer_t* peer, server_packet_type type, const void *data, size_t size, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
const auto salt = GetPeerData<salt_t>(peer);
|
|
if(salt == NULL) {
|
|
LOG_W("Contacting " << peer->address << " before handshake");
|
|
return false;
|
|
}
|
|
return send(peer, makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0), *salt).get(), channel);
|
|
}
|
|
template<typename D>
|
|
bool sendTo(peer_t* peer, server_packet_type type, const D& data, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
static_assert(!std::is_pointer<D>::value);
|
|
return sendTo(peer, type, &data, sizeof(data), channel, flags);
|
|
}
|
|
|
|
/// Send unsalt to single peer
|
|
bool send(peer_t* peer, packet_t* packet, channel_type channel) {
|
|
return enet_peer_send(peer, (enet_uint8)channel, packet) == 0;
|
|
}
|
|
bool send(peer_t* peer, server_packet_type type, const void *data, size_t size, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
return send(peer, makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0)).get(), channel);
|
|
}
|
|
template<typename D>
|
|
bool send(peer_t* peer, server_packet_type type, const D& data, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
static_assert(!std::is_pointer<D>::value);
|
|
return send(peer, type, &data, sizeof(data), channel, flags);
|
|
}
|
|
|
|
/// Send to all connected peers
|
|
void broadcast(packet_t* packet, channel_type channel) {
|
|
if (host != nullptr)
|
|
enet_host_broadcast(host, (enet_uint8)channel, packet);
|
|
}
|
|
void broadcast(server_packet_type type, const void *data, size_t size, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
broadcast(makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0)).get(), channel);
|
|
}
|
|
template<typename D>
|
|
void broadcast(server_packet_type type, const D& data, channel_type channel, std::optional<enet_uint32> flags = {}) {
|
|
static_assert(!std::is_pointer<D>::value);
|
|
broadcast(type, &data, sizeof(data), channel, flags);
|
|
}
|
|
|
|
static PacketWriter makePacket(server_packet_type type, const void* data, size_t size, enet_uint32 flags) {
|
|
assert(type >= server_packet_type::BROADCASTED);
|
|
auto packet = PacketWriter(sizeof(server_packet_type) + size, flags);
|
|
packet.write(type);
|
|
if (data != nullptr)
|
|
packet.write(data, size);
|
|
return packet;
|
|
}
|
|
static PacketWriter makePacket(server_packet_type type, const void* data, size_t size, enet_uint32 flags, salt_t salt) {
|
|
assert(type < server_packet_type::BROADCASTED);
|
|
auto packet = PacketWriter(sizeof(server_packet_type) + sizeof(salt_t) + size, flags);
|
|
packet.write(type);
|
|
packet.write(salt);
|
|
if (data != nullptr)
|
|
packet.write(data, size);
|
|
return packet;
|
|
}
|
|
|
|
private:
|
|
ENetHost *host;
|
|
};
|
|
} |