|
@@ -0,0 +1,496 @@
|
|
|
+#include "core/Network.h"
|
|
|
+
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+void coreInitInPacket(CoreInPacket* in, const void* data, size_t n) {
|
|
|
+ in->data = data;
|
|
|
+ in->size = n;
|
|
|
+ in->index = 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadU8(CoreInPacket* in, u8* u) {
|
|
|
+ return coreInPacketRead(in, u, sizeof(*u));
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadU16(CoreInPacket* in, u16* u) {
|
|
|
+ if(coreInPacketRead(in, u, sizeof(*u))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ *u = ntohs(*u);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadU32(CoreInPacket* in, u32* u) {
|
|
|
+ if(coreInPacketRead(in, u, sizeof(*u))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ *u = ntohl(*u);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadI8(CoreInPacket* in, i8* i) {
|
|
|
+ u8 u;
|
|
|
+ if(coreInPacketReadU8(in, &u)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ *i = (i8)((i32)u - (i32)128);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadI16(CoreInPacket* in, i16* i) {
|
|
|
+ u16 u;
|
|
|
+ if(coreInPacketReadU16(in, &u)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ *i = (i16)((i32)u - (i32)32768);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadI32(CoreInPacket* in, i32* i) {
|
|
|
+ u32 u;
|
|
|
+ if(coreInPacketReadU32(in, &u)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if(u < 2147483648) {
|
|
|
+ *i = (i32)((i32)u - (i32)2147483648);
|
|
|
+ } else {
|
|
|
+ *i = (i32)(u - (u32)2147483648);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketReadFloat(CoreInPacket* in, float* f) {
|
|
|
+ u32 u;
|
|
|
+ static_assert(sizeof(u) == sizeof(*f), "float and u32 size do not match");
|
|
|
+ if(coreInPacketReadU32(in, &u)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ memcpy(&f, &u, sizeof(float));
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+size_t coreInPacketReadString(CoreInPacket* in, char* buffer, size_t n) {
|
|
|
+ if(n == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ u16 size;
|
|
|
+ if(coreInPacketReadU16(in, &size)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ size_t end = size;
|
|
|
+ n--;
|
|
|
+ while(n-- > 0 && end-- > 0) {
|
|
|
+ u8 u;
|
|
|
+ if(coreInPacketReadU8(in, &u)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ *(buffer++) = (char)u;
|
|
|
+ }
|
|
|
+ *buffer = '\0';
|
|
|
+ return size;
|
|
|
+}
|
|
|
+
|
|
|
+bool coreInPacketRead(CoreInPacket* in, void* buffer, size_t n) {
|
|
|
+ if(in->index + n > in->size) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ memcpy(buffer, in->data + in->index, n);
|
|
|
+ in->index += n;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void coreInitOutPacket(CoreOutPacket* out) {
|
|
|
+ coreInitBuffer(&out->data);
|
|
|
+}
|
|
|
+
|
|
|
+void coreDestroyOutPacket(CoreOutPacket* out) {
|
|
|
+ coreDestroyBuffer(&out->data);
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteU8(CoreOutPacket* out, u8 u) {
|
|
|
+ coreAddSizedBufferData(&out->data, &u, sizeof(u));
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteU16(CoreOutPacket* out, u16 u) {
|
|
|
+ u = htons(u);
|
|
|
+ coreAddSizedBufferData(&out->data, &u, sizeof(u));
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteU32(CoreOutPacket* out, u32 u) {
|
|
|
+ u = htonl(u);
|
|
|
+ coreAddSizedBufferData(&out->data, &u, sizeof(u));
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteI8(CoreOutPacket* out, i8 i) {
|
|
|
+ if(i < 0) {
|
|
|
+ return coreOutPacketWriteU8(out, (u8)((i32)i + (i32)128));
|
|
|
+ }
|
|
|
+ return coreOutPacketWriteU8(out, (u8)((u32)i + (u32)128));
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteI16(CoreOutPacket* out, i16 i) {
|
|
|
+ if(i < 0) {
|
|
|
+ return coreOutPacketWriteU16(out, (u16)((i32)i + (i32)32768));
|
|
|
+ }
|
|
|
+ return coreOutPacketWriteU16(out, (u16)((u32)i + (u32)32768));
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteI32(CoreOutPacket* out, i32 i) {
|
|
|
+ if(i < 0) {
|
|
|
+ return coreOutPacketWriteU32(out, (u32)(i + (i32)2147483648));
|
|
|
+ }
|
|
|
+ return coreOutPacketWriteU32(out, (u32)((u32)i + (u32)2147483648));
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteFloat(CoreOutPacket* out, float f) {
|
|
|
+ u32 u;
|
|
|
+ static_assert(sizeof(u) == sizeof(f), "float and u32 size do not match");
|
|
|
+ memcpy(&u, &f, sizeof(float));
|
|
|
+ return coreOutPacketWriteU32(out, u);
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWriteString(CoreOutPacket* out, const char* buffer,
|
|
|
+ size_t n) {
|
|
|
+ size_t end = n > 65535 ? 65535 : n;
|
|
|
+ coreOutPacketWriteU16(out, (u16)end);
|
|
|
+ for(size_t i = 0; i < end; i++) {
|
|
|
+ coreOutPacketWriteU8(out, (u8)(*(buffer++)));
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+CoreOutPacket* coreOutPacketWrite(CoreOutPacket* out, const void* buffer,
|
|
|
+ size_t n) {
|
|
|
+ coreAddSizedBufferData(&out->data, buffer, n);
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+bool ENet::add() {
|
|
|
+ if(enetCounter == 0 && enet_initialize() != 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ enetCounter++;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void ENet::remove() {
|
|
|
+ if(enetCounter > 0 && --enetCounter == 0) {
|
|
|
+ enet_deinitialize();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static_assert(sizeof(enet_uint16) == sizeof(Client::Port),
|
|
|
+ "client port has wrong type");
|
|
|
+
|
|
|
+static ENetHost* client = nullptr;
|
|
|
+static ENetPeer* connection = nullptr;
|
|
|
+static int connectTicks = 0;
|
|
|
+static int connectTimeoutTicks = 0;
|
|
|
+static int disconnectTicks = 0;
|
|
|
+static int disconnectTimeoutTicks = 0;
|
|
|
+static Client::OnConnect onConnect = []() {};
|
|
|
+static Client::OnDisconnect onDisconnect = []() {};
|
|
|
+static Client::OnPacket onPacket = [](InPacket&) {};
|
|
|
+
|
|
|
+Error Client::start() {
|
|
|
+ if(client != nullptr) {
|
|
|
+ return {"already started"};
|
|
|
+ } else if(ENet::add()) {
|
|
|
+ return {"cannot initialize enet"};
|
|
|
+ }
|
|
|
+ client = enet_host_create(nullptr, 1, 2, 0, 0);
|
|
|
+ if(client == nullptr) {
|
|
|
+ ENet::remove();
|
|
|
+ return {"cannot create enet client host"};
|
|
|
+ }
|
|
|
+ return {};
|
|
|
+}
|
|
|
+
|
|
|
+void Client::stop() {
|
|
|
+ if(connection != nullptr) {
|
|
|
+ onDisconnect();
|
|
|
+ enet_peer_disconnect_now(connection, 0);
|
|
|
+ connection = nullptr;
|
|
|
+ }
|
|
|
+ if(client != nullptr) {
|
|
|
+ enet_host_destroy(client);
|
|
|
+ ENet::remove();
|
|
|
+ client = nullptr;
|
|
|
+ }
|
|
|
+ connectTicks = 0;
|
|
|
+ disconnectTicks = 0;
|
|
|
+}
|
|
|
+
|
|
|
+Error Client::connect(const char* server, Port port, int timeoutTicks) {
|
|
|
+ if(client == nullptr) {
|
|
|
+ return {"client not started"};
|
|
|
+ } else if(connection != nullptr) {
|
|
|
+ return {"connection already exists"};
|
|
|
+ }
|
|
|
+
|
|
|
+ ENetAddress address;
|
|
|
+ memset(&address, 0, sizeof(ENetAddress));
|
|
|
+ enet_address_set_host(&address, server);
|
|
|
+ address.port = port;
|
|
|
+
|
|
|
+ connection = enet_host_connect(client, &address, 3, 0);
|
|
|
+ if(connection == nullptr) {
|
|
|
+ return {"cannot create connection"};
|
|
|
+ }
|
|
|
+ connectTicks = 1;
|
|
|
+ connectTimeoutTicks = timeoutTicks;
|
|
|
+ return {};
|
|
|
+}
|
|
|
+
|
|
|
+void Client::disconnect(int timeoutTicks) {
|
|
|
+ if(connection == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ connectTicks = 0;
|
|
|
+ enet_peer_disconnect(connection, 0);
|
|
|
+ disconnectTicks = 1;
|
|
|
+ disconnectTimeoutTicks = timeoutTicks;
|
|
|
+}
|
|
|
+
|
|
|
+void Client::send(OutPacket& p, PacketSendMode mode) {
|
|
|
+ if(client != nullptr && connection != nullptr && connectTicks < 0) {
|
|
|
+ constexpr enet_uint32 flags[] = {ENET_PACKET_FLAG_RELIABLE, 0,
|
|
|
+ ENET_PACKET_FLAG_UNSEQUENCED};
|
|
|
+ enet_uint8 index = static_cast<enet_uint8>(mode);
|
|
|
+ enet_peer_send(connection, index,
|
|
|
+ enet_packet_create(
|
|
|
+ p.buffer,
|
|
|
+static_cast<size_t>(p.buffer.getLength()), flags[index]));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Client::tick() {
|
|
|
+ if(client == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ENetEvent e;
|
|
|
+ while(enet_host_service(client, &e, 0) > 0) {
|
|
|
+ switch(e.type) {
|
|
|
+ case ENET_EVENT_TYPE_CONNECT:
|
|
|
+ connectTicks = -1;
|
|
|
+ onConnect();
|
|
|
+ break;
|
|
|
+ case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
|
|
|
+ case ENET_EVENT_TYPE_DISCONNECT:
|
|
|
+ disconnectTicks = 0;
|
|
|
+ onDisconnect();
|
|
|
+ connection = nullptr;
|
|
|
+ break;
|
|
|
+ case ENET_EVENT_TYPE_NONE: return;
|
|
|
+ case ENET_EVENT_TYPE_RECEIVE:
|
|
|
+ InPacket in(e.packet->data,
|
|
|
+ static_cast<int>(e.packet->dataLength));
|
|
|
+ onPacket(in);
|
|
|
+ enet_packet_destroy(e.packet);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(connectTicks >= 1 && ++connectTicks > connectTimeoutTicks) {
|
|
|
+ connectTicks = 0;
|
|
|
+ disconnect(connectTimeoutTicks);
|
|
|
+ }
|
|
|
+ if(disconnectTicks >= 1 && ++disconnectTicks > disconnectTimeoutTicks) {
|
|
|
+ disconnectTicks = 0;
|
|
|
+ onDisconnect();
|
|
|
+ if(connection != nullptr) {
|
|
|
+ enet_peer_reset(connection);
|
|
|
+ connection = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Client::setConnectHandler(OnConnect oc) {
|
|
|
+ onConnect = oc;
|
|
|
+}
|
|
|
+
|
|
|
+void Client::setDisconnectHandler(OnDisconnect od) {
|
|
|
+ onDisconnect = od;
|
|
|
+}
|
|
|
+
|
|
|
+void Client::setPacketHandler(OnPacket op) {
|
|
|
+ onPacket = op;
|
|
|
+}
|
|
|
+
|
|
|
+void Client::resetHandler() {
|
|
|
+ onConnect = []() {};
|
|
|
+ onDisconnect = []() {};
|
|
|
+ onPacket = [](InPacket&) {};
|
|
|
+}
|
|
|
+
|
|
|
+bool Client::isConnecting() {
|
|
|
+ return connectTicks >= 1;
|
|
|
+}
|
|
|
+
|
|
|
+bool Client::isConnected() {
|
|
|
+ return connectTicks < 0;
|
|
|
+}
|
|
|
+
|
|
|
+static_assert(sizeof(enet_uint16) == sizeof(Server::Port),
|
|
|
+ "client port has wrong type");
|
|
|
+
|
|
|
+static ENetHost* server;
|
|
|
+static HashMap<Server::Client, ENetPeer*> clients;
|
|
|
+static Server::Client idCounter = 1;
|
|
|
+static Server::OnConnect onConnect = [](Server::Client) {};
|
|
|
+static Server::OnDisconnect onDisconnect = [](Server::Client) {};
|
|
|
+static Server::OnPacket onPacket = [](Server::Client, InPacket&) {};
|
|
|
+
|
|
|
+Error Server::start(Port port, int maxClients) {
|
|
|
+ if(maxClients <= 0) {
|
|
|
+ return {"invalid max client amount"};
|
|
|
+ } else if(server != nullptr) {
|
|
|
+ return {"already started"};
|
|
|
+ } else if(ENet::add()) {
|
|
|
+ return {"cannot initialize enet"};
|
|
|
+ }
|
|
|
+
|
|
|
+ ENetAddress address;
|
|
|
+ memset(&address, 0, sizeof(ENetAddress));
|
|
|
+ address.host = ENET_HOST_ANY;
|
|
|
+ address.port = port;
|
|
|
+
|
|
|
+ server = enet_host_create(&address, static_cast<unsigned
|
|
|
+int>(maxClients), 3, 0, 0); if(server == nullptr) { ENet::remove(); return
|
|
|
+{"cannot create enet server host"};
|
|
|
+ }
|
|
|
+ return {};
|
|
|
+}
|
|
|
+
|
|
|
+void Server::stop() {
|
|
|
+ if(server == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for(ENetPeer* peer : clients.values()) {
|
|
|
+ enet_peer_reset(peer);
|
|
|
+ }
|
|
|
+ enet_host_destroy(server);
|
|
|
+ server = nullptr;
|
|
|
+ ENet::remove();
|
|
|
+}
|
|
|
+
|
|
|
+static void writeId(ENetPeer* peer, Server::Client id) {
|
|
|
+ static_assert(sizeof(peer->data) >= sizeof(id),
|
|
|
+ "private data not big enough for id");
|
|
|
+ memcpy(&(peer->data), &id, sizeof(id));
|
|
|
+}
|
|
|
+
|
|
|
+static Server::Client getId(ENetPeer* peer) {
|
|
|
+ Server::Client id = -1;
|
|
|
+ memcpy(&id, &(peer->data), sizeof(id));
|
|
|
+ return id;
|
|
|
+}
|
|
|
+
|
|
|
+static void handleConnect(ENetEvent& e) {
|
|
|
+ Server::Client id = idCounter++;
|
|
|
+ if(clients.tryEmplace(id, e.peer)) {
|
|
|
+ LOG_WARNING("id is connected twice");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ writeId(e.peer, id);
|
|
|
+ onConnect(id);
|
|
|
+}
|
|
|
+
|
|
|
+static void handlePacket(ENetEvent& e) {
|
|
|
+ if(e.peer->data == nullptr) {
|
|
|
+ LOG_WARNING("client without data sent package");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Server::Client id = getId(e.peer);
|
|
|
+ InPacket in(e.packet->data, static_cast<int>(e.packet->dataLength));
|
|
|
+ onPacket(id, in);
|
|
|
+}
|
|
|
+
|
|
|
+static void handleDisconnect(ENetEvent& e) {
|
|
|
+ if(e.peer->data == nullptr) {
|
|
|
+ LOG_WARNING("client without data disconnected");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Server::Client id = getId(e.peer);
|
|
|
+ onDisconnect(id);
|
|
|
+ if(clients.remove(id)) {
|
|
|
+ LOG_WARNING("removed non existing client");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Server::tick() {
|
|
|
+ if(server == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ENetEvent e;
|
|
|
+ while(enet_host_service(server, &e, 0) > 0) {
|
|
|
+ switch(e.type) {
|
|
|
+ case ENET_EVENT_TYPE_CONNECT: handleConnect(e); break;
|
|
|
+ case ENET_EVENT_TYPE_RECEIVE:
|
|
|
+ handlePacket(e);
|
|
|
+ enet_packet_destroy(e.packet);
|
|
|
+ break;
|
|
|
+ case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
|
|
|
+ case ENET_EVENT_TYPE_DISCONNECT: handleDisconnect(e); break;
|
|
|
+ case ENET_EVENT_TYPE_NONE: return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static ENetPacket* fromBuffer(const Buffer& buffer, int index) {
|
|
|
+ constexpr enet_uint32 flags[] = {ENET_PACKET_FLAG_RELIABLE, 0,
|
|
|
+ ENET_PACKET_FLAG_UNSEQUENCED};
|
|
|
+ return enet_packet_create(
|
|
|
+ buffer, static_cast<unsigned int>(buffer.getLength()),
|
|
|
+flags[index]);
|
|
|
+}
|
|
|
+
|
|
|
+void Server::send(const OutPacket& p, PacketSendMode mode) {
|
|
|
+ if(server != nullptr) {
|
|
|
+ int index = static_cast<int>(mode);
|
|
|
+ enet_host_broadcast(server, static_cast<enet_uint8>(index),
|
|
|
+ fromBuffer(p.buffer, index));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Server::send(Server::Client client, const OutPacket& p,
|
|
|
+ PacketSendMode mode) {
|
|
|
+ if(server == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ENetPeer** peer = clients.search(client);
|
|
|
+ if(peer != nullptr) {
|
|
|
+ int index = static_cast<int>(mode);
|
|
|
+ enet_peer_send(*peer, static_cast<enet_uint8>(index),
|
|
|
+ fromBuffer(p.buffer, index));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Server::disconnect(Client client) {
|
|
|
+ ENetPeer** peer = clients.search(client);
|
|
|
+ if(peer != nullptr) {
|
|
|
+ enet_peer_disconnect(*peer, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Server::setConnectHandler(OnConnect oc) {
|
|
|
+ onConnect = oc;
|
|
|
+}
|
|
|
+
|
|
|
+void Server::setDisconnectHandler(OnDisconnect od) {
|
|
|
+ onDisconnect = od;
|
|
|
+}
|
|
|
+
|
|
|
+void Server::setPacketHandler(OnPacket op) {
|
|
|
+ onPacket = op;
|
|
|
+}
|
|
|
+
|
|
|
+void Server::resetHandler() {
|
|
|
+ onConnect = [](Server::Client) {};
|
|
|
+ onDisconnect = [](Server::Client) {};
|
|
|
+ onPacket = [](Server::Client, InPacket&) {};
|
|
|
+}*/
|