Client.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204
  1. /*
  2. * Client.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 "Client.h"
  12. #include <SDL.h>
  13. #include <boost/uuid/uuid.hpp>
  14. #include <boost/uuid/uuid_io.hpp>
  15. #include <boost/uuid/uuid_generators.hpp>
  16. #include "CMusicHandler.h"
  17. #include "../lib/mapping/CCampaignHandler.h"
  18. #include "../CCallback.h"
  19. #include "../lib/CConsoleHandler.h"
  20. #include "CGameInfo.h"
  21. #include "../lib/CGameState.h"
  22. #include "CPlayerInterface.h"
  23. #include "../lib/StartInfo.h"
  24. #include "../lib/battle/BattleInfo.h"
  25. #include "../lib/CModHandler.h"
  26. #include "../lib/CArtHandler.h"
  27. #include "../lib/CGeneralTextHandler.h"
  28. #include "../lib/CHeroHandler.h"
  29. #include "../lib/CTownHandler.h"
  30. #include "../lib/CBuildingHandler.h"
  31. #include "../lib/spells/CSpellHandler.h"
  32. #include "../lib/serializer/CTypeList.h"
  33. #include "../lib/serializer/Connection.h"
  34. #include "../lib/serializer/CLoadIntegrityValidator.h"
  35. #ifndef VCMI_ANDROID
  36. #include "../lib/Interprocess.h"
  37. #endif
  38. #include "../lib/NetPacks.h"
  39. #include "../lib/VCMI_Lib.h"
  40. #include "../lib/VCMIDirs.h"
  41. #include "../lib/mapping/CMap.h"
  42. #include "../lib/mapping/CMapService.h"
  43. #include "../lib/JsonNode.h"
  44. #include "mapHandler.h"
  45. #include "../lib/CConfigHandler.h"
  46. #include "CPreGame.h"
  47. #include "battle/CBattleInterface.h"
  48. #include "../lib/CThreadHelper.h"
  49. #include "../lib/CScriptingModule.h"
  50. #include "../lib/registerTypes/RegisterTypes.h"
  51. #include "gui/CGuiHandler.h"
  52. #include "CMT.h"
  53. extern std::string NAME;
  54. #ifdef VCMI_ANDROID
  55. #include "lib/CAndroidVMHelper.h"
  56. #endif
  57. #ifdef VCMI_ANDROID
  58. std::atomic_bool androidTestServerReadyFlag;
  59. #endif
  60. ThreadSafeVector<int> CClient::waitingRequest;
  61. template <typename T> class CApplyOnCL;
  62. class CBaseForCLApply
  63. {
  64. public:
  65. virtual void applyOnClAfter(CClient *cl, void *pack) const =0;
  66. virtual void applyOnClBefore(CClient *cl, void *pack) const =0;
  67. virtual ~CBaseForCLApply(){}
  68. template<typename U> static CBaseForCLApply *getApplier(const U * t=nullptr)
  69. {
  70. return new CApplyOnCL<U>();
  71. }
  72. };
  73. template <typename T> class CApplyOnCL : public CBaseForCLApply
  74. {
  75. public:
  76. void applyOnClAfter(CClient *cl, void *pack) const override
  77. {
  78. T *ptr = static_cast<T*>(pack);
  79. ptr->applyCl(cl);
  80. }
  81. void applyOnClBefore(CClient *cl, void *pack) const override
  82. {
  83. T *ptr = static_cast<T*>(pack);
  84. ptr->applyFirstCl(cl);
  85. }
  86. };
  87. template <> class CApplyOnCL<CPack> : public CBaseForCLApply
  88. {
  89. public:
  90. void applyOnClAfter(CClient *cl, void *pack) const override
  91. {
  92. logGlobal->error("Cannot apply on CL plain CPack!");
  93. assert(0);
  94. }
  95. void applyOnClBefore(CClient *cl, void *pack) const override
  96. {
  97. logGlobal->error("Cannot apply on CL plain CPack!");
  98. assert(0);
  99. }
  100. };
  101. static CApplier<CBaseForCLApply> *applier = nullptr;
  102. void CClient::init()
  103. {
  104. waitingRequest.clear();
  105. hotSeat = false;
  106. {
  107. TLockGuard _(connectionHandlerMutex);
  108. connectionHandler.reset();
  109. }
  110. pathInfo = nullptr;
  111. applier = new CApplier<CBaseForCLApply>();
  112. registerTypesClientPacks1(*applier);
  113. registerTypesClientPacks2(*applier);
  114. IObjectInterface::cb = this;
  115. serv = nullptr;
  116. gs = nullptr;
  117. erm = nullptr;
  118. terminate = false;
  119. }
  120. CClient::CClient()
  121. {
  122. init();
  123. }
  124. CClient::CClient(CConnection *con, StartInfo *si)
  125. {
  126. init();
  127. newGame(con,si);
  128. }
  129. CClient::~CClient()
  130. {
  131. delete applier;
  132. }
  133. void CClient::waitForMoveAndSend(PlayerColor color)
  134. {
  135. try
  136. {
  137. setThreadName("CClient::waitForMoveAndSend");
  138. assert(vstd::contains(battleints, color));
  139. BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
  140. if(ba.actionType != EActionType::CANCEL)
  141. {
  142. logNetwork->trace("Send battle action to server: %s", ba.toString());
  143. MakeAction temp_action(ba);
  144. sendRequest(&temp_action, color);
  145. }
  146. }
  147. catch(boost::thread_interrupted&)
  148. {
  149. logNetwork->debug("Wait for move thread was interrupted and no action will be send. Was a battle ended by spell?");
  150. }
  151. catch(...)
  152. {
  153. handleException();
  154. }
  155. }
  156. void CClient::run()
  157. {
  158. setThreadName("CClient::run");
  159. try
  160. {
  161. while(!terminate)
  162. {
  163. CPack *pack = serv->retreivePack(); //get the package from the server
  164. if (terminate)
  165. {
  166. vstd::clear_pointer(pack);
  167. break;
  168. }
  169. handlePack(pack);
  170. }
  171. }
  172. //catch only asio exceptions
  173. catch (const boost::system::system_error& e)
  174. {
  175. logNetwork->error("Lost connection to server, ending listening thread!");
  176. logNetwork->error(e.what());
  177. if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected
  178. {
  179. logNetwork->error("Something wrong, lost connection while game is still ongoing...");
  180. throw;
  181. }
  182. }
  183. }
  184. void CClient::save(const std::string & fname)
  185. {
  186. if(gs->curB)
  187. {
  188. logNetwork->error("Game cannot be saved during battle!");
  189. return;
  190. }
  191. SaveGame save_game(fname);
  192. sendRequest((CPackForClient*)&save_game, PlayerColor::NEUTRAL);
  193. }
  194. void CClient::endGame(bool closeConnection)
  195. {
  196. //suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
  197. for (auto& i : playerint)
  198. i.second->finish();
  199. // Game is ending
  200. // Tell the network thread to reach a stable state
  201. if (closeConnection)
  202. stopConnection();
  203. logNetwork->info("Closed connection.");
  204. GH.curInt = nullptr;
  205. {
  206. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  207. logNetwork->info("Ending current game!");
  208. if(GH.topInt())
  209. {
  210. GH.topInt()->deactivate();
  211. }
  212. GH.listInt.clear();
  213. GH.objsToBlit.clear();
  214. GH.statusbar = nullptr;
  215. logNetwork->info("Removed GUI.");
  216. vstd::clear_pointer(const_cast<CGameInfo*>(CGI)->mh);
  217. vstd::clear_pointer(gs);
  218. logNetwork->info("Deleted mapHandler and gameState.");
  219. LOCPLINT = nullptr;
  220. }
  221. playerint.clear();
  222. battleints.clear();
  223. callbacks.clear();
  224. battleCallbacks.clear();
  225. CGKeys::reset();
  226. CGMagi::reset();
  227. CGObelisk::reset();
  228. logNetwork->info("Deleted playerInts.");
  229. logNetwork->info("Client stopped.");
  230. }
  231. #if 1
  232. void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const ui16 port)
  233. {
  234. PlayerColor player(player_); //intentional shadowing
  235. logNetwork->info("Loading procedure started!");
  236. CServerHandler sh;
  237. if(server)
  238. sh.startServer();
  239. else
  240. serv = sh.justConnectToServer(ipaddr, port);
  241. CStopWatch tmh;
  242. std::unique_ptr<CLoadFile> loader;
  243. try
  244. {
  245. boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
  246. boost::filesystem::path controlServerSaveName;
  247. if (CResourceHandler::get("local")->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME)))
  248. {
  249. controlServerSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME));
  250. }
  251. else// create entry for server savegame. Triggered if save was made after launch and not yet present in res handler
  252. {
  253. controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1");
  254. CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true);
  255. }
  256. if(clientSaveName.empty())
  257. throw std::runtime_error("Cannot open client part of " + fname);
  258. if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName))
  259. throw std::runtime_error("Cannot open server part of " + fname);
  260. {
  261. CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION);
  262. loadCommonState(checkingLoader);
  263. loader = checkingLoader.decay();
  264. }
  265. logNetwork->info("Loaded common part of save %d ms", tmh.getDiff());
  266. const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
  267. const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
  268. pathInfo = make_unique<CPathsInfo>(getMapSize());
  269. CGI->mh->init();
  270. logNetwork->info("Initing maphandler: %d ms", tmh.getDiff());
  271. }
  272. catch(std::exception &e)
  273. {
  274. logGlobal->error("Cannot load game %s. Error: %s", fname, e.what());
  275. throw; //obviously we cannot continue here
  276. }
  277. /*
  278. if(!server)
  279. player = PlayerColor(player_);
  280. */
  281. std::set<PlayerColor> clientPlayers;
  282. if(server)
  283. serv = sh.connectToServer();
  284. //*loader >> *this;
  285. if(server)
  286. {
  287. tmh.update();
  288. ui8 pom8;
  289. *serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player
  290. *serv << fname;
  291. *serv >> pom8;
  292. if(pom8)
  293. throw std::runtime_error("Server cannot open the savegame!");
  294. else
  295. logNetwork->info("Server opened savegame properly.");
  296. }
  297. if(server)
  298. {
  299. for(auto & elem : gs->scenarioOps->playerInfos)
  300. {
  301. if(!std::count(humanplayerindices.begin(),humanplayerindices.end(),elem.first.getNum()) || elem.first==player)
  302. clientPlayers.insert(elem.first);
  303. }
  304. clientPlayers.insert(PlayerColor::NEUTRAL);
  305. }
  306. else
  307. {
  308. clientPlayers.insert(player);
  309. }
  310. std::cout << "CLIENTPLAYERS:\n";
  311. for(auto x : clientPlayers)
  312. std::cout << x << std::endl;
  313. std::cout << "ENDCLIENTPLAYERS\n";
  314. serialize(loader->serializer, loader->serializer.fileVersion, clientPlayers);
  315. *serv << ui32(clientPlayers.size());
  316. for(auto & elem : clientPlayers)
  317. *serv << ui8(elem.getNum());
  318. serv->addStdVecItems(gs); /*why is this here?*/
  319. //*loader >> *this;
  320. logNetwork->info("Loaded client part of save %d ms", tmh.getDiff());
  321. logNetwork->info("Sent info to server: %d ms", tmh.getDiff());
  322. //*serv << clientPlayers;
  323. serv->enableStackSendingByID();
  324. serv->disableSmartPointerSerialization();
  325. // logGlobal->trace("Objects:");
  326. // for(int i = 0; i < gs->map->objects.size(); i++)
  327. // {
  328. // auto o = gs->map->objects[i];
  329. // if(o)
  330. // logGlobal->trace("\tindex=%5d, id=%5d; address=%5d, pos=%s, name=%s", i, o->id, (int)o.get(), o->pos, o->getHoverText());
  331. // else
  332. // logGlobal->trace("\tindex=%5d --- nullptr", i);
  333. // }
  334. }
  335. #endif
  336. void CClient::newGame( CConnection *con, StartInfo *si )
  337. {
  338. enum {SINGLE, HOST, GUEST} networkMode = SINGLE;
  339. if (con == nullptr)
  340. {
  341. CServerHandler sh;
  342. serv = sh.connectToServer();
  343. }
  344. else
  345. {
  346. serv = con;
  347. networkMode = con->isHost() ? HOST : GUEST;
  348. }
  349. CConnection &c = *serv;
  350. ////////////////////////////////////////////////////
  351. logNetwork->info("\tWill send info to server...");
  352. CStopWatch tmh;
  353. if(networkMode == SINGLE)
  354. {
  355. ui8 pom8;
  356. c << ui8(2) << ui8(1); //new game; one client
  357. c << *si;
  358. c >> pom8;
  359. if(pom8) throw std::runtime_error("Server cannot open the map!");
  360. }
  361. c >> si;
  362. logNetwork->info("\tSending/Getting info to/from the server: %d ms", tmh.getDiff());
  363. c.enableStackSendingByID();
  364. c.disableSmartPointerSerialization();
  365. // Initialize game state
  366. CMapService mapService;
  367. gs = new CGameState();
  368. logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
  369. gs->init(&mapService, si, settings["general"]["saveRandomMaps"].Bool());
  370. logNetwork->info("Initializing GameState (together): %d ms", tmh.getDiff());
  371. // Now after possible random map gen, we know exact player count.
  372. // Inform server about how many players client handles
  373. std::set<PlayerColor> myPlayers;
  374. for(auto & elem : gs->scenarioOps->playerInfos)
  375. {
  376. if((networkMode == SINGLE) //single - one client has all player
  377. || (networkMode != SINGLE && serv->connectionID == elem.second.playerID) //multi - client has only "its players"
  378. || (networkMode == HOST && elem.second.playerID == PlayerSettings::PLAYER_AI))//multi - host has all AI players
  379. {
  380. myPlayers.insert(elem.first); //add player
  381. }
  382. }
  383. if(networkMode != GUEST)
  384. myPlayers.insert(PlayerColor::NEUTRAL);
  385. c << myPlayers;
  386. // Init map handler
  387. if(gs->map)
  388. {
  389. if(!settings["session"]["headless"].Bool())
  390. {
  391. const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
  392. CGI->mh->map = gs->map;
  393. logNetwork->info("Creating mapHandler: %d ms", tmh.getDiff());
  394. CGI->mh->init();
  395. }
  396. pathInfo = make_unique<CPathsInfo>(getMapSize());
  397. logNetwork->info("Initializing mapHandler (together): %d ms", tmh.getDiff());
  398. }
  399. int humanPlayers = 0;
  400. for(auto & elem : gs->scenarioOps->playerInfos)//initializing interfaces for players
  401. {
  402. PlayerColor color = elem.first;
  403. gs->currentPlayer = color;
  404. if(!vstd::contains(myPlayers, color))
  405. continue;
  406. logNetwork->trace("Preparing interface for player %s", color.getStr());
  407. if(elem.second.playerID == PlayerSettings::PLAYER_AI)
  408. {
  409. auto AiToGive = aiNameForPlayer(elem.second, false);
  410. logNetwork->info("Player %s will be lead by %s", color, AiToGive);
  411. installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color);
  412. }
  413. else
  414. {
  415. installNewPlayerInterface(std::make_shared<CPlayerInterface>(color), color);
  416. humanPlayers++;
  417. }
  418. }
  419. if(settings["session"]["spectate"].Bool())
  420. {
  421. installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
  422. }
  423. loadNeutralBattleAI();
  424. serv->addStdVecItems(gs);
  425. hotSeat = (humanPlayers > 1);
  426. // std::vector<FileInfo> scriptModules;
  427. // CFileUtility::getFilesWithExt(scriptModules, LIB_DIR "/scripting", "." LIB_EXT);
  428. // for(FileInfo &m : scriptModules)
  429. // {
  430. // CScriptingModule * nm = CDynLibHandler::getNewScriptingModule(m.name);
  431. // privilegedGameEventReceivers.push_back(nm);
  432. // privilegedBattleEventReceivers.push_back(nm);
  433. // nm->giveActionCB(this);
  434. // nm->giveInfoCB(this);
  435. // nm->init();
  436. //
  437. // erm = nm; //something tells me that there'll at most one module and it'll be ERM
  438. // }
  439. }
  440. void CClient::serialize(BinarySerializer & h, const int version)
  441. {
  442. assert(h.saving);
  443. h & hotSeat;
  444. {
  445. ui8 players = playerint.size();
  446. h & players;
  447. for(auto i = playerint.begin(); i != playerint.end(); i++)
  448. {
  449. LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
  450. assert(i->first == i->second->playerID);
  451. h & i->first;
  452. h & i->second->dllName;
  453. h & i->second->human;
  454. i->second->saveGame(h, version);
  455. }
  456. }
  457. }
  458. void CClient::serialize(BinaryDeserializer & h, const int version)
  459. {
  460. assert(!h.saving);
  461. h & hotSeat;
  462. {
  463. ui8 players = 0; //fix for uninitialized warning
  464. h & players;
  465. for(int i=0; i < players; i++)
  466. {
  467. std::string dllname;
  468. PlayerColor pid;
  469. bool isHuman = false;
  470. h & pid;
  471. h & dllname;
  472. h & isHuman;
  473. LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid);
  474. std::shared_ptr<CGameInterface> nInt;
  475. if(dllname.length())
  476. {
  477. if(pid == PlayerColor::NEUTRAL)
  478. {
  479. installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
  480. //TODO? consider serialization
  481. continue;
  482. }
  483. else
  484. {
  485. assert(!isHuman);
  486. nInt = CDynLibHandler::getNewAI(dllname);
  487. }
  488. }
  489. else
  490. {
  491. assert(isHuman);
  492. nInt = std::make_shared<CPlayerInterface>(pid);
  493. }
  494. nInt->dllName = dllname;
  495. nInt->human = isHuman;
  496. nInt->playerID = pid;
  497. installNewPlayerInterface(nInt, pid);
  498. nInt->loadGame(h, version); //another evil cast, check above
  499. }
  500. if(!vstd::contains(battleints, PlayerColor::NEUTRAL))
  501. loadNeutralBattleAI();
  502. }
  503. }
  504. void CClient::serialize(BinarySerializer & h, const int version, const std::set<PlayerColor> & playerIDs)
  505. {
  506. assert(h.saving);
  507. h & hotSeat;
  508. {
  509. ui8 players = playerint.size();
  510. h & players;
  511. for(auto i = playerint.begin(); i != playerint.end(); i++)
  512. {
  513. LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
  514. assert(i->first == i->second->playerID);
  515. h & i->first;
  516. h & i->second->dllName;
  517. h & i->second->human;
  518. i->second->saveGame(h, version);
  519. }
  520. }
  521. }
  522. void CClient::serialize(BinaryDeserializer & h, const int version, const std::set<PlayerColor> & playerIDs)
  523. {
  524. assert(!h.saving);
  525. h & hotSeat;
  526. {
  527. ui8 players = 0; //fix for uninitialized warning
  528. h & players;
  529. for(int i=0; i < players; i++)
  530. {
  531. std::string dllname;
  532. PlayerColor pid;
  533. bool isHuman = false;
  534. h & pid;
  535. h & dllname;
  536. h & isHuman;
  537. LOG_TRACE_PARAMS(logGlobal, "Loading player %s interface", pid);
  538. std::shared_ptr<CGameInterface> nInt;
  539. if(dllname.length())
  540. {
  541. if(pid == PlayerColor::NEUTRAL)
  542. {
  543. if(playerIDs.count(pid))
  544. installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
  545. //TODO? consider serialization
  546. continue;
  547. }
  548. else
  549. {
  550. assert(!isHuman);
  551. nInt = CDynLibHandler::getNewAI(dllname);
  552. }
  553. }
  554. else
  555. {
  556. assert(isHuman);
  557. nInt = std::make_shared<CPlayerInterface>(pid);
  558. }
  559. nInt->dllName = dllname;
  560. nInt->human = isHuman;
  561. nInt->playerID = pid;
  562. nInt->loadGame(h, version);
  563. if(settings["session"]["onlyai"].Bool() && isHuman)
  564. {
  565. removeGUI();
  566. nInt.reset();
  567. dllname = aiNameForPlayer(false);
  568. nInt = CDynLibHandler::getNewAI(dllname);
  569. nInt->dllName = dllname;
  570. nInt->human = false;
  571. nInt->playerID = pid;
  572. installNewPlayerInterface(nInt, pid);
  573. GH.totalRedraw();
  574. }
  575. else
  576. {
  577. if(playerIDs.count(pid))
  578. installNewPlayerInterface(nInt, pid);
  579. }
  580. }
  581. if(settings["session"]["spectate"].Bool())
  582. {
  583. removeGUI();
  584. auto p = std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR);
  585. installNewPlayerInterface(p, PlayerColor::SPECTATOR, true);
  586. GH.curInt = p.get();
  587. LOCPLINT->activateForSpectator();
  588. GH.totalRedraw();
  589. }
  590. if(playerIDs.count(PlayerColor::NEUTRAL))
  591. loadNeutralBattleAI();
  592. }
  593. }
  594. void CClient::handlePack( CPack * pack )
  595. {
  596. if(pack == nullptr)
  597. {
  598. logNetwork->error("Dropping nullptr CPack! You should check whether client and server ABI matches.");
  599. return;
  600. }
  601. CBaseForCLApply *apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
  602. if(apply)
  603. {
  604. boost::unique_lock<boost::recursive_mutex> guiLock(*CPlayerInterface::pim);
  605. apply->applyOnClBefore(this, pack);
  606. logNetwork->trace("\tMade first apply on cl");
  607. gs->apply(pack);
  608. logNetwork->trace("\tApplied on gs");
  609. apply->applyOnClAfter(this, pack);
  610. logNetwork->trace("\tMade second apply on cl");
  611. }
  612. else
  613. {
  614. logNetwork->error("Message %s cannot be applied, cannot find applier!", typeList.getTypeInfo(pack)->name());
  615. }
  616. delete pack;
  617. }
  618. void CClient::finishCampaign( std::shared_ptr<CCampaignState> camp )
  619. {
  620. }
  621. void CClient::proposeNextMission(std::shared_ptr<CCampaignState> camp)
  622. {
  623. GH.pushInt(new CBonusSelection(camp));
  624. }
  625. void CClient::stopConnection()
  626. {
  627. terminate = true;
  628. if(serv)
  629. {
  630. boost::unique_lock<boost::mutex>(*serv->wmx);
  631. if(serv->isHost()) //request closing connection
  632. {
  633. logNetwork->info("Connection has been requested to be closed.");
  634. CloseServer close_server;
  635. sendRequest(&close_server, PlayerColor::NEUTRAL);
  636. logNetwork->info("Sent closing signal to the server");
  637. }
  638. else
  639. {
  640. LeaveGame leave_Game;
  641. sendRequest(&leave_Game, PlayerColor::NEUTRAL);
  642. logNetwork->info("Sent leaving signal to the server");
  643. }
  644. }
  645. {
  646. TLockGuard _(connectionHandlerMutex);
  647. if(connectionHandler)//end connection handler
  648. {
  649. if(connectionHandler->get_id() != boost::this_thread::get_id())
  650. connectionHandler->join();
  651. logNetwork->info("Connection handler thread joined");
  652. connectionHandler.reset();
  653. }
  654. }
  655. if (serv) //and delete connection
  656. {
  657. serv->close();
  658. vstd::clear_pointer(serv);
  659. logNetwork->warn("Our socket has been closed.");
  660. }
  661. }
  662. void CClient::battleStarted(const BattleInfo * info)
  663. {
  664. for(auto &battleCb : battleCallbacks)
  665. {
  666. if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
  667. || battleCb.first >= PlayerColor::PLAYER_LIMIT)
  668. {
  669. battleCb.second->setBattle(info);
  670. }
  671. }
  672. // for(ui8 side : info->sides)
  673. // if(battleCallbacks.count(side))
  674. // battleCallbacks[side]->setBattle(info);
  675. std::shared_ptr<CPlayerInterface> att, def;
  676. auto &leftSide = info->sides[0], &rightSide = info->sides[1];
  677. //If quick combat is not, do not prepare interfaces for battleint
  678. if(!settings["adventure"]["quickCombat"].Bool())
  679. {
  680. if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human)
  681. att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[leftSide.color] );
  682. if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human)
  683. def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
  684. }
  685. if(!settings["session"]["headless"].Bool())
  686. {
  687. if(!!att || !!def)
  688. {
  689. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  690. auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
  691. Rect((screen->w - 800)/2,
  692. (screen->h - 600)/2, 800, 600), att, def);
  693. GH.pushInt(bi);
  694. }
  695. else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  696. {
  697. //TODO: This certainly need improvement
  698. auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
  699. spectratorInt->cb->setBattle(info);
  700. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  701. auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
  702. Rect((screen->w - 800)/2,
  703. (screen->h - 600)/2, 800, 600), att, def, spectratorInt);
  704. GH.pushInt(bi);
  705. }
  706. }
  707. auto callBattleStart = [&](PlayerColor color, ui8 side){
  708. if(vstd::contains(battleints, color))
  709. battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side);
  710. };
  711. callBattleStart(leftSide.color, 0);
  712. callBattleStart(rightSide.color, 1);
  713. callBattleStart(PlayerColor::UNFLAGGABLE, 1);
  714. if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  715. callBattleStart(PlayerColor::SPECTATOR, 1);
  716. if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
  717. {
  718. boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]);
  719. }
  720. }
  721. void CClient::battleFinished()
  722. {
  723. stopAllBattleActions();
  724. for(auto & side : gs->curB->sides)
  725. if(battleCallbacks.count(side.color))
  726. battleCallbacks[side.color]->setBattle(nullptr);
  727. if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  728. battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
  729. }
  730. void CClient::loadNeutralBattleAI()
  731. {
  732. installNewBattleInterface(CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String()), PlayerColor::NEUTRAL);
  733. }
  734. void CClient::commitPackage( CPackForClient *pack )
  735. {
  736. CommitPackage cp;
  737. cp.freePack = false;
  738. cp.packToCommit = pack;
  739. sendRequest(&cp, PlayerColor::NEUTRAL);
  740. }
  741. PlayerColor CClient::getLocalPlayer() const
  742. {
  743. if(LOCPLINT)
  744. return LOCPLINT->playerID;
  745. return getCurrentPlayer();
  746. }
  747. void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt)
  748. {
  749. setThreadName("CClient::commenceTacticPhaseForInt");
  750. try
  751. {
  752. battleInt->yourTacticPhase(gs->curB->tacticDistance);
  753. if(gs && !!gs->curB && gs->curB->tacticDistance) //while awaiting for end of tactics phase, many things can happen (end of battle... or game)
  754. {
  755. MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID).get()));
  756. sendRequest(&ma, battleInt->playerID);
  757. }
  758. }
  759. catch(...)
  760. {
  761. handleException();
  762. }
  763. }
  764. void CClient::invalidatePaths()
  765. {
  766. // turn pathfinding info into invalid. It will be regenerated later
  767. boost::unique_lock<boost::mutex> pathLock(pathInfo->pathMx);
  768. pathInfo->hero = nullptr;
  769. }
  770. const CPathsInfo * CClient::getPathsInfo(const CGHeroInstance *h)
  771. {
  772. assert(h);
  773. boost::unique_lock<boost::mutex> pathLock(pathInfo->pathMx);
  774. if (pathInfo->hero != h)
  775. {
  776. gs->calculatePaths(h, *pathInfo.get());
  777. }
  778. return pathInfo.get();
  779. }
  780. int CClient::sendRequest(const CPack *request, PlayerColor player)
  781. {
  782. static ui32 requestCounter = 0;
  783. ui32 requestID = requestCounter++;
  784. logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(*request).name(), requestID);
  785. waitingRequest.pushBack(requestID);
  786. serv->sendPackToServer(*request, player, requestID);
  787. if(vstd::contains(playerint, player))
  788. playerint[player]->requestSent(dynamic_cast<const CPackForServer*>(request), requestID);
  789. return requestID;
  790. }
  791. void CClient::campaignMapFinished( std::shared_ptr<CCampaignState> camp )
  792. {
  793. endGame(false);
  794. GH.curInt = CGPreGame::create();
  795. auto & epilogue = camp->camp->scenarios[camp->mapsConquered.back()].epilog;
  796. auto finisher = [=]()
  797. {
  798. if(camp->mapsRemaining.size())
  799. proposeNextMission(camp);
  800. else
  801. finishCampaign(camp);
  802. };
  803. if(epilogue.hasPrologEpilog)
  804. {
  805. GH.pushInt(new CPrologEpilogVideo(epilogue, finisher));
  806. }
  807. else
  808. {
  809. finisher();
  810. }
  811. }
  812. void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb)
  813. {
  814. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  815. PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
  816. if(!color)
  817. privilegedGameEventReceivers.push_back(gameInterface);
  818. playerint[colorUsed] = gameInterface;
  819. logGlobal->trace("\tInitializing the interface for player %s", colorUsed);
  820. auto cb = std::make_shared<CCallback>(gs, color, this);
  821. callbacks[colorUsed] = cb;
  822. battleCallbacks[colorUsed] = cb;
  823. gameInterface->init(cb);
  824. installNewBattleInterface(gameInterface, color, battlecb);
  825. }
  826. void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback)
  827. {
  828. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  829. PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
  830. if(!color)
  831. privilegedBattleEventReceivers.push_back(battleInterface);
  832. battleints[colorUsed] = battleInterface;
  833. if(needCallback)
  834. {
  835. logGlobal->trace("\tInitializing the battle interface for player %s", *color);
  836. auto cbc = std::make_shared<CBattleCallback>(color, this);
  837. battleCallbacks[colorUsed] = cbc;
  838. battleInterface->init(cbc);
  839. }
  840. }
  841. std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
  842. {
  843. if(ps.name.size())
  844. {
  845. const boost::filesystem::path aiPath = VCMIDirs::get().fullLibraryPath("AI", ps.name);
  846. if (boost::filesystem::exists(aiPath))
  847. return ps.name;
  848. }
  849. return aiNameForPlayer(battleAI);
  850. }
  851. std::string CClient::aiNameForPlayer(bool battleAI)
  852. {
  853. const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
  854. std::string goodAI = battleAI ? settings["server"]["neutralAI"].String() : settings["server"]["playerAI"].String();
  855. std::string badAI = battleAI ? "StupidAI" : "EmptyAI";
  856. //TODO what about human players
  857. if(battleints.size() >= sensibleAILimit)
  858. return badAI;
  859. return goodAI;
  860. }
  861. void CClient::startPlayerBattleAction(PlayerColor color)
  862. {
  863. stopPlayerBattleAction(color);
  864. if(vstd::contains(battleints, color))
  865. {
  866. auto thread = std::make_shared<boost::thread>(std::bind(&CClient::waitForMoveAndSend, this, color));
  867. playerActionThreads[color] = thread;
  868. }
  869. }
  870. void CClient::stopPlayerBattleAction(PlayerColor color)
  871. {
  872. if(vstd::contains(playerActionThreads, color))
  873. {
  874. auto thread = playerActionThreads.at(color);
  875. if(thread->joinable())
  876. {
  877. thread->interrupt();
  878. thread->join();
  879. }
  880. playerActionThreads.erase(color);
  881. }
  882. }
  883. void CClient::stopAllBattleActions()
  884. {
  885. while(!playerActionThreads.empty())
  886. stopPlayerBattleAction(playerActionThreads.begin()->first);
  887. }
  888. void CServerHandler::startServer()
  889. {
  890. if(settings["session"]["donotstartserver"].Bool())
  891. return;
  892. th.update();
  893. #ifdef VCMI_ANDROID
  894. CAndroidVMHelper envHelper;
  895. envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true);
  896. #else
  897. serverThread = new boost::thread(&CServerHandler::callServer, this); //runs server executable;
  898. #endif
  899. if(verbose)
  900. logNetwork->info("Setting up thread calling server: %d ms", th.getDiff());
  901. }
  902. void CServerHandler::waitForServer()
  903. {
  904. if(settings["session"]["donotstartserver"].Bool())
  905. return;
  906. if(!serverThread)
  907. startServer();
  908. th.update();
  909. #ifndef VCMI_ANDROID
  910. if(shared)
  911. shared->sr->waitTillReady();
  912. #else
  913. logNetwork->info("waiting for server");
  914. while (!androidTestServerReadyFlag.load())
  915. {
  916. logNetwork->info("still waiting...");
  917. boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
  918. }
  919. logNetwork->info("waiting for server finished...");
  920. androidTestServerReadyFlag = false;
  921. #endif
  922. if(verbose)
  923. logNetwork->info("Waiting for server: %d ms", th.getDiff());
  924. }
  925. CConnection * CServerHandler::connectToServer()
  926. {
  927. waitForServer();
  928. th.update(); //put breakpoint here to attach to server before it does something stupid
  929. #ifndef VCMI_ANDROID
  930. CConnection *ret = justConnectToServer(settings["server"]["server"].String(), shared ? shared->sr->port : 0);
  931. #else
  932. CConnection *ret = justConnectToServer(settings["server"]["server"].String());
  933. #endif
  934. if(verbose)
  935. logNetwork->info("\tConnecting to the server: %d ms", th.getDiff());
  936. return ret;
  937. }
  938. ui16 CServerHandler::getDefaultPort()
  939. {
  940. if(settings["session"]["serverport"].Integer())
  941. return settings["session"]["serverport"].Integer();
  942. else
  943. return settings["server"]["port"].Integer();
  944. }
  945. std::string CServerHandler::getDefaultPortStr()
  946. {
  947. return boost::lexical_cast<std::string>(getDefaultPort());
  948. }
  949. CServerHandler::CServerHandler(bool runServer)
  950. {
  951. serverThread = nullptr;
  952. shared = nullptr;
  953. verbose = true;
  954. uuid = boost::uuids::to_string(boost::uuids::random_generator()());
  955. #ifndef VCMI_ANDROID
  956. if(settings["session"]["donotstartserver"].Bool() || settings["session"]["disable-shm"].Bool())
  957. return;
  958. std::string sharedMemoryName = "vcmi_memory";
  959. if(settings["session"]["enable-shm-uuid"].Bool())
  960. {
  961. //used or automated testing when multiple clients start simultaneously
  962. sharedMemoryName += "_" + uuid;
  963. }
  964. try
  965. {
  966. shared = new SharedMemory(sharedMemoryName, true);
  967. }
  968. catch(...)
  969. {
  970. vstd::clear_pointer(shared);
  971. logNetwork->error("Cannot open interprocess memory. Continue without it...");
  972. }
  973. #endif
  974. }
  975. CServerHandler::~CServerHandler()
  976. {
  977. delete shared;
  978. delete serverThread; //detaches, not kills thread
  979. }
  980. void CServerHandler::callServer()
  981. {
  982. #ifndef VCMI_ANDROID
  983. setThreadName("CServerHandler::callServer");
  984. const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
  985. std::string comm = VCMIDirs::get().serverPath().string()
  986. + " --port=" + getDefaultPortStr()
  987. + " --run-by-client"
  988. + " --uuid=" + uuid;
  989. if(shared)
  990. {
  991. comm += " --enable-shm";
  992. if(settings["session"]["enable-shm-uuid"].Bool())
  993. comm += " --enable-shm-uuid";
  994. }
  995. comm += " > \"" + logName + '\"';
  996. int result = std::system(comm.c_str());
  997. if (result == 0)
  998. {
  999. logNetwork->info("Server closed correctly");
  1000. serverAlive.setn(false);
  1001. }
  1002. else
  1003. {
  1004. logNetwork->error("Error: server failed to close correctly or crashed!");
  1005. logNetwork->error("Check %s for more info", logName);
  1006. serverAlive.setn(false);
  1007. // TODO: make client return to main menu if server actually crashed during game.
  1008. // exit(1);// exit in case of error. Othervice without working server VCMI will hang
  1009. }
  1010. #endif
  1011. }
  1012. CConnection * CServerHandler::justConnectToServer(const std::string &host, const ui16 port)
  1013. {
  1014. CConnection *ret = nullptr;
  1015. while(!ret)
  1016. {
  1017. try
  1018. {
  1019. logNetwork->info("Establishing connection...");
  1020. ret = new CConnection( host.size() ? host : settings["server"]["server"].String(),
  1021. port ? port : getDefaultPort(),
  1022. NAME);
  1023. ret->connectionID = 1; // TODO: Refactoring for the server so IDs set outside of CConnection
  1024. }
  1025. catch(...)
  1026. {
  1027. logNetwork->error("\nCannot establish connection! Retrying within 2 seconds");
  1028. SDL_Delay(2000);
  1029. }
  1030. }
  1031. return ret;
  1032. }
  1033. #ifdef VCMI_ANDROID
  1034. extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jobject cls)
  1035. {
  1036. logNetwork->info("Received server ready signal");
  1037. androidTestServerReadyFlag.store(true);
  1038. }
  1039. extern "C" JNIEXPORT bool JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jobject cls)
  1040. {
  1041. logGlobal->info("Received emergency save game request");
  1042. if(!LOCPLINT || !LOCPLINT->cb)
  1043. {
  1044. return false;
  1045. }
  1046. LOCPLINT->cb->save("Saves/_Android_Autosave");
  1047. return true;
  1048. }
  1049. #endif