Browse Source

refactored several single instance classes to static calls with internal
classes

Kajetan Johannes Hammerle 4 years ago
parent
commit
5d117bf71d

+ 32 - 35
common/block/BlockRegistry.cpp

@@ -5,52 +5,49 @@
 
 #include "BlockRegistry.h"
 
-namespace BlockRegistry
+static Block NULL_BLOCK(0, "null_block");
+static std::unordered_map<std::string, u16> registry;
+static std::vector<Block> idMap = {NULL_BLOCK};
+
+void BlockRegistry::loadFromFile(const std::string& path)
 {
-    static Block NULL_BLOCK(0, "null_block");
-    static std::unordered_map<std::string, u16> registry;
-    static std::vector<Block> idMap = {NULL_BLOCK};
-    
-    void 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::ifstream in;
-        in.open(path);
+        std::getline(in, line, '\n');
         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;
+            break;
         }
+        u16 id = idMap.size();
+        idMap.emplace_back(id, line);
+        registry[line] = id;
     }
+}
 
-    const Block& getBlock(const std::string& name)
+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())
     {
-        const std::unordered_map<std::string, u16>::const_iterator& iter = registry.find(name);
-        if(iter == registry.end())
-        {
-            return NULL_BLOCK;
-        }    
-        return idMap[iter->second];
+        return NULL_BLOCK;
     }
+    return idMap[iter->second];
+}
 
-    const Block& getBlock(u16 id)
+const Block& BlockRegistry::getBlock(u16 id)
+{
+    if(id >= idMap.size())
     {
-        if(id >= idMap.size())
-        {
-            return NULL_BLOCK;
-        }
-        return idMap[id];
+        return NULL_BLOCK;
     }
+    return idMap[id];
 }
 

+ 1 - 1
meson.build

@@ -4,7 +4,7 @@ project('cubes plus plus', 'cpp')
 
 sourcesCommon = ['common/stream/Stream.cpp', 'common/block/BlockRegistry.cpp','common/block/Block.cpp', 'common/utils/DataVector.cpp', 'common/world/Chunk.cpp', 'common/world/World.cpp']
 
-sourcesServer = ['server/Main.cpp', 'server/GameServer.cpp', 'server/network/Server.cpp', 'server/commands/CommandManager.cpp', 'server/commands/CommandUtils.cpp', 'server/commands/GeneralCommands.cpp', 'server/commands/ServerCommands.cpp']
+sourcesServer = ['server/Main.cpp', 'server/GameServer.cpp', 'server/network/Server.cpp', 'server/commands/CommandManager.cpp', 'server/commands/CommandUtils.cpp', 'server/commands/ServerCommands.cpp']
 
 #sourcesClient = ['client/Main.cpp', 'client/engine/Clock.cpp', 'client/engine/DirectRenderer.cpp', 'client/engine/KeyManager.cpp', 'client/engine/Mesh.cpp', 'client/engine/MouseManager.cpp', 'client/engine/Shader.cpp', 'client/engine/Texture.cpp', 'client/engine/Utils.cpp', 'client/engine/Wrapper.cpp', 'client/engine/shader/ShaderProgram.cpp', 'client/engine/shader/WorldShader.cpp', 'client/engine/shader/FramebufferRectangle.cpp', 'client/engine/shader/SSAOShader.cpp', 'client/engine/shader/SSAOBlurShader.cpp', 'client/engine/shader/WorldPostShader.cpp', 'client/engine/shader/OverlayShader.cpp', 'client/GameClient.cpp', 'client/rendering/ChunkRenderer.cpp', 'client/rendering/ClientChunkProvider.cpp', 'client/rendering/block/BlockRenderer.cpp', 'client/rendering/block/BlockRenderers.cpp', 'client/rendering/entity/EntityRenderer.cpp', 'client/rendering/gui/GUI.cpp', 'client/rendering/gui/StartMenu.cpp', 'client/math/Matrix3D.cpp', 'client/math/Matrix3DStack.cpp', 'client/math/StackOverflow.cpp', 'client/math/StackUnderflow.cpp', 'client/math/Vector3D.cpp', 'client/math/Plane3D.cpp', 'client/math/Camera3D.cpp', 'client/network/Client.cpp', 'client/network/ClientListener.cpp']
 

