From 5e37d936f659ee968f6fcd2abe3a36f042af1b57 Mon Sep 17 00:00:00 2001
From: Joonatan Korpela <joonatan.korpela@paivola.fi>
Date: Mon, 1 Mar 2021 16:14:52 +0200
Subject: [PATCH] WIP Protocol: Server doesn't crash when opening and closing
 connections

---
 src/cpp/game/game.cpp               | 19 ++++++++----
 src/cpp/game/game.hpp               | 16 +++++-----
 src/cpp/game/world.cpp              | 30 ------------------
 src/cpp/{game => logic}/player.cpp  |  4 +++
 src/cpp/{game => logic}/player.hpp  |  1 +
 src/cpp/{game => logic}/sprite.hpp  |  0
 src/cpp/{game => logic}/stage.cpp   |  0
 src/cpp/{game => logic}/stage.hpp   |  0
 src/cpp/logic/world.cpp             | 47 +++++++++++++++++++++++++++++
 src/cpp/{game => logic}/world.hpp   |  5 +--
 src/cpp/protocol/clientHandler.cpp  | 10 +++++-
 src/cpp/protocol/clientHandler.hpp  |  8 +++--
 src/cpp/websocket-server/server.cpp | 37 +++++++++++++----------
 src/cpp/websocket-server/server.hpp |  3 +-
 14 files changed, 112 insertions(+), 68 deletions(-)
 delete mode 100644 src/cpp/game/world.cpp
 rename src/cpp/{game => logic}/player.cpp (80%)
 rename src/cpp/{game => logic}/player.hpp (93%)
 rename src/cpp/{game => logic}/sprite.hpp (100%)
 rename src/cpp/{game => logic}/stage.cpp (100%)
 rename src/cpp/{game => logic}/stage.hpp (100%)
 create mode 100644 src/cpp/logic/world.cpp
 rename src/cpp/{game => logic}/world.hpp (85%)

