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 : }