+ 89 - 61
server/GameServer.cpp

@@ -1,5 +1,9 @@
 #include <iostream>
 #include <cstring>
+#include <chrono>
+#include <mutex>
+#include <thread>
+#include <vector>
 
 #include <poll.h>
 #include <readline/readline.h>
@@ -8,15 +12,25 @@
 
 #include "server/GameServer.h"
 #include "server/network/Server.h"
+#include "server/commands/ServerCommands.h"
+#include "server/commands/CommandManager.h"
 
-GameServer::GameServer()
+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 bool once = false;
+
+static void init()
 {
-    rl_bind_key ('\t', rl_insert);
-    
-    static GameServer* pointer = this;
-    rl_event_hook = []()
+    rl_bind_key('\t', rl_insert);
+    rl_event_hook = []() 
     {
-        if(!pointer->serverCommands.isRunning())
+        if(!serverCommands.isRunning())
         {
             rl_stuff_char('\n');
         }
@@ -24,58 +38,61 @@ GameServer::GameServer()
     };
 }
 
-void GameServer::start(uint16_t port, uint16_t maxClients)
+static void readFromConsole()
 {
-    std::cout << port << std::endl;
-    Server server(port, maxClients, *this);
-    if(!server.isRunning())
-    {
-        return;
-    }
-    
-    auto lastTime = std::chrono::steady_clock::now().time_since_epoch();
-    auto lag = lastTime.zero();
-    
-    std::thread commandThread = std::thread(&GameServer::readFromConsole, this);
-    
     while(serverCommands.isRunning())
     {
-        auto time = std::chrono::steady_clock::now().time_since_epoch();
-        lag += time - lastTime;
-        lastTime = time;
-        while(lag >= NANOS_PER_TICK)
+        char* test = readline("> ");
+        if(test == nullptr)
         {
-            lag -= NANOS_PER_TICK;
-            tick();
+            continue;
         }
-        
-        auto waitTime = NANOS_PER_TICK - lag - MIN_NANO_SLEEP;
-        if(waitTime > MIN_NANO_SLEEP)
+        std::string command(test);
+        if(command.size() != 0)
         {
-            std::this_thread::sleep_for(waitTime);
+            add_history(test);
+            std::lock_guard<std::mutex> lg(comandQueueMutex);
+            commandQueue.push_back(command);
         }
+        free(test);
     }
-    
-    commandThread.join();
 }
 
-void GameServer::onFullServerClientConnect(int socket) const
+static void handleCommandQueue()
+{
+    std::lock_guard<std::mutex> lg(comandQueueMutex);
+    for(std::string& s : commandQueue)
+    {
+        CommandManager::execute(serverCommands, s);
+    }
+    commandQueue.clear();
+}
+
+static void tick()
+{
+    rl_clear_visible_line();
+    handleCommandQueue();
+    std::cout << "tick\n";
+    rl_forced_update_display();
+}
+
+static void onFullServerClientConnect(int socket)
 {
     Stream answer;
     answer.write("Sorry, the server is full");
     answer.sendToSocket(socket);
 }
 
-void GameServer::onClientConnect(int socket) const
+static void onClientConnect(int socket)
 {
-    std::cout << socket << " has connected" << std::endl;
-    
+    std::cout << socket << " has connected\n";
+
     Stream answer;
     answer.write("Welcome to the server.");
     answer.sendToSocket(socket);
 }
 
-void GameServer::onClientPackage(int socket, Stream& in) const
+static void onClientPackage(int socket, Stream& in)
 {
     std::string s = "";
     while(in.hasData())
@@ -90,46 +107,57 @@ void GameServer::onClientPackage(int socket, Stream& in) const
     answer.sendToSocket(socket);
 }
 
