Client.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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 "CMusicHandler.h"
  14. #include "../lib/mapping/CCampaignHandler.h"
  15. #include "../CCallback.h"
  16. #include "../lib/CConsoleHandler.h"
  17. #include "CGameInfo.h"
  18. #include "../lib/CGameState.h"
  19. #include "CPlayerInterface.h"
  20. #include "../lib/StartInfo.h"
  21. #include "../lib/battle/BattleInfo.h"
  22. #include "../lib/CModHandler.h"
  23. #include "../lib/CArtHandler.h"
  24. #include "../lib/CGeneralTextHandler.h"
  25. #include "../lib/CHeroHandler.h"
  26. #include "../lib/CTownHandler.h"
  27. #include "../lib/CBuildingHandler.h"
  28. #include "../lib/spells/CSpellHandler.h"
  29. #include "../lib/serializer/CTypeList.h"
  30. #include "../lib/serializer/Connection.h"
  31. #include "../lib/serializer/CLoadIntegrityValidator.h"
  32. #include "../lib/NetPacks.h"
  33. #include "../lib/VCMI_Lib.h"
  34. #include "../lib/VCMIDirs.h"
  35. #include "../lib/mapping/CMap.h"
  36. #include "../lib/mapping/CMapService.h"
  37. #include "../lib/JsonNode.h"
  38. #include "mapHandler.h"
  39. #include "../lib/CConfigHandler.h"
  40. #include "mainmenu/CMainMenu.h"
  41. #include "mainmenu/CCampaignScreen.h"
  42. #include "lobby/CBonusSelection.h"
  43. #include "battle/CBattleInterface.h"
  44. #include "../lib/CThreadHelper.h"
  45. #include "../lib/CScriptingModule.h"
  46. #include "../lib/registerTypes/RegisterTypes.h"
  47. #include "gui/CGuiHandler.h"
  48. #include "CMT.h"
  49. #include "CServerHandler.h"
  50. #ifdef VCMI_ANDROID
  51. #include "lib/CAndroidVMHelper.h"
  52. #endif
  53. #ifdef VCMI_ANDROID
  54. std::atomic_bool androidTestServerReadyFlag;
  55. #endif
  56. ThreadSafeVector<int> CClient::waitingRequest;
  57. template<typename T> class CApplyOnCL;
  58. class CBaseForCLApply
  59. {
  60. public:
  61. virtual void applyOnClAfter(CClient * cl, void * pack) const =0;
  62. virtual void applyOnClBefore(CClient * cl, void * pack) const =0;
  63. virtual ~CBaseForCLApply(){}
  64. template<typename U> static CBaseForCLApply * getApplier(const U * t = nullptr)
  65. {
  66. return new CApplyOnCL<U>();
  67. }
  68. };
  69. template<typename T> class CApplyOnCL : public CBaseForCLApply
  70. {
  71. public:
  72. void applyOnClAfter(CClient * cl, void * pack) const override
  73. {
  74. T * ptr = static_cast<T *>(pack);
  75. ptr->applyCl(cl);
  76. }
  77. void applyOnClBefore(CClient * cl, void * pack) const override
  78. {
  79. T * ptr = static_cast<T *>(pack);
  80. ptr->applyFirstCl(cl);
  81. }
  82. };
  83. template<> class CApplyOnCL<CPack>: public CBaseForCLApply
  84. {
  85. public:
  86. void applyOnClAfter(CClient * cl, void * pack) const override
  87. {
  88. logGlobal->error("Cannot apply on CL plain CPack!");
  89. assert(0);
  90. }
  91. void applyOnClBefore(CClient * cl, void * pack) const override
  92. {
  93. logGlobal->error("Cannot apply on CL plain CPack!");
  94. assert(0);
  95. }
  96. };
  97. CClient::CClient()
  98. {
  99. waitingRequest.clear();
  100. pathInfo = nullptr;
  101. applier = std::make_shared<CApplier<CBaseForCLApply>>();
  102. registerTypesClientPacks1(*applier);
  103. registerTypesClientPacks2(*applier);
  104. IObjectInterface::cb = this;
  105. gs = nullptr;
  106. erm = nullptr;
  107. }
  108. void CClient::newGame()
  109. {
  110. CSH->th->update();
  111. CMapService mapService;
  112. gs = new CGameState();
  113. logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
  114. gs->init(&mapService, CSH->si.get(), settings["general"]["saveRandomMaps"].Bool());
  115. logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff());
  116. initMapHandler();
  117. initPlayerInterfaces();
  118. }
  119. void CClient::loadGame()
  120. {
  121. logNetwork->info("Loading procedure started!");
  122. std::unique_ptr<CLoadFile> loader;
  123. try
  124. {
  125. boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME));
  126. boost::filesystem::path controlServerSaveName;
  127. if(CResourceHandler::get("local")->existsResource(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME)))
  128. {
  129. controlServerSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME));
  130. }
  131. else // create entry for server savegame. Triggered if save was made after launch and not yet present in res handler
  132. {
  133. controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1");
  134. CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true);
  135. }
  136. if(clientSaveName.empty())
  137. throw std::runtime_error("Cannot open client part of " + CSH->si->mapname);
  138. if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName))
  139. throw std::runtime_error("Cannot open server part of " + CSH->si->mapname);
  140. {
  141. CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION);
  142. loadCommonState(checkingLoader);
  143. loader = checkingLoader.decay();
  144. }
  145. }
  146. catch(std::exception & e)
  147. {
  148. logGlobal->error("Cannot load game %s. Error: %s", CSH->si->mapname, e.what());
  149. throw; //obviously we cannot continue here
  150. }
  151. logNetwork->trace("Loaded common part of save %d ms", CSH->th->getDiff());
  152. gs->updateOnLoad(CSH->si.get());
  153. initMapHandler();
  154. serialize(loader->serializer, loader->serializer.fileVersion);
  155. initPlayerInterfaces();
  156. }
  157. void CClient::serialize(BinarySerializer & h, const int version)
  158. {
  159. assert(h.saving);
  160. ui8 players = playerint.size();
  161. h & players;
  162. for(auto i = playerint.begin(); i != playerint.end(); i++)
  163. {
  164. logGlobal->trace("Saving player %s interface", i->first);
  165. assert(i->first == i->second->playerID);
  166. h & i->first;
  167. h & i->second->dllName;
  168. h & i->second->human;
  169. i->second->saveGame(h, version);
  170. }
  171. }
  172. void CClient::serialize(BinaryDeserializer & h, const int version)
  173. {
  174. assert(!h.saving);
  175. if(version < 787)
  176. {
  177. bool hotSeat = false;
  178. h & hotSeat;
  179. }
  180. ui8 players = 0;
  181. h & players;
  182. for(int i = 0; i < players; i++)
  183. {
  184. std::string dllname;
  185. PlayerColor pid;
  186. bool isHuman = false;
  187. h & pid;
  188. h & dllname;
  189. h & isHuman;
  190. assert(dllname.length() == 0 || !isHuman);
  191. if(pid == PlayerColor::NEUTRAL)
  192. {
  193. logGlobal->trace("Neutral battle interfaces are not serialized.");
  194. continue;
  195. }
  196. logGlobal->trace("Loading player %s interface", pid);
  197. std::shared_ptr<CGameInterface> nInt;
  198. if(dllname.length())
  199. nInt = CDynLibHandler::getNewAI(dllname);
  200. else
  201. nInt = std::make_shared<CPlayerInterface>(pid);
  202. nInt->dllName = dllname;
  203. nInt->human = isHuman;
  204. nInt->playerID = pid;
  205. nInt->loadGame(h, version);
  206. // Client no longer handle this player at all
  207. if(!vstd::contains(CSH->getAllClientPlayers(CSH->c->connectionID), pid))
  208. {
  209. logGlobal->trace("Player %s is not belong to this client. Destroying interface", pid);
  210. }
  211. else if(isHuman && !vstd::contains(CSH->getHumanColors(), pid))
  212. {
  213. logGlobal->trace("Player %s is no longer controlled by human. Destroying interface", pid);
  214. }
  215. else if(!isHuman && vstd::contains(CSH->getHumanColors(), pid))
  216. {
  217. logGlobal->trace("Player %s is no longer controlled by AI. Destroying interface", pid);
  218. }
  219. else
  220. {
  221. installNewPlayerInterface(nInt, pid);
  222. continue;
  223. }
  224. nInt.reset();
  225. }
  226. logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff());
  227. }
  228. void CClient::save(const std::string & fname)
  229. {
  230. if(gs->curB)
  231. {
  232. logNetwork->error("Game cannot be saved during battle!");
  233. return;
  234. }
  235. SaveGame save_game(fname);
  236. sendRequest(&save_game, PlayerColor::NEUTRAL);
  237. }
  238. void CClient::endGame()
  239. {
  240. //suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
  241. for(auto & i : playerint)
  242. i.second->finish();
  243. GH.curInt = nullptr;
  244. {
  245. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  246. logNetwork->info("Ending current game!");
  247. if(GH.topInt())
  248. {
  249. GH.topInt()->deactivate();
  250. }
  251. GH.listInt.clear();
  252. GH.objsToBlit.clear();
  253. GH.statusbar = nullptr;
  254. logNetwork->info("Removed GUI.");
  255. vstd::clear_pointer(const_cast<CGameInfo *>(CGI)->mh);
  256. vstd::clear_pointer(gs);
  257. logNetwork->info("Deleted mapHandler and gameState.");
  258. LOCPLINT = nullptr;
  259. }
  260. playerint.clear();
  261. battleints.clear();
  262. callbacks.clear();
  263. battleCallbacks.clear();
  264. logNetwork->info("Deleted playerInts.");
  265. logNetwork->info("Client stopped.");
  266. }
  267. void CClient::initMapHandler()
  268. {
  269. // TODO: CMapHandler initialization can probably go somewhere else
  270. // It's can't be before initialization of interfaces
  271. // During loading CPlayerInterface from serialized state it's depend on MH
  272. if(!settings["session"]["headless"].Bool())
  273. {
  274. const_cast<CGameInfo *>(CGI)->mh = new CMapHandler();
  275. CGI->mh->map = gs->map;
  276. logNetwork->trace("Creating mapHandler: %d ms", CSH->th->getDiff());
  277. CGI->mh->init();
  278. logNetwork->trace("Initializing mapHandler (together): %d ms", CSH->th->getDiff());
  279. }
  280. pathInfo = make_unique<CPathsInfo>(getMapSize());
  281. }
  282. void CClient::initPlayerInterfaces()
  283. {
  284. for(auto & elem : CSH->si->playerInfos)
  285. {
  286. PlayerColor color = elem.first;
  287. if(!vstd::contains(CSH->getAllClientPlayers(CSH->c->connectionID), color))
  288. continue;
  289. if(vstd::contains(playerint, color))
  290. continue;
  291. logNetwork->trace("Preparing interface for player %s", color.getStr());
  292. if(elem.second.isControlledByAI())
  293. {
  294. auto AiToGive = aiNameForPlayer(elem.second, false);
  295. logNetwork->info("Player %s will be lead by %s", color, AiToGive);
  296. installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color);
  297. }
  298. else
  299. {
  300. installNewPlayerInterface(std::make_shared<CPlayerInterface>(color), color);
  301. }
  302. }
  303. if(settings["session"]["spectate"].Bool())
  304. {
  305. installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
  306. }
  307. if(CSH->getAllClientPlayers(CSH->c->connectionID).count(PlayerColor::NEUTRAL))
  308. installNewBattleInterface(CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String()), PlayerColor::NEUTRAL);
  309. logNetwork->trace("Initialized player interfaces %d ms", CSH->th->getDiff());
  310. }
  311. std::string CClient::aiNameForPlayer(const PlayerSettings & ps, bool battleAI)
  312. {
  313. if(ps.name.size())
  314. {
  315. const boost::filesystem::path aiPath = VCMIDirs::get().fullLibraryPath("AI", ps.name);
  316. if(boost::filesystem::exists(aiPath))
  317. return ps.name;
  318. }
  319. return aiNameForPlayer(battleAI);
  320. }
  321. std::string CClient::aiNameForPlayer(bool battleAI)
  322. {
  323. const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
  324. std::string goodAI = battleAI ? settings["server"]["neutralAI"].String() : settings["server"]["playerAI"].String();
  325. std::string badAI = battleAI ? "StupidAI" : "EmptyAI";
  326. //TODO what about human players
  327. if(battleints.size() >= sensibleAILimit)
  328. return badAI;
  329. return goodAI;
  330. }
  331. void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb)
  332. {
  333. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  334. PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
  335. if(!color)
  336. privilegedGameEventReceivers.push_back(gameInterface);
  337. playerint[colorUsed] = gameInterface;
  338. logGlobal->trace("\tInitializing the interface for player %s", colorUsed);
  339. auto cb = std::make_shared<CCallback>(gs, color, this);
  340. callbacks[colorUsed] = cb;
  341. battleCallbacks[colorUsed] = cb;
  342. gameInterface->init(cb);
  343. installNewBattleInterface(gameInterface, color, battlecb);
  344. }
  345. void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback)
  346. {
  347. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  348. PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
  349. if(!color)
  350. privilegedBattleEventReceivers.push_back(battleInterface);
  351. battleints[colorUsed] = battleInterface;
  352. if(needCallback)
  353. {
  354. logGlobal->trace("\tInitializing the battle interface for player %s", *color);
  355. auto cbc = std::make_shared<CBattleCallback>(color, this);
  356. battleCallbacks[colorUsed] = cbc;
  357. battleInterface->init(cbc);
  358. }
  359. }
  360. void CClient::handlePack(CPack * pack)
  361. {
  362. CBaseForCLApply * apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
  363. if(apply)
  364. {
  365. boost::unique_lock<boost::recursive_mutex> guiLock(*CPlayerInterface::pim);
  366. apply->applyOnClBefore(this, pack);
  367. logNetwork->trace("\tMade first apply on cl: %s", typeList.getTypeInfo(pack)->name());
  368. gs->apply(pack);
  369. logNetwork->trace("\tApplied on gs: %s", typeList.getTypeInfo(pack)->name());
  370. apply->applyOnClAfter(this, pack);
  371. logNetwork->trace("\tMade second apply on cl: %s", typeList.getTypeInfo(pack)->name());
  372. }
  373. else
  374. {
  375. logNetwork->error("Message %s cannot be applied, cannot find applier!", typeList.getTypeInfo(pack)->name());
  376. }
  377. delete pack;
  378. }
  379. void CClient::commitPackage(CPackForClient * pack)
  380. {
  381. CommitPackage cp;
  382. cp.freePack = false;
  383. cp.packToCommit = pack;
  384. sendRequest(&cp, PlayerColor::NEUTRAL);
  385. }
  386. int CClient::sendRequest(const CPackForServer * request, PlayerColor player)
  387. {
  388. static ui32 requestCounter = 0;
  389. ui32 requestID = requestCounter++;
  390. logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(*request).name(), requestID);
  391. waitingRequest.pushBack(requestID);
  392. request->requestID = requestID;
  393. request->player = player;
  394. CSH->c->sendPack(request);
  395. if(vstd::contains(playerint, player))
  396. playerint[player]->requestSent(request, requestID);
  397. return requestID;
  398. }
  399. void CClient::battleStarted(const BattleInfo * info)
  400. {
  401. for(auto & battleCb : battleCallbacks)
  402. {
  403. if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
  404. || battleCb.first >= PlayerColor::PLAYER_LIMIT)
  405. {
  406. battleCb.second->setBattle(info);
  407. }
  408. }
  409. // for(ui8 side : info->sides)
  410. // if(battleCallbacks.count(side))
  411. // battleCallbacks[side]->setBattle(info);
  412. std::shared_ptr<CPlayerInterface> att, def;
  413. auto & leftSide = info->sides[0], & rightSide = info->sides[1];
  414. //If quick combat is not, do not prepare interfaces for battleint
  415. if(!settings["adventure"]["quickCombat"].Bool())
  416. {
  417. if(vstd::contains(playerint, leftSide.color) && playerint[leftSide.color]->human)
  418. att = std::dynamic_pointer_cast<CPlayerInterface>(playerint[leftSide.color]);
  419. if(vstd::contains(playerint, rightSide.color) && playerint[rightSide.color]->human)
  420. def = std::dynamic_pointer_cast<CPlayerInterface>(playerint[rightSide.color]);
  421. }
  422. if(!settings["session"]["headless"].Bool())
  423. {
  424. Rect battleIntRect((screen->w - 800)/2, (screen->h - 600)/2, 800, 600);
  425. if(!!att || !!def)
  426. {
  427. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  428. GH.pushIntT<CBattleInterface>(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, battleIntRect, att, def);
  429. }
  430. else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  431. {
  432. //TODO: This certainly need improvement
  433. auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
  434. spectratorInt->cb->setBattle(info);
  435. boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
  436. GH.pushIntT<CBattleInterface>(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, battleIntRect, att, def, spectratorInt);
  437. }
  438. }
  439. auto callBattleStart = [&](PlayerColor color, ui8 side)
  440. {
  441. if(vstd::contains(battleints, color))
  442. battleints[color]->battleStart(leftSide.armyObject, rightSide.armyObject, info->tile, leftSide.hero, rightSide.hero, side);
  443. };
  444. callBattleStart(leftSide.color, 0);
  445. callBattleStart(rightSide.color, 1);
  446. callBattleStart(PlayerColor::UNFLAGGABLE, 1);
  447. if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  448. callBattleStart(PlayerColor::SPECTATOR, 1);
  449. if(info->tacticDistance && vstd::contains(battleints, info->sides[info->tacticsSide].color))
  450. {
  451. boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]);
  452. }
  453. }
  454. void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt)
  455. {
  456. setThreadName("CClient::commenceTacticPhaseForInt");
  457. try
  458. {
  459. battleInt->yourTacticPhase(gs->curB->tacticDistance);
  460. if(gs && !!gs->curB && gs->curB->tacticDistance) //while awaiting for end of tactics phase, many things can happen (end of battle... or game)
  461. {
  462. MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID).get()));
  463. sendRequest(&ma, battleInt->playerID);
  464. }
  465. }
  466. catch(...)
  467. {
  468. handleException();
  469. }
  470. }
  471. void CClient::battleFinished()
  472. {
  473. stopAllBattleActions();
  474. for(auto & side : gs->curB->sides)
  475. if(battleCallbacks.count(side.color))
  476. battleCallbacks[side.color]->setBattle(nullptr);
  477. if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
  478. battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
  479. }
  480. void CClient::startPlayerBattleAction(PlayerColor color)
  481. {
  482. stopPlayerBattleAction(color);
  483. if(vstd::contains(battleints, color))
  484. {
  485. auto thread = std::make_shared<boost::thread>(std::bind(&CClient::waitForMoveAndSend, this, color));
  486. playerActionThreads[color] = thread;
  487. }
  488. }
  489. void CClient::stopPlayerBattleAction(PlayerColor color)
  490. {
  491. if(vstd::contains(playerActionThreads, color))
  492. {
  493. auto thread = playerActionThreads.at(color);
  494. if(thread->joinable())
  495. {
  496. thread->interrupt();
  497. thread->join();
  498. }
  499. playerActionThreads.erase(color);
  500. }
  501. }
  502. void CClient::stopAllBattleActions()
  503. {
  504. while(!playerActionThreads.empty())
  505. stopPlayerBattleAction(playerActionThreads.begin()->first);
  506. }
  507. void CClient::waitForMoveAndSend(PlayerColor color)
  508. {
  509. try
  510. {
  511. setThreadName("CClient::waitForMoveAndSend");
  512. assert(vstd::contains(battleints, color));
  513. BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
  514. if(ba.actionType != EActionType::CANCEL)
  515. {
  516. logNetwork->trace("Send battle action to server: %s", ba.toString());
  517. MakeAction temp_action(ba);
  518. sendRequest(&temp_action, color);
  519. }
  520. }
  521. catch(boost::thread_interrupted &)
  522. {
  523. logNetwork->debug("Wait for move thread was interrupted and no action will be send. Was a battle ended by spell?");
  524. }
  525. catch(...)
  526. {
  527. handleException();
  528. }
  529. }
  530. void CClient::invalidatePaths()
  531. {
  532. // turn pathfinding info into invalid. It will be regenerated later
  533. boost::unique_lock<boost::mutex> pathLock(pathInfo->pathMx);
  534. pathInfo->hero = nullptr;
  535. }
  536. const CPathsInfo * CClient::getPathsInfo(const CGHeroInstance * h)
  537. {
  538. assert(h);
  539. boost::unique_lock<boost::mutex> pathLock(pathInfo->pathMx);
  540. if(pathInfo->hero != h)
  541. {
  542. gs->calculatePaths(h, *pathInfo.get());
  543. }
  544. return pathInfo.get();
  545. }
  546. void CClient::calculatePaths(std::shared_ptr<CPathfinderConfig> config, const CGHeroInstance * hero)
  547. {
  548. boost::unique_lock<boost::mutex> pathLock(config->nodeStorage->getMutex());
  549. gs->calculatePaths(config, hero);
  550. }
  551. PlayerColor CClient::getLocalPlayer() const
  552. {
  553. if(LOCPLINT)
  554. return LOCPLINT->playerID;
  555. return getCurrentPlayer();
  556. }
  557. #ifdef VCMI_ANDROID
  558. extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerReady(JNIEnv * env, jobject cls)
  559. {
  560. logNetwork->info("Received server ready signal");
  561. androidTestServerReadyFlag.store(true);
  562. }
  563. extern "C" JNIEXPORT bool JNICALL Java_eu_vcmi_vcmi_NativeMethods_tryToSaveTheGame(JNIEnv * env, jobject cls)
  564. {
  565. logGlobal->info("Received emergency save game request");
  566. if(!LOCPLINT || !LOCPLINT->cb)
  567. {
  568. return false;
  569. }
  570. LOCPLINT->cb->save("Saves/_Android_Autosave");
  571. return true;
  572. }
  573. #endif