Client.cpp 32 KB

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