-void GameServer::onClientDisconnect(int socket) const
+static void onClientDisconnect(int socket)
 {
-    std::cout << socket << " has disconnected" << std::endl;
-    
+    std::cout << socket << " has disconnected\n";
+
     Stream answer;
     answer.write("Bye.");
     answer.sendToSocket(socket);
 }
 
-void GameServer::tick()
+static void loop()
 {
-    rl_clear_visible_line();
-    handleCommandQueue();
-    std::cout << "tick\n";
-    rl_forced_update_display();
-}
+    auto lastTime = std::chrono::steady_clock::now().time_since_epoch();
+    auto lag = lastTime.zero();
 
-void GameServer::readFromConsole()
-{
+    std::thread commandThread = std::thread(readFromConsole);
     while(serverCommands.isRunning())
     {
-        char* test = readline("> ");
-        std::string command(test);
-        if(command.size() != 0)
+        auto time = std::chrono::steady_clock::now().time_since_epoch();
+        lag += time - lastTime;
+        lastTime = time;
+        while(lag >= NANOS_PER_TICK)
         {
-            add_history(test);
-            std::lock_guard<std::mutex> lg(comandQueueMutex);
-            commandQueue.push_back(command);
+            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);
         }
-        free(test);
     }
+    commandThread.join();
 }
 
-void GameServer::handleCommandQueue()
+void GameServer::start(u16 port, u16 maxClients)
 {
-    std::lock_guard<std::mutex> lg(comandQueueMutex);
-    for(std::string& s : commandQueue)
+    if(once)
     {
-        commandManager.execute(serverCommands, s);
+        return;
     }
-    commandQueue.clear();
-}
-
+    once = true;
+    
+    init();
+    Server::setFullServerClientConnectFunction(onFullServerClientConnect);
+    Server::setClientConnectFunction(onClientConnect);
+    Server::setClientPackageFunction(onClientPackage);
+    Server::setClientDisconnectFunction(onClientDisconnect);
+    if(!Server::start(port, maxClients))
+    {
+        return;
+    }
+    loop();
+}

+ 4 - 32
server/GameServer.h

@@ -1,40 +1,12 @@
 #ifndef GAMESERVER_H
 #define GAMESERVER_H
 
-#include <chrono>
-#include <vector>
-#include <mutex>
-
 #include "common/utils/Types.h"
-#include "server/network/IServerListener.h"
-#include "server/commands/CommandManager.h"
-#include "commands/ServerCommands.h"
 
-class GameServer : public IServerListener
-{
-public:
-    GameServer();
-    
-    void start(uint16_t port, uint16_t maxClients);
-    
-    void onFullServerClientConnect(int socket) const override;
-    void onClientConnect(int socket) const override;
-    void onClientPackage(int socket, Stream& in) const override;
-    void onClientDisconnect(int socket) const override;
-    
-private:
-    void readFromConsole();
-    void handleCommandQueue();
-    const std::chrono::nanoseconds NANOS_PER_TICK = std::chrono::nanoseconds(50000000);
-    const std::chrono::nanoseconds MIN_NANO_SLEEP = std::chrono::nanoseconds(300000);
-    void tick();
-    
-    CommandManager commandManager;
-    ServerCommands serverCommands;
-    
-    std::vector<std::string> commandQueue;
-    std::mutex comandQueueMutex; 
-};
+namespace GameServer
+{   
+    void start(u16 port, u16 maxClients);
+}
 
 #endif
 

+ 1 - 3
server/Main.cpp

@@ -4,8 +4,6 @@
 int main(int /*argc*/, char** /*argv*/)
 {
     BlockRegistry::loadFromFile("resources/blocks");
-
-    GameServer server;
-    server.start(25565, 2);
+    GameServer::start(25565, 2);
     return 0;
 }

+ 20 - 8
server/commands/CommandManager.cpp