diff --git a/src/cpp/game/game.cpp b/src/cpp/game/game.cpp
index 77e480d66..6837a2339 100644
--- a/src/cpp/game/game.cpp
+++ b/src/cpp/game/game.cpp
@@ -35,33 +35,40 @@ void Game::init(Engine & engine) {
 // Called by the engine at regular intervals which are determined by the TPS (ticks per second)
 void Game::update() {
     // Update the game
+    world.update();
 }
 
 // Called by the engine at regular intervals which are determined by the FPS (frames per second)
 // The name frames per second can be a bit misleading as the server doesn't do any of the visualization
 void Game::render() {
     // Send game state to connected clients
+    server.sendAll("TEST");
 }
 
 // Called by the server every time a new connection is established
-ClientHandler & Game::clientConnected(ConnectionHandle connectionHandle) {
+ClientHandler* Game::clientConnected(ConnectionHandle connectionHandle) {
     // Get a playerId for the new client
     int playerId = playerIdCounter++;
     
     // Create a new player object
-    Player & player = world.createNewPlayer(playerId);
+    Player* player = world.createNewPlayer(playerId);
     // Add player to the player map
-    playerMap.insert(pair<int, Player*>(playerId, &player));
+    playerMap.insert(pair<int, Player*>(playerId, player));
 
     // Create a new client handler
-    ClientHandler clientHandler = ClientHandler(connectionHandle, player, playerId, *this);
+    ClientHandler* clientHandler = new ClientHandler(connectionHandle, player, playerId, *this);
     // Add client handler to the client map
-    clientMap.insert(pair<int, ClientHandler*>(playerId, &clientHandler));
+    clientMap.insert(pair<int, ClientHandler*>(playerId, clientHandler));
 
     // Return the client handler for the use of the server
     return clientHandler;
 }
 
-void Game::clientDisconnected(ConnectionHandle connectionHandle) {
+void Game::clientDisconnected(int playerId) {
+    // Remove player
+    playerMap.erase(playerId);
     // Remove client
+    clientMap.erase(playerId);
+
+    world.removePlayer(playerId);
 }
diff --git a/src/cpp/game/game.hpp b/src/cpp/game/game.hpp
index 98cff86ec..b6a8a6c25 100644
--- a/src/cpp/game/game.hpp
+++ b/src/cpp/game/game.hpp
@@ -5,26 +5,26 @@
 #include "../engine/engine.hpp"
 #include "../protocol/clientHandler.hpp"
 #include "../websocket-server/server.hpp"
-#include "world.hpp"
-#include "player.hpp"
+#include "../logic/world.hpp"
+#include "../logic/player.hpp"
 
 typedef websocketpp::connection_hdl ConnectionHandle;
 
 // The game class orchestrates everything in the game; it is pretty much the core of the program
 class Game {
     public:
-        Game(Engine &, Server &);
+        Game(Engine&, Server&);
         void start();
-        void init(Engine &);
+        void init(Engine&);
         void update();
         void render();
 
-        ClientHandler& clientConnected(ConnectionHandle connection);
-        void clientDisconnected(ConnectionHandle connection);
+        ClientHandler* clientConnected(ConnectionHandle connection);
+        void clientDisconnected(int playerId);
     
     private:
-        Server & server;
-        Engine & engine;
+        Server& server;
+        Engine& engine;
         World world;
         map<int, ClientHandler*> clientMap;
         map<int, Player*> playerMap;
diff --git a/src/cpp/game/world.cpp b/src/cpp/game/world.cpp
deleted file mode 100644
index 021a9ea31..000000000
--- a/src/cpp/game/world.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "world.hpp"
-
-using namespace std;
-
-World::World() {
-    worlds.insert( pair<Stage*, vector<Sprite*>>(&mainStage, {}) );
-}
-
-void World::tick() {
-    for(auto const & world : worlds) {
-        Stage * stage = world.first;
-        vector<Sprite*> sprites = world.second;
-        for(Sprite * sprite : sprites) {
-            sprite->tick();
-        }
-    }
-}
-
-Player & World::createNewPlayer(int id) {
-    vector<int> spawnPoint = mainStage.getSpawnPoint();
-    Player player = Player(spawnPoint[0], spawnPoint[1], id);
-    vector<Sprite*> worldSprites = worlds[&mainStage];
-    worldSprites.push_back(&player);
-    return player;
-}
-
-string World::toString() {
-    // TODO
-    return "";
-}
\ No newline at end of file
diff --git a/src/cpp/game/player.cpp b/src/cpp/logic/player.cpp
similarity index 80%
rename from src/cpp/game/player.cpp
rename to src/cpp/logic/player.cpp
index 8cdd9e4d2..27158e170 100644
--- a/src/cpp/game/player.cpp
+++ b/src/cpp/logic/player.cpp
@@ -11,6 +11,10 @@ void Player::tick() {
 
 }
 
+int Player::getId() {
+    return id;
+}
+
 string Player::toString() {
     
 }
\ No newline at end of file
diff --git a/src/cpp/game/player.hpp b/src/cpp/logic/player.hpp
similarity index 93%
rename from src/cpp/game/player.hpp
rename to src/cpp/logic/player.hpp
index 5d12670b7..b084a046c 100644
--- a/src/cpp/game/player.hpp
+++ b/src/cpp/logic/player.hpp
@@ -7,6 +7,7 @@ class Player : public Sprite {
         Player(double, double, int);
         void tick();
         string toString();
+        int getId();
 
     private:
         double xPos;
diff --git a/src/cpp/game/sprite.hpp b/src/cpp/logic/sprite.hpp
similarity index 100%
rename from src/cpp/game/sprite.hpp
rename to src/cpp/logic/sprite.hpp
diff --git a/src/cpp/game/stage.cpp b/src/cpp/logic/stage.cpp
similarity index 100%
rename from src/cpp/game/stage.cpp
rename to src/cpp/logic/stage.cpp
diff --git a/src/cpp/game/stage.hpp b/src/cpp/logic/stage.hpp
similarity index 100%
rename from src/cpp/game/stage.hpp
rename to src/cpp/logic/stage.hpp
diff --git a/src/cpp/logic/world.cpp b/src/cpp/logic/world.cpp
new file mode 100644
index 000000000..573e05dca
--- /dev/null
+++ b/src/cpp/logic/world.cpp
@@ -0,0 +1,47 @@
+#include "world.hpp"
+
+using namespace std;
+
+World::World() {
+    worlds.insert( pair<Stage*, vector<Sprite*>>(&mainStage, {}) );
+}
+
+void World::update() {
+    for(auto const & world : worlds) {
+        Stage * stage = world.first;
+        vector<Sprite*> sprites = world.second;
+        for(Sprite * sprite : sprites) {
+            sprite->tick();
+        }
+    }
+}
+
+Player* World::createNewPlayer(int id) {
+    vector<int> spawnPoint = mainStage.getSpawnPoint();
+    Player* player = new Player(spawnPoint[0], spawnPoint[1], id);
+    vector<Sprite*> worldSprites = worlds[&mainStage];
+    worldSprites.push_back(player);
+    return player;
+}
+
+void World::removePlayer(int id) {
+    for(auto const & world : worlds) {
+        Stage * stage = world.first;
+        vector<Sprite*> sprites = world.second;
+        for(int i = 0; i < sprites.size(); i++) {
+            Sprite* sprite = sprites[i];
+            if(Player* player = dynamic_cast<Player*>(sprite)) {
+                if(player->getId() == id) {
+                    sprites.erase(sprites.begin()+i);
+                    delete player;
+                    return;
+                }
+            }
+        }
+    }
+}
+
+string World::toString() {
+    // TODO
+    return "";
+}
\ No newline at end of file
diff --git a/src/cpp/game/world.hpp b/src/cpp/logic/world.hpp
similarity index 85%
rename from src/cpp/game/world.hpp
rename to src/cpp/logic/world.hpp
index 8c60ad8d2..81af818c5 100644
--- a/src/cpp/game/world.hpp
+++ b/src/cpp/logic/world.hpp
@@ -18,8 +18,9 @@ will update each sprite's and its own state.
 class World {
     public:
         World();
-        void tick();
-        Player & createNewPlayer(int);
+        void update();
+        Player* createNewPlayer(int);
+        void removePlayer(int);
         string toString(); 
     
     private:
diff --git a/src/cpp/protocol/clientHandler.cpp b/src/cpp/protocol/clientHandler.cpp
index 11b9e617d..eb8b2cf5a 100644
--- a/src/cpp/protocol/clientHandler.cpp
+++ b/src/cpp/protocol/clientHandler.cpp
@@ -1,7 +1,7 @@
 #include "clientHandler.hpp"
 #include "../game/game.hpp"
 
-ClientHandler::ClientHandler(ConnectionHandle _connectionHandle, Player& _player, int id, Game& _game) : player(_player), game(_game) {
+ClientHandler::ClientHandler(ConnectionHandle _connectionHandle, Player* _player, int id, Game& _game) : player(_player), game(_game) {
 
     connectionHandle = _connectionHandle;
     playerId = id;
@@ -11,6 +11,10 @@ void ClientHandler::onMessage(ConnectionHandle connectionHandle, MessagePtr msg)
     // Parse the message
 }
 
+void ClientHandler::onClose(ConnectionHandle connectionHandle) {
+    server->disconnectClient(this);
+}
+
 ConnectionHandle ClientHandler::getConnectionHandle() {
     return connectionHandle;
 }
@@ -18,3 +22,7 @@ ConnectionHandle ClientHandler::getConnectionHandle() {
 void ClientHandler::setServer(Server* _server) {
     server = _server;
 }
+
+int ClientHandler::getPlayerId() {
+    return playerId;
+}
\ No newline at end of file
diff --git a/src/cpp/protocol/clientHandler.hpp b/src/cpp/protocol/clientHandler.hpp
index 795dc826a..583d59a0a 100644
--- a/src/cpp/protocol/clientHandler.hpp
+++ b/src/cpp/protocol/clientHandler.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../game/player.hpp"
+#include "../logic/player.hpp"
 
 #include <websocketpp/config/asio_no_tls.hpp>
 #include <websocketpp/server.hpp>
@@ -17,15 +17,17 @@ using namespace std;
 
 class ClientHandler {
     public:
-        ClientHandler(ConnectionHandle, Player&, int, Game&);
+        ClientHandler(ConnectionHandle, Player*, int, Game&);
         ConnectionHandle getConnectionHandle();
         void onMessage(ConnectionHandle connectionHandle, MessagePtr msg);
+        void onClose(ConnectionHandle connectionHandle);
         void setServer(Server*);
+        int getPlayerId();
 
     private:
         int playerId;
         ConnectionHandle connectionHandle;
-        Player& player;
+        Player* player;
         Game& game;
         Server* server;
 };
\ No newline at end of file
diff --git a/src/cpp/websocket-server/server.cpp b/src/cpp/websocket-server/server.cpp
index 36e971898..665af9215 100644
--- a/src/cpp/websocket-server/server.cpp
+++ b/src/cpp/websocket-server/server.cpp
@@ -11,33 +11,40 @@ using namespace std;
 Server::Server() {
     
     // Set logging settings
-    endpoint.set_access_channels(websocketpp::log::alevel::all);
+    endpoint.set_access_channels(websocketpp::log::alevel::none);
     endpoint.clear_error_channels(websocketpp::log::alevel::frame_payload);
 
     // Initialize Asio
     endpoint.init_asio();
 
     // Set message handler
-    endpoint.set_message_handler(bind(&Server::onMessage, this, placeholders::_1, placeholders::_2));
     endpoint.set_open_handler(bind(&Server::onOpen, this, placeholders::_1));
-    endpoint.set_close_handler(bind(&Server::onClose, this, placeholders::_1));
 }
 
 void Server::onOpen(ConnectionHandle connectionHandle) {
     printf("Connection opened\n");
+    
     const Connection& connection = endpoint.get_con_from_hdl(connectionHandle);
-    ClientHandler& client = game->clientConnected(connectionHandle);
-    connection->set_message_handler(bind(&ClientHandler::onMessage, &client, placeholders::_1, placeholders::_2));
-    clients.push_back(&client);
-}
+    ClientHandler* client = game->clientConnected(connectionHandle);
+    client->setServer(this);
 
-void Server::onClose(ConnectionHandle connectionHandle) {
-    printf("Connection closed\n");
+    connection->set_message_handler(bind(&ClientHandler::onMessage, client, placeholders::_1, placeholders::_2));
+    connection->set_close_handler(bind(&ClientHandler::onClose, client, placeholders::_1));
+
+    clients.push_back(client);
 }
 
-void Server::onMessage(ConnectionHandle connectionHandle, MessagePtr msg) {
-    
-    //endpoint.send(connection, msg->get_payload(), msg->get_opcode());
+void Server::disconnectClient(ClientHandler* clientHandler) {
+    for(int i = 0; i < clients.size(); i++) {
+        ClientHandler* handler = clients[i];
+        if(clientHandler == handler) {
+            clients.erase(clients.begin()+i);
+            break;
+        }
+    }
+    game->clientDisconnected(clientHandler->getPlayerId());
+    delete clientHandler;
+    printf("Connection closed\n");
 }
 
 thread Server::start(int port) {
@@ -64,17 +71,15 @@ void Server::setGame(Game & _game) {
     game = &_game;
 }
 
-void Server::send(Connection & connection, string message) {
+void Server::send(Connection& connection, string message) {
 
 }
 
 void Server::sendAll(string message) {
-    /*
     for(ClientHandler * client : clients) {
-        const Connection & connection = client->getConnection();
+        const Connection & connection = endpoint.get_con_from_hdl(client->getConnectionHandle());
         endpoint.send(connection, message, websocketpp::frame::opcode::text);
     }
-    */
 }
 
 const Connection& Server::getConnectionFromHandle(ConnectionHandle& handle) {
diff --git a/src/cpp/websocket-server/server.hpp b/src/cpp/websocket-server/server.hpp
index f2d48d4f5..fb3f04f36 100644
--- a/src/cpp/websocket-server/server.hpp
+++ b/src/cpp/websocket-server/server.hpp
@@ -26,15 +26,14 @@ class Server {
         void setGame(Game&);
         void send(Connection&, string);
         void sendAll(string);
+        void disconnectClient(ClientHandler* handler);
         const Connection& getConnectionFromHandle(ConnectionHandle& handle);
 
     private:
         Game * game;
         WebsocketServer endpoint;
         vector<ClientHandler*> clients;
-        void onMessage(ConnectionHandle hdl, MessagePtr msg);
         void onOpen(ConnectionHandle hdl);
-        void onClose(ConnectionHandle hdl);
         void sendMessage(Connection connection);
         void startRunLoop();
 };
-- 
GitLab