Browse Source

refactoring

Kajetan Johannes Hammerle 2 years ago
parent
commit
b158b86c97

+ 0 - 2
client/utils/Clock.cpp

@@ -1,8 +1,6 @@
 #include "client/utils/Clock.h"
 #include "client/rendering/wrapper/GLFWWrapper.h"
 
-#include <iostream>
-
 Clock::Clock() : last(GLFWWrapper::getTimeNanos()), index(0), sum(0), time(0) {
 }
 

+ 3 - 2
common/block/Block.cpp

@@ -1,12 +1,13 @@
 #include "common/block/Block.h"
 
-Block::Block(u16 id, const std::string& registry) : id(id), registry(registry) {
+Block::Block(u16 id, const HashedString& registry, const BlockBuilder& builder) : id(id), registry(registry) {
+    (void) builder;
 }
 
 u16 Block::getId() const {
     return id;
 }
 
-const std::string& Block::getRegistry() const {
+const HashedString& Block::getRegistry() const {
     return registry;
 }

+ 5 - 6
common/block/Block.h

@@ -1,19 +1,18 @@
 #ifndef BLOCK_H
 #define BLOCK_H
 
-#include <string>
-
-#include "common/utils/Types.h"
+#include "common/utils/HashedString.h"
+#include "common/block/BlockBuilder.h"
 
 class Block {
 public:
-    Block(u16 id, const std::string& registry);
+    Block(u16 id, const HashedString& registry, const BlockBuilder& builder);
     u16 getId() const;
-    const std::string& getRegistry() const;
+    const HashedString& getRegistry() const;
 
 private:
     const u16 id;
-    const std::string registry;
+    const HashedString registry;
 };
 
 #endif

+ 8 - 0
common/block/BlockBuilder.cpp

@@ -0,0 +1,8 @@
+#include "common/block/BlockBuilder.h"
+
+BlockBuilder::BlockBuilder() {
+}
+
+BlockBuilder& BlockBuilder::test() {
+    return *this;
+}

+ 10 - 0
common/block/BlockBuilder.h

@@ -0,0 +1,10 @@
+#ifndef BLOCKBUILDER_H
+#define BLOCKBUILDER_H
+
+class BlockBuilder {
+public:
+    BlockBuilder();
+    BlockBuilder& test();
+};
+
+#endif

+ 23 - 34
common/block/BlockRegistry.cpp

@@ -1,44 +1,33 @@
-#include <unordered_map>
-#include <vector>
-#include <fstream>
-#include <iostream>
+#include "common/block/BlockRegistry.h"
 
-#include "BlockRegistry.h"
+BlockRegistry::BlockRegistry() {
+    add("air", BlockBuilder().test());
+    add("stone", BlockBuilder().test());
+    add("dirt", BlockBuilder().test());
+    add("grass", BlockBuilder().test());
+}
 
-static Block NULL_BLOCK(0, "null_block");
-static std::unordered_map<std::string, u16> registry;
-static std::vector<Block> idMap = {NULL_BLOCK};
+void BlockRegistry::add(const char* registry, const BlockBuilder& builder) {
+    u16 id = blocks.size();
+    blocks.emplace_back(id, registry, builder);
+    registryToId[registry] = id;
+}
 
-void BlockRegistry::loadFromFile(const std::string& path) {
-    std::ifstream in;
-    in.open(path);
-    if(in.fail()) {
-        std::cout << "cannot read blocks from file '" << path << "'" << std::endl;
-        return;
-    }
-    std::string line;
-    while(true) {
-        std::getline(in, line, '\n');
-        if(in.fail()) {
-            break;
-        }
-        u16 id = idMap.size();
-        idMap.emplace_back(id, line);
-        registry[line] = id;
+const Block& BlockRegistry::getBlock(const HashedString& registry) const {
+    auto iter = registryToId.find(registry);
+    if(iter == registryToId.end()) {
+        return blocks[0];
     }
+    return getBlock(iter->second);
 }
 
-const Block& BlockRegistry::getBlock(const std::string& name) {
-    const std::unordered_map<std::string, u16>::const_iterator& iter = registry.find(name);
-    if(iter == registry.end()) {
-        return NULL_BLOCK;
+const Block& BlockRegistry::getBlock(u16 id) const {
+    if(id < blocks.size()) {
+        return blocks[id];
     }
-    return idMap[iter->second];
+    return blocks[0];
 }
 
-const Block& BlockRegistry::getBlock(u16 id) {
-    if(id >= idMap.size()) {
-        return NULL_BLOCK;
-    }
-    return idMap[id];
+size_t BlockRegistry::Hasher::operator()(const HashedString& key) const {
+    return key.hashCode();
 }

+ 19 - 7
common/block/BlockRegistry.h

@@ -1,14 +1,26 @@
 #ifndef BLOCKREGISTRY_H
 #define BLOCKREGISTRY_H
 
-#include "common/utils/Types.h"
+#include <vector>
+#include <unordered_map>
+
+#include "common/utils/HashedString.h"
 #include "common/block/Block.h"
 
-namespace BlockRegistry {
-    void loadFromFile(const std::string& path);
-    const Block& getBlock(const std::string& registry);
-    const Block& getBlock(u16 id);
-}
+class BlockRegistry {
+public:
+    BlockRegistry();
+    const Block& getBlock(const HashedString& registry) const;
+    const Block& getBlock(u16 id) const;
+
+private:
+    void add(const char* registry, const BlockBuilder& builder);
 
-#endif
+    std::vector<Block> blocks;
+    struct Hasher {
+        size_t operator()(const HashedString& key) const;
+    };
+    std::unordered_map<HashedString, u16, Hasher> registryToId;
+};
 
+#endif

+ 13 - 6
common/utils/Array.h

@@ -3,29 +3,32 @@
 
 #include "common/utils/Types.h"
 
-template<typename T, u64 N>
+template<typename T, uint N>
 class Array final {
 public:
 
+    Array() {
+    }
+
     Array(const T& t) {
-        for(u64 i = 0; i < N; i++) {
+        for(uint i = 0; i < N; i++) {
             data[i] = t;
         }
     }
 
-    const T& operator[](u64 index) const {
+    const T& operator[](uint index) const {
         return data[index];
     }
 
-    T& operator[](u64 index) {
+    T& operator[](uint index) {
         return data[index];
     }
 
-    const T* operator+(u64 index) const {
+    const T* operator+(uint index) const {
         return data + index;
     }
 
-    T* operator+(u64 index) {
+    T* operator+(uint index) {
         return data + index;
     }
 
@@ -45,6 +48,10 @@ public:
         return data + N;
     }
 
+    uint getLength() const {
+        return N;
+    }
+
 private:
     T data[N];
 };

+ 25 - 0
common/utils/HashedString.cpp

@@ -0,0 +1,25 @@
+#include "common/utils/HashedString.h"
+
+HashedString::HashedString(const char* str) : length(0), hash(0) {
+    for(; length < LENGTH - 1 && str[length] != '\0'; length++) {
+        data[length] = str[length];
+        hash = hash * 257 + str[length];
+    }
+    data[length] = '\0';
+}
+
+bool HashedString::operator==(const HashedString& other) const {
+    return hash == other.hash && length == other.length;
+}
+
+bool HashedString::operator!=(const HashedString& other) const {
+    return !(*this == other);
+}
+
+HashedString::operator const char*() const {
+    return data;
+}
+
+u32 HashedString::hashCode() const {
+    return hash;
+}

+ 25 - 0
common/utils/HashedString.h

@@ -0,0 +1,25 @@
+#ifndef HASHEDSTRING_H
+#define HASHEDSTRING_H
+
+#include <unordered_map>
+
+#include "common/utils/Types.h"
+
+class HashedString {
+public:
+    HashedString(const char* str);
+    
+    bool operator==(const HashedString& other) const;
+    bool operator!=(const HashedString& other) const;
+    operator const char*() const;
+    
+    u32 hashCode() const;
+
+private:
+    static constexpr uint LENGTH = 32 - sizeof (u8) - sizeof (u32);
+    char data[LENGTH];
+    u8 length;
+    u32 hash;
+};
+
+#endif

+ 31 - 0
common/utils/RingBuffer.h

@@ -0,0 +1,31 @@
+#ifndef RINGBUFFER_H
+#define RINGBUFFER_H
+
+#include "common/utils/Array.h"
+
+template<typename T, uint N>
+class RingBuffer {
+public:
+
+    void write(const T& t) {
+        data[writeIndex] = t;
+        writeIndex = (writeIndex + 1) % N;
+    }
+
+    bool canRead() const {
+        return writeIndex != readIndex;
+    }
+
+    T read() {
+        T& t = data[readIndex];
+        readIndex = (readIndex + 1) % N;
+        return t;
+    }
+
+private:
+    Array<T, N> data;
+    uint writeIndex = 0;
+    uint readIndex = 0;
+};
+
+#endif

+ 26 - 0
common/utils/String.cpp

@@ -0,0 +1,26 @@
+#include <cstring>
+
+#include "common/utils/String.h"
+
+String::String() : length(0) {
+    data[0] = '\0';
+}
+
+String::String(const char* str) : length(0) {
+    for(; length < BUFFER_LENGTH - 1 && str[length] != '\0'; length++) {
+        data[length] = str[length];
+    }
+    data[length] = '\0';
+}
+
+bool String::operator==(const String& other) const {
+    return length == other.length && strcmp(data, other.data) == 0;
+}
+
+bool String::operator!=(const String& other) const {
+    return !(*this == other);
+}
+
+String::operator const char*() const {
+    return data;
+}

+ 21 - 0
common/utils/String.h

@@ -0,0 +1,21 @@
+#ifndef STRING_H
+#define STRING_H
+
+#include "common/utils/Types.h"
+
+class String {
+public:
+    String();
+    String(const char* str);
+    
+    bool operator==(const String& other) const;
+    bool operator!=(const String& other) const;
+    operator const char*() const;
+
+private:
+    static constexpr uint BUFFER_LENGTH = 256;
+    uint length;
+    char data[BUFFER_LENGTH];
+};
+
+#endif

+ 4 - 2
meson.build

@@ -1,8 +1,10 @@
 project('cubes plus plus', 'cpp')
 
-sourcesCommon = ['common/block/BlockRegistry.cpp', 'common/block/Block.cpp', 'common/stream/Stream.cpp', 'common/world/Chunk.cpp', 'common/world/World.cpp', 'common/utils/Face.cpp', 'common/utils/DataVector.cpp']
+# 'common/world/Chunk.cpp', 'common/world/World.cpp', 'common/utils/Face.cpp'
 
-sourcesServer = ['server/GameServer.cpp', 'server/commands/CommandUtils.cpp', 'server/commands/ServerCommands.cpp', 'server/commands/CommandManager.cpp', 'server/network/Server.cpp', 'server/Main.cpp']
+sourcesCommon = ['common/block/BlockBuilder.cpp', 'common/block/Block.cpp', 'common/block/BlockRegistry.cpp', 'common/utils/HashedString.cpp', 'common/stream/Stream.cpp', 'common/utils/DataVector.cpp', 'common/utils/String.cpp']
+
+sourcesServer = ['server/Main.cpp', 'server/network/Server.cpp', 'server/GameServer.cpp', 'server/commands/CommandUtils.cpp', 'server/commands/ServerCommands.cpp', 'server/commands/CommandManager.cpp', 'server/network/Socket.cpp', 'server/commands/CommandEditor.cpp', 'server/Clock.cpp']
 
 sourcesClient = ['client/Main.cpp', 'client/rendering/WindowSize.cpp', 'client/math/Frustum.cpp', 'client/math/Ray.cpp', 'client/rendering/Framebuffers.cpp', 'client/rendering/wrapper/GLFWWrapper.cpp', 'client/rendering/wrapper/Window.cpp', 'client/rendering/Engine.cpp', 'client/input/Keys.cpp', 'client/rendering/wrapper/Shader.cpp', 'client/rendering/Shaders.cpp', 'client/utils/Utils.cpp', 'client/rendering/Mesh.cpp', 'client/math/Matrix.cpp', 'client/math/MatrixStack.cpp', 'client/math/Vector.cpp', 'client/math/Camera.cpp', 'client/math/Plane.cpp', 'client/Game.cpp', 'client/input/MouseButtons.cpp', 'client/rendering/FileTexture.cpp', 'client/rendering/FontRenderer.cpp', 'client/rendering/wrapper/Framebuffer.cpp', 'client/rendering/NoiseTexture.cpp', 'client/utils/Clock.cpp', 'client/input/Control.cpp', 'client/rendering/RenderSettings.cpp', 'client/rendering/wrapper/VertexBuffer.cpp', 'client/rendering/wrapper/StreamBuffer.cpp', 'client/rendering/wrapper/Texture.cpp', 'client/utils/PNGReader.cpp', 'client/rendering/wrapper/GLWrapper.cpp', 'client/rendering/Renderer.cpp']
 

+ 34 - 0
server/Clock.cpp

@@ -0,0 +1,34 @@
+#include <chrono>
+#include <thread>
+
+#include "server/Clock.h"
+
+Clock::Clock() : last(getNanos()), index(0), sum(0), time(0) {
+}
+
+u64 Clock::update() {
+    index = (index + 1) & (length - 1);
+    u64 current = getNanos();
+    sum -= time[index];
+    time[index] = current - last;
+    sum += time[index];
+    last = current;
+    return time[index];
+}
+
+u64 Clock::getLength() const {
+    return length;
+}
+
+float Clock::getUpdatesPerSecond() const {
+    return length * (1000000000.0f / sum);
+}
+
+u64 Clock::getNanos() const {
+    return std::chrono::steady_clock::now().time_since_epoch().count();
+}
+
+void Clock::wait(u64 nanos) const {
+    std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
+}
+

+ 26 - 0
server/Clock.h

@@ -0,0 +1,26 @@
+#ifndef CLOCK_H
+#define CLOCK_H
+
+#include "common/utils/Types.h"
+#include "common/utils/Array.h"
+
+class Clock final {
+public:
+    Clock();
+    u64 update();
+    u64 getLength() const;
+    float getUpdatesPerSecond() const;
+    void wait(u64 nanos) const;
+
+private:
+    u64 getNanos() const;
+    
+    static constexpr u64 bits = 7;
+    static constexpr u64 length = 1 << bits;
+    u64 last;
+    u64 index;
+    s64 sum;
+    Array<u64, length> time;
+};
+
+#endif

+ 11 - 92
server/GameServer.cpp

@@ -1,76 +1,29 @@
 #include <iostream>
-#include <cstring>
-#include <chrono>
-#include <mutex>
-#include <thread>
-#include <vector>
-
-#include <poll.h>
-#include <readline/readline.h>
-#include <readline/history.h>
-#include <unistd.h>
 
 #include "server/GameServer.h"
-#include "server/network/Server.h"
-#include "server/commands/ServerCommands.h"
 #include "server/commands/CommandManager.h"
 
-static ServerCommands serverCommands;
-
-static const std::chrono::nanoseconds NANOS_PER_TICK = std::chrono::nanoseconds(50000000);
-static const std::chrono::nanoseconds MIN_NANO_SLEEP = std::chrono::nanoseconds(300000);
-
-static std::vector<std::string> commandQueue;
-static std::mutex comandQueueMutex;
-
-static void init() {
-    rl_bind_key('\t', rl_insert);
-    rl_event_hook = []() {
-        if(!serverCommands.isRunning()) {
-            rl_stuff_char('\n');
-        }
-        return 0;
-    };
+GameServer::GameServer(const Clock& tps) : tps(tps) {
 }
 
-static void readFromConsole() {
-    while(serverCommands.isRunning()) {
-        char* test = readline("> ");
-        if(test == nullptr) {
-            continue;
-        }
-        std::string command(test);
-        if(command.size() != 0) {
-            add_history(test);
-            std::lock_guard<std::mutex> lg(comandQueueMutex);
-            commandQueue.push_back(command);
-        }
-        free(test);
-    }
+void GameServer::tick() {
 }
 
-static void handleCommandQueue() {
-    std::lock_guard<std::mutex> lg(comandQueueMutex);
-    for(std::string& s : commandQueue) {
-        CommandManager::execute(serverCommands, s);
+void GameServer::handleCommands(CommandEditor& editor, ServerCommands& serverCommands) {
+    while(editor.hasCommand()) {
+        String s = editor.readCommand();
+        std::string s2 = static_cast<const char*> (s);
+        CommandManager::execute(serverCommands, s2);
     }
-    commandQueue.clear();
-}
-
-static void tick() {
-    rl_clear_visible_line();
-    handleCommandQueue();
-    std::cout << "tick\n";
-    rl_forced_update_display();
 }
 
-static void onFullServerClientConnect(int socket) {
+void GameServer::onFullServerClientConnect(int socket) {
     Stream answer;
     answer.write("Sorry, the server is full");
     answer.sendToSocket(socket);
 }
 
-static void onClientConnect(int socket) {
+void GameServer::onClientConnect(int socket) {
     std::cout << socket << " has connected\n";
 
     Stream answer;
@@ -78,7 +31,7 @@ static void onClientConnect(int socket) {
     answer.sendToSocket(socket);
 }
 
-static void onClientPackage(int socket, Stream& in) {
+void GameServer::onClientPackage(int socket, Stream& in) {
     std::string s = "";
     while(in.hasData()) {
         char c;
@@ -91,44 +44,10 @@ static void onClientPackage(int socket, Stream& in) {
     answer.sendToSocket(socket);
 }
 
-static void onClientDisconnect(int socket) {
+void GameServer::onClientDisconnect(int socket) {
     std::cout << socket << " has disconnected\n";
 
     Stream answer;
     answer.write("Bye.");
     answer.sendToSocket(socket);
-}
-
-static void loop() {
-    auto lastTime = std::chrono::steady_clock::now().time_since_epoch();
-    auto lag = lastTime.zero();
-
-    std::thread commandThread = std::thread(readFromConsole);
-    while(serverCommands.isRunning()) {
-        auto time = std::chrono::steady_clock::now().time_since_epoch();
-        lag += time - lastTime;
-        lastTime = time;
-        while(lag >= NANOS_PER_TICK) {
-            lag -= NANOS_PER_TICK;
-            tick();
-        }
-
-        auto waitTime = NANOS_PER_TICK - lag - MIN_NANO_SLEEP;
-        if(waitTime > MIN_NANO_SLEEP) {
-            std::this_thread::sleep_for(waitTime);
-        }
-    }
-    commandThread.join();
-}
-
-void GameServer::start(u16 port, u16 maxClients) {
-    init();
-    Server::setFullServerClientConnectFunction(onFullServerClientConnect);
-    Server::setClientConnectFunction(onClientConnect);
-    Server::setClientPackageFunction(onClientPackage);
-    Server::setClientDisconnectFunction(onClientDisconnect);
-    if(!Server::start(port, maxClients)) {
-        return;
-    }
-    loop();
 }

+ 19 - 4
server/GameServer.h

@@ -1,10 +1,25 @@
 #ifndef GAMESERVER_H
 #define GAMESERVER_H
 
-#include "common/utils/Types.h"
+#include "server/commands/CommandEditor.h"
+#include "server/commands/ServerCommands.h"
+#include "common/stream/Stream.h"
+#include "server/Clock.h"
 
-namespace GameServer {
-    void start(u16 port, u16 maxClients);
-}
+class GameServer final {
+public:
+    GameServer(const Clock& tps);
+
+    void tick();
+    void handleCommands(CommandEditor& editor, ServerCommands& serverCommands);
+
+    void onFullServerClientConnect(int socket);
+    void onClientConnect(int socket);
+    void onClientPackage(int socket, Stream& in);
+    void onClientDisconnect(int socket);
+
+private:
+    const Clock& tps;
+};
 
 #endif

+ 32 - 4
server/Main.cpp

@@ -1,8 +1,36 @@
+#include "server/network/Server.h"
 #include "server/GameServer.h"
-#include "common/block/BlockRegistry.h"
+#include "server/Clock.h"
 
-int main(int /*argc*/, char** /*argv*/) {
-    BlockRegistry::loadFromFile("resources/blocks");
-    GameServer::start(25565, 2);
+static const u64 NANOS_PER_TICK = 50000000;
+
+int main() {
+    Socket listener;
+    if(listener.hasError() || listener.setNonLinger() || listener.listenOnPort(25565, 5)) {
+        return 0;
+    }
+    Clock tps;
+    GameServer gameServer(tps);
+    Server server(listener, 20, gameServer);
+    ServerCommands serverCommands;
+    CommandEditor editor;
+    
+    Clock clock;
+    u64 lag = 0;
+    while(serverCommands.isRunning()) {
+        lag += clock.update();
+        while(lag >= NANOS_PER_TICK) {
+            lag -= NANOS_PER_TICK;
+            tps.update();
+            editor.preTick();
+            gameServer.handleCommands(editor, serverCommands);
+            gameServer.tick();
+            editor.postTick();
+        }
+        u64 waitNanos = NANOS_PER_TICK - lag;
+        if(waitNanos > 300000) {
+            clock.wait(waitNanos);
+        }
+    }
     return 0;
 }

+ 60 - 0
server/commands/CommandEditor.cpp

@@ -0,0 +1,60 @@
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "server/commands/CommandEditor.h"
+
+std::mutex CommandEditor::queueMutex;
+RingBuffer<String, 5> CommandEditor::queue;
+
+CommandEditor::CommandEditor() : running(true), readThread(&CommandEditor::loop, this) {
+    rl_bind_key('\t', rl_insert);
+}
+
+CommandEditor::~CommandEditor() {
+    running = false;
+    readThread.join();
+    rl_callback_handler_remove();
+    rl_clear_visible_line();
+}
+
+void CommandEditor::preTick() const {
+    rl_clear_visible_line();
+}
+
+void CommandEditor::postTick() const {
+    rl_forced_update_display();
+}
+
+bool CommandEditor::hasCommand() const {
+    return queue.canRead();
+}
+
+String CommandEditor::readCommand() {
+    std::lock_guard<std::mutex> lg(queueMutex);
+    return queue.read();
+}
+
+void CommandEditor::onLineRead(char* line) {
+    std::lock_guard<std::mutex> lg(queueMutex);
+    add_history(line);
+    queue.write(line);
+    free(line);
+}
+
+void CommandEditor::loop() {
+    rl_callback_handler_install("> ", onLineRead);
+    rl_set_keyboard_input_timeout(1);
+    while(running) {
+        struct pollfd fds;
+        fds.fd = STDIN_FILENO;
+        fds.events = POLLIN;
+        fds.revents = 0;
+        int result = poll(&fds, 1, 1);
+        if(result > 0) {
+            rl_callback_read_char();
+        }
+    }
+    rl_callback_handler_remove();
+}

+ 37 - 0
server/commands/CommandEditor.h

@@ -0,0 +1,37 @@
+#ifndef COMMANDEDITOR_H
+#define COMMANDEDITOR_H
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+
+#include "common/utils/RingBuffer.h"
+#include "common/utils/String.h"
+
+class CommandEditor final {
+public:
+    CommandEditor();
+    ~CommandEditor();
+    CommandEditor(const CommandEditor& other) = delete;
+    CommandEditor& operator=(const CommandEditor& other) = delete;
+    CommandEditor(CommandEditor&& other) = delete;
+    CommandEditor& operator=(CommandEditor&& other) = delete;
+    
+    void preTick() const;
+    void postTick() const;
+    
+    bool hasCommand() const;
+    String readCommand();
+
+private:
+    static void onLineRead(char* line); 
+    void loop(); 
+    
+    static std::mutex queueMutex;
+    static RingBuffer<String, 5> queue;
+    
+    std::atomic_bool running;
+    std::thread readThread;
+};
+
+#endif

+ 2 - 3
server/commands/ServerCommands.h

@@ -3,7 +3,7 @@
 
 #include <atomic>
 
-class ServerCommands {
+class ServerCommands final {
 public:
     ServerCommands();
 
@@ -14,5 +14,4 @@ private:
     std::atomic_bool running;
 };
 
-#endif
-
+#endif

+ 37 - 112
server/network/Server.cpp

@@ -1,7 +1,6 @@
 #include <iostream>
 #include <cstring>
 #include <vector>
-#include <atomic>
 #include <thread>
 #include <mutex>
 
@@ -11,8 +10,40 @@
 #include <poll.h>
 
 #include "server/network/Server.h"
+#include "server/network/Socket.h"
 
-static void printError(const char* message) {
+Server::Server(const Socket& connectionListener, u16 maxClients, GameServer& gameServer) :
+connectionListener(connectionListener), gameServer(gameServer), running(true),
+listenerThread(&Server::listenForClients, this) {
+    (void) maxClients;
+}
+
+Server::~Server() {
+    running = false;
+    listenerThread.join();
+}
+
+void Server::listenForClients() const {
+    while(running) {
+        WaitResult result = connectionListener.waitForConnection(100);
+        if(result == WaitResult::ERROR) {
+            break;
+        } else if(result == WaitResult::TIMEOUT) {
+            continue;
+        }
+        int clientSocket = connectionListener.acceptConnection();
+        if(clientSocket < 0) {
+            continue;
+        }
+        /*if(addClient(clientSocket)) {
+            if(close(clientSocket) == -1) {
+                printError("cannot close client socket");
+            }
+        }*/
+    }
+}
+
+/*static void printError(const char* message) {
     std::cout << message << ": " << strerror(errno) << "\n";
 }
 
@@ -40,28 +71,16 @@ struct ConnectedClient {
 };
 
 struct InternServer final {
-    InternServer() : listenerSocket(-1), listenerThread([]() {
-    })
-, shouldRun(true), clients(nullptr) {
+
+    InternServer() : clients(nullptr) {
     }
 
     ~InternServer() {
-        shouldRun = false;
-        listenerThread.join();
-
-        if(listenerSocket != -1) {
-            if(close(listenerSocket) == -1) {
-                printError("cannot close listener socket");
-            }
-        }
         if(clients != nullptr) {
             delete[] clients;
         }
     }
 
-    int listenerSocket;
-    std::thread listenerThread;
-    std::atomic_bool shouldRun;
     ConnectedClient* clients;
 };
 
@@ -84,8 +103,6 @@ static void defaultClientDisconnect(int) {
 }
 
 static std::mutex clientMutex;
-static u16 clientAmount = 0;
-static u16 maxClients = 0;
 static Server::FullServerClientConnectFunction onFullServerClientConnect = defaultFullServerClientConnect;
 static Server::ClientConnectFunction onClientConnect = defaultClientConnect;
 static Server::ClientPackageFunction onClientPackage = defaultClientPackage;
@@ -114,7 +131,7 @@ static void listenOnClient(ConnectedClient& cc) {
     fds.events = POLLIN; // wait until data is ready to read
     fds.revents = 0; // return events - none
 
-    onClientConnect(cc.socket);
+    //onClientConnect(cc.socket);
 
     Stream st;
     while(server.shouldRun) {
@@ -183,96 +200,4 @@ static bool addClient(int clientSocket) {
 
         return false;
     }
-}
-
-static void listenForClients() {
-    while(server.shouldRun) {
-        // wait until a connection arrives with timeout, this prevents being 
-        // stuck in accept
-        struct pollfd fds;
-        fds.fd = server.listenerSocket; // file descriptor for polling
-        fds.events = POLLIN; // wait until data is ready to read
-        fds.revents = 0; // return events - none
-        // nfds_t - 1 - amount of passed in structs
-        // timeout - 100 - milliseconds to wait until an event occurs
-        // returns 0 on timeout, -1 on error, and >0 on success
-        int pollData = poll(&fds, 1, 100);
-        if(pollData > 0) {
-            struct sockaddr_in clientSocketData;
-            // accepts an incoming client connection and stores the data in the
-            // given struct, returns a nonnegative file descriptor on success
-            socklen_t addrlen = sizeof (struct sockaddr_in);
-            int clientSocket = accept(server.listenerSocket, (struct sockaddr*) &clientSocketData, &addrlen);
-            if(clientSocket >= 0) {
-                if(addClient(clientSocket)) {
-                    if(close(clientSocket) == -1) {
-                        printError("cannot close client socket");
-                    }
-                }
-            } else {
-                printError("accept error");
-                break;
-            }
-        } else if(pollData == -1) {
-            printError("poll error");
-            break;
-        }
-    }
-}
-
-bool Server::start(u16 port, u16 inMaxClients) {
-    // create socket for incoming connections
-    // domain - AF_INET - IPv4 Internet protocols
-    // type - SOCK_STREAM - two-way, connection-based byte streams
-    // protocol - 0 - use standard protocol for the given socket type
-    server.listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
-    if(server.listenerSocket == -1) {
-        printError("cannot create listener socket");
-        return false;
-    }
-
-    // prevents clients from blocking the port if the server exits
-    // this is useful if server and client run on the same system
-    struct linger sl;
-    sl.l_onoff = 1; // nonzero to linger on close
-    sl.l_linger = 0; // time to linger
-    // sockfd - listenerSocket - modified socket
-    // level - SOL_SOCKET - manipulate options at the sockets API level
-    // optname - SO_LINGER - identifier of the option
-    if(setsockopt(server.listenerSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof (struct linger)) == -1) {
-        printError("cannot set non lingering");
-        return false;
-    }
-
-    // specify binding data
-    struct sockaddr_in connectSocketData;
-    // clear padding
-    memset(&connectSocketData, 0, sizeof (struct sockaddr_in));
-    // IPv4 Internet protocols
-    connectSocketData.sin_family = AF_INET;
-    // port in network byte order
-    connectSocketData.sin_port = htons(port);
-    // address in network byte order, accept any incoming messages
-    connectSocketData.sin_addr.s_addr = htons(INADDR_ANY);
-    // bind the socket
-    if(bind(server.listenerSocket, (struct sockaddr*) &connectSocketData, sizeof (struct sockaddr_in)) == -1) {
-        printError("cannot bind listener socket");
-        return false;
-    }
-
-    // mark the socket as handler for connection requests
-    // backlog - 5 - max queue length of pending connections
-    if(listen(server.listenerSocket, 5) == -1) {
-        printError("cannot start listening on socket");
-        return false;
-    }
-
-    server.shouldRun = true;
-
-    maxClients = inMaxClients;
-    server.clients = new ConnectedClient[inMaxClients];
-    // join empty spawn thread
-    server.listenerThread.join();
-    server.listenerThread = std::thread(listenForClients);
-    return true;
-}
+}*/

+ 23 - 13
server/network/Server.h

@@ -1,20 +1,30 @@
 #ifndef SERVER_H
 #define SERVER_H
 
-#include "common/utils/Types.h"
-#include "common/stream/Stream.h"
+#include <thread>
+#include <atomic>
 
-namespace Server {
-    typedef void (*FullServerClientConnectFunction) (int);
-    typedef void (*ClientConnectFunction) (int);
-    typedef void (*ClientPackageFunction) (int, Stream&);
-    typedef void (*ClientDisconnectFunction) (int);
+#include "server/network/Socket.h"
+#include "server/GameServer.h"
 
-    bool start(u16 port, u16 maxClients);
-    void setFullServerClientConnectFunction(FullServerClientConnectFunction f);
-    void setClientConnectFunction(ClientConnectFunction f);
-    void setClientPackageFunction(ClientPackageFunction f);
-    void setClientDisconnectFunction(ClientDisconnectFunction f);
-}
+class Server final {
+public:
+    Server(const Socket& connectionListener, u16 maxClients, GameServer& gameServer);
+    ~Server();
+    Server(const Server& other) = delete;
+    Server& operator=(const Server& other) = delete;
+    Server(Server&& other) = delete;
+    Server& operator=(Server&& other) = delete;
+    
+    void start();
+    
+private:
+    void listenForClients() const;
+
+    const Socket& connectionListener;
+    GameServer gameServer;
+    std::atomic_bool running;
+    std::thread listenerThread;
+};
 
 #endif

+ 103 - 0
server/network/Socket.cpp

@@ -0,0 +1,103 @@
+#include <sys/socket.h>
+#include <iostream>
+#include <cstring>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include "server/network/Socket.h"
+
+Socket::Socket() : socketId(-1) {
+    // domain - AF_INET - IPv4 Internet protocols
+    // type - SOCK_STREAM - two-way, connection-based byte streams
+    // protocol - 0 - use standard protocol for the given socket type
+    socketId = socket(AF_INET, SOCK_STREAM, 0);
+    if(socketId == -1) {
+        std::cout << "cannot create socket: " << strerror(errno) << "\n";
+    }
+}
+
+Socket::~Socket() {
+    if(socketId == -1) {
+        return;
+    }
+    if(close(socketId) == -1) {
+        std::cout << "cannot close socket: " << strerror(errno) << "\n";
+    }
+}
+
+bool Socket::hasError() const {
+    return socketId == -1;
+}
+
+bool Socket::setNonLinger() {
+    // prevents clients from blocking the port if the server exits
+    // this is useful if server and client run on the same system
+    struct linger sl;
+    sl.l_onoff = 1; // nonzero to linger on close
+    sl.l_linger = 0; // time to linger
+    // sockfd - listenerSocket - modified socket
+    // level - SOL_SOCKET - manipulate options at the sockets API level
+    // optname - SO_LINGER - identifier of the option
+    if(setsockopt(socketId, SOL_SOCKET, SO_LINGER, &sl, sizeof (struct linger)) == -1) {
+        std::cout << "cannot set non lingering: " << strerror(errno) << "\n";
+        return true;
+    }
+    return false;
+}
+
+bool Socket::listenOnPort(u16 port, uint queueLength) const {
+    // specify binding data
+    struct sockaddr_in connectSocketData;
+    // clear padding
+    memset(&connectSocketData, 0, sizeof (struct sockaddr_in));
+    // IPv4 Internet protocols
+    connectSocketData.sin_family = AF_INET;
+    // port in network byte order
+    connectSocketData.sin_port = htons(port);
+    // address in network byte order, accept any incoming messages
+    connectSocketData.sin_addr.s_addr = htons(INADDR_ANY);
+    // bind the socket
+    if(bind(socketId, (struct sockaddr*) &connectSocketData, sizeof (struct sockaddr_in)) == -1) {
+        std::cout << "cannot bind socket: " << strerror(errno) << "\n";
+        return true;
+    }
+    // mark the socket as handler for connection requests
+    // backlog - queueLength - max queue length of pending connections
+    if(listen(socketId, queueLength) == -1) {
+        std::cout << "cannot start listening on socket: " << strerror(errno) << "\n";
+        return true;
+    }
+    return false;
+}
+
+WaitResult Socket::waitForConnection(uint timeoutMillis) const {
+    // wait until a connection arrives with timeout, this prevents being stuck in accept
+    struct pollfd fds;
+    fds.fd = socketId; // file descriptor for polling
+    fds.events = POLLIN; // wait until data is ready to read
+    fds.revents = 0; // return events - none
+    // nfds_t - 1 - amount of passed in structs
+    // timeout - timeoutMillis - milliseconds to wait until an event occurs
+    int result = poll(&fds, 1, timeoutMillis);
+    if(result > 0) {
+        return WaitResult::SUCCESS;
+    } else if(result == 0) {
+        return WaitResult::TIMEOUT;
+    }
+    std::cout << "poll error: " << strerror(errno) << "\n";
+    return WaitResult::ERROR;
+}
+
+int Socket::acceptConnection() const {
+    struct sockaddr_in clientSocketData;
+    // accepts an incoming client connection and stores the data in the given struct
+    socklen_t addrlen = sizeof (struct sockaddr_in);
+    int clientSocket = accept(socketId, (struct sockaddr*) &clientSocketData, &addrlen);
+    if(clientSocket >= 0) {
+        return clientSocket;
+    }
+    std::cout << "accept error: " << strerror(errno) << "\n";
+    return -1;
+}
+

+ 29 - 0
server/network/Socket.h

@@ -0,0 +1,29 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include "common/utils/Types.h"
+
+enum WaitResult {
+    TIMEOUT, SUCCESS, ERROR
+};
+
+class Socket final {
+public:
+    Socket();
+    ~Socket();
+    Socket(const Socket& other) = delete;
+    Socket& operator=(const Socket& other) = delete;
+    Socket(Socket&& other) = delete;
+    Socket& operator=(Socket&& other) = delete;
+    
+    bool hasError() const;
+    bool setNonLinger();
+    bool listenOnPort(u16 port, uint queueLength) const;
+    WaitResult waitForConnection(uint timeoutMillis) const;
+    int acceptConnection() const;
+
+private:
+    int socketId;
+};
+
+#endif