@@ -1,28 +1,40 @@
 #include <iostream>
+#include <unordered_map>
+#include <vector>
 
 #include "server/commands/CommandManager.h"
 #include "server/commands/CommandUtils.h"
-#include "server/commands/GeneralCommands.h"
 
-CommandManager::CommandManager()
+static void commandTest(ServerCommands& /*sc*/, const std::vector<std::string>& args)
 {
-    registerCommand("test", GeneralCommands::test);
-    registerCommand("stop", GeneralCommands::stop);
+    std::cout << "test command" << std::endl;
+    for(size_t i = 0; i < args.size(); i++)
+    {
+        std::cout << " - " << args[i] << std::endl;
+    }
 }
 
-void CommandManager::registerCommand(const std::string& name, Command command)
+static void commandStop(ServerCommands& sc, const std::vector<std::string>& /*args*/)
 {
-    commands[name] = command;
+    sc.stop();
 }
 
-void CommandManager::execute(ServerCommands& sc, const std::string& rawCommand) const
+typedef void (*Command) (ServerCommands&, const std::vector<std::string>&);
+
+static std::unordered_map<std::string, Command> commands(
+{
+    {"test", commandTest},
+    {"stop", commandStop}
+});
+
+void CommandManager::execute(ServerCommands& sc, const std::string& rawCommand)
 {
     std::vector<std::string> args;
     
     std::string command;
     if(CommandUtils::splitString(rawCommand, command, args))
     {
-        std::cout << "Invalid command syntax: '" << rawCommand << "'" << std::endl;
+        std::cout << "Invalid command syntax: '" << rawCommand << "'\n";
         return;
     }
 

+ 3 - 16
server/commands/CommandManager.h

@@ -1,25 +1,12 @@
 #ifndef COMMANDMANAGER_H
 #define COMMANDMANAGER_H
 
-#include <unordered_map>
-#include <vector>
-
 #include "ServerCommands.h"
 
-class CommandManager
+namespace CommandManager
 {
-public:
-    typedef void (*Command) (ServerCommands&, const std::vector<std::string>&);
-    
-    CommandManager();
-    
-    void execute(ServerCommands& sc, const std::string& rawCommand) const;
-
-private:
-    std::unordered_map<std::string, Command> commands;
-    
-    void registerCommand(const std::string& name, Command command);
-};
+    void execute(ServerCommands& sc, const std::string& rawCommand);
+}
 
 #endif
 

+ 0 - 17
server/commands/GeneralCommands.cpp

@@ -1,17 +0,0 @@
-#include <iostream>
-
-#include "server/commands/GeneralCommands.h"
-
-void GeneralCommands::test(ServerCommands& /*sc*/, const std::vector<std::string>& args)
-{
-    std::cout << "test command" << std::endl;
-    for(size_t i = 0; i < args.size(); i++)
-    {
-        std::cout << " - " << args[i] << std::endl;
-    }
-}
-
-void GeneralCommands::stop(ServerCommands& sc, const std::vector<std::string>& /*args*/)
-{
-    sc.stop();
-}

+ 0 - 15
server/commands/GeneralCommands.h

@@ -1,15 +0,0 @@
-#ifndef GENERALCOMMANDS_H
-#define GENERALCOMMANDS_H
-
-#include <vector>
-#include <string>
-#include "server/commands/ServerCommands.h"
-
-namespace GeneralCommands
-{
-    void test(ServerCommands& sc, const std::vector<std::string>& args);
-    void stop(ServerCommands& sc, const std::vector<std::string>& args);
-}
-
-#endif
-

+ 0 - 17
server/network/IServerListener.h

@@ -1,17 +0,0 @@
-#ifndef ISERVERLISTENER_H
-#define ISERVERLISTENER_H
-
-#include "common/stream/Stream.h"
-
-class IServerListener
-{
-public:
-    virtual ~IServerListener() = default;
-    virtual void onFullServerClientConnect(int socket) const = 0;
-    virtual void onClientConnect(int socket) const = 0;
-    virtual void onClientPackage(int socket, Stream& in) const = 0;
-    virtual void onClientDisconnect(int socket) const = 0;
-};
-
-#endif
-

