NetPacksLobbyServer.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * NetPacksLobbyServer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "LobbyNetPackVisitors.h"
  12. #include "CVCMIServer.h"
  13. #include "CGameHandler.h"
  14. #include "../lib/StartInfo.h"
  15. #include "../lib/CRandomGenerator.h"
  16. #include "../lib/campaign/CampaignState.h"
  17. #include "../lib/entities/faction/CTownHandler.h"
  18. #include "../lib/entities/faction/CFaction.h"
  19. #include "../lib/gameState/CGameState.h"
  20. #include "../lib/serializer/Connection.h"
  21. #include "../lib/mapping/CMapInfo.h"
  22. #include "../lib/mapping/CMapHeader.h"
  23. #include "../lib/filesystem/Filesystem.h"
  24. void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack)
  25. {
  26. if(pack.isForServer())
  27. {
  28. result = srv.isClientHost(connection->connectionID);
  29. }
  30. }
  31. void ApplyOnServerAfterAnnounceNetPackVisitor::visitForLobby(CPackForLobby & pack)
  32. {
  33. // Propagate options after every CLobbyPackToServer
  34. if(pack.isForServer())
  35. {
  36. srv.updateAndPropagateLobbyState();
  37. }
  38. }
  39. void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
  40. {
  41. result = srv.getState() == EServerState::LOBBY;
  42. }
  43. void ApplyOnServerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
  44. {
  45. auto compatibleVersion = std::min(pack.version, ESerializationVersion::CURRENT);
  46. connection->setSerializationVersion(compatibleVersion);
  47. srv.clientConnected(connection, pack.names, pack.uuid, pack.mode);
  48. // Server need to pass some data to newly connected client
  49. pack.clientId = connection->connectionID;
  50. pack.mode = srv.si->mode;
  51. pack.hostClientId = srv.hostClientId;
  52. pack.version = compatibleVersion;
  53. result = true;
  54. }
  55. void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
  56. {
  57. srv.updateAndPropagateLobbyState();
  58. // FIXME: what is this??? We do NOT support reconnection into ongoing game - at the very least queries and battles are NOT serialized
  59. // if(srv.getState() == EServerState::GAMEPLAY)
  60. // {
  61. // //immediately start game
  62. // std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame);
  63. // startGameForReconnectedPlayer->initializedStartInfo = srv.si;
  64. // startGameForReconnectedPlayer->initializedGameState = srv.gh->gameState();
  65. // startGameForReconnectedPlayer->clientId = pack.c->connectionID;
  66. // srv.announcePack(std::move(startGameForReconnectedPlayer));
  67. // }
  68. }
  69. void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
  70. {
  71. if(pack.clientId != connection->connectionID)
  72. {
  73. result = false;
  74. return;
  75. }
  76. if(pack.shutdownServer)
  77. {
  78. if(!srv.wasStartedByClient())
  79. {
  80. result = false;
  81. return;
  82. }
  83. if(connection->connectionID != srv.hostClientId)
  84. {
  85. result = false;
  86. return;
  87. }
  88. }
  89. result = true;
  90. }
  91. void ApplyOnServerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
  92. {
  93. connection->getConnection()->close();
  94. srv.clientDisconnected(connection);
  95. result = true;
  96. }
  97. void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
  98. {
  99. srv.updateAndPropagateLobbyState();
  100. }
  101. void ClientPermissionsCheckerNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack)
  102. {
  103. result = true;
  104. }
  105. void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
  106. {
  107. if(srv.getState() != EServerState::LOBBY)
  108. {
  109. result = false;
  110. return;
  111. }
  112. srv.updateStartInfoOnMapChange(pack.mapInfo, pack.mapGenOpts);
  113. result = true;
  114. }
  115. void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack)
  116. {
  117. srv.si->mapname = pack.ourCampaign->getFilename();
  118. srv.si->mode = EStartMode::CAMPAIGN;
  119. srv.si->campState = pack.ourCampaign;
  120. srv.si->turnTimerInfo = TurnTimerInfo{};
  121. bool isCurrentMapConquerable = pack.ourCampaign->currentScenario() && pack.ourCampaign->isAvailable(*pack.ourCampaign->currentScenario());
  122. auto scenarios = pack.ourCampaign->allScenarios();
  123. for(std::set<CampaignScenarioID>::reverse_iterator itr = scenarios.rbegin(); itr != scenarios.rend(); itr++) // reverse -> on multiple scenario selection set lowest id at the end
  124. {
  125. auto scenarioID = *itr;
  126. if(pack.ourCampaign->isAvailable(scenarioID))
  127. {
  128. if(!isCurrentMapConquerable || (isCurrentMapConquerable && scenarioID == *pack.ourCampaign->currentScenario()))
  129. {
  130. srv.setCampaignMap(scenarioID);
  131. }
  132. }
  133. }
  134. result = true;
  135. }
  136. void ApplyOnServerNetPackVisitor::visitLobbySetCampaignMap(LobbySetCampaignMap & pack)
  137. {
  138. srv.setCampaignMap(pack.mapId);
  139. result = true;
  140. }
  141. void ApplyOnServerNetPackVisitor::visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack)
  142. {
  143. srv.setCampaignBonus(pack.bonusId);
  144. result = true;
  145. }
  146. void ClientPermissionsCheckerNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack)
  147. {
  148. result = srv.isClientHost(connection->connectionID);
  149. }
  150. void ClientPermissionsCheckerNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack)
  151. {
  152. result = srv.isClientHost(connection->connectionID);
  153. }
  154. void ApplyOnServerNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack)
  155. {
  156. srv.prepareToRestart();
  157. result = true;
  158. }
  159. void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack)
  160. {
  161. for(const auto & connection : srv.activeConnections)
  162. connection->enterLobbyConnectionMode();
  163. }
  164. void ClientPermissionsCheckerNetPackVisitor::visitLobbyPrepareStartGame(LobbyPrepareStartGame & pack)
  165. {
  166. result = srv.isClientHost(connection->connectionID);
  167. }
  168. void ClientPermissionsCheckerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
  169. {
  170. result = srv.isClientHost(connection->connectionID);
  171. }
  172. void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
  173. {
  174. try
  175. {
  176. srv.verifyStateBeforeStart(true);
  177. }
  178. catch(...)
  179. {
  180. result = false;
  181. return;
  182. }
  183. // Server will prepare gamestate and we announce StartInfo to clients
  184. if(!srv.prepareToStartGame())
  185. {
  186. result = false;
  187. return;
  188. }
  189. pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->gameState().getInitialStartInfo());
  190. pack.initializedGameState = srv.gh->gs;
  191. result = true;
  192. }
  193. void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
  194. {
  195. if(pack.clientId == -1) //do not restart game for single client only
  196. srv.startGameImmediately();
  197. else
  198. {
  199. for(const auto & connection : srv.activeConnections)
  200. {
  201. if(connection->connectionID == pack.clientId)
  202. {
  203. connection->setCallback(srv.gh->gameInfo());
  204. srv.reconnectPlayer(pack.clientId);
  205. }
  206. }
  207. }
  208. }
  209. void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
  210. {
  211. result = srv.isClientHost(connection->connectionID);
  212. }
  213. void ApplyOnServerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
  214. {
  215. result = true;
  216. }
  217. void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
  218. {
  219. auto result = srv.passHost(pack.newHostConnectionId);
  220. if(!result)
  221. {
  222. logGlobal->error("passHost returned false. What does it mean?");
  223. }
  224. }
  225. void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack)
  226. {
  227. if(srv.isClientHost(connection->connectionID))
  228. {
  229. result = true;
  230. return;
  231. }
  232. if(vstd::contains(srv.getAllClientPlayers(connection->connectionID), pack.color))
  233. {
  234. result = true;
  235. return;
  236. }
  237. result = false;
  238. }
  239. void ApplyOnServerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack)
  240. {
  241. switch(pack.what)
  242. {
  243. case LobbyChangePlayerOption::TOWN_ID:
  244. srv.optionSetCastle(pack.color, FactionID(pack.value));
  245. break;
  246. case LobbyChangePlayerOption::TOWN:
  247. srv.optionNextCastle(pack.color, pack.value);
  248. break;
  249. case LobbyChangePlayerOption::HERO_ID:
  250. srv.optionSetHero(pack.color, HeroTypeID(pack.value));
  251. break;
  252. case LobbyChangePlayerOption::HERO:
  253. srv.optionNextHero(pack.color, pack.value);
  254. break;
  255. case LobbyChangePlayerOption::BONUS_ID:
  256. srv.optionSetBonus(pack.color, PlayerStartingBonus(pack.value));
  257. break;
  258. case LobbyChangePlayerOption::BONUS:
  259. srv.optionNextBonus(pack.color, pack.value);
  260. break;
  261. }
  262. result = true;
  263. }
  264. void ApplyOnServerNetPackVisitor::visitLobbySetPlayer(LobbySetPlayer & pack)
  265. {
  266. srv.setPlayer(pack.clickedColor);
  267. result = true;
  268. }
  269. void ApplyOnServerNetPackVisitor::visitLobbySetPlayerName(LobbySetPlayerName & pack)
  270. {
  271. srv.setPlayerName(pack.color, pack.name);
  272. result = true;
  273. }
  274. void ApplyOnServerNetPackVisitor::visitLobbySetPlayerHandicap(LobbySetPlayerHandicap & pack)
  275. {
  276. srv.setPlayerHandicap(pack.color, pack.handicap);
  277. result = true;
  278. }
  279. void ApplyOnServerNetPackVisitor::visitLobbySetSimturns(LobbySetSimturns & pack)
  280. {
  281. srv.si->simturnsInfo = pack.simturnsInfo;
  282. result = true;
  283. }
  284. void ApplyOnServerNetPackVisitor::visitLobbySetTurnTime(LobbySetTurnTime & pack)
  285. {
  286. srv.si->turnTimerInfo = pack.turnTimerInfo;
  287. result = true;
  288. }
  289. void ApplyOnServerNetPackVisitor::visitLobbySetExtraOptions(LobbySetExtraOptions & pack)
  290. {
  291. srv.si->extraOptionsInfo = pack.extraOptionsInfo;
  292. result = true;
  293. }
  294. void ApplyOnServerNetPackVisitor::visitLobbySetDifficulty(LobbySetDifficulty & pack)
  295. {
  296. srv.si->difficulty = std::clamp<uint8_t>(pack.difficulty, 0, 4);
  297. result = true;
  298. }
  299. void ApplyOnServerNetPackVisitor::visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack)
  300. {
  301. srv.si->playerInfos[pack.targetPlayerColor].connectedPlayerIDs.insert(pack.targetConnectedPlayer);
  302. result = true;
  303. }
  304. void ClientPermissionsCheckerNetPackVisitor::visitLobbyPvPAction(LobbyPvPAction & pack)
  305. {
  306. result = true;
  307. }
  308. void ApplyOnServerNetPackVisitor::visitLobbyPvPAction(LobbyPvPAction & pack)
  309. {
  310. std::vector<FactionID> allowedTowns;
  311. for (auto const & factionID : LIBRARY->townh->getDefaultAllowed())
  312. if(std::find(pack.bannedTowns.begin(), pack.bannedTowns.end(), factionID) == pack.bannedTowns.end())
  313. allowedTowns.push_back(factionID);
  314. std::vector<FactionID> randomFaction1;
  315. std::sample(allowedTowns.begin(), allowedTowns.end(), std::back_inserter(randomFaction1), 1, std::mt19937{std::random_device{}()});
  316. std::vector<FactionID> randomFaction2;
  317. std::sample(allowedTowns.begin(), allowedTowns.end(), std::back_inserter(randomFaction2), 1, std::mt19937{std::random_device{}()});
  318. MetaString txt;
  319. switch(pack.action) {
  320. case LobbyPvPAction::COIN:
  321. txt.appendTextID("vcmi.lobby.pvp.coin.hover");
  322. txt.appendRawString(" - " + std::to_string(srv.gh->getRandomGenerator().nextInt(1)));
  323. srv.announceTxt(txt);
  324. break;
  325. case LobbyPvPAction::RANDOM_TOWN:
  326. if(!allowedTowns.size())
  327. break;
  328. txt.appendTextID("core.overview.3");
  329. txt.appendRawString(" - ");
  330. txt.appendTextID(LIBRARY->townh->getById(randomFaction1[0])->getNameTextID());
  331. srv.announceTxt(txt);
  332. break;
  333. case LobbyPvPAction::RANDOM_TOWN_VS:
  334. if(!allowedTowns.size())
  335. break;
  336. txt.appendTextID("core.overview.3");
  337. txt.appendRawString(" - ");
  338. txt.appendTextID(LIBRARY->townh->getById(randomFaction1[0])->getNameTextID());
  339. txt.appendRawString(" ");
  340. txt.appendTextID("vcmi.lobby.pvp.versus");
  341. txt.appendRawString(" ");
  342. txt.appendTextID(LIBRARY->townh->getById(randomFaction2[0])->getNameTextID());
  343. srv.announceTxt(txt);
  344. break;
  345. }
  346. result = true;
  347. }
  348. void ClientPermissionsCheckerNetPackVisitor::visitLobbyDelete(LobbyDelete & pack)
  349. {
  350. result = srv.isClientHost(connection->connectionID);
  351. }
  352. void ApplyOnServerNetPackVisitor::visitLobbyDelete(LobbyDelete & pack)
  353. {
  354. if(pack.type == LobbyDelete::EType::SAVEGAME || pack.type == LobbyDelete::EType::RANDOMMAP)
  355. {
  356. auto res = ResourcePath(pack.name, pack.type == LobbyDelete::EType::SAVEGAME ? EResType::SAVEGAME : EResType::MAP);
  357. auto name = CResourceHandler::get()->getResourceName(res);
  358. if (!name)
  359. {
  360. logGlobal->error("Failed to find resource with name '%s'", res.getOriginalName());
  361. return;
  362. }
  363. boost::system::error_code ec;
  364. auto file = boost::filesystem::canonical(*name, ec);
  365. if (ec)
  366. {
  367. logGlobal->error("Failed to delete file '%s'. Reason: %s", res.getOriginalName(), ec.message());
  368. }
  369. else
  370. {
  371. boost::filesystem::remove(file);
  372. if(boost::filesystem::is_empty(file.parent_path()))
  373. boost::filesystem::remove(file.parent_path());
  374. }
  375. }
  376. else if(pack.type == LobbyDelete::EType::SAVEGAME_FOLDER)
  377. {
  378. auto res = ResourcePath("Saves/" + pack.name, EResType::DIRECTORY);
  379. auto name = CResourceHandler::get()->getResourceName(res);
  380. if (!name)
  381. {
  382. logGlobal->error("Failed to find folder with name '%s'", res.getOriginalName());
  383. return;
  384. }
  385. boost::system::error_code ec;
  386. auto folder = boost::filesystem::canonical(*name);
  387. if (ec)
  388. {
  389. logGlobal->error("Failed to delete folder '%s'. Reason: %s", res.getOriginalName(), ec.message());
  390. }
  391. else
  392. {
  393. boost::filesystem::remove_all(folder);
  394. }
  395. }
  396. LobbyUpdateState lus;
  397. lus.state = srv;
  398. lus.refreshList = true;
  399. srv.announcePack(lus);
  400. }