Browse Source

completed overhaul of server and client

Kajetan Johannes Hammerle 2 years ago
parent
commit
65519cd914
13 changed files with 528 additions and 474 deletions
  1. 117 37
      network/Client.cpp
  2. 22 37
      network/Client.h
  3. 2 4
      network/ENet.cpp
  4. 4 11
      network/ENet.h
  5. 26 66
      network/Packet.cpp
  6. 23 39
      network/Packet.h
  7. 128 47
      network/Server.cpp
  8. 17 112
      network/Server.h
  9. 155 118
      tests/NetworkTests.cpp
  10. 25 1
      tests/Test.cpp
  11. 6 0
      tests/Test.h
  12. 3 2
      tests/UniquePointerTests.cpp
  13. 0 0
      utils/UniquePointer.h

+ 117 - 37
network/Client.cpp

@@ -1,73 +1,153 @@
-#include <cassert>
+#include "libs/enet/include/enet.h"
 
 #include "network/Client.h"
+#include "network/ENet.h"
 
-Client::Client() : client(nullptr), connection(nullptr) {
-    ENet::add();
-}
+static_assert(sizeof(enet_uint16) == sizeof(Client::Port),
+              "client port has wrong type");
 
-Client::~Client() {
-    disconnect();
-    enet_host_destroy(client);
-    ENet::remove();
-}
+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() {
-    assert(client == nullptr);
-    if(ENet::hasError()) {
+    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 {};
 }
 
-Error Client::connect(const char* server, Port port, int timeout) {
-    assert(client != nullptr);
-    assert(connection == nullptr);
+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));
-    ENetEvent event;
     enet_address_set_host(&address, server);
     address.port = port;
 
     connection = enet_host_connect(client, &address, 3, 0);
     if(connection == nullptr) {
-        return {"server is not available"};
-    }
-
-    if(enet_host_service(client, &event, timeout) <= 0 ||
-       event.type != ENET_EVENT_TYPE_CONNECT) {
-        disconnect();
-        return {"connection failed"};
+        return {"cannot create connection"};
     }
+    connectTicks = 1;
+    connectTimeoutTicks = timeoutTicks;
     return {};
 }
 