+ 210 - 149
server/network/Server.cpp

@@ -1,5 +1,9 @@
 #include <iostream>
 #include <cstring>
+#include <vector>
+#include <atomic>
+#include <thread>
+#include <mutex>
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -8,174 +12,178 @@
 
 #include "server/network/Server.h"
 
-// the empty function ensures the thread is joinable
-Server::ConnectedClient::ConnectedClient() : th([]() {}), socket(-1)
+static void printError(const char* message)
 {
+    std::cout << message << ": " << strerror(errno) << "\n";
 }
 
-Server::Server(uint16_t port, uint16_t maxClients, const IServerListener& listener) : 
-    shouldRun(false), port(port), maxClients(maxClients), serverListener(listener), 
-    listenerSocket(-1), clientAmount(0), clients(nullptr)
+struct ConnectedClient
 {
-    // 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
-    listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
-    if(listenerSocket == -1)
+    ~ConnectedClient()
     {
-        printError("cannot create listener socket");
-        return;
-    }
-    
-    // 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(listenerSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger)) == -1)
-    {
-        printError("cannot set non lingering");
-        return;
-    }
-    
-    // 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(listenerSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) == -1)
-    {
-        printError("cannot bind listener socket");
-        return;
-    }
-    
-    // mark the socket as handler for connection requests
-    // backlog - 5 - max queue length of pending connections
-    if(listen(listenerSocket, 5) == -1)
-    {
-        printError("cannot start listening on socket");
-        return;
+        if(socket != -1)
+        {
+            if(shutdown(socket, SHUT_RDWR))
+            {
+                printError("cannot shutdown client socket");
+            }
+            if(close(socket) == -1)
+            {
+                printError("cannot close client socket");
+            }
+        }
+        if(th.joinable())
+        {
+            th.join();
+        }
+        else
+        {
+            std::cout << "cannot join client connection thread\n";
+        }
     }
     
-    shouldRun = true;
-    listenerThread = std::thread(&Server::listenForClients, this);
-    
-    clients = new ConnectedClient[maxClients];
-}
+    std::thread th = std::thread([]() {});
+    int socket = -1;
+};
 
-Server::~Server()
+struct InternServer final
 {
-    shouldRun = false;
-    listenerThread.join();
-    
-    if(listenerSocket != -1)
+    InternServer() : listenerSocket(-1), shouldRun(true), clients(nullptr)
     {
-        if(close(listenerSocket) == -1)
-        {
-            printError("cannot close listener socket");
-        }
     }
     
-    if(clients != nullptr)
+    ~InternServer()
     {
-        for(uint16_t i = 0; i < maxClients; i++)
+        shouldRun = false;
+        listenerThread.join();
+
+        if(listenerSocket != -1)
         {
-            if(clients[i].socket != -1)
-            {
-                if(shutdown(clients[i].socket, SHUT_RDWR))
-                {
-                    printError("cannot shutdown client socket");
-                }
-                if(close(clients[i].socket) == -1)
-                {
-                    printError("cannot close client socket");
-                }
-            }
-            if(clients[i].th.joinable())
+            if(close(listenerSocket) == -1)
             {
-                clients[i].th.join();
-            }
-            else
-            {
-                std::cout << "cannot join client connection thread " << std::endl;
+                printError("cannot close listener socket");
             }
         }
-        delete[] clients;
+        if(clients != nullptr)
+        {
+            delete[] clients;
+        }
     }
+    
+    int listenerSocket;
+    std::thread listenerThread;
+    std::atomic_bool shouldRun;
+    ConnectedClient* clients;
+};
+
+static InternServer server;
+
+static void defaultFullServerClientConnect(int)
+{
+    std::cout << "default onFullServerClientConnectFunction\n";
 }
 
