LCOV - code coverage report
Current view: top level - server - Server.cpp (source / functions) Hit Total Coverage
Test: code-coverage.info.cleaned Lines: 92 135 68.1 %
Date: 2023-01-28 00:08:57 Functions: 8 9 88.9 %

          Line data    Source code
       1             : #include <server.hpp>
       2             : #include <boost/asio.hpp>
       3             : #include <boost/asio/ip/tcp.hpp>
       4             : #include <iostream>
       5             : 
       6             : 
       7             : using namespace server;
       8             : 
       9           1 : void Server::start(unsigned short port)
      10             : {
      11           1 :     boost::asio::io_context io_context;
      12           1 :     boost::asio::ip::tcp::acceptor acceptor(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));
      13             : 
      14           3 :     while (running.load())
      15             :     {
      16             : 
      17           3 :         boost::asio::ip::tcp::socket socket(io_context);
      18           3 :         acceptor.accept(socket);
      19             : 
      20           2 :         handleAccept(acceptor, socket);
      21           2 :     }
      22           0 : }
      23             : 
      24           2 : void Server::handleAccept(boost::asio::ip::tcp::acceptor &acceptor, boost::asio::ip::tcp::socket &socket)
      25             : {
      26           2 :     if (running.load())
      27             :     {
      28           2 :         std::thread t(&Server::handleClient, this, std::move(socket));
      29           2 :         t.detach();
      30           2 :     }
      31             :     else
      32             :     {
      33           0 :         acceptor.cancel();
      34             :     }
      35           2 : }
      36             : 
      37           2 : void Server::handleClient(boost::asio::ip::tcp::socket socket)
      38             : {
      39           2 :     std::cout << "New client connected" << std::endl;
      40           2 :     auto player = std::make_shared<shared::Player>();
      41           2 :     player->setSocket(socket);
      42             : 
      43           2 :     boost::asio::streambuf request;
      44             :     try
      45             :     {
      46           2 :         boost::asio::read_until(player->getSocket(), request, '\n');
      47             :     }
      48           0 :     catch (const boost::system::system_error &e)
      49             :     {
      50           0 :         std::cerr << "Error: " << e.what() << std::endl;
      51           0 :         player->disconnectPlayer();
      52           0 :         return;
      53           0 :     }
      54           2 :     std::istream requestStream(&request);
      55           2 :     std::string gameId;
      56           2 :     std::string username;
      57           2 :     std::shared_ptr<GameEngine> game = nullptr;
      58           2 :     requestStream >> gameId >> username;
      59           2 :     player->setUsername(username);
      60             : 
      61             :     // New game
      62           2 :     if (gameId.compare("new") == 0)
      63             :     {
      64           1 :         player->setUsername(username);
      65           1 :         game = createNewGame(player);
      66             :     }
      67             :     // Connection to existing game
      68             :     else
      69             :     {
      70           1 :         game = getGameById(gameId);
      71           1 :         bool playerConnected = false;
      72           1 :         if (game != nullptr)
      73             :         {
      74           1 :             playerConnected = connectPlayerToGame(player, game);
      75             :         }
      76             : 
      77           1 :         if (!playerConnected)
      78             :         {
      79           0 :             std::string response = "Error: game not available\n";
      80             : 
      81           0 :             std::unique_lock<std::mutex> lock(player->socketWriteMutex);
      82           0 :             boost::asio::write(player->getSocket(), boost::asio::buffer(response));
      83           0 :             lock.unlock();
      84             : 
      85           0 :             player->disconnectPlayer();
      86           0 :             return;
      87           0 :         }
      88             :     }
      89             : 
      90           4 :     std::string response = "connected " + game->getTime() + " server Game ID: " + game->getId() + "\n";
      91             :     {
      92           2 :         std::lock_guard<std::mutex> lock(player->socketWriteMutex);
      93           2 :         boost::asio::write(player->getSocket(), boost::asio::buffer(response));
      94           2 :     }
      95             : 
      96           2 :     sendGameInfo(game, player);
      97             :     
      98           7 :     while (player->connectedToSocket.load())
      99             :     {
     100           7 :         boost::asio::streambuf receiveBuffer;
     101           7 :         boost::system::error_code error;
     102           7 :         std::size_t bytesTransferred{0};
     103             : 
     104             :         try
     105             :         {
     106           7 :             std::lock_guard<std::mutex> lock(player->socketReadMutex);
     107           7 :             bytesTransferred = boost::asio::read_until(player->getSocket(), receiveBuffer, '\n', error);
     108           5 :         }
     109           0 :         catch (const boost::system::system_error &e)
     110             :         {
     111           0 :             std::cerr << "Error: " << e.what() << std::endl;
     112           0 :         }
     113             : 
     114           5 :         if (error == boost::asio::error::operation_aborted || error == boost::asio::error::eof)
     115             :         {
     116           0 :             player->disconnectPlayer();
     117           0 :             continue;
     118             :         }
     119           5 :         else if (error)
     120             :         {
     121           0 :             std::cerr << "Error: " << error.message() << std::endl;
     122           0 :             player->disconnectPlayer();
     123           0 :             continue;
     124             :         }
     125           5 :         if (bytesTransferred)
     126             :         {
     127           5 :             processMessage(receiveBuffer, player, game);
     128           5 :             receiveBuffer.consume(receiveBuffer.size());
     129             :         }
     130           5 :     }
     131           0 :     std::cout << "Player " << player->getName() << " disconnected" << std::endl;
     132           0 : }
     133             : 
     134           2 : void Server::sendGameInfo(std::shared_ptr<GameEngine> game, std::shared_ptr<shared::Player> player)
     135             : {
     136           5 :     for (auto &p : game->getPlayers())
     137             :     {
     138           3 :         if (p->getName() != player->getName())
     139             :         {
     140           2 :             std::string response = "infoplayer " + p->getName() + "\n";
     141           1 :             std::lock_guard<std::mutex> lock(p->socketWriteMutex);
     142           1 :             boost::asio::write(player->getSocket(), boost::asio::buffer(response));
     143           1 :         }
     144             :     }
     145           2 : }
     146             : 
     147           5 : void Server::processMessage(boost::asio::streambuf& receiveBuffer, std::shared_ptr<shared::Player> player, std::shared_ptr<GameEngine> game)
     148             : {
     149           5 :     std::istream receiveStream(&receiveBuffer);
     150           5 :     std::string messageReceived;
     151          10 :     while(std::getline(receiveStream, messageReceived))
     152             :     {
     153           5 :         if (messageReceived.size() == 0)
     154             :         {
     155           0 :             continue;
     156             :         }
     157           5 :         else if (messageReceived.find("response") == 0)
     158             :         {
     159           0 :             registerClientAnswer(messageReceived, player);
     160             :         }
     161           5 :         else if (messageReceived.find("binary") == 0) // binary reception
     162             :         {
     163           0 :             size_t size = std::stoi(messageReceived.substr(7));
     164           0 :             std::string data = binary.receive(player, receiveStream, size);
     165           0 :             registerClientAnswer(data, player);
     166           0 :         }
     167             :         else
     168             :         {
     169           5 :             game->processClientRequest(messageReceived, player);
     170             :         }
     171             :     }
     172             : 
     173           5 : }
     174             : 
     175           1 : std::shared_ptr<GameEngine> Server::getGameById(std::string gameId)
     176             : {
     177           1 :     std::lock_guard<std::mutex> lock(gamesMutex);
     178           1 :     for (auto &g : games)
     179             :     {
     180           1 :         if (g->getId() == gameId)
     181             :         {
     182           1 :             return g;
     183             :         }
     184             :     }
     185           0 :     return nullptr;
     186           1 : }
     187             : 
     188           1 : bool Server::connectPlayerToGame(std::shared_ptr<shared::Player> player, std::shared_ptr<GameEngine> game)
     189             : {
     190             :     // check if a player with the same username already exists in the game and is in disconnected state
     191           2 :     for (auto &p : game->getPlayers())
     192             :     {
     193           1 :         if (p.get() == player.get())
     194             :         {
     195           0 :             p->setSocket(player->getSocket());
     196           0 :             player.swap(p);
     197           0 :             std::cout << "Player " << player->getName() << " reconnected to game " << game->getId() << std::endl;
     198           0 :             return true;
     199             :         }
     200             :     }
     201           1 :     bool res = game->addPlayer(player);
     202           1 :     if (!res)
     203             :     {
     204           0 :         player->disconnectPlayer();
     205           0 :         return false;
     206             :     }
     207             :     else
     208             :     {
     209           1 :         std::cout << "Player " << player->getName() << " connected to game " << game->getId() << std::endl;
     210           2 :         std::string newUserConnected = "newplayer " + game->getTime() + " " + "server" + " " + player->getName() + "\n";
     211           1 :         game->sendToEveryone(newUserConnected);
     212           1 :         return true;
     213           1 :     }
     214             : }
     215             : 
     216           1 : std::shared_ptr<GameEngine> Server::createNewGame(std::shared_ptr<shared::Player> player)
     217             : {
     218           1 :     auto game = std::make_shared<GameEngine>(games, player);
     219             : 
     220           1 :     std::cout << "Player " << player->getName() << " created game " << game->getId() << std::endl;
     221             : 
     222           1 :     std::lock_guard<std::mutex> lock(gamesMutex);
     223           1 :     games.push_back(game);
     224           2 :     return game;
     225           1 : }
     226             : 
     227           0 : void Server::registerClientAnswer(const std::string &response, std::shared_ptr<shared::Player> player)
     228             : {
     229           0 :     std::lock_guard<std::mutex> lock(player->qAndA.sharedDataMutex);
     230           0 :     player->qAndA.answer = response;
     231           0 :     player->qAndA.answerReady = true;
     232           0 :     player->qAndA.condition.notify_one();
     233           0 : }

Generated by: LCOV version 1.14