-void Client::disconnect() {
+void Client::disconnect(int timeoutTicks) {
     if(connection == nullptr) {
         return;
     }
-    ENetEvent e;
+    connectTicks = 0;
     enet_peer_disconnect(connection, 0);
-    while(enet_host_service(client, &e, 3000) > 0) {
+    disconnectTicks = 1;
+    disconnectTimeoutTicks = timeoutTicks;
+}
+
+void Client::send(OutPacket& p, PacketType pt) {
+    if(client != nullptr && connection != nullptr && connectTicks < 0) {
+        constexpr enet_uint32 flags[] = {ENET_PACKET_FLAG_RELIABLE, 0,
+                                         ENET_PACKET_FLAG_UNSEQUENCED};
+        int index = static_cast<int>(pt);
+        enet_peer_send(
+            connection, index,
+            enet_packet_create(p.buffer, 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_RECEIVE: enet_packet_destroy(e.packet); break;
-            case ENET_EVENT_TYPE_DISCONNECT: connection = nullptr; return;
-            default: break;
+            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, 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;
         }
     }
-    enet_peer_reset(connection);
-    connection = nullptr;
 }
 
-void Client::send(OutPacket& p) {
-    assert(client != nullptr);
-    if(p.packet != nullptr) {
-        p.packet->dataLength = p.index;
-        enet_peer_send(connection, p.channel, p.packet);
-        p.packet = 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;
 }

+ 22 - 37
network/Client.h

@@ -1,48 +1,33 @@
 #ifndef CLIENT_H
 #define CLIENT_H
 
-#include "network/ENet.h"
 #include "network/Packet.h"
 #include "utils/Error.h"
+#include "utils/Types.h"
 
-struct Client final {
-    typedef enet_uint16 Port;
+namespace Client {
+    typedef uint16 Port;
+    typedef void (*OnConnect)();
+    typedef void (*OnDisconnect)();
+    typedef void (*OnPacket)(InPacket&);
 
-private:
-    ENetHost* client;
-    ENetPeer* connection;
+    Error start();
+    void stop();
 
-public:
-    Client();
-    Client(const Client&) = delete;
-    Client(Client&&) = delete;
-    ~Client();
-    Client& operator=(const Client&) = delete;
-    Client& operator=(Client&&) = delete;
+    Error connect(const char* server, Port port, int timeoutTicks);
+    void disconnect(int timeoutTicks);
 
-    Error start();
-    Error connect(const char* server, Port port, int timeout);
-    void disconnect();
-
-    template<typename T>
-    void consumeEvents(T& consumer) {
-        ENetEvent e;
-        while(enet_host_service(client, &e, 0) > 0) {
-            switch(e.type) {
-                case ENET_EVENT_TYPE_CONNECT: break;
-                case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
-                case ENET_EVENT_TYPE_DISCONNECT: consumer.onDisconnect(); break;
-                case ENET_EVENT_TYPE_NONE: return;
-                case ENET_EVENT_TYPE_RECEIVE:
-                    InPacket in(e.packet);
-                    consumer.onPacket(in);
-                    enet_packet_destroy(e.packet);
-                    break;
-            }
-        }
-    }
-
-    void send(OutPacket& p);
-};
+    void tick();
+
+    void send(OutPacket& p, PacketType pt);
+
+    void setConnectHandler(OnConnect oc);
+    void setDisconnectHandler(OnDisconnect od);
+    void setPacketHandler(OnPacket op);
+    void resetHandler();
+
+    bool isConnecting();
+    bool isConnected();
+}
 
 #endif

+ 2 - 4
network/ENet.cpp

@@ -1,4 +1,6 @@
 #define ENET_IMPLEMENTATION
+#include "libs/enet/include/enet.h"
+
 #include "network/ENet.h"
 
 static int enetCounter = 0;
@@ -15,8 +17,4 @@ void ENet::remove() {
     if(enetCounter > 0 && --enetCounter == 0) {
         enet_deinitialize();
     }
-}
-
-bool ENet::hasError() {
-    return enetCounter <= 0;
 }

+ 4 - 11
network/ENet.h

@@ -1,16 +1,9 @@
 #ifndef ENET_H
 #define ENET_H
 
-#include "libs/enet/include/enet.h"
-
-class ENet final {
-    friend class Client;
-    friend class Server;
-
-    ENet() = delete;
-    static bool add();
-    static void remove();
-    static bool hasError();
-};
+namespace ENet {
+    bool add();
+    void remove();
+}
 
 #endif

+ 26 - 66
network/Packet.cpp

@@ -1,18 +1,19 @@
-#include <utility>
+#include "libs/enet/include/enet.h"
 
 #include "network/Packet.h"
 
 static_assert(sizeof(float) == sizeof(uint32),
               "sizeof(float) != sizeof(uint32)");
 
-InPacket::InPacket(ENetPacket* packet) : packet(packet), index(0) {
+InPacket::InPacket(const void* data, int size)
+    : data(static_cast<const char*>(data)), size(size), index(0) {
 }
 
-bool InPacket::read(void* buffer, unsigned int length) {
-    if(index + length > packet->dataLength) {
+bool InPacket::read(void* buffer, int length) {
+    if(index + length > size) {
         return true;
     }
-    memcpy(buffer, packet->data + index, length);
+    memcpy(buffer, data + index, length);
     index += length;
     return false;
 }
@@ -85,90 +86,49 @@ bool InPacket::readFloat(float& f) {
     return false;
 }
 
-OutPacket::OutPacket(unsigned int size, int flags, int channel)
-    : packet(enet_packet_create(nullptr, size, flags)), index(0),
-      channel(channel) {
+OutPacket::OutPacket(int initialSize) : buffer(initialSize) {
 }
 
-OutPacket OutPacket::reliable(unsigned int size) {
-    return OutPacket(size, ENET_PACKET_FLAG_RELIABLE, 0);
-}
-
-OutPacket OutPacket::sequenced(unsigned int size) {
-    return OutPacket(size, 0, 1);
-}
-
-OutPacket OutPacket::unsequenced(unsigned int size) {
-    return OutPacket(size, ENET_PACKET_FLAG_UNSEQUENCED, 2);
-}
-
-OutPacket::~OutPacket() {
-    enet_packet_destroy(packet);
-}
-
-OutPacket::OutPacket(const OutPacket& other)
-    : packet(enet_packet_copy(other.packet)), index(other.index) {
-}
-
-OutPacket::OutPacket(OutPacket&& other) : packet(nullptr), index(0) {
-    std::swap(packet, other.packet);
-    std::swap(index, other.index);
-}
-
-OutPacket& OutPacket::operator=(OutPacket other) {
-    std::swap(packet, other.packet);
-    std::swap(index, other.index);
+OutPacket& OutPacket::writeU8(uint8 u) {
+    buffer.add(u);
     return *this;
 }
 
-void OutPacket::write(const void* buffer, unsigned int length) {
-    if(packet == nullptr || index + length > packet->dataLength) {
-        return;
-    }
-    memcpy(packet->data + index, buffer, length);
-    index += length;
-}
-
-void OutPacket::writeU8(uint8 u) {
-    write(&u, sizeof(u));
-}
-
-void OutPacket::writeU16(uint16 u) {
+OutPacket& OutPacket::writeU16(uint16 u) {
     u = htons(u);
-    write(&u, sizeof(u));
+    buffer.add(u);
+    return *this;
 }
 
-void OutPacket::writeU32(uint32 u) {
+OutPacket& OutPacket::writeU32(uint32 u) {
     u = htonl(u);
-    write(&u, sizeof(u));
+    buffer.add(u);
+    return *this;
 }
 
-void OutPacket::writeS8(int8 s) {
+OutPacket& OutPacket::writeS8(int8 s) {
     if(s < 0) {
-        writeU8(s + 128);
-    } else {
-        writeU8(static_cast<uint8>(s) + 128);
+        return writeU8(s + 128);
     }
+    return writeU8(static_cast<uint8>(s) + 128u);
 }
 
-void OutPacket::writeS16(int16 s) {
+OutPacket& OutPacket::writeS16(int16 s) {
     if(s < 0) {
-        writeU16(s + 32768);
-    } else {
-        writeU16(static_cast<uint16>(s) + 32768);
+        return writeU16(s + 32768);
     }
+    return writeU16(static_cast<uint16>(s) + 32768u);
 }
 
-void OutPacket::writeS32(int32 s) {
+OutPacket& OutPacket::writeS32(int32 s) {
     if(s < 0) {
-        writeU32(s + 2147483648);
-    } else {
-        writeU32(static_cast<uint32>(s) + 2147483648);
+        return writeU32(s + 2147483648);
     }
+    return writeU32(static_cast<uint32>(s) + 2147483648u);
 }
 
-void OutPacket::writeFloat(float f) {
+OutPacket& OutPacket::writeFloat(float f) {
     uint32 u;
     memcpy(&u, &f, sizeof(float));
-    writeU32(u);
+    return writeU32(u);
 }

+ 23 - 39
network/Packet.h

@@ -1,22 +1,20 @@
 #ifndef PACKET_H
 #define PACKET_H
 
-#include "network/ENet.h"
+#include "utils/Buffer.h"
 #include "utils/StringBuffer.h"
 #include "utils/Types.h"
 
-class InPacket {
-    ENetPacket* packet;
-    unsigned int index;
-
-    friend class Client;
-    friend class Server;
+enum class PacketType { RELIABLE, SEQUENCED, UNSEQUENCED };
 
-    InPacket(ENetPacket* packet);
-
-    bool read(void* buffer, unsigned int length);
+class InPacket {
+    const char* data;
+    int size;
+    int index;
 
 public:
+    InPacket(const void* data, int size);
+
     bool readU8(uint8& u);
     bool readU16(uint16& u);
     bool readU32(uint32& u);
@@ -41,47 +39,33 @@ public:
         }
         return false;
     }
-};
-
-class OutPacket {
-    ENetPacket* packet;
-    unsigned int index;
-    int channel;
 
-    friend class Client;
-    friend class Server;
-
-    OutPacket(unsigned int size, int flags, int channel);
+private:
+    bool read(void* buffer, int length);
+};
 
-public:
-    static OutPacket reliable(unsigned int size);
-    static OutPacket sequenced(unsigned int size);
-    static OutPacket unsequenced(unsigned int size);
+struct OutPacket {
+    Buffer buffer;
 
-    ~OutPacket();
-    OutPacket(const OutPacket& other);
-    OutPacket(OutPacket&& other);
-    OutPacket& operator=(OutPacket other);
+    OutPacket(int initialSize);
 
-    void writeU8(uint8 u);
-    void writeU16(uint16 u);
-    void writeU32(uint32 u);
-    void writeS8(int8 s);
-    void writeS16(int16 s);
-    void writeS32(int32 s);
-    void writeFloat(float f);
+    OutPacket& writeU8(uint8 u);
+    OutPacket& writeU16(uint16 u);
+    OutPacket& writeU32(uint32 u);
+    OutPacket& writeS8(int8 s);
+    OutPacket& writeS16(int16 s);
+    OutPacket& writeS32(int32 s);
+    OutPacket& writeFloat(float f);
 
     template<int N>
-    void writeString(const StringBuffer<N>& s) {
+    OutPacket& writeString(const StringBuffer<N>& s) {
         uint16 end = s.getLength() > 65535 ? 65535 : s.getLength();
         writeU16(end);
         for(unsigned int i = 0; i < end; i++) {
             writeS8(s[i]);
         }
+        return *this;
     }
-
-private:
-    void write(const void* buffer, unsigned int length);
 };
 
 #endif

+ 128 - 47
network/Server.cpp

@@ -1,79 +1,160 @@
-#include <cassert>
-#include <utility>
+#include "libs/enet/include/enet.h"
 
+#include "network/ENet.h"
 #include "network/Server.h"
+#include "utils/HashMap.h"
+#include "utils/Logger.h"
 
-Server::Client::Client(ENetPeer* peer, int id) : peer(peer), id(id) {
-}
+static_assert(sizeof(enet_uint16) == sizeof(Server::Port),
+              "client port has wrong type");
 
-Server::Client::~Client() {
-    if(peer != nullptr) {
-        enet_peer_disconnect(peer, 0);
+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(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, maxClients, 3, 0, 0);
+    if(server == nullptr) {
+        ENet::remove();
+        return {"cannot create enet server host"};
     }
+    return {};
 }
 
-Server::Client::Client(Client&& other) : Client(nullptr, -1) {
-    std::swap(peer, other.peer);
-    std::swap(id, other.id);
+void Server::stop() {
+    if(server == nullptr) {
+        return;
+    }
+    for(ENetPeer* peer : clients.values()) {
+        enet_peer_reset(peer);
+    }
+    enet_host_destroy(server);
+    server = nullptr;
+    ENet::remove();
 }
 
-Server::Client& Server::Client::operator=(Client&& other) {
-    std::swap(peer, other.peer);
-    std::swap(id, other.id);
-    return *this;
+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));
 }
 
-int Server::Client::getId() const {
+Server::Client getId(ENetPeer* peer) {
+    Server::Client id = -1;
+    memcpy(&id, &(peer->data), sizeof(id));
     return id;
 }
 
-void Server::Client::send(OutPacket& p) {
-    if(p.packet != nullptr) {
-        p.packet->dataLength = p.index;
-        enet_peer_send(peer, p.channel, p.packet);
-        p.packet = nullptr;
+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);
 }
 
-Server::Server() : server(nullptr), idCounter(1) {
-    ENet::add();
+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, e.packet->dataLength);
+    onPacket(id, in);
 }
 
-Server::~Server() {
-    for(Client& client : clients.values()) {
-        enet_peer_reset(client.peer);
+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");
     }
-    enet_host_destroy(server);
-    ENet::remove();
 }
 
-Error Server::start(Port port, int maxClients) {
-    assert(server == nullptr);
-    if(ENet::hasError()) {
-        return {"cannot initialize enet"};
+void Server::tick() {
+    if(server == nullptr) {
+        return;
     }
-    ENetAddress address;
-    memset(&address, 0, sizeof(ENetAddress));
-    address.host = ENET_HOST_ANY;
-    address.port = port;
+    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;
+        }
+    }
+}
 
-    server = enet_host_create(&address, maxClients, 3, 0, 0);
+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, buffer.getLength(), flags[index]);
+}
+
+void Server::send(const OutPacket& p, PacketType pt) {
+    if(server != nullptr) {
+        int index = static_cast<int>(pt);
+        enet_host_broadcast(server, index, fromBuffer(p.buffer, index));
+    }
+}
+
+void Server::send(Server::Client client, const OutPacket& p, PacketType pt) {
     if(server == nullptr) {
-        return {"cannot create enet server host"};
+        return;
+    }
+    ENetPeer** peer = clients.search(client);
+    if(peer != nullptr) {
+        int index = static_cast<int>(pt);
+        enet_peer_send(*peer, index, fromBuffer(p.buffer, index));
     }
-    return {};
 }
 
-void Server::send(OutPacket& p) {
-    assert(server != nullptr);
-    if(p.packet != nullptr) {
-        p.packet->dataLength = p.index;
-        enet_host_broadcast(server, p.channel, p.packet);
-        p.packet = nullptr;
+void Server::disconnect(Client client) {
+    ENetPeer** peer = clients.search(client);
+    if(peer != nullptr) {
+        enet_peer_disconnect(*peer, 0);
     }
 }
 
-void Server::disconnect(Client& client) {
-    enet_peer_reset(client.peer);
-    clients.remove(client.getId());
+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&) {};
 }

+ 17 - 112
network/Server.h

@@ -1,126 +1,31 @@
 #ifndef SERVER_H
 #define SERVER_H
 
-#include "network/ENet.h"
 #include "network/Packet.h"
 #include "utils/Error.h"
-#include "utils/HashMap.h"
-#include "utils/Logger.h"
-#include "utils/StringBuffer.h"
+#include "utils/Types.h"
 
-struct Server final {
-    typedef enet_uint16 Port;
-
-    class Client final {
-        friend class Server;
-
-        ENetPeer* peer;
-        int id;
-
-        friend HashMap<int, Client>;
-
-        Client(ENetPeer* peer, int id);
-        Client(const Client&) = delete;
-        Client(Client&& other);
-        Client& operator=(const Client&) = delete;
-        Client& operator=(Client&& other);
-
-    public:
-        ~Client();
-
-        int getId() const;
-        void send(OutPacket& p);
-    };
-
-private:
-    ENetHost* server;
-    HashMap<int, Client> clients;
-    int idCounter;
-
-public:
-    Server();
-    Server(const Server&) = delete;
-    Server(Server&&) = delete;
-    ~Server();
-    Server& operator=(const Server&) = delete;
-    Server& operator=(Server&&) = delete;
+namespace Server {
+    typedef uint16 Port;
+    typedef int Client;
+    typedef void (*OnConnect)(Client);
+    typedef void (*OnDisconnect)(Client);
+    typedef void (*OnPacket)(Client, InPacket&);
 
     Error start(Port port, int maxClients);
+    void stop();
 
-    template<typename T>
-    void consumeEvents(T& consumer) {
-        ENetEvent e;
-        while(enet_host_service(server, &e, 0) > 0) {
-            switch(e.type) {
-                case ENET_EVENT_TYPE_CONNECT: onConnect(e, consumer); break;
-                case ENET_EVENT_TYPE_RECEIVE:
-                    onPacket(e, consumer);
-                    enet_packet_destroy(e.packet);
-                    break;
-                case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
-                case ENET_EVENT_TYPE_DISCONNECT:
-                    onDisconnect(e, consumer);
-                    break;
-                case ENET_EVENT_TYPE_NONE: return;
-            }
-        }
-    }
-
-    void send(OutPacket& p);
-    void disconnect(Client& client);
+    void tick();
 
-private:
-    template<typename T>
-    void onConnect(ENetEvent& e, T& consumer) {
-        int id = idCounter++;
-        if(clients.tryEmplace(id, e.peer, id)) {
-            LOG_WARNING("id is connected twice");
-            return;
-        }
-        static_assert(sizeof(e.peer->data) >= sizeof(id),
-                      "private data not big enough for id");
-        memcpy(&(e.peer->data), &id, sizeof(id));
-        Client* client = clients.search(id);
-        if(client != nullptr) {
-            consumer.onConnect(*client);
-        } else {
-            LOG_WARNING("cannot find added client");
-        }
-    }
+    void send(const OutPacket& p, PacketType pt);
+    void send(Client client, const OutPacket& p, PacketType pt);
 
-    template<typename T>
-    void onPacket(ENetEvent& e, T& consumer) {
-        if(e.peer->data == nullptr) {
-            LOG_WARNING("client without data sent package");
-            return;
-        }
-        int id = -1;
-        memcpy(&id, &(e.peer->data), sizeof(id));
-        Client* client = clients.search(id);
-        if(client != nullptr) {
-            InPacket in(e.packet);
-            consumer.onPacket(*client, in);
-        } else {
-            LOG_WARNING("client with invalid id sent package");
-        }
-    }
+    void disconnect(Client client);
 
-    template<typename T>
-    void onDisconnect(ENetEvent& e, T& consumer) {
-        if(e.peer->data == nullptr) {
-            LOG_WARNING("client without data disconnected");
-            return;
-        }
-        int id = -1;
-        memcpy(&id, &(e.peer->data), sizeof(id));
-        Client* client = clients.search(id);
-        if(client != nullptr) {
-            consumer.onDisconnect(*client);
-            clients.remove(id);
-        } else {
-            LOG_WARNING("client has invalid id");
-        }
-    }
-};
+    void setConnectHandler(OnConnect oc);
+    void setDisconnectHandler(OnDisconnect od);
+    void setPacketHandler(OnPacket op);
+    void resetHandler();
+}
 
 #endif

+ 155 - 118
tests/NetworkTests.cpp

@@ -1,45 +1,74 @@
-#include <atomic>
-#include <thread>
+#include <iostream>
 
 #include "network/Client.h"
 #include "network/Server.h"
 #include "tests/NetworkTests.h"
 #include "tests/Test.h"
 
-static void sleep(int millis) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(millis));
+bool checkError(Test& test, const Error e, const char* msg) {
+    if(e.has()) {
+        test.checkFalse(true, msg);
+        std::cout << "Error: " << e.message << "\n";
+        return true;
+    }
+    return false;
 }
 
-static int packageCounter = 0;
-
-struct ServerConsumer {
-    bool connected = false;
-    bool disconnect = false;
-
-    uint8 data1 = 0;
-    uint16 data2 = 0;
-    uint32 data3 = 0;
-    int8 data4 = 0;
-    int16 data5 = 0;
-    int32 data6 = 0;
-    int8 data7 = 0;
-    int16 data8 = 0;
-    int32 data9 = 0;
-    StringBuffer<20> data10;
-    float data11 = 0.0f;
-
-    void onConnect(Server::Client& client) {
-        (void)client;
-        connected = true;
+static void tickClient(int ticks) {
+    for(int i = 0; i < ticks; i++) {
+        Client::tick();
     }
+}
 
-    void onDisconnect(Server::Client& client) {
-        (void)client;
-        disconnect = true;
+static void tick(int ticks) {
+    for(int i = 0; i < ticks; i++) {
+        Client::tick();
+        Server::tick();
     }
+}
 
-    void onPacket(Server::Client& client, InPacket& in) {
-        (void)client;
+static void testConnect(Test& test, PacketType pt) {
+    static bool clientConnected = false;
+    static bool clientDisconnected = false;
+    static bool clientPackage = false;
+    clientConnected = false;
+    clientDisconnected = false;
+    clientPackage = false;
+
+    static int packageCounter = 0;
+    static bool serverConnected = false;
+    static bool serverDisconnect = false;
+    static uint8 data1 = 0;
+    static uint16 data2 = 0;
+    static uint32 data3 = 0;
+    static int8 data4 = 0;
+    static int16 data5 = 0;
+    static int32 data6 = 0;
+    static int8 data7 = 0;
+    static int16 data8 = 0;
+    static int32 data9 = 0;
+    static StringBuffer<20> data10;
+    static float data11 = 0.0f;
+
+    serverConnected = false;
+    serverDisconnect = false;
+    data1 = 0;
+    data2 = 0;
+    data3 = 0;
+    data4 = 0;
+    data5 = 0;
+    data6 = 0;
+    data7 = 0;
+    data8 = 0;
+    data9 = 0;
+    data10.clear();
+    data11 = 0.0f;
+
+    Server::resetHandler();
+    Server::setConnectHandler([](Server::Client) { serverConnected = true; });
+    Server::setDisconnectHandler(
+        [](Server::Client) { serverDisconnect = true; });
+    Server::setPacketHandler([](Server::Client client, InPacket& in) {
         in.readU8(data1);
         in.readU16(data2);
         in.readU32(data3);
@@ -53,115 +82,123 @@ struct ServerConsumer {
         in.readFloat(data11);
 
         if(packageCounter == 0) {
-            OutPacket out = OutPacket::reliable(0);
-            client.send(out);
+            OutPacket out(0);
+            Server::send(client, out, PacketType::RELIABLE);
         } else if(packageCounter == 1) {
-            OutPacket out = OutPacket::sequenced(0);
-            client.send(out);
+            OutPacket out(0);
+            Server::send(client, out, PacketType::SEQUENCED);
         } else if(packageCounter == 2) {
-            OutPacket out = OutPacket::unsequenced(0);
-            client.send(out);
+            OutPacket out(0);
+            Server::send(client, out, PacketType::UNSEQUENCED);
         }
         packageCounter++;
-    }
-};
-
-struct ClientConsumer {
-    bool package = false;
-
-    void onDisconnect() {
-    }
+    });
 
-    void onPacket(InPacket& in) {
-        (void)in;
-        package = true;
-    }
-};
+    Client::resetHandler();
+    Client::setConnectHandler([]() { clientConnected = true; });
+    Client::setDisconnectHandler([]() { clientDisconnected = true; });
+    Client::setPacketHandler([](InPacket&) { clientPackage = true; });
 
-static void testConnect(Test& test, OutPacket out) {
-    Server server;
-    if(server.start(54321, 5).has()) {
-        test.checkEqual(false, true, "server can initialize");
+    if(checkError(test, Server::start(54321, 5), "server can initialize")) {
         return;
-    }
-    Client client;
-    if(client.start().has()) {
-        test.checkEqual(false, true, "client can initialize");
+    } else if(checkError(test, Client::start(), "client can initialize")) {
+        return;
+    } else if(checkError(test, Client::connect("127.0.0.1", 54321, 90),
+                         "start connection failed")) {
         return;
     }
+    test.checkFalse(Client::isConnected(), "client not connected yet");
+    test.checkTrue(Client::isConnecting(), "client is connecting");
 
-    std::atomic_bool running(true);
-    ServerConsumer serverConsumer;
-    std::thread listen([&running, &server, &serverConsumer]() {
-        while(running) {
-            server.consumeEvents(serverConsumer);
-        }
-    });
-
-    test.checkEqual(false, client.connect("127.0.0.1", 54321, 5).has(),
-                    "connection failed");
+    tick(100);
 
-    ClientConsumer clientConsumer;
-    for(int i = 0; i < 100; i++) {
-        client.consumeEvents(clientConsumer);
-    }
+    test.checkTrue(clientConnected, "client called connect callback");
+    test.checkTrue(Client::isConnected(), "client is connected");
+    test.checkFalse(Client::isConnecting(), "client is no more connecting");
 
-    out.writeU8(0xF1);
-    out.writeU16(0xF123);
-    out.writeU32(0xF1234567);
-    out.writeS8(-0x71);
-    out.writeS16(-0x7123);
-    out.writeS32(-0x71234567);
-    out.writeS8(0x71);
-    out.writeS16(0x7123);
-    out.writeS32(0x71234567);
+    OutPacket out(50);
+    out.writeU8(0xF1).writeU16(0xF123).writeU32(0xF1234567);
+    out.writeS8(-0x71).writeS16(-0x7123).writeS32(-0x71234567);
+    out.writeS8(0x71).writeS16(0x7123).writeS32(0x71234567);
     StringBuffer<20> s("Hi there");
     out.writeString(s);
     out.writeFloat(252345.983f);
-    client.send(out);
+    Client::send(out, pt);
+
+    tick(100);
+
+    test.checkTrue(clientPackage, "client has received data");
+    test.checkTrue(serverConnected, "server has connection");
+
+    test.checkUnsigned8(0xF1, data1, "correct value is sent 1");
+    test.checkUnsigned16(0xF123, data2, "correct value is sent 2");
+    test.checkEqual(0xF1234567u, data3, "correct value is sent 3");
+    test.checkSigned8(-0x71, data4, "correct value is sent 4");
+    test.checkSigned16(-0x7123, data5, "correct value is sent 5");
+    test.checkEqual(-0x71234567, data6, "correct value is sent 6");
+    test.checkSigned8(0x71, data7, "correct value is sent 7");
+    test.checkSigned16(0x7123, data8, "correct value is sent 8");
+    test.checkEqual(0x71234567, data9, "correct value is sent 9");
+    test.checkEqual(s, data10, "correct value is sent 10");
+    test.checkFloat(252345.983f, data11, 0.01f, "correct value is sent 11");
+
+    Client::disconnect(90);
+    test.checkFalse(Client::isConnected(), "client was disconnected");
+    test.checkFalse(Client::isConnecting(), "client is not connecting");
+    tick(100);
+    test.checkTrue(clientDisconnected, "client has disconnected");
+    test.checkTrue(serverDisconnect, "client has disconnected on server");
+
+    Client::stop();
+    Server::stop();
+}
 
-    for(int i = 0; i < 100; i++) {
-        client.consumeEvents(clientConsumer);
+static void testDisconnect(Test& test) {
+    static bool disconnected = false;
+    disconnected = false;
+    Client::resetHandler();
+    Client::setDisconnectHandler([]() { disconnected = true; });
+    if(checkError(test, Client::start(), "client can initialize")) {
+        return;
+    } else if(checkError(test, Client::connect("127.0.0.1", 54321, 90),
+                         "start connection failed")) {
+        return;
     }
+    test.checkFalse(Client::isConnected(), "client not connected yet");
+    test.checkTrue(Client::isConnecting(), "client is connecting");
+    Client::disconnect(50);
+    tickClient(100);
+    test.checkFalse(Client::isConnected(), "client was disconnected");
+    test.checkFalse(Client::isConnecting(), "client is not connecting");
+    test.checkTrue(disconnected, "client has disconnected");
+    Client::stop();
+}
 
-    test.checkEqual(true, clientConsumer.package, "client has received data");
-    test.checkEqual(true, serverConsumer.connected, "server has connection");
-
-    test.checkEqual(static_cast<uint8>(0xF1), serverConsumer.data1,
-                    "correct value is sent 1");
-    test.checkEqual(static_cast<uint16>(0xF123), serverConsumer.data2,
-                    "correct value is sent 2");
-    test.checkEqual(0xF1234567u, serverConsumer.data3,
-                    "correct value is sent 3");
-    test.checkEqual(static_cast<int8>(-0x71), serverConsumer.data4,
-                    "correct value is sent 4");
-    test.checkEqual(static_cast<int16>(-0x7123), serverConsumer.data5,
-                    "correct value is sent 5");
-    test.checkEqual(-0x71234567, serverConsumer.data6,
-                    "correct value is sent 6");
-    test.checkEqual(static_cast<int8>(0x71), serverConsumer.data7,
-                    "correct value is sent 7");
-    test.checkEqual(static_cast<int16>(0x7123), serverConsumer.data8,
-                    "correct value is sent 8");
-    test.checkEqual(0x71234567, serverConsumer.data9,
-                    "correct value is sent 9");
-    test.checkEqual(s, serverConsumer.data10, "correct value is sent 10");
-    test.checkFloat(252345.983f, serverConsumer.data11, 0.01f,
-                    "correct value is sent 11");
-
-    client.disconnect();
-    sleep(100);
-
-    test.checkEqual(true, serverConsumer.disconnect, "client has disconnected");
-
-    running = false;
-    listen.join();
+static void testStop(Test& test) {
+    static bool disconnected = false;
+    disconnected = false;
+    Client::resetHandler();
+    Client::setDisconnectHandler([]() { disconnected = true; });
+    if(checkError(test, Client::start(), "client can initialize")) {
+        return;
+    } else if(checkError(test, Client::connect("127.0.0.1", 54321, 90),
+                         "start connection failed")) {
+        return;
+    }
+    test.checkFalse(Client::isConnected(), "client not connected yet");
+    test.checkTrue(Client::isConnecting(), "client is connecting");
+    Client::stop();
+    test.checkFalse(Client::isConnected(), "client was disconnected");
+    test.checkFalse(Client::isConnecting(), "client is not connecting");
+    test.checkTrue(disconnected, "client has disconnected");
 }
 
 void NetworkTests::test() {
     Test test("Network");
-    testConnect(test, OutPacket::unsequenced(50));
-    testConnect(test, OutPacket::reliable(50));
-    testConnect(test, OutPacket::sequenced(50));
+    testConnect(test, PacketType::UNSEQUENCED);
+    testConnect(test, PacketType::RELIABLE);
+    testConnect(test, PacketType::SEQUENCED);
+    testDisconnect(test);
+    testStop(test);
     test.finalize();
 }

+ 25 - 1
tests/Test.cpp

@@ -43,4 +43,28 @@ void Test::checkFloat(float wanted, float actual, float error,
                       .append(actual)
                       .append("'"));
     }
-}
+}
+
+void Test::checkTrue(bool actual, const char* text) {
+    checkEqual(true, actual, text);
+}
+
+void Test::checkFalse(bool actual, const char* text) {
+    checkEqual(false, actual, text);
+}
+
+void Test::checkUnsigned8(uint8 wanted, uint8 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Test::checkUnsigned16(uint16 wanted, uint16 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Test::checkSigned8(int8 wanted, int8 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}
+
+void Test::checkSigned16(int16 wanted, int16 actual, const char* text) {
+    checkEqual(wanted, actual, text);
+}

+ 6 - 0
tests/Test.h

@@ -38,6 +38,12 @@ public:
     }
 
     void checkFloat(float wanted, float actual, float error, const char* text);
+    void checkTrue(bool actual, const char* text);
+    void checkFalse(bool actual, const char* text);
+    void checkUnsigned8(uint8 wanted, uint8 actual, const char* text);
+    void checkUnsigned16(uint16 wanted, uint16 actual, const char* text);
+    void checkSigned8(int8 wanted, int8 actual, const char* text);
+    void checkSigned16(int16 wanted, int16 actual, const char* text);
 };
 
 #endif

+ 3 - 2
tests/UniquePointerTests.cpp

@@ -1,6 +1,6 @@
 #include "tests/UniquePointerTests.h"
-#include "memory/UniquePointer.h"
 #include "tests/Test.h"
+#include "utils/UniquePointer.h"
 
 struct B {
     static int instances;
@@ -32,7 +32,8 @@ static void testMoveDestroys(Test& test) {
         p1 = std::move(p2);
         test.checkEqual(1, B::instances, "one after move");
     }
-    test.checkEqual(0, B::instances, "everything destroyed correctly after move");
+    test.checkEqual(0, B::instances,
+                    "everything destroyed correctly after move");
 }
 
 void UniquePointerTests::test() {

+ 0 - 0
memory/UniquePointer.h → utils/UniquePointer.h