-bool Server::isRunning() const
+static void defaultClientConnect(int)
 {
-    return shouldRun;
+    std::cout << "default onClientConnectFunction\n";
 }
 
-void Server::printError(const char* message) const
+static void defaultClientPackage(int, Stream&)
 {
-    std::cout << message << ": " << strerror(errno) << std::endl;
+    std::cout << "default onClientPackageFunction\n";
 }
 
-void Server::listenForClients()
+static void defaultClientDisconnect(int)
 {
-    while(shouldRun)
+    std::cout << "default onClientDisconnectFunction\n";
+}
+
+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;
+static Server::ClientDisconnectFunction onClientDisconnect = defaultClientDisconnect;
+
+void Server::setFullServerClientConnectFunction(Server::FullServerClientConnectFunction f)
+{
+    onFullServerClientConnect = f;
+}
+
+void Server::setClientConnectFunction(Server::ClientConnectFunction f)
+{
+    onClientConnect = f;
+}
+
+void Server::setClientPackageFunction(Server::ClientPackageFunction f)
+{
+    onClientPackage = f;
+}
+
+void Server::setClientDisconnectFunction(Server::ClientDisconnectFunction f)
+{
+    onClientDisconnect = f;
+}
+
+static void listenOnClient(ConnectedClient& cc)
+{
+    // poll data
+    struct pollfd fds;
+    fds.fd = cc.socket; // file descriptor for polling
+    fds.events = POLLIN; // wait until data is ready to read
+    fds.revents = 0; // return events - none
+    
+    onClientConnect(cc.socket);
+    
+    Stream st;
+    while(server.shouldRun)
     {
-        // wait until a connection arrives with timeout, this prevents being 
-        // stuck in accept
-        struct pollfd fds;
-        fds.fd = 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(listenerSocket, (struct sockaddr*) &clientSocketData, &addrlen);
-            if(clientSocket >= 0)
+            st.readSocket(cc.socket);
+            if(st.hasData())
             {
-                //std::cout << inet_ntoa(clientSocketData.sin_addr) << ":" << (int) ntohs(clientSocketData.sin_port) << std::endl;
-                if(addClient(clientSocket))
-                {
-                    if(close(clientSocket) == -1)
-                    {
-                        printError("cannot close client socket");
-                    }
-                }
+                onClientPackage(cc.socket, st);
             }
             else
             {
-                printError("accept error");
+                // client closed connection
                 break;
             }
         }
         else if(pollData == -1)
         {
-            printError("poll error");
+            printError("cannot poll from client");
             break;
         }
     }
+    
+    onClientDisconnect(cc.socket);
+    
+    // reset slot for another client
+    if(server.shouldRun)
+    {
+        std::lock_guard<std::mutex> lg(clientMutex);
+        if(close(cc.socket) == -1)
+        {
+            printError("cannot close socket of client");
+        }
+        cc.socket = -1;
+        clientAmount--;
+    }
 }
 
-bool Server::addClient(int clientSocket)
+static bool addClient(int clientSocket)
 {
     std::lock_guard<std::mutex> lg(clientMutex);
     if(clientAmount >= maxClients)
     {
-        serverListener.onFullServerClientConnect(clientSocket);
+        onFullServerClientConnect(clientSocket);
         return true;
     }
     else
@@ -184,7 +192,7 @@ bool Server::addClient(int clientSocket)
         uint16_t index = 0;
         while(index < maxClients)
         {
-            if(clients[index].socket == -1)
+            if(server.clients[index].socket == -1)
             {
                 break;
             }
@@ -193,20 +201,20 @@ bool Server::addClient(int clientSocket)
         
         if(index >= maxClients)
         {
-            std::cout << "cannot find free slot - even if there should be one" << std::endl;
+            std::cout << "cannot find free slot - even if there should be one\n";
             return true;
         }
 
         //ensure old thread has ended
-        if(!clients[index].th.joinable())
+        if(!server.clients[index].th.joinable())
         {
-            std::cout << "cannot join thread of non used client connection" << std::endl;
+            std::cout << "cannot join thread of non used client connection\n";
             return true;
         }
-        clients[index].th.join();
+        server.clients[index].th.join();
 
-        clients[index].socket = clientSocket;
-        clients[index].th = std::thread(&Server::listenOnClient, this, std::ref(clients[index]));
+        server.clients[index].socket = clientSocket;
+        server.clients[index].th = std::thread(listenOnClient, std::ref(server.clients[index]));
 
         clientAmount++;
 
@@ -214,54 +222,107 @@ bool Server::addClient(int clientSocket)
     }
 }
 
