2
0

CServerHandler.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. /*
  2. * CServerHandler.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 "CServerHandler.h"
  12. #include "Client.h"
  13. #include "CGameInfo.h"
  14. #include "ServerRunner.h"
  15. #include "GameChatHandler.h"
  16. #include "CPlayerInterface.h"
  17. #include "gui/CGuiHandler.h"
  18. #include "gui/WindowHandler.h"
  19. #include "globalLobby/GlobalLobbyClient.h"
  20. #include "lobby/CSelectionBase.h"
  21. #include "lobby/CLobbyScreen.h"
  22. #include "windows/InfoWindows.h"
  23. #include "mainmenu/CMainMenu.h"
  24. #include "mainmenu/CPrologEpilogVideo.h"
  25. #include "mainmenu/CHighScoreScreen.h"
  26. #include "../lib/CConfigHandler.h"
  27. #include "../lib/CGeneralTextHandler.h"
  28. #include "../lib/CThreadHelper.h"
  29. #include "../lib/StartInfo.h"
  30. #include "../lib/TurnTimerInfo.h"
  31. #include "../lib/VCMIDirs.h"
  32. #include "../lib/campaign/CampaignState.h"
  33. #include "../lib/CPlayerState.h"
  34. #include "../lib/mapping/CMapInfo.h"
  35. #include "../lib/mapObjects/CGTownInstance.h"
  36. #include "../lib/mapObjects/MiscObjects.h"
  37. #include "../lib/modding/ModIncompatibility.h"
  38. #include "../lib/rmg/CMapGenOptions.h"
  39. #include "../lib/serializer/Connection.h"
  40. #include "../lib/filesystem/Filesystem.h"
  41. #include "../lib/registerTypes/RegisterTypesLobbyPacks.h"
  42. #include "../lib/serializer/CMemorySerializer.h"
  43. #include "../lib/UnlockGuard.h"
  44. #include <boost/uuid/uuid.hpp>
  45. #include <boost/uuid/uuid_io.hpp>
  46. #include <boost/uuid/uuid_generators.hpp>
  47. #include "../lib/serializer/Cast.h"
  48. #include "LobbyClientNetPackVisitors.h"
  49. #include <vcmi/events/EventBus.h>
  50. template<typename T> class CApplyOnLobby;
  51. class CBaseForLobbyApply
  52. {
  53. public:
  54. virtual bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const = 0;
  55. virtual void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const = 0;
  56. virtual ~CBaseForLobbyApply(){};
  57. template<typename U> static CBaseForLobbyApply * getApplier(const U * t = nullptr)
  58. {
  59. return new CApplyOnLobby<U>();
  60. }
  61. };
  62. template<typename T> class CApplyOnLobby : public CBaseForLobbyApply
  63. {
  64. public:
  65. bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
  66. {
  67. auto & ref = static_cast<T&>(pack);
  68. ApplyOnLobbyHandlerNetPackVisitor visitor(*handler);
  69. logNetwork->trace("\tImmediately apply on lobby: %s", typeid(ref).name());
  70. ref.visit(visitor);
  71. return visitor.getResult();
  72. }
  73. void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
  74. {
  75. auto & ref = static_cast<T &>(pack);
  76. ApplyOnLobbyScreenNetPackVisitor visitor(*handler, lobby);
  77. logNetwork->trace("\tApply on lobby from queue: %s", typeid(ref).name());
  78. ref.visit(visitor);
  79. }
  80. };
  81. template<> class CApplyOnLobby<CPack>: public CBaseForLobbyApply
  82. {
  83. public:
  84. bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
  85. {
  86. logGlobal->error("Cannot apply plain CPack!");
  87. assert(0);
  88. return false;
  89. }
  90. void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
  91. {
  92. logGlobal->error("Cannot apply plain CPack!");
  93. assert(0);
  94. }
  95. };
  96. CServerHandler::~CServerHandler()
  97. {
  98. if (serverRunner)
  99. serverRunner->shutdown();
  100. networkHandler->stop();
  101. try
  102. {
  103. if (serverRunner)
  104. serverRunner->wait();
  105. serverRunner.reset();
  106. {
  107. auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
  108. threadNetwork.join();
  109. }
  110. }
  111. catch (const std::runtime_error & e)
  112. {
  113. logGlobal->error("Failed to shut down network thread! Reason: %s", e.what());
  114. assert(0);
  115. }
  116. }
  117. CServerHandler::CServerHandler()
  118. : networkHandler(INetworkHandler::createHandler())
  119. , lobbyClient(std::make_unique<GlobalLobbyClient>())
  120. , gameChat(std::make_unique<GameChatHandler>())
  121. , applier(std::make_unique<CApplier<CBaseForLobbyApply>>())
  122. , threadNetwork(&CServerHandler::threadRunNetwork, this)
  123. , state(EClientState::NONE)
  124. , serverPort(0)
  125. , campaignStateToSend(nullptr)
  126. , screenType(ESelectionScreen::unknown)
  127. , serverMode(EServerMode::NONE)
  128. , loadMode(ELoadMode::NONE)
  129. , client(nullptr)
  130. {
  131. uuid = boost::uuids::to_string(boost::uuids::random_generator()());
  132. registerTypesLobbyPacks(*applier);
  133. }
  134. void CServerHandler::setHighScoreCalc(const std::shared_ptr<HighScoreCalculation> &newHighScoreCalc)
  135. {
  136. campaignScoreCalculator = newHighScoreCalc;
  137. }
  138. void CServerHandler::threadRunNetwork()
  139. {
  140. logGlobal->info("Starting network thread");
  141. setThreadName("runNetwork");
  142. networkHandler->run();
  143. logGlobal->info("Ending network thread");
  144. }
  145. void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen, EServerMode newServerMode, const std::vector<std::string> & playerNames)
  146. {
  147. hostClientId = -1;
  148. setState(EClientState::NONE);
  149. serverMode = newServerMode;
  150. mapToStart = nullptr;
  151. th = std::make_unique<CStopWatch>();
  152. logicConnection.reset();
  153. si = std::make_shared<StartInfo>();
  154. localPlayerNames.clear();
  155. si->difficulty = 1;
  156. si->mode = mode;
  157. screenType = screen;
  158. localPlayerNames.clear();
  159. if(!playerNames.empty()) //if have custom set of player names - use it
  160. localPlayerNames = playerNames;
  161. else
  162. localPlayerNames.push_back(settings["general"]["playerName"].String());
  163. gameChat->resetMatchState();
  164. lobbyClient->resetMatchState();
  165. }
  166. GameChatHandler & CServerHandler::getGameChat()
  167. {
  168. return *gameChat;
  169. }
  170. GlobalLobbyClient & CServerHandler::getGlobalLobby()
  171. {
  172. return *lobbyClient;
  173. }
  174. INetworkHandler & CServerHandler::getNetworkHandler()
  175. {
  176. return *networkHandler;
  177. }
  178. void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
  179. {
  180. logNetwork->trace("\tLocal server startup has been requested");
  181. #ifdef VCMI_MOBILE
  182. // mobile apps can't spawn separate processes - only thread mode is available
  183. serverRunner.reset(new ServerThreadRunner());
  184. #else
  185. if (settings["server"]["useProcess"].Bool())
  186. serverRunner.reset(new ServerProcessRunner());
  187. else
  188. serverRunner.reset(new ServerThreadRunner());
  189. #endif
  190. auto si = std::make_shared<StartInfo>();
  191. auto lastDifficulty = settings["general"]["lastDifficulty"];
  192. si->difficulty = lastDifficulty.Integer();
  193. logNetwork->trace("\tStarting local server");
  194. serverRunner->start(getLocalPort(), connectToLobby, si);
  195. logNetwork->trace("\tConnecting to local server");
  196. connectToServer(getLocalHostname(), getLocalPort());
  197. logNetwork->trace("\tWaiting for connection");
  198. }
  199. void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
  200. {
  201. logNetwork->info("Establishing connection to %s:%d...", addr, port);
  202. setState(EClientState::CONNECTING);
  203. serverHostname = addr;
  204. serverPort = port;
  205. if (!isServerLocal())
  206. {
  207. Settings remoteAddress = settings.write["server"]["remoteHostname"];
  208. remoteAddress->String() = addr;
  209. Settings remotePort = settings.write["server"]["remotePort"];
  210. remotePort->Integer() = port;
  211. }
  212. networkHandler->connectToRemote(*this, addr, port);
  213. }
  214. void CServerHandler::onConnectionFailed(const std::string & errorMessage)
  215. {
  216. assert(getState() == EClientState::CONNECTING);
  217. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  218. if (isServerLocal())
  219. {
  220. // retry - local server might be still starting up
  221. logNetwork->debug("\nCannot establish connection. %s. Retrying...", errorMessage);
  222. networkHandler->createTimer(*this, std::chrono::milliseconds(100));
  223. }
  224. else
  225. {
  226. // remote server refused connection - show error message
  227. setState(EClientState::NONE);
  228. CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.mainMenu.serverConnectionFailed"), {});
  229. }
  230. }
  231. void CServerHandler::onTimer()
  232. {
  233. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  234. if(getState() == EClientState::CONNECTION_CANCELLED)
  235. {
  236. logNetwork->info("Connection aborted by player!");
  237. serverRunner->wait();
  238. serverRunner.reset();
  239. if (GH.windows().topWindow<CSimpleJoinScreen>() != nullptr)
  240. GH.windows().popWindows(1);
  241. return;
  242. }
  243. assert(isServerLocal());
  244. networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort());
  245. }
  246. void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)
  247. {
  248. assert(getState() == EClientState::CONNECTING);
  249. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  250. networkConnection = netConnection;
  251. logNetwork->info("Connection established");
  252. if (serverMode == EServerMode::LOBBY_GUEST)
  253. {
  254. // say hello to lobby to switch connection to proxy mode
  255. getGlobalLobby().sendProxyConnectionLogin(netConnection);
  256. }
  257. logicConnection = std::make_shared<CConnection>(netConnection);
  258. logicConnection->uuid = uuid;
  259. logicConnection->enterLobbyConnectionMode();
  260. sendClientConnecting();
  261. }
  262. void CServerHandler::applyPackOnLobbyScreen(CPackForLobby & pack)
  263. {
  264. const CBaseForLobbyApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(&pack)); //find the applier
  265. apply->applyOnLobbyScreen(dynamic_cast<CLobbyScreen *>(SEL), this, pack);
  266. GH.windows().totalRedraw();
  267. }
  268. std::set<PlayerColor> CServerHandler::getHumanColors()
  269. {
  270. return clientHumanColors(logicConnection->connectionID);
  271. }
  272. PlayerColor CServerHandler::myFirstColor() const
  273. {
  274. return clientFirstColor(logicConnection->connectionID);
  275. }
  276. bool CServerHandler::isMyColor(PlayerColor color) const
  277. {
  278. return isClientColor(logicConnection->connectionID, color);
  279. }
  280. ui8 CServerHandler::myFirstId() const
  281. {
  282. return clientFirstId(logicConnection->connectionID);
  283. }
  284. EClientState CServerHandler::getState() const
  285. {
  286. return state;
  287. }
  288. void CServerHandler::setState(EClientState newState)
  289. {
  290. if (newState == EClientState::CONNECTION_CANCELLED && serverRunner != nullptr)
  291. serverRunner->shutdown();
  292. state = newState;
  293. }
  294. bool CServerHandler::isServerLocal() const
  295. {
  296. return serverRunner != nullptr;
  297. }
  298. bool CServerHandler::isHost() const
  299. {
  300. return logicConnection && hostClientId == logicConnection->connectionID;
  301. }
  302. bool CServerHandler::isGuest() const
  303. {
  304. return !logicConnection || hostClientId != logicConnection->connectionID;
  305. }
  306. const std::string & CServerHandler::getLocalHostname() const
  307. {
  308. return settings["server"]["localHostname"].String();
  309. }
  310. ui16 CServerHandler::getLocalPort() const
  311. {
  312. return settings["server"]["localPort"].Integer();
  313. }
  314. const std::string & CServerHandler::getRemoteHostname() const
  315. {
  316. return settings["server"]["remoteHostname"].String();
  317. }
  318. ui16 CServerHandler::getRemotePort() const
  319. {
  320. return settings["server"]["remotePort"].Integer();
  321. }
  322. const std::string & CServerHandler::getCurrentHostname() const
  323. {
  324. return serverHostname;
  325. }
  326. ui16 CServerHandler::getCurrentPort() const
  327. {
  328. return serverPort;
  329. }
  330. void CServerHandler::sendClientConnecting() const
  331. {
  332. LobbyClientConnected lcc;
  333. lcc.uuid = uuid;
  334. lcc.names = localPlayerNames;
  335. lcc.mode = si->mode;
  336. sendLobbyPack(lcc);
  337. }
  338. void CServerHandler::sendClientDisconnecting()
  339. {
  340. // FIXME: This is workaround needed to make sure client not trying to sent anything to non existed server
  341. if(getState() == EClientState::DISCONNECTING)
  342. {
  343. assert(0);
  344. return;
  345. }
  346. setState(EClientState::DISCONNECTING);
  347. mapToStart = nullptr;
  348. LobbyClientDisconnected lcd;
  349. lcd.clientId = logicConnection->connectionID;
  350. logNetwork->info("Connection has been requested to be closed.");
  351. if(isServerLocal())
  352. {
  353. lcd.shutdownServer = true;
  354. logNetwork->info("Sent closing signal to the server");
  355. }
  356. else
  357. {
  358. logNetwork->info("Sent leaving signal to the server");
  359. }
  360. sendLobbyPack(lcd);
  361. networkConnection->close();
  362. networkConnection.reset();
  363. logicConnection.reset();
  364. waitForServerShutdown();
  365. }
  366. void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
  367. {
  368. setState(EClientState::LOBBY_CAMPAIGN);
  369. LobbySetCampaign lsc;
  370. lsc.ourCampaign = newCampaign;
  371. sendLobbyPack(lsc);
  372. }
  373. void CServerHandler::setCampaignMap(CampaignScenarioID mapId) const
  374. {
  375. if(getState() == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
  376. return;
  377. LobbySetCampaignMap lscm;
  378. lscm.mapId = mapId;
  379. sendLobbyPack(lscm);
  380. }
  381. void CServerHandler::setCampaignBonus(int bonusId) const
  382. {
  383. if(getState() == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
  384. return;
  385. LobbySetCampaignBonus lscb;
  386. lscb.bonusId = bonusId;
  387. sendLobbyPack(lscb);
  388. }
  389. void CServerHandler::setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts) const
  390. {
  391. LobbySetMap lsm;
  392. lsm.mapInfo = to;
  393. lsm.mapGenOpts = mapGenOpts;
  394. sendLobbyPack(lsm);
  395. }
  396. void CServerHandler::setPlayer(PlayerColor color) const
  397. {
  398. LobbySetPlayer lsp;
  399. lsp.clickedColor = color;
  400. sendLobbyPack(lsp);
  401. }
  402. void CServerHandler::setPlayerName(PlayerColor color, const std::string & name) const
  403. {
  404. LobbySetPlayerName lspn;
  405. lspn.color = color;
  406. lspn.name = name;
  407. sendLobbyPack(lspn);
  408. }
  409. void CServerHandler::setPlayerOption(ui8 what, int32_t value, PlayerColor player) const
  410. {
  411. LobbyChangePlayerOption lcpo;
  412. lcpo.what = what;
  413. lcpo.value = value;
  414. lcpo.color = player;
  415. sendLobbyPack(lcpo);
  416. }
  417. void CServerHandler::setDifficulty(int to) const
  418. {
  419. LobbySetDifficulty lsd;
  420. lsd.difficulty = to;
  421. sendLobbyPack(lsd);
  422. }
  423. void CServerHandler::setSimturnsInfo(const SimturnsInfo & info) const
  424. {
  425. LobbySetSimturns pack;
  426. pack.simturnsInfo = info;
  427. sendLobbyPack(pack);
  428. }
  429. void CServerHandler::setTurnTimerInfo(const TurnTimerInfo & info) const
  430. {
  431. LobbySetTurnTime lstt;
  432. lstt.turnTimerInfo = info;
  433. sendLobbyPack(lstt);
  434. }
  435. void CServerHandler::setExtraOptionsInfo(const ExtraOptionsInfo & info) const
  436. {
  437. LobbySetExtraOptions lseo;
  438. lseo.extraOptionsInfo = info;
  439. sendLobbyPack(lseo);
  440. }
  441. void CServerHandler::sendMessage(const std::string & txt) const
  442. {
  443. std::istringstream readed;
  444. readed.str(txt);
  445. std::string command;
  446. readed >> command;
  447. if(command == "!passhost")
  448. {
  449. std::string id;
  450. readed >> id;
  451. if(id.length())
  452. {
  453. LobbyChangeHost lch;
  454. lch.newHostConnectionId = boost::lexical_cast<int>(id);
  455. sendLobbyPack(lch);
  456. }
  457. }
  458. else if(command == "!forcep")
  459. {
  460. std::string connectedId;
  461. std::string playerColorId;
  462. readed >> connectedId;
  463. readed >> playerColorId;
  464. if(connectedId.length() && playerColorId.length())
  465. {
  466. ui8 connected = boost::lexical_cast<int>(connectedId);
  467. auto color = PlayerColor(boost::lexical_cast<int>(playerColorId));
  468. if(color.isValidPlayer() && playerNames.find(connected) != playerNames.end())
  469. {
  470. LobbyForceSetPlayer lfsp;
  471. lfsp.targetConnectedPlayer = connected;
  472. lfsp.targetPlayerColor = color;
  473. sendLobbyPack(lfsp);
  474. }
  475. }
  476. }
  477. else
  478. {
  479. gameChat->sendMessageLobby(playerNames.find(myFirstId())->second.name, txt);
  480. }
  481. }
  482. void CServerHandler::sendGuiAction(ui8 action) const
  483. {
  484. LobbyGuiAction lga;
  485. lga.action = static_cast<LobbyGuiAction::EAction>(action);
  486. sendLobbyPack(lga);
  487. }
  488. void CServerHandler::sendRestartGame() const
  489. {
  490. GH.windows().createAndPushWindow<CLoadingScreen>();
  491. LobbyRestartGame endGame;
  492. sendLobbyPack(endGame);
  493. }
  494. bool CServerHandler::validateGameStart(bool allowOnlyAI) const
  495. {
  496. try
  497. {
  498. verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
  499. }
  500. catch(ModIncompatibility & e)
  501. {
  502. logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
  503. std::string errorMsg;
  504. if(!e.whatMissing().empty())
  505. {
  506. errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToEnable") + '\n';
  507. errorMsg += e.whatMissing();
  508. }
  509. if(!e.whatExcessive().empty())
  510. {
  511. errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToDisable") + '\n';
  512. errorMsg += e.whatExcessive();
  513. }
  514. showServerError(errorMsg);
  515. return false;
  516. }
  517. catch(std::exception & e)
  518. {
  519. logGlobal->error("Exception during startScenario: %s", e.what());
  520. showServerError( std::string("Unable to start map! Reason: ") + e.what());
  521. return false;
  522. }
  523. return true;
  524. }
  525. void CServerHandler::sendStartGame(bool allowOnlyAI) const
  526. {
  527. verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
  528. if(!settings["session"]["headless"].Bool())
  529. GH.windows().createAndPushWindow<CLoadingScreen>();
  530. LobbyPrepareStartGame lpsg;
  531. sendLobbyPack(lpsg);
  532. LobbyStartGame lsg;
  533. sendLobbyPack(lsg);
  534. }
  535. void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
  536. {
  537. mapToStart = to;
  538. }
  539. void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
  540. {
  541. if(CMM)
  542. CMM->disable();
  543. campaignScoreCalculator = nullptr;
  544. switch(si->mode)
  545. {
  546. case EStartMode::NEW_GAME:
  547. client->newGame(gameState);
  548. break;
  549. case EStartMode::CAMPAIGN:
  550. client->newGame(gameState);
  551. break;
  552. case EStartMode::LOAD_GAME:
  553. client->loadGame(gameState);
  554. break;
  555. default:
  556. throw std::runtime_error("Invalid mode");
  557. }
  558. // After everything initialized we can accept CPackToClient netpacks
  559. logicConnection->enterGameplayConnectionMode(client->gameState());
  560. setState(EClientState::GAMEPLAY);
  561. }
  562. HighScoreParameter CServerHandler::prepareHighScores(PlayerColor player, bool victory)
  563. {
  564. const auto * gs = client->gameState();
  565. const auto * playerState = gs->getPlayerState(player);
  566. HighScoreParameter param;
  567. param.difficulty = gs->getStartInfo()->difficulty;
  568. param.day = gs->getDate();
  569. param.townAmount = gs->howManyTowns(player);
  570. param.usedCheat = gs->getPlayerState(player)->cheated;
  571. param.hasGrail = false;
  572. for(const CGHeroInstance * h : playerState->heroes)
  573. if(h->hasArt(ArtifactID::GRAIL))
  574. param.hasGrail = true;
  575. for(const CGTownInstance * t : playerState->towns)
  576. if(t->builtBuildings.count(BuildingID::GRAIL))
  577. param.hasGrail = true;
  578. param.allDefeated = true;
  579. for (PlayerColor otherPlayer(0); otherPlayer < PlayerColor::PLAYER_LIMIT; ++otherPlayer)
  580. {
  581. auto ps = gs->getPlayerState(otherPlayer, false);
  582. if(ps && otherPlayer != player && !ps->checkVanquished())
  583. param.allDefeated = false;
  584. }
  585. param.scenarioName = gs->getMapHeader()->name.toString();
  586. param.playerName = gs->getStartInfo()->playerInfos.find(player)->second.name;
  587. return param;
  588. }
  589. void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victory)
  590. {
  591. HighScoreParameter param = prepareHighScores(player, victory);
  592. if(victory && client->gameState()->getStartInfo()->campState)
  593. {
  594. startCampaignScenario(param, client->gameState()->getStartInfo()->campState);
  595. }
  596. else
  597. {
  598. HighScoreCalculation scenarioHighScores;
  599. scenarioHighScores.parameters.push_back(param);
  600. scenarioHighScores.isCampaign = false;
  601. endGameplay();
  602. GH.defActionsDef = 63;
  603. CMM->menu->switchToTab("main");
  604. GH.windows().createAndPushWindow<CHighScoreInputScreen>(victory, scenarioHighScores);
  605. }
  606. }
  607. void CServerHandler::endGameplay()
  608. {
  609. // Game is ending
  610. // Tell the network thread to reach a stable state
  611. sendClientDisconnecting();
  612. logNetwork->info("Closed connection.");
  613. client->endGame();
  614. client.reset();
  615. if(CMM)
  616. {
  617. GH.curInt = CMM.get();
  618. CMM->enable();
  619. }
  620. else
  621. {
  622. GH.curInt = CMainMenu::create().get();
  623. }
  624. }
  625. void CServerHandler::restartGameplay()
  626. {
  627. client->endGame();
  628. client.reset();
  629. logicConnection->enterLobbyConnectionMode();
  630. }
  631. void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs)
  632. {
  633. std::shared_ptr<CampaignState> ourCampaign = cs;
  634. if (!cs)
  635. ourCampaign = si->campState;
  636. if(campaignScoreCalculator == nullptr)
  637. {
  638. campaignScoreCalculator = std::make_shared<HighScoreCalculation>();
  639. campaignScoreCalculator->isCampaign = true;
  640. campaignScoreCalculator->parameters.clear();
  641. }
  642. param.campaignName = cs->getNameTranslated();
  643. campaignScoreCalculator->parameters.push_back(param);
  644. endGameplay();
  645. auto & epilogue = ourCampaign->scenario(*ourCampaign->lastScenario()).epilog;
  646. auto finisher = [this, ourCampaign]()
  647. {
  648. if(ourCampaign->campaignSet != "" && ourCampaign->isCampaignFinished())
  649. {
  650. Settings entry = persistentStorage.write["completedCampaigns"][ourCampaign->getFilename()];
  651. entry->Bool() = true;
  652. }
  653. GH.windows().pushWindow(CMM);
  654. GH.windows().pushWindow(CMM->menu);
  655. if(!ourCampaign->isCampaignFinished())
  656. CMM->openCampaignLobby(ourCampaign);
  657. else
  658. {
  659. CMM->openCampaignScreen(ourCampaign->campaignSet);
  660. GH.windows().createAndPushWindow<CHighScoreInputScreen>(true, *campaignScoreCalculator);
  661. }
  662. };
  663. if(epilogue.hasPrologEpilog)
  664. {
  665. GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
  666. }
  667. else
  668. {
  669. finisher();
  670. }
  671. }
  672. void CServerHandler::showServerError(const std::string & txt) const
  673. {
  674. if(auto w = GH.windows().topWindow<CLoadingScreen>())
  675. GH.windows().popWindow(w);
  676. CInfoWindow::showInfoDialog(txt, {});
  677. }
  678. int CServerHandler::howManyPlayerInterfaces()
  679. {
  680. int playerInts = 0;
  681. for(auto pint : client->playerint)
  682. {
  683. if(dynamic_cast<CPlayerInterface *>(pint.second.get()))
  684. playerInts++;
  685. }
  686. return playerInts;
  687. }
  688. ELoadMode CServerHandler::getLoadMode()
  689. {
  690. if(loadMode != ELoadMode::TUTORIAL && getState() == EClientState::GAMEPLAY)
  691. {
  692. if(si->campState)
  693. return ELoadMode::CAMPAIGN;
  694. for(auto pn : playerNames)
  695. {
  696. if(pn.second.connection != logicConnection->connectionID)
  697. return ELoadMode::MULTI;
  698. }
  699. if(howManyPlayerInterfaces() > 1) //this condition will work for hotseat mode OR multiplayer with allowed more than 1 color per player to control
  700. return ELoadMode::MULTI;
  701. return ELoadMode::SINGLE;
  702. }
  703. return loadMode;
  704. }
  705. void CServerHandler::debugStartTest(std::string filename, bool save)
  706. {
  707. logGlobal->info("Starting debug test with file: %s", filename);
  708. auto mapInfo = std::make_shared<CMapInfo>();
  709. if(save)
  710. {
  711. resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, EServerMode::LOCAL, {});
  712. mapInfo->saveInit(ResourcePath(filename, EResType::SAVEGAME));
  713. }
  714. else
  715. {
  716. resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOCAL, {});
  717. mapInfo->mapInit(filename);
  718. }
  719. if(settings["session"]["donotstartserver"].Bool())
  720. connectToServer(getLocalHostname(), getLocalPort());
  721. else
  722. startLocalServerAndConnect(false);
  723. boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
  724. while(!settings["session"]["headless"].Bool() && !GH.windows().topWindow<CLobbyScreen>())
  725. boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
  726. while(!mi || mapInfo->fileURI != mi->fileURI)
  727. {
  728. setMapInfo(mapInfo);
  729. boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
  730. }
  731. // "Click" on color to remove us from it
  732. setPlayer(myFirstColor());
  733. while(myFirstColor() != PlayerColor::CANNOT_DETERMINE)
  734. boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
  735. while(true)
  736. {
  737. try
  738. {
  739. sendStartGame();
  740. break;
  741. }
  742. catch(...)
  743. {
  744. }
  745. boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
  746. }
  747. }
  748. class ServerHandlerCPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
  749. {
  750. private:
  751. CServerHandler & handler;
  752. public:
  753. ServerHandlerCPackVisitor(CServerHandler & handler)
  754. :handler(handler)
  755. {
  756. }
  757. bool callTyped() override { return false; }
  758. void visitForLobby(CPackForLobby & lobbyPack) override
  759. {
  760. handler.visitForLobby(lobbyPack);
  761. }
  762. void visitForClient(CPackForClient & clientPack) override
  763. {
  764. handler.visitForClient(clientPack);
  765. }
  766. };
  767. void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<std::byte> & message)
  768. {
  769. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  770. if(getState() == EClientState::DISCONNECTING)
  771. return;
  772. CPack * pack = logicConnection->retrievePack(message);
  773. ServerHandlerCPackVisitor visitor(*this);
  774. pack->visit(visitor);
  775. }
  776. void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
  777. {
  778. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  779. if (connection != networkConnection)
  780. {
  781. // ServerHandler already closed this connection on its own
  782. // This is the final call from network thread that informs serverHandler that connection has died
  783. // ignore it since serverHandler have already shut down this connection (and possibly started a new one)
  784. return;
  785. }
  786. waitForServerShutdown();
  787. if(getState() == EClientState::DISCONNECTING)
  788. {
  789. // Note: this branch can be reached on app shutdown, when main thread holds mutex till destruction
  790. logNetwork->info("Successfully closed connection to server!");
  791. return;
  792. }
  793. logNetwork->error("Lost connection to server! Connection has been closed");
  794. if(client)
  795. {
  796. endGameplay();
  797. GH.defActionsDef = 63;
  798. CMM->menu->switchToTab("main");
  799. showServerError(CGI->generaltexth->translate("vcmi.server.errors.disconnected"));
  800. }
  801. else
  802. {
  803. LobbyClientDisconnected lcd;
  804. lcd.clientId = logicConnection->connectionID;
  805. applyPackOnLobbyScreen(lcd);
  806. }
  807. networkConnection.reset();
  808. }
  809. void CServerHandler::waitForServerShutdown()
  810. {
  811. if (!serverRunner)
  812. return; // may not exist for guest in MP
  813. serverRunner->wait();
  814. int exitCode = serverRunner->exitCode();
  815. serverRunner.reset();
  816. if (exitCode == 0)
  817. {
  818. logNetwork->info("Server closed correctly");
  819. }
  820. else
  821. {
  822. if (getState() == EClientState::CONNECTING)
  823. {
  824. showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
  825. setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect
  826. }
  827. logNetwork->error("Error: server failed to close correctly or crashed!");
  828. logNetwork->error("Check log file for more info");
  829. }
  830. serverRunner.reset();
  831. }
  832. void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
  833. {
  834. if(applier->getApplier(CTypeList::getInstance().getTypeID(&lobbyPack))->applyOnLobbyHandler(this, lobbyPack))
  835. {
  836. if(!settings["session"]["headless"].Bool())
  837. applyPackOnLobbyScreen(lobbyPack);
  838. }
  839. }
  840. void CServerHandler::visitForClient(CPackForClient & clientPack)
  841. {
  842. client->handlePack(&clientPack);
  843. }
  844. void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
  845. {
  846. if(getState() != EClientState::STARTING)
  847. logicConnection->sendPack(&pack);
  848. }
  849. bool CServerHandler::inLobbyRoom() const
  850. {
  851. return serverMode == EServerMode::LOBBY_HOST || serverMode == EServerMode::LOBBY_GUEST;
  852. }
  853. bool CServerHandler::inGame() const
  854. {
  855. return logicConnection != nullptr;
  856. }