Line data Source code
1 : #include <server.hpp> 2 : #include <algorithm> 3 : #include <random> 4 : #include <iomanip> 5 : #include <iostream> 6 : 7 : #define MAX_PLAYERS 2 8 : 9 : using namespace server; 10 : 11 1 : GameEngine::GameEngine(std::vector<std::shared_ptr<GameEngine>> &games, std::shared_ptr<shared::Player> player) 12 : { 13 1 : gameId = GameEngine::generateRandomId(games); 14 1 : gameMap = std::make_shared<shared::Map>(); 15 1 : players.push_back(player); 16 1 : } 17 : 18 1 : bool GameEngine::addPlayer(std::shared_ptr<shared::Player> player) 19 : { 20 1 : if (isPublic && (players.size() < MAX_PLAYERS)) 21 : { 22 1 : players.push_back(player); 23 : } 24 : else 25 : { 26 0 : return false; 27 : } 28 : 29 1 : if (players.size() == MAX_PLAYERS) 30 : { 31 1 : std::cout << "Tous les joueurs sont connectés\n" 32 1 : << std::endl; 33 1 : std::thread gameThread(&GameEngine::runGame, this); 34 1 : gameThread.detach(); 35 1 : } 36 1 : return true; 37 : } 38 : 39 1 : void GameEngine::askClientToPlayARule(std::shared_ptr<shared::Player> player, shared::RuleArgsStruct &ruleArgs) 40 : { 41 : 42 1 : shared::Rules rules; 43 : do 44 : { 45 1 : std::unique_lock<std::mutex> lock(player->qAndA.sharedDataMutex); 46 1 : player->qAndA.question = "playturn\n"; 47 1 : lock.unlock(); 48 1 : askClient(player); 49 : 50 0 : lock.lock(); 51 0 : binary.castToObject(player->qAndA.answer, ruleArgs); 52 0 : lock.unlock(); 53 : 54 0 : ruleArgs.gameMap = this->gameMap; 55 0 : ruleArgs.currentPlayer = player; 56 : 57 0 : } while (!(rules.runTheRule(ruleArgs))); 58 0 : } 59 : 60 1 : void GameEngine::runGame() // rename rungame 61 : { 62 1 : std::cout << "start game" << std::endl; 63 1 : std::cout << "map initialized" << std::endl; 64 1 : std::string message = "chat "; 65 1 : message += getTime() + " "; 66 1 : message += "Game started\n"; 67 1 : sendToEveryone(message); 68 : while (true) // TODO: add condition to stop the game (victory of a player) 69 : { 70 1 : for (auto player : players) 71 : { 72 1 : std::cout << "start of turn of player: " << player->getName() << std::endl; 73 1 : shared::RuleArgsStruct ruleArgs; 74 1 : askClientToPlayARule(player, ruleArgs); 75 : 76 0 : ruleArgs.playerName = player->getName(); 77 : 78 0 : std::string struc; 79 0 : binary.castToBinary(ruleArgs, struc); 80 0 : sendToEveryone(struc, true); 81 0 : } 82 0 : } 83 0 : } 84 : 85 3 : std::vector<std::shared_ptr<shared::Player>> &GameEngine::getPlayers() 86 : { 87 3 : return players; 88 : } 89 : 90 1 : std::string GameEngine::generateRandomId(const std::vector<std::shared_ptr<GameEngine>> &games) 91 : { 92 1 : static const std::string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 93 1 : static std::mt19937 generator(std::random_device{}()); 94 1 : static std::uniform_int_distribution<std::string::size_type> distribution(0, alphabet.size() - 1); 95 : 96 1 : std::string id; 97 : do 98 : { 99 1 : id.resize(6); 100 1 : std::generate(id.begin(), id.end(), [&] 101 6 : { return alphabet[distribution(generator)]; }); 102 1 : } while (std::any_of(games.begin(), games.end(), [&](const auto &game) 103 0 : { return game->getId() == id; })); 104 1 : return id; 105 0 : } 106 : 107 6 : std::string GameEngine::getId() 108 : { 109 6 : return gameId; 110 : } 111 : 112 5 : void GameEngine::processClientRequest(std::string requestString, std::shared_ptr<shared::Player> player) 113 : { 114 5 : std::vector<std::string> requestComponents = splitString(requestString, ' '); 115 5 : if (requestComponents.size() < 1) 116 : { 117 0 : return; 118 : } 119 : 120 5 : std::string command = requestComponents[0]; 121 : 122 5 : std::string response = "response error: invalid command\n"; 123 5 : if (command.find("getstate") == 0) 124 : { 125 0 : response = "response" + player->getName() + " is connected\n"; 126 : } 127 5 : else if (command.find("chat") == 0) 128 : { 129 0 : requestString = requestString.substr(5); 130 0 : std::string message = "chat "; 131 0 : message += getTime() + " "; 132 0 : message += player->getName() + " " + requestString + "\n"; 133 0 : sendToEveryone(message); 134 0 : response = "response ok\n"; 135 0 : } 136 5 : else if (command.find("getmap") == 0) 137 : { 138 2 : if (gameMap == nullptr) 139 : { 140 0 : response = "error: map not initialized\n"; 141 : } 142 : else 143 : { 144 2 : binary.castToBinary(*gameMap, response); 145 2 : binary.send(player, response); 146 2 : return; 147 : } 148 : } 149 3 : else if (command.find("setmapparam") == 0) 150 : { 151 3 : if (requestComponents.size() == 3) 152 : { 153 3 : std::string param = requestComponents[1]; 154 3 : std::string value = requestComponents[2]; 155 3 : response = setMapParam(param, value) ? "response ok\n" : "response error: invalid parameter\n"; 156 3 : } 157 : } 158 : 159 3 : std::lock_guard<std::mutex> lock(player->socketWriteMutex); 160 3 : boost::asio::write(player->getSocket(), boost::asio::buffer(response)); 161 9 : } 162 : 163 3 : bool GameEngine::setMapParam(std::string ¶m, std::string &value) 164 : { 165 : int paramValue; 166 : try 167 : { 168 3 : paramValue = std::stoi(value); 169 : } 170 0 : catch (std::exception &e) 171 : { 172 0 : return false; 173 0 : } 174 3 : if (param.find("width") == 0) 175 : { 176 1 : gameMap->setMapWidth(paramValue); 177 : } 178 2 : else if (param.find("height") == 0) 179 : { 180 1 : gameMap->setMapHeight(paramValue); 181 : } 182 1 : else if (param.find("generate") == 0) 183 : { 184 1 : gameMap->generateRandomMap(paramValue); 185 : } 186 : else 187 : { 188 0 : return false; 189 : } 190 3 : return true; 191 : } 192 : 193 5 : std::vector<std::string> GameEngine::splitString(std::string str, char delimiter) 194 : { 195 5 : std::vector<std::string> components; 196 5 : std::stringstream ss(str); 197 5 : std::string component; 198 16 : while (std::getline(ss, component, delimiter)) 199 : { 200 11 : components.push_back(component); 201 : } 202 10 : return components; 203 5 : } 204 : 205 1 : void GameEngine::askClient(std::shared_ptr<shared::Player> player) 206 : { 207 1 : std::unique_lock<std::mutex> responseLock(player->qAndA.sharedDataMutex); 208 1 : player->qAndA.answerReady = false; 209 : { 210 1 : std::lock_guard<std::mutex> lock(player->socketWriteMutex); 211 1 : boost::asio::write(player->getSocket(), boost::asio::buffer(player->qAndA.question)); 212 1 : } 213 : 214 1 : player->qAndA.condition.wait(responseLock, [&] 215 1 : { return player->qAndA.answerReady; }); 216 : 217 0 : std::string response = player->qAndA.answer; 218 0 : player->qAndA.answerReady = false; 219 0 : } 220 : 221 2 : void GameEngine::sendToEveryone(std::string message, bool isBinary) 222 : { 223 6 : for (auto &player : players) 224 : { 225 4 : if (!isBinary && player->connectedToSocket.load()) 226 : { 227 4 : std::lock_guard<std::mutex> lock(player->socketWriteMutex); 228 4 : boost::asio::write(player->getSocket(), boost::asio::buffer(message)); 229 4 : } 230 0 : else if (player->connectedToSocket.load()) 231 : { 232 0 : binary.send(player, message, false); 233 : } 234 : } 235 2 : } 236 : 237 : /*! 238 : * @brief This function return the time under a string format 239 : * For example, if the time is 1:34pm, the function will return "13:34" 240 : * @return std::string 241 : */ 242 4 : std::string GameEngine::getTime() 243 : { 244 4 : std::time_t t = std::time(nullptr); 245 4 : std::tm tm = *std::localtime(&t); 246 4 : std::stringstream ss; 247 4 : ss << std::put_time(&tm, "%H:%M"); 248 8 : return ss.str(); 249 4 : }