-void Server::listenOnClient(ConnectedClient& cc)
+static void listenForClients()
 {
-    // poll data
-    struct pollfd fds;
-    fds.fd = cc.socket; // file descriptor for polling
-    fds.events = POLLIN; // wait until data is ready to read
-    fds.revents = 0; // return events - none
-    
-    serverListener.onClientConnect(cc.socket);
-    
-    Stream st;
-    while(shouldRun)
+    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)
         {
-            st.readSocket(cc.socket);
-            if(st.hasData())
+            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)
             {
-                serverListener.onClientPackage(cc.socket, st);
+                if(addClient(clientSocket))
+                {
+                    if(close(clientSocket) == -1)
+                    {
+                        printError("cannot close client socket");
+                    }
+                }
             }
             else
             {
-                // client closed connection
+                printError("accept error");
                 break;
             }
         }
         else if(pollData == -1)
         {
-            printError("cannot poll from client");
+            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;
+    }
     
-    serverListener.onClientDisconnect(cc.socket);
+    // 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;
+    }
     
-    // reset slot for another client
-    if(shouldRun)
+    // 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)
     {
-        std::lock_guard<std::mutex> lg(clientMutex);
-        if(close(cc.socket) == -1)
-        {
-            printError("cannot close socket of client");
-        }
-        cc.socket = -1;
-        clientAmount--;
+        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];
+    server.listenerThread = std::thread(listenForClients);
+    return true;
 }

+ 13 - 39
server/network/Server.h

@@ -1,47 +1,21 @@
 #ifndef SERVER_H
 #define SERVER_H
 
-#include <thread>
-#include <mutex>
-#include <atomic>
+#include "common/utils/Types.h"
+#include "common/stream/Stream.h"
 
-#include "server/network/IServerListener.h"
-
-class Server final
+namespace Server
 {
-private:
-    struct ConnectedClient
-    {
-        ConnectedClient();
-        std::thread th;
-        int socket;
-    };
-    
-public:
-    Server(uint16_t port, uint16_t maxClients, const IServerListener& listener);
-    ~Server();
-
-    bool isRunning() const;
-    
-private:
-    void printError(const char* message) const;
-    void listenForClients();
-    bool addClient(int clientSocket);
-    void listenOnClient(ConnectedClient& cc);
-    
-    std::atomic_bool shouldRun;
-    
-    uint16_t port;
-    uint16_t maxClients;
-    
-    const IServerListener& serverListener;
-    
-    int listenerSocket;
-    std::thread listenerThread;
+    typedef void (*FullServerClientConnectFunction) (int);
+    typedef void (*ClientConnectFunction) (int);
+    typedef void (*ClientPackageFunction) (int, Stream&);
+    typedef void (*ClientDisconnectFunction) (int);
     
-    uint16_t clientAmount;
-    ConnectedClient* clients;
-    std::mutex clientMutex;
-};
+    bool start(u16 port, u16 maxClients);
+    void setFullServerClientConnectFunction(FullServerClientConnectFunction f);
+    void setClientConnectFunction(ClientConnectFunction f);
+    void setClientPackageFunction(ClientPackageFunction f);
+    void setClientDisconnectFunction(ClientDisconnectFunction f);
+}
 
 #endif