浏览代码

Merge branch 'develop' into autosave

Michael 2 年之前
父节点
当前提交
b22a9ff2d0
共有 100 个文件被更改,包括 325 次插入389 次删除
  1. 11 9
      AI/Nullkiller/AIGateway.cpp
  2. 0 4
      AI/Nullkiller/AIUtility.cpp
  3. 4 2
      AI/Nullkiller/AIUtility.h
  4. 0 3
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  5. 0 3
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  6. 0 3
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  7. 0 3
      AI/Nullkiller/Behaviors/ClusterBehavior.cpp
  8. 0 3
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  9. 0 3
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  10. 0 3
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  11. 0 3
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  12. 0 3
      AI/Nullkiller/Engine/DeepDecomposer.cpp
  13. 0 2
      AI/Nullkiller/Engine/FuzzyEngines.cpp
  14. 1 4
      AI/Nullkiller/Engine/Nullkiller.cpp
  15. 0 3
      AI/Nullkiller/Goals/AbstractGoal.cpp
  16. 0 3
      AI/Nullkiller/Goals/AdventureSpellCast.cpp
  17. 0 3
      AI/Nullkiller/Goals/BuildBoat.cpp
  18. 0 4
      AI/Nullkiller/Goals/BuildThis.cpp
  19. 0 3
      AI/Nullkiller/Goals/BuyArmy.cpp
  20. 0 2
      AI/Nullkiller/Goals/CaptureObject.cpp
  21. 0 3
      AI/Nullkiller/Goals/CompleteQuest.cpp
  22. 0 3
      AI/Nullkiller/Goals/Composition.cpp
  23. 0 3
      AI/Nullkiller/Goals/DigAtTile.cpp
  24. 0 3
      AI/Nullkiller/Goals/DismissHero.cpp
  25. 0 3
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp
  26. 0 3
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  27. 0 3
      AI/Nullkiller/Goals/RecruitHero.cpp
  28. 0 3
      AI/Nullkiller/Goals/SaveResources.cpp
  29. 0 3
      AI/Nullkiller/Markers/ArmyUpgrade.cpp
  30. 0 3
      AI/Nullkiller/Markers/HeroExchange.cpp
  31. 0 3
      AI/Nullkiller/Markers/UnlockCluster.cpp
  32. 0 3
      AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
  33. 2 5
      AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp
  34. 0 3
      AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp
  35. 0 3
      AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp
  36. 1 4
      AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp
  37. 1 1
      AI/StupidAI/StupidAI.cpp
  38. 0 4
      AI/VCAI/AIUtility.cpp
  39. 4 2
      AI/VCAI/AIUtility.h
  40. 0 3
      AI/VCAI/FuzzyEngines.cpp
  41. 1 4
      AI/VCAI/FuzzyHelper.cpp
  42. 2 0
      AI/VCAI/FuzzyHelper.h
  43. 0 4
      AI/VCAI/Goals/AbstractGoal.cpp
  44. 0 4
      AI/VCAI/Goals/AdventureSpellCast.cpp
  45. 0 5
      AI/VCAI/Goals/Build.cpp
  46. 0 4
      AI/VCAI/Goals/BuildBoat.cpp
  47. 0 5
      AI/VCAI/Goals/BuildThis.cpp
  48. 0 5
      AI/VCAI/Goals/BuyArmy.cpp
  49. 0 5
      AI/VCAI/Goals/ClearWayTo.cpp
  50. 0 5
      AI/VCAI/Goals/CollectRes.cpp
  51. 0 4
      AI/VCAI/Goals/CompleteQuest.cpp
  52. 0 5
      AI/VCAI/Goals/Conquer.cpp
  53. 0 5
      AI/VCAI/Goals/DigAtTile.cpp
  54. 2 6
      AI/VCAI/Goals/Explore.cpp
  55. 0 4
      AI/VCAI/Goals/FindObj.cpp
  56. 0 5
      AI/VCAI/Goals/GatherArmy.cpp
  57. 0 5
      AI/VCAI/Goals/GatherTroops.cpp
  58. 1 6
      AI/VCAI/Goals/GetArtOfType.cpp
  59. 0 5
      AI/VCAI/Goals/RecruitHero.cpp
  60. 0 4
      AI/VCAI/Goals/VisitHero.cpp
  61. 0 5
      AI/VCAI/Goals/VisitObj.cpp
  62. 0 5
      AI/VCAI/Goals/VisitTile.cpp
  63. 0 5
      AI/VCAI/Goals/Win.cpp
  64. 11 9
      AI/VCAI/VCAI.cpp
  65. 6 3
      Global.h
  66. 11 8
      client/CMT.cpp
  67. 4 4
      client/CMusicHandler.cpp
  68. 10 4
      client/CPlayerInterface.cpp
  69. 13 12
      client/CServerHandler.cpp
  70. 1 1
      client/CVideoHandler.cpp
  71. 4 1
      client/Client.cpp
  72. 1 0
      client/LobbyClientNetPackVisitors.h
  73. 24 6
      client/NetPacksLobbyClient.cpp
  74. 2 0
      client/adventureMap/CInGameConsole.cpp
  75. 7 22
      client/battle/BattleInterface.cpp
  76. 1 1
      client/battle/BattleWindow.cpp
  77. 0 8
      client/eventsSDL/InputSourceKeyboard.cpp
  78. 6 20
      client/gui/CGuiHandler.cpp
  79. 0 2
      client/gui/CGuiHandler.h
  80. 51 19
      client/mainmenu/CMainMenu.cpp
  81. 9 6
      client/mainmenu/CMainMenu.h
  82. 1 1
      client/mapView/mapHandler.cpp
  83. 1 1
      client/windows/CSpellWindow.cpp
  84. 5 1
      config/mainmenu.json
  85. 2 2
      lib/CConsoleHandler.cpp
  86. 2 7
      lib/CRandomGenerator.cpp
  87. 0 1
      lib/CRandomGenerator.h
  88. 5 3
      lib/CThreadHelper.cpp
  89. 4 4
      lib/CThreadHelper.h
  90. 51 0
      lib/LoadProgress.cpp
  91. 24 1
      lib/LoadProgress.h
  92. 1 0
      lib/NetPackVisitor.h
  93. 5 0
      lib/NetPacksLib.cpp
  94. 12 0
      lib/NetPacksLobby.h
  95. 10 3
      lib/battle/BattleAction.cpp
  96. 1 0
      lib/battle/BattleAction.h
  97. 5 3
      lib/gameState/CGameState.cpp
  98. 3 2
      lib/gameState/CGameState.h
  99. 1 1
      lib/logging/CLogger.cpp
  100. 1 1
      lib/mapObjects/CGCreature.cpp

+ 11 - 9
AI/Nullkiller/AIGateway.cpp

@@ -34,26 +34,26 @@ const float RETREAT_THRESHOLD = 0.3f;
 const double RETREAT_ABSOLUTE_THRESHOLD = 10000.;
 
 //one thread may be turn of AI and another will be handling a side effect for AI2
-boost::thread_specific_ptr<CCallback> cb;
-boost::thread_specific_ptr<AIGateway> ai;
+thread_local CCallback * cb = nullptr;
+thread_local AIGateway * ai = nullptr;
 
 //helper RAII to manage global ai/cb ptrs
 struct SetGlobalState
 {
 	SetGlobalState(AIGateway * AI)
 	{
-		assert(!ai.get());
-		assert(!cb.get());
+		assert(!ai);
+		assert(!cb);
 
-		ai.reset(AI);
-		cb.reset(AI->myCb.get());
+		ai = AI;
+		cb = AI->myCb.get();
 	}
 	~SetGlobalState()
 	{
 		//TODO: how to handle rm? shouldn't be called after ai is destroyed, hopefully
 		//TODO: to ensure that, make rm unique_ptr
-		ai.release();
-		cb.release();
+		ai = nullptr;
+		cb = nullptr;
 	}
 };
 
@@ -1472,6 +1472,8 @@ void AIGateway::requestActionASAP(std::function<void()> whatToDo)
 		boost::shared_lock<boost::shared_mutex> gsLock(CGameState::mutex);
 		whatToDo();
 	});
+
+	newThread.detach();
 }
 
 void AIGateway::lostHero(HeroPtr h)
@@ -1605,7 +1607,7 @@ void AIStatus::waitTillFree()
 {
 	boost::unique_lock<boost::mutex> lock(mx);
 	while(battle != NO_BATTLE || !remainingQueries.empty() || !objectsBeingVisited.empty() || ongoingHeroMovement)
-		cv.timed_wait(lock, boost::posix_time::milliseconds(10));
+		cv.wait_for(lock, boost::chrono::milliseconds(10));
 }
 
 bool AIStatus::haveTurn()

+ 0 - 4
AI/Nullkiller/AIUtility.cpp

@@ -25,10 +25,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<AIGateway> ai;
-
-//extern static const int3 dirs[8];
-
 const CGObjectInstance * ObjectIdRef::operator->() const
 {
 	return cb->getObj(id, false);

+ 4 - 2
AI/Nullkiller/AIUtility.h

@@ -57,6 +57,7 @@ using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
 namespace NKAI
 {
 struct creInfo;
+class AIGateway;
 class Nullkiller;
 
 const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
@@ -67,7 +68,8 @@ const int ALLOWED_ROAMING_HEROES = 8;
 extern const float SAFE_ATTACK_CONSTANT;
 extern const int GOLD_RESERVE;
 
-extern boost::thread_specific_ptr<CCallback> cb;
+extern thread_local CCallback * cb;
+extern thread_local AIGateway * ai;
 
 enum HeroRole
 {
@@ -201,7 +203,7 @@ void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retriev
 template<class Func>
 void foreach_neighbour(const int3 & pos, const Func & foo)
 {
-	CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
+	CCallback * cbp = cb; // avoid costly retrieval of thread-specific pointer
 	for(const int3 & dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;

+ 0 - 3
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -20,9 +20,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string BuildingBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string BuyArmyBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -19,9 +19,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 template <typename T>

+ 0 - 3
AI/Nullkiller/Behaviors/ClusterBehavior.cpp

@@ -19,9 +19,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string ClusterBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -25,9 +25,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 const float TREAT_IGNORE_RATIO = 2;
 
 using namespace Goals;

+ 0 - 3
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -23,9 +23,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string GatherArmyBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string RecruitHeroBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -21,9 +21,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string StartupBehavior::toString() const

+ 0 - 3
AI/Nullkiller/Engine/DeepDecomposer.cpp

@@ -24,9 +24,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 void DeepDecomposer::reset()

+ 0 - 2
AI/Nullkiller/Engine/FuzzyEngines.cpp

@@ -20,8 +20,6 @@ namespace NKAI
 #define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
 #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
 
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 engineBase::engineBase()
 {
 	rules = new fl::RuleBlock();

+ 1 - 4
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -24,9 +24,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 #if NKAI_TRACE_LEVEL >= 1
@@ -341,7 +338,7 @@ void Nullkiller::executeTask(Goals::TTask task)
 
 	try
 	{
-		task->accept(ai.get());
+		task->accept(ai);
 		logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
 	}
 	catch(goalFulfilledException &)

+ 0 - 3
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -15,9 +15,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 TSubgoal Goals::sptr(const AbstractGoal & tmp)

+ 0 - 3
AI/Nullkiller/Goals/AdventureSpellCast.cpp

@@ -14,9 +14,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const

+ 0 - 3
AI/Nullkiller/Goals/BuildBoat.cpp

@@ -15,9 +15,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool BuildBoat::operator==(const BuildBoat & other) const

+ 0 - 4
AI/Nullkiller/Goals/BuildThis.cpp

@@ -17,12 +17,8 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
-
 BuildThis::BuildThis(BuildingID Bid, const CGTownInstance * tid)
 	: ElementarGoal(Goals::BUILD_STRUCTURE)
 {

+ 0 - 3
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool BuyArmy::operator==(const BuyArmy & other) const

+ 0 - 2
AI/Nullkiller/Goals/CaptureObject.cpp

@@ -18,8 +18,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-
 using namespace Goals;
 
 bool CaptureObject::operator==(const CaptureObject & other) const

+ 0 - 3
AI/Nullkiller/Goals/CompleteQuest.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool isKeyMaster(const QuestInfo & q)

+ 0 - 3
AI/Nullkiller/Goals/Composition.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool Composition::operator==(const Composition & other) const

+ 0 - 3
AI/Nullkiller/Goals/DigAtTile.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool DigAtTile::operator==(const DigAtTile & other) const

+ 0 - 3
AI/Nullkiller/Goals/DismissHero.cpp

@@ -14,9 +14,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool DismissHero::operator==(const DismissHero & other) const

+ 0 - 3
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(

+ 0 - 3
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -15,9 +15,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)

+ 0 - 3
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 std::string RecruitHero::toString() const

+ 0 - 3
AI/Nullkiller/Goals/SaveResources.cpp

@@ -15,9 +15,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool SaveResources::operator==(const SaveResources & other) const

+ 0 - 3
AI/Nullkiller/Markers/ArmyUpgrade.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 ArmyUpgrade::ArmyUpgrade(const AIPath & upgradePath, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade)

+ 0 - 3
AI/Nullkiller/Markers/HeroExchange.cpp

@@ -17,9 +17,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool HeroExchange::operator==(const HeroExchange & other) const

+ 0 - 3
AI/Nullkiller/Markers/UnlockCluster.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 using namespace Goals;
 
 bool UnlockCluster::operator==(const UnlockCluster & other) const

+ 0 - 3
AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 namespace AIPathfinding
 {
 	void BattleAction::execute(const CGHeroInstance * hero) const

+ 2 - 5
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

@@ -20,14 +20,11 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 namespace AIPathfinding
 {
 	void BuildBoatAction::execute(const CGHeroInstance * hero) const
 	{
-		return Goals::BuildBoat(shipyard).accept(ai.get());
+		return Goals::BuildBoat(shipyard).accept(ai);
 	}
 
 	Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const
@@ -80,7 +77,7 @@ namespace AIPathfinding
 
 	void SummonBoatAction::execute(const CGHeroInstance * hero) const
 	{
-		Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai.get());
+		Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
 	}
 
 	const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const

+ 0 - 3
AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 namespace AIPathfinding
 {
 	void BuyArmyAction::execute(const CGHeroInstance * hero) const

+ 0 - 3
AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp

@@ -16,9 +16,6 @@
 namespace NKAI
 {
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 namespace AIPathfinding
 {
 	bool QuestAction::canAct(const AIPathNode * node) const

+ 1 - 4
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp

@@ -18,9 +18,6 @@ namespace NKAI
 
 using namespace AIPathfinding;
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-
 void TownPortalAction::execute(const CGHeroInstance * hero) const
 {
 	auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
@@ -28,7 +25,7 @@ void TownPortalAction::execute(const CGHeroInstance * hero) const
 	goal.town = target;
 	goal.tile = target->visitablePos();
 
-	goal.accept(ai.get());
+	goal.accept(ai);
 }
 
 std::string TownPortalAction::toString() const

+ 1 - 1
AI/StupidAI/StupidAI.cpp

@@ -108,7 +108,7 @@ void CStupidAI::yourTacticPhase(int distance)
 
 void CStupidAI::activeStack( const CStack * stack )
 {
-	//boost::this_thread::sleep(boost::posix_time::seconds(2));
+	//boost::this_thread::sleep_for(boost::chrono::seconds(2));
 	print("activeStack called for " + stack->nodeName());
 	ReachabilityInfo dists = cb->getReachability(stack);
 	std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;

+ 0 - 4
AI/VCAI/AIUtility.cpp

@@ -21,12 +21,8 @@
 #include "../../lib/mapObjects/CQuest.h"
 #include "../../lib/mapping/CMapDefines.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
 extern FuzzyHelper * fh;
 
-//extern static const int3 dirs[8];
-
 const CGObjectInstance * ObjectIdRef::operator->() const
 {
 	return cb->getObj(id, false);

+ 4 - 2
AI/VCAI/AIUtility.h

@@ -18,6 +18,7 @@
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../CCallback.h"
 
+class VCAI;
 class CCallback;
 struct creInfo;
 
@@ -33,7 +34,8 @@ const int ALLOWED_ROAMING_HEROES = 8;
 extern const double SAFE_ATTACK_CONSTANT;
 extern const int GOLD_RESERVE;
 
-extern boost::thread_specific_ptr<CCallback> cb;
+extern thread_local CCallback * cb;
+extern thread_local VCAI * ai;
 
 //provisional class for AI to store a reference to an owned hero object
 //checks if it's valid on access, should be used in place of const CGHeroInstance*
@@ -192,7 +194,7 @@ void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retriev
 template<class Func>
 void foreach_neighbour(const int3 & pos, const Func & foo)
 {
-	CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
+	CCallback * cbp = cb; // avoid costly retrieval of thread-specific pointer
 	for(const int3 & dir : int3::getDirs())
 	{
 		const int3 n = pos + dir;

+ 0 - 3
AI/VCAI/FuzzyEngines.cpp

@@ -18,9 +18,6 @@
 #define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
 #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
 
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 engineBase::engineBase()
 {
 	rules = new fl::RuleBlock();

+ 1 - 4
AI/VCAI/FuzzyHelper.cpp

@@ -23,9 +23,6 @@
 
 FuzzyHelper * fh;
 
-extern boost::thread_specific_ptr<VCAI> ai;
-extern boost::thread_specific_ptr<CCallback> cb;
-
 Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec)
 {
 	if(vec.empty())
@@ -216,7 +213,7 @@ void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pa
 
 ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
 {
-	return evaluateDanger(tile, visitor, ai.get());
+	return evaluateDanger(tile, visitor, ai);
 }
 
 ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai)

+ 2 - 0
AI/VCAI/FuzzyHelper.h

@@ -51,3 +51,5 @@ public:
 	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai);
 	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
 };
+
+extern FuzzyHelper * fh;

+ 0 - 4
AI/VCAI/Goals/AbstractGoal.cpp

@@ -16,10 +16,6 @@
 #include "../BuildingManager.h"
 #include "../../../lib/StringConstants.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 TSubgoal Goals::sptr(const AbstractGoal & tmp)

+ 0 - 4
AI/VCAI/Goals/AdventureSpellCast.cpp

@@ -14,10 +14,6 @@
 #include "../AIhelper.h"
 #include "../../../lib/mapObjects/CGTownInstance.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const

+ 0 - 5
AI/VCAI/Goals/Build.cpp

@@ -19,11 +19,6 @@
 #include "../../../lib/mapObjects/CGTownInstance.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 TGoalVec Build::getAllPossibleSubgoals()

+ 0 - 4
AI/VCAI/Goals/BuildBoat.cpp

@@ -13,10 +13,6 @@
 #include "../FuzzyHelper.h"
 #include "../AIhelper.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool BuildBoat::operator==(const BuildBoat & other) const

+ 0 - 5
AI/VCAI/Goals/BuildThis.cpp

@@ -18,11 +18,6 @@
 #include "../../../lib/mapObjects/CGTownInstance.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool BuildThis::operator==(const BuildThis & other) const

+ 0 - 5
AI/VCAI/Goals/BuyArmy.cpp

@@ -13,11 +13,6 @@
 #include "../AIhelper.h"
 #include "../../../lib/mapObjects/CGTownInstance.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool BuyArmy::operator==(const BuyArmy & other) const

+ 0 - 5
AI/VCAI/Goals/ClearWayTo.cpp

@@ -16,11 +16,6 @@
 #include "../FuzzyHelper.h"
 #include "../AIhelper.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool ClearWayTo::operator==(const ClearWayTo & other) const

+ 0 - 5
AI/VCAI/Goals/CollectRes.cpp

@@ -18,11 +18,6 @@
 #include "../../../lib/mapObjects/CGMarket.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool CollectRes::operator==(const CollectRes & other) const

+ 0 - 4
AI/VCAI/Goals/CompleteQuest.cpp

@@ -14,10 +14,6 @@
 #include "../AIhelper.h"
 #include "../../../lib/mapObjects/CQuest.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool CompleteQuest::operator==(const CompleteQuest & other) const

+ 0 - 5
AI/VCAI/Goals/Conquer.cpp

@@ -17,11 +17,6 @@
 #include "../BuildingManager.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool Conquer::operator==(const Conquer & other) const

+ 0 - 5
AI/VCAI/Goals/DigAtTile.cpp

@@ -13,11 +13,6 @@
 #include "../VCAI.h"
 #include "../AIUtility.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool DigAtTile::operator==(const DigAtTile & other) const

+ 2 - 6
AI/VCAI/Goals/Explore.cpp

@@ -18,10 +18,6 @@
 #include "../../../lib/StringConstants.h"
 #include "../../../lib/CPlayerState.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 namespace Goals
@@ -41,8 +37,8 @@ namespace Goals
 
 		ExplorationHelper(HeroPtr h, bool gatherArmy)
 		{
-			cbp = cb.get();
-			aip = ai.get();
+			cbp = cb;
+			aip = ai;
 			hero = h;
 			ts = cbp->getPlayerTeam(ai->playerID);
 			sightRadius = hero->getSightRadius();

+ 0 - 4
AI/VCAI/Goals/FindObj.cpp

@@ -14,10 +14,6 @@
 #include "../VCAI.h"
 #include "../AIUtility.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool FindObj::operator==(const FindObj & other) const

+ 0 - 5
AI/VCAI/Goals/GatherArmy.cpp

@@ -18,11 +18,6 @@
 #include "../../../lib/mapObjects/CGTownInstance.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool GatherArmy::operator==(const GatherArmy & other) const

+ 0 - 5
AI/VCAI/Goals/GatherTroops.cpp

@@ -18,11 +18,6 @@
 #include "../../../lib/mapObjects/CGTownInstance.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool GatherTroops::operator==(const GatherTroops & other) const

+ 1 - 6
AI/VCAI/Goals/GetArtOfType.cpp

@@ -13,11 +13,6 @@
 #include "../VCAI.h"
 #include "../AIUtility.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool GetArtOfType::operator==(const GetArtOfType & other) const
@@ -28,4 +23,4 @@ bool GetArtOfType::operator==(const GetArtOfType & other) const
 TSubgoal GetArtOfType::whatToDoToAchieve()
 {
 	return sptr(FindObj(Obj::ARTIFACT, aid));
-}
+}

+ 0 - 5
AI/VCAI/Goals/RecruitHero.cpp

@@ -17,11 +17,6 @@
 #include "../BuildingManager.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 TSubgoal RecruitHero::whatToDoToAchieve()

+ 0 - 4
AI/VCAI/Goals/VisitHero.cpp

@@ -18,10 +18,6 @@
 #include "../ResourceManager.h"
 #include "../BuildingManager.h"
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool VisitHero::operator==(const VisitHero & other) const

+ 0 - 5
AI/VCAI/Goals/VisitObj.cpp

@@ -17,11 +17,6 @@
 #include "../BuildingManager.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool VisitObj::operator==(const VisitObj & other) const

+ 0 - 5
AI/VCAI/Goals/VisitTile.cpp

@@ -17,11 +17,6 @@
 #include "../BuildingManager.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 bool VisitTile::operator==(const VisitTile & other) const

+ 0 - 5
AI/VCAI/Goals/Win.cpp

@@ -19,11 +19,6 @@
 #include "../../../lib/mapObjects/CGTownInstance.h"
 #include "../../../lib/StringConstants.h"
 
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
 using namespace Goals;
 
 TSubgoal Win::whatToDoToAchieve()

+ 11 - 9
AI/VCAI/VCAI.cpp

@@ -38,8 +38,8 @@ extern FuzzyHelper * fh;
 const double SAFE_ATTACK_CONSTANT = 1.5;
 
 //one thread may be turn of AI and another will be handling a side effect for AI2
-boost::thread_specific_ptr<CCallback> cb;
-boost::thread_specific_ptr<VCAI> ai;
+thread_local CCallback * cb = nullptr;
+thread_local VCAI * ai = nullptr;
 
 //std::map<int, std::map<int, int> > HeroView::infosCount;
 
@@ -48,18 +48,18 @@ struct SetGlobalState
 {
 	SetGlobalState(VCAI * AI)
 	{
-		assert(!ai.get());
-		assert(!cb.get());
+		assert(!ai);
+		assert(!cb);
 
-		ai.reset(AI);
-		cb.reset(AI->myCb.get());
+		ai = AI;
+		cb = AI->myCb.get();
 	}
 	~SetGlobalState()
 	{
 		//TODO: how to handle rm? shouldn't be called after ai is destroyed, hopefully
 		//TODO: to ensure that, make rm unique_ptr
-		ai.release();
-		cb.release();
+		ai = nullptr;
+		cb = nullptr;
 	}
 };
 
@@ -2497,6 +2497,8 @@ void VCAI::requestActionASAP(std::function<void()> whatToDo)
 		boost::shared_lock<boost::shared_mutex> gsLock(CGameState::mutex);
 		whatToDo();
 	});
+
+	newThread.detach();
 }
 
 void VCAI::lostHero(HeroPtr h)
@@ -2675,7 +2677,7 @@ void AIStatus::waitTillFree()
 {
 	boost::unique_lock<boost::mutex> lock(mx);
 	while(battle != NO_BATTLE || !remainingQueries.empty() || !objectsBeingVisited.empty() || ongoingHeroMovement)
-		cv.timed_wait(lock, boost::posix_time::milliseconds(100));
+		cv.wait_for(lock, boost::chrono::milliseconds(100));
 }
 
 bool AIStatus::haveTurn()

+ 6 - 3
Global.h

@@ -149,8 +149,8 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/algorithm/string.hpp>
 #include <boost/crc.hpp>
 #include <boost/current_function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/date_time/posix_time/posix_time_io.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/time_formatters.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
 #include <boost/filesystem/path.hpp>
@@ -165,7 +165,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/range/adaptor/filtered.hpp>
 #include <boost/range/adaptor/reversed.hpp>
 #include <boost/range/algorithm.hpp>
-#include <boost/thread.hpp>
+#include <boost/thread/thread_only.hpp>
+#include <boost/thread/shared_mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/thread/once.hpp>
 
 #ifndef M_PI
 #  define M_PI 3.14159265358979323846

+ 11 - 8
client/CMT.cpp

@@ -27,11 +27,12 @@
 #include "render/IScreenHandler.h"
 #include "render/Graphics.h"
 
-#include "../lib/filesystem/Filesystem.h"
+#include "../lib/CConfigHandler.h"
 #include "../lib/CGeneralTextHandler.h"
+#include "../lib/CThreadHelper.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/VCMI_Lib.h"
-#include "../lib/CConfigHandler.h"
+#include "../lib/filesystem/Filesystem.h"
 
 #include "../lib/logging/CBasicLogConfigurator.h"
 
@@ -53,8 +54,6 @@
 namespace po = boost::program_options;
 namespace po_style = boost::program_options::command_line_style;
 
-extern boost::thread_specific_ptr<bool> inGuiThread;
-
 static po::variables_map vm;
 
 #ifndef VCMI_IOS
@@ -297,7 +296,11 @@ int main(int argc, char * argv[])
 
 #ifndef VCMI_NO_THREADED_LOAD
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
-	boost::thread loading(init);
+	boost::thread loading([]()
+	{
+		setThreadName("initialize");
+		init();
+	});
 #else
 	init();
 #endif
@@ -411,7 +414,7 @@ int main(int argc, char * argv[])
 	else
 	{
 		while(true)
-			boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+			boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
 	}
 
 	return 0;
@@ -429,7 +432,7 @@ void playIntro()
 
 static void mainLoop()
 {
-	inGuiThread.reset(new bool(true));
+	setThreadName("MainGUI");
 
 	while(1) //main SDL events loop
 	{
@@ -470,7 +473,7 @@ static void quitApplication()
 
 	vstd::clear_pointer(console);// should be removed after everything else since used by logging
 
-	boost::this_thread::sleep(boost::posix_time::milliseconds(750));//???
+	boost::this_thread::sleep_for(boost::chrono::milliseconds(750));//???
 
 	if(!settings["session"]["headless"].Bool())
 		GH.screenHandler().close();

+ 4 - 4
client/CMusicHandler.cpp

@@ -238,7 +238,7 @@ void CSoundHandler::setChannelVolume(int channel, ui32 percent)
 
 void CSoundHandler::setCallback(int channel, std::function<void()> function)
 {
-	boost::unique_lock lockGuard(mutexCallbacks);
+	boost::mutex::scoped_lock lockGuard(mutexCallbacks);
 
 	auto iter = callbacks.find(channel);
 
@@ -251,7 +251,7 @@ void CSoundHandler::setCallback(int channel, std::function<void()> function)
 
 void CSoundHandler::soundFinishedCallback(int channel)
 {
-	boost::unique_lock lockGuard(mutexCallbacks);
+	boost::mutex::scoped_lock lockGuard(mutexCallbacks);
 
 	if (callbacks.count(channel) == 0)
 		return;
@@ -272,14 +272,14 @@ void CSoundHandler::soundFinishedCallback(int channel)
 
 void CSoundHandler::initCallback(int channel)
 {
-	boost::unique_lock lockGuard(mutexCallbacks);
+	boost::mutex::scoped_lock lockGuard(mutexCallbacks);
 	assert(callbacks.count(channel) == 0);
 	callbacks[channel] = {};
 }
 
 void CSoundHandler::initCallback(int channel, const std::function<void()> & function)
 {
-	boost::unique_lock lockGuard(mutexCallbacks);
+	boost::mutex::scoped_lock lockGuard(mutexCallbacks);
 	assert(callbacks.count(channel) == 0);
 	callbacks[channel].push_back(function);
 }

+ 10 - 4
client/CPlayerInterface.cpp

@@ -76,6 +76,7 @@
 #include "../lib/UnlockGuard.h"
 #include "../lib/RoadHandler.h"
 #include "../lib/TerrainHandler.h"
+#include "../lib/CThreadHelper.h"
 #include "CServerHandler.h"
 // FIXME: only needed for CGameState::mutex
 #include "../lib/gameState/CGameState.h"
@@ -135,7 +136,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player):
 	makingTurn = false;
 	showingDialog = new CondSh<bool>(false);
 	cingconsole = new CInGameConsole();
-	GH.terminate_cond->set(false);
 	firstCall = 1; //if loading will be overwritten in serialize
 	autosaveCount = 0;
 	isAutoFightOn = false;
@@ -654,6 +654,11 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID build
 
 void CPlayerInterface::battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2)
 {
+	// when battle starts, game will send battleStart pack *before* movement confirmation
+	// and since network thread wait for battle intro to play, movement confirmation will only happen after intro
+	// leading to several bugs, such as blocked input during intro
+	stillMoveHero.setn(STOP_MOVE);
+
 	//Don't wait for dialogs when we are non-active hot-seat player
 	if (LOCPLINT == this)
 		waitForAllDialogs();
@@ -1447,7 +1452,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
 		{
 			auto unlockPim = vstd::makeUnlockGuard(*pim);
 			IgnoreEvents ignore(*this);
-			boost::this_thread::sleep(boost::posix_time::milliseconds(focusTime));
+			boost::this_thread::sleep_for(boost::chrono::milliseconds(focusTime));
 		}
 	}
 	CCS->curh->show();
@@ -1591,7 +1596,6 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 		{
 			if(adventureInt)
 			{
-				GH.terminate_cond->setn(true);
 				GH.windows().popWindows(GH.windows().count());
 				adventureInt.reset();
 			}
@@ -1874,7 +1878,7 @@ void CPlayerInterface::waitForAllDialogs(bool unlockPim)
 	while(!dialogs.empty())
 	{
 		auto unlock = vstd::makeUnlockGuardIf(*pim, unlockPim);
-		boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(5));
 	}
 	waitWhileDialog(unlockPim);
 }
@@ -1933,6 +1937,8 @@ void CPlayerInterface::setMovementStatus(bool value)
 
 void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 {
+	setThreadName("doMoveHero");
+
 	int i = 1;
 	auto getObj = [&](int3 coord, bool ignoreHero)
 	{

+ 13 - 12
client/CServerHandler.cpp

@@ -231,7 +231,7 @@ void CServerHandler::startLocalServerAndConnect()
 	while(!androidTestServerReadyFlag.load())
 	{
 		logNetwork->info("still waiting...");
-		boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
 	}
 	logNetwork->info("waiting for server finished...");
 	androidTestServerReadyFlag = false;
@@ -261,7 +261,7 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po
 		catch(std::runtime_error & error)
 		{
 			logNetwork->warn("\nCannot establish connection. %s Retrying in 1 second", error.what());
-			boost::this_thread::sleep(boost::posix_time::seconds(1));
+			boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
 		}
 	}
 
@@ -307,7 +307,7 @@ void CServerHandler::stopServerConnection()
 {
 	if(c->handler)
 	{
-		while(!c->handler->timed_join(boost::posix_time::milliseconds(50)))
+		while(!c->handler->timed_join(boost::chrono::milliseconds(50)))
 			applyPacksOnLobbyScreen();
 		c->handler->join();
 	}
@@ -574,6 +574,8 @@ void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
 
 void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
 {
+	setThreadName("startGameplay");
+
 	if(CMM)
 		CMM->disable();
 	client = new CClient();
@@ -630,7 +632,6 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
 	{
 		if(CMM)
 		{
-			GH.terminate_cond->setn(false);
 			GH.curInt = CMM.get();
 			CMM->enable();
 		}
@@ -761,20 +762,20 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
 	else
 		startLocalServerAndConnect();
 
-	boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+	boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
 
 	while(!settings["session"]["headless"].Bool() && !GH.windows().topWindow<CLobbyScreen>())
-		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
 
 	while(!mi || mapInfo->fileURI != CSH->mi->fileURI)
 	{
 		setMapInfo(mapInfo);
-		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
 	}
 	// "Click" on color to remove us from it
 	setPlayer(myFirstColor());
 	while(myFirstColor() != PlayerColor::CANNOT_DETERMINE)
-		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
 
 	while(true)
 	{
@@ -787,7 +788,7 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
 		{
 
 		}
-		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
 	}
 }
 
@@ -817,7 +818,7 @@ public:
 
 void CServerHandler::threadHandleConnection()
 {
-	setThreadName("CServerHandler::threadHandleConnection");
+	setThreadName("handleConnection");
 	c->enterLobbyConnectionMode();
 
 	try
@@ -826,7 +827,7 @@ void CServerHandler::threadHandleConnection()
 		while(c->connected)
 		{
 			while(state == EClientState::STARTING)
-				boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+				boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
 
 			CPack * pack = c->retrievePack();
 			if(state == EClientState::DISCONNECTING)
@@ -898,7 +899,7 @@ void CServerHandler::visitForClient(CPackForClient & clientPack)
 void CServerHandler::threadRunServer()
 {
 #if !defined(VCMI_MOBILE)
-	setThreadName("CServerHandler::threadRunServer");
+	setThreadName("runServer");
 	const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string();
 	std::string comm = VCMIDirs::get().serverPath().string()
 		+ " --port=" + std::to_string(getHostPort())

+ 1 - 1
client/CVideoHandler.cpp

@@ -461,7 +461,7 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
 		double frameDurationSec = packet_duration * av_q2d(format->streams[stream]->time_base);
 		uint32_t timeToSleepMillisec = 1000 * (frameDurationSec);
 
-		boost::this_thread::sleep(boost::posix_time::millisec(timeToSleepMillisec));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(timeToSleepMillisec));
 	}
 
 	return true;

+ 4 - 1
client/Client.cpp

@@ -181,7 +181,10 @@ void CClient::newGame(CGameState * initializedGameState)
 	gs->preInit(VLC);
 	logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff());
 	if(!initializedGameState)
-		gs->init(&mapService, CSH->si.get(), settings["general"]["saveRandomMaps"].Bool());
+	{
+		Load::ProgressAccumulator progressTracking;
+		gs->init(&mapService, CSH->si.get(), progressTracking, settings["general"]["saveRandomMaps"].Bool());
+	}
 	logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff());
 
 	initMapHandler();

+ 1 - 0
client/LobbyClientNetPackVisitors.h

@@ -53,6 +53,7 @@ public:
 	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) override;
 	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) override;
 	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyLoadProgress(LobbyLoadProgress & pack) override;
 	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
 	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) override;
 };

+ 24 - 6
client/NetPacksLobbyClient.cpp

@@ -59,6 +59,9 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClient
 
 void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
+	if(auto w = GH.windows().topWindow<CLoadingScreen>())
+		GH.windows().popWindow(w);
+	
 	if(GH.windows().count() > 0)
 		GH.windows().popWindows(1);
 }
@@ -122,16 +125,31 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pac
 		handler.si = pack.initializedStartInfo;
 		handler.si->mode = modeBackup;
 	}
-	if(settings["session"]["headless"].Bool())
-		handler.startGameplay(pack.initializedGameState);
+	handler.startGameplay(pack.initializedGameState);
 }
 
 void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
-		return;
-	
-	GH.windows().createAndPushWindow<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, &handler, pack.initializedGameState));
+	if(auto w = GH.windows().topWindow<CLoadingScreen>())
+	{
+		w->finish();
+		w->tick(0);
+		w->redraw();
+	}
+	else
+		GH.windows().createAndPushWindow<CLoadingScreen>();
+}
+
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyLoadProgress(LobbyLoadProgress & pack)
+{
+	if(auto w = GH.windows().topWindow<CLoadingScreen>())
+	{
+		w->set(pack.progress);
+		w->tick(0);
+		w->redraw();
+	}
+	else
+		GH.windows().createAndPushWindow<CLoadingScreen>();
 }
 
 void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)

+ 2 - 0
client/adventureMap/CInGameConsole.cpp

@@ -27,6 +27,7 @@
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/CThreadHelper.h"
 #include "../../lib/TextOperations.h"
 #include "../../lib/mapObjects/CArmedInstance.h"
 
@@ -255,6 +256,7 @@ void CInGameConsole::endEnteringText(bool processEnteredText)
 			//some commands like gosolo don't work when executed from GUI thread
 			auto threadFunction = [=]()
 			{
+				setThreadName("processCommand");
 				ClientCommandManager commandController;
 				commandController.processCommand(txt.substr(1), true);
 			};

+ 7 - 22
client/battle/BattleInterface.cpp

@@ -43,6 +43,7 @@
 #include "../../lib/NetPacks.h"
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/TerrainHandler.h"
+#include "../../lib/CThreadHelper.h"
 
 BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *army2,
 		const CGHeroInstance *hero1, const CGHeroInstance *hero2,
@@ -104,11 +105,9 @@ void BattleInterface::playIntroSoundAndUnlockInterface()
 {
 	auto onIntroPlayed = [this]()
 	{
+		boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
 		if(LOCPLINT->battleInt)
-		{
-			boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
 			onIntroSoundPlayed();
-		}
 	};
 
 	int battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);
@@ -326,7 +325,7 @@ void BattleInterface::battleFinished(const BattleResult& br, QueryID queryID)
 		curInt->cb->selectionMade(selection, queryID);
 	};
 	GH.windows().pushWindow(wnd);
-	
+
 	curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
 	CPlayerInterface::battleInt = nullptr;
 }
@@ -602,28 +601,13 @@ void BattleInterface::startAction(const BattleAction & action)
 		return;
 	}
 
-	const CStack *stack = curInt->cb->battleGetStackByID(action.stackNumber);
-
-	if (stack)
-	{
-		windowObject->updateQueue();
-	}
-	else
-	{
-		assert(action.actionType == EActionType::HERO_SPELL); //only cast spell is valid action without acting stack number
-	}
-
 	stacksController->startAction(action);
 
-	if(action.actionType == EActionType::HERO_SPELL) //when hero casts spell
-		return;
-
-	if (!stack)
-	{
-		logGlobal->error("Something wrong with stackNumber in actionStarted. Stack number: %d", action.stackNumber);
+	if (!action.isUnitAction())
 		return;
-	}
 
+	assert(curInt->cb->battleGetStackByID(action.stackNumber));
+	windowObject->updateQueue();
 	effectsController->startAction(action);
 }
 
@@ -731,6 +715,7 @@ void BattleInterface::requestAutofightingAIToTakeAction()
 			// HOWEVER this thread won't atttempt to lock game state, potentially leading to races
 			boost::thread aiThread([this, activeStack]()
 			{
+				setThreadName("autofightingAI");
 				curInt->autofightingAI->activeStack(activeStack);
 			});
 			aiThread.detach();

+ 1 - 1
client/battle/BattleWindow.cpp

@@ -407,7 +407,7 @@ void BattleWindow::bFleef()
 		auto txt = boost::format(CGI->generaltexth->allTexts[340]) % heroName; //The Shackles of War are present.  %s can not retreat!
 
 		//printing message
-		owner.curInt->showInfoDialog(boost::to_string(txt), comps);
+		owner.curInt->showInfoDialog(boost::str(txt), comps);
 	}
 }
 

+ 0 - 8
client/eventsSDL/InputSourceKeyboard.cpp

@@ -59,14 +59,6 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
 		Settings s = settings.write["session"];
 		switch(key.keysym.sym)
 		{
-			case SDLK_F5:
-				if(settings["session"]["spectate-locked-pim"].Bool())
-					CPlayerInterface::pim->unlock();
-				else
-					CPlayerInterface::pim->lock();
-				s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
-				break;
-
 			case SDLK_F6:
 				s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
 				break;

+ 6 - 20
client/gui/CGuiHandler.cpp

@@ -36,7 +36,7 @@
 
 CGuiHandler GH;
 
-boost::thread_specific_ptr<bool> inGuiThread;
+static thread_local bool inGuiThread = false;
 
 SObjectConstruction::SObjectConstruction(CIntObject *obj)
 :myObj(obj)
@@ -69,6 +69,8 @@ SSetCaptureState::~SSetCaptureState()
 
 void CGuiHandler::init()
 {
+	inGuiThread = true;
+
 	inputHandlerInstance = std::make_unique<InputHandler>();
 	eventDispatcherInstance = std::make_unique<EventDispatcher>();
 	windowHandlerInstance = std::make_unique<WindowHandler>();
@@ -109,20 +111,8 @@ void CGuiHandler::stopTextInput()
 
 void CGuiHandler::renderFrame()
 {
-	// Updating GUI requires locking pim mutex (that protects screen and GUI state).
-	// During game:
-	// When ending the game, the pim mutex might be hold by other thread,
-	// that will notify us about the ending game by setting terminate_cond flag.
-	//in PreGame terminate_cond stay false
-
-	bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded
-	while(!terminate_cond->get() && !(acquiredTheLockOnPim = CPlayerInterface::pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate
-		boost::this_thread::sleep(boost::posix_time::milliseconds(1));
-
-	if(acquiredTheLockOnPim)
 	{
-		// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock.
-		boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim, boost::adopt_lock);
+		boost::recursive_mutex::scoped_lock un(*CPlayerInterface::pim);
 
 		if(nullptr != curInt)
 			curInt->update();
@@ -150,14 +140,10 @@ CGuiHandler::CGuiHandler()
 	, captureChildren(false)
 	, curInt(nullptr)
 	, fakeStatusBar(std::make_shared<EmptyStatusBar>())
-	, terminate_cond (new CondSh<bool>(false))
 {
 }
 
-CGuiHandler::~CGuiHandler()
-{
-	delete terminate_cond;
-}
+CGuiHandler::~CGuiHandler() = default;
 
 ShortcutHandler & CGuiHandler::shortcuts()
 {
@@ -207,7 +193,7 @@ void CGuiHandler::drawFPSCounter()
 
 bool CGuiHandler::amIGuiThread()
 {
-	return inGuiThread.get() && *inGuiThread;
+	return inGuiThread;
 }
 
 void CGuiHandler::dispatchMainThread(const std::function<void()> & functor)

+ 0 - 2
client/gui/CGuiHandler.h

@@ -99,8 +99,6 @@ public:
 
 	/// Calls provided functor in main thread on next execution frame
 	void dispatchMainThread(const std::function<void()> & functor);
-
-	CondSh<bool> * terminate_cond; // confirm termination
 };
 
 extern CGuiHandler GH; //global gui handler

+ 51 - 19
client/mainmenu/CMainMenu.cpp

@@ -392,7 +392,6 @@ std::shared_ptr<CMainMenu> CMainMenu::create()
 	if(!CMM)
 		CMM = std::shared_ptr<CMainMenu>(new CMainMenu());
 
-	GH.terminate_cond->setn(false);
 	return CMM;
 }
 
@@ -562,12 +561,14 @@ void CSimpleJoinScreen::startConnectThread(const std::string & addr, ui16 port)
 	// https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md#threads-and-the-java-vm
 	CVCMIServer::reuseClientJNIEnv(SDL_AndroidGetJNIEnv());
 #endif
-	boost::thread(&CSimpleJoinScreen::connectThread, this, addr, port);
+	boost::thread connector(&CSimpleJoinScreen::connectThread, this, addr, port);
+
+	connector.detach();
 }
 
 void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port)
 {
-	setThreadName("CSimpleJoinScreen::connectThread");
+	setThreadName("connectThread");
 	if(!addr.length())
 		CSH->startLocalServerAndConnect();
 	else
@@ -582,36 +583,67 @@ void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port)
 	});
 }
 
-CLoadingScreen::CLoadingScreen(std::function<void()> loader)
-	: CWindowObject(BORDERED, getBackground()), loadingThread(loader)
+CLoadingScreen::CLoadingScreen()
+	: CWindowObject(BORDERED, getBackground())
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+	
+	addUsedEvents(TIME);
+	
 	CCS->musich->stopMusic(5000);
+	
+	const auto & conf = CMainMenuConfig::get().getConfig()["loading"];
+	if(conf.isStruct())
+	{
+		const int posx = conf["x"].Integer(), posy = conf["y"].Integer();
+		const int blockSize = conf["size"].Integer();
+		const int blocksAmount = conf["amount"].Integer();
+		
+		for(int i = 0; i < blocksAmount; ++i)
+		{
+			progressBlocks.push_back(std::make_shared<CAnimImage>(conf["name"].String(), i, 0, posx + i * blockSize, posy));
+			progressBlocks.back()->deactivate();
+			progressBlocks.back()->visible = false;
+		}
+	}
 }
 
 CLoadingScreen::~CLoadingScreen()
 {
-	loadingThread.join();
 }
 
-void CLoadingScreen::showAll(Canvas & to)
+void CLoadingScreen::tick(uint32_t msPassed)
 {
-	//FIXME: filling screen with transparency? BLACK intended?
-	//Rect rect(0, 0, to->w, to->h);
-	//CSDL_Ext::fillRect(to, rect, Colors::TRANSPARENCY);
-
-	CWindowObject::showAll(to);
+	if(!progressBlocks.empty())
+	{
+		int status = float(get()) / 255.f * progressBlocks.size();
+		
+		for(int i = 0; i < status; ++i)
+		{
+			progressBlocks.at(i)->activate();
+			progressBlocks.at(i)->visible = true;
+		}
+	}
 }
 
 std::string CLoadingScreen::getBackground()
 {
-	const auto & conf = CMainMenuConfig::get().getConfig()["loading"].Vector();
+	std::string fname = "loadbar";
+	const auto & conf = CMainMenuConfig::get().getConfig()["loading"];
 
-	if(conf.empty())
+	if(conf.isStruct())
 	{
-		return "loadbar";
-	}
-	else
-	{
-		return RandomGeneratorUtil::nextItem(conf, CRandomGenerator::getDefault())->String();
+		if(conf["background"].isVector())
+			return RandomGeneratorUtil::nextItem(conf["background"].Vector(), CRandomGenerator::getDefault())->String();
+		
+		if(conf["background"].isString())
+			return conf["background"].String();
+		
+		return fname;
 	}
+	
+	if(conf.isVector() && !conf.Vector().empty())
+		return RandomGeneratorUtil::nextItem(conf.Vector(), CRandomGenerator::getDefault())->String();
+	
+	return fname;
 }

+ 9 - 6
client/mainmenu/CMainMenu.h

@@ -11,6 +11,7 @@
 
 #include "../windows/CWindowObject.h"
 #include "../../lib/JsonNode.h"
+#include "../../lib/LoadProgress.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -22,9 +23,11 @@ class CTextInput;
 class CGStatusBar;
 class CTextBox;
 class CTabbedInt;
+class CAnimImage;
 class CAnimation;
 class CButton;
 class CFilledTexture;
+class CLabel;
 
 
 // TODO: Find new location for these enums
@@ -178,17 +181,17 @@ public:
 	CSimpleJoinScreen(bool host = true);
 };
 
-class CLoadingScreen : public CWindowObject
+class CLoadingScreen : virtual public CWindowObject, virtual public Load::Progress
 {
-	boost::thread loadingThread;
-
+	std::vector<std::shared_ptr<CAnimImage>> progressBlocks;
+	
 	std::string getBackground();
 
-public:
-	CLoadingScreen(std::function<void()> loader);
+public:	
+	CLoadingScreen();
 	~CLoadingScreen();
 
-	void showAll(Canvas & to) override;
+	void tick(uint32_t msPassed) override;
 };
 
 extern std::shared_ptr<CMainMenu> CMM;

+ 1 - 1
client/mapView/mapHandler.cpp

@@ -38,7 +38,7 @@ void CMapHandler::waitForOngoingAnimations()
 	while(CGI->mh->hasOngoingAnimations())
 	{
 		auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim);
-		boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(1));
 	}
 }
 

+ 1 - 1
client/windows/CSpellWindow.cpp

@@ -562,7 +562,7 @@ void CSpellWindow::SpellArea::hover(bool on)
 	if(mySpell)
 	{
 		if(on)
-			owner->statusBar->write(boost::to_string(boost::format("%s (%s)") % mySpell->getNameTranslated() % CGI->generaltexth->allTexts[171+mySpell->level]));
+			owner->statusBar->write(boost::str(boost::format("%s (%s)") % mySpell->getNameTranslated() % CGI->generaltexth->allTexts[171+mySpell->level]));
 		else
 			owner->statusBar->clear();
 	}

+ 5 - 1
config/mainmenu.json

@@ -2,7 +2,11 @@
 	//images used in game selection screen
 	"game-select" : ["gamselb0", "gamselb1"],
 
-	"loading" : ["loadbar"],
+	"loading" :
+	{
+		"background" : ["loadbar"],
+		"x": 395, "y": 548, "size": 18, "amount": 20, "name": "loadprog"
+	},
 
 	//Main menu window, consists of several sub-menus aka items
 	"window":

+ 2 - 2
lib/CConsoleHandler.cpp

@@ -214,7 +214,7 @@ void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color)
 
 int CConsoleHandler::run() const
 {
-	setThreadName("CConsoleHandler::run");
+	setThreadName("consoleHandler");
 	//disabling sync to make in_avail() work (othervice always returns 0)
 	{
 		TLockGuard _(smx);
@@ -233,7 +233,7 @@ int CConsoleHandler::run() const
 					(*cb)(buffer, false);
 		}
 		else
-			boost::this_thread::sleep(boost::posix_time::millisec(100));
+			boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
 
 		boost::this_thread::interruption_point();
 #else

+ 2 - 7
lib/CRandomGenerator.cpp

@@ -13,8 +13,6 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-boost::thread_specific_ptr<CRandomGenerator> CRandomGenerator::defaultRand;
-
 CRandomGenerator::CRandomGenerator()
 {
 	resetSeed();
@@ -84,11 +82,8 @@ double CRandomGenerator::nextDouble()
 
 CRandomGenerator & CRandomGenerator::getDefault()
 {
-	if(!defaultRand.get())
-	{
-		defaultRand.reset(new CRandomGenerator());
-	}
-	return *defaultRand;
+	static thread_local CRandomGenerator defaultRand;
+	return defaultRand;
 }
 
 TGenerator & CRandomGenerator::getStdGenerator()

+ 0 - 1
lib/CRandomGenerator.h

@@ -80,7 +80,6 @@ public:
 
 private:
 	TGenerator rand;
-	static boost::thread_specific_ptr<CRandomGenerator> defaultRand;
 
 public:
 	template <typename Handler>

+ 5 - 3
lib/CThreadHelper.cpp

@@ -29,10 +29,12 @@ CThreadHelper::CThreadHelper(std::vector<std::function<void()>> * Tasks, int Thr
 }
 void CThreadHelper::run()
 {
-	boost::thread_group grupa;
+	std::vector<boost::thread> group;
 	for(int i=0;i<threads;i++)
-		grupa.create_thread(std::bind(&CThreadHelper::processTasks,this));
-	grupa.join_all();
+		group.emplace_back(std::bind(&CThreadHelper::processTasks,this));
+
+	for (auto & thread : group)
+		thread.join();
 
 	//thread group deletes threads, do not free manually
 }

+ 4 - 4
lib/CThreadHelper.h

@@ -46,15 +46,15 @@ public:
 
 	void run()
 	{
-		boost::thread_group grupa;
+		std::vector<boost::thread> group;
 		for(size_t i=0; i<threads; i++)
 		{
 			std::shared_ptr<Payload> payload = context.at(i);
-
-			grupa.create_thread(std::bind(&ThreadPool::processTasks, this, payload));
+			group.emplace_back(std::bind(&ThreadPool::processTasks, this, payload));
 		}
 
-		grupa.join_all();
+		for (auto & thread : group)
+			thread.join();
 
 		//thread group deletes threads, do not free manually
 	}

+ 51 - 0
lib/LoadProgress.cpp

@@ -18,6 +18,11 @@ Progress::Progress(): _progress(std::numeric_limits<Type>::min())
 	setupSteps(100);
 }
 
+Progress::Progress(int steps): _progress(std::numeric_limits<Type>::min())
+{
+	setupSteps(steps);
+}
+
 Type Progress::get() const
 {
 	if(_step >= _maxSteps)
@@ -82,3 +87,49 @@ void Progress::step(int count)
 		_step += count;
 	}
 }
+
+void ProgressAccumulator::include(const Progress & p)
+{
+	boost::unique_lock<boost::mutex> guard(_mx);
+	_progress.emplace_back(p);
+}
+
+void ProgressAccumulator::exclude(const Progress & p)
+{
+	boost::unique_lock<boost::mutex> guard(_mx);
+	for(auto i = _progress.begin(); i != _progress.end(); ++i)
+	{
+		if(&i->get() == &p)
+		{
+			_accumulated += static_cast<long long>(p.get()) * p._maxSteps;
+			_steps += p._maxSteps;
+			_progress.erase(i);
+			return;
+		}
+	}
+}
+
+bool ProgressAccumulator::finished() const
+{
+	boost::unique_lock<boost::mutex> guard(_mx);
+	for(auto i : _progress)
+		if(!i.get().finished())
+			return false;
+	return true;
+}
+
+Type ProgressAccumulator::get() const
+{
+	boost::unique_lock<boost::mutex> guard(_mx);
+	auto sum = _accumulated;
+	auto totalSteps = _steps;
+	for(auto p : _progress)
+	{
+		sum += static_cast<long long>(p.get().get()) * p.get()._maxSteps;
+		totalSteps += p.get()._maxSteps;
+	}
+	
+	if(totalSteps)
+		sum /= totalSteps;
+	return static_cast<Type>(sum);
+}

+ 24 - 1
lib/LoadProgress.h

@@ -18,6 +18,8 @@ namespace Load
 
 using Type = unsigned char;
 
+class ProgressAccumulator;
+
 /*
  * Purpose of that class is to track progress of computations
  * Derive from this class if you want to translate user or system
@@ -29,8 +31,9 @@ class DLL_LINKAGE Progress
 public:
 	
 	//Sets current state to 0.
-	//Amount of steps to finish progress will be equal to 100
+	//Amount of steps to finish progress will be equal to 100 for default constructor
 	Progress();
+	Progress(int steps);
 	virtual ~Progress() = default;
 
 	//Returns current state of the progress
@@ -67,5 +70,25 @@ public:
 private:
 	std::atomic<Type> _progress, _target;
 	std::atomic<int> _step, _maxSteps;
+	
+	friend class ProgressAccumulator;
 };
+
+class DLL_LINKAGE ProgressAccumulator
+{
+public:
+	ProgressAccumulator() = default;
+	
+	void include(const Progress &);
+	void exclude(const Progress &);
+	
+	bool finished() const;
+	Type get() const;
+	
+private:
+	mutable boost::mutex _mx;
+	long long _accumulated = 0, _steps = 0;
+	std::vector<std::reference_wrapper<const Progress>> _progress;
+};
+
 }

+ 1 - 0
lib/NetPackVisitor.h

@@ -145,6 +145,7 @@ public:
 	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) {}
 	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) {}
 	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) {}
+	virtual void visitLobbyLoadProgress(LobbyLoadProgress & pack) {}
 	virtual void visitLobbyEndGame(LobbyEndGame & pack) {}
 	virtual void visitLobbyStartGame(LobbyStartGame & pack) {}
 	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) {}

+ 5 - 0
lib/NetPacksLib.cpp

@@ -688,6 +688,11 @@ void LobbyGuiAction::visitTyped(ICPackVisitor & visitor)
 	visitor.visitLobbyGuiAction(*this);
 }
 
+void LobbyLoadProgress::visitTyped(ICPackVisitor & visitor)
+{
+	visitor.visitLobbyLoadProgress(*this);
+}
+
 void LobbyEndGame::visitTyped(ICPackVisitor & visitor)
 {
 	visitor.visitLobbyEndGame(*this);

+ 12 - 0
lib/NetPacksLobby.h

@@ -99,6 +99,18 @@ struct DLL_LINKAGE LobbyGuiAction : public CLobbyPackToPropagate
 	}
 };
 
+struct DLL_LINKAGE LobbyLoadProgress : public CLobbyPackToPropagate
+{
+	unsigned char progress;
+	
+	virtual void visitTyped(ICPackVisitor & visitor) override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & progress;
+	}
+};
+
 struct DLL_LINKAGE LobbyEndGame : public CLobbyPackToPropagate
 {
 	bool closeConnection = false, restart = false;

+ 10 - 3
lib/battle/BattleAction.cpp

@@ -194,7 +194,8 @@ void BattleAction::setTarget(const battle::Target & target_)
 
 bool BattleAction::isUnitAction() const
 {
-	static const std::array<EActionType, 9> actions = {
+	static const std::array<EActionType, 109> actions = {
+		EActionType::NO_ACTION,
 		EActionType::WALK,
 		EActionType::WAIT,
 		EActionType::DEFEND,
@@ -205,7 +206,6 @@ bool BattleAction::isUnitAction() const
 		EActionType::BAD_MORALE,
 		EActionType::STACK_HEAL
 	};
-
 	return vstd::contains(actions, actionType);
 }
 
@@ -215,7 +215,15 @@ bool BattleAction::isSpellAction() const
 		EActionType::HERO_SPELL,
 		EActionType::MONSTER_SPELL
 	};
+	return vstd::contains(actions, actionType);
+}
 
+bool BattleAction::isBattleEndAction() const
+{
+	static const std::array<EActionType, 2> actions = {
+		EActionType::RETREAT,
+		EActionType::SURRENDER
+	};
 	return vstd::contains(actions, actionType);
 }
 
@@ -227,7 +235,6 @@ bool BattleAction::isTacticsAction() const
 		EActionType::RETREAT,
 		EActionType::SURRENDER
 	};
-
 	return vstd::contains(actions, actionType);
 }
 

+ 1 - 0
lib/battle/BattleAction.h

@@ -46,6 +46,7 @@ public:
 	bool isTacticsAction() const;
 	bool isUnitAction() const;
 	bool isSpellAction() const;
+	bool isBattleEndAction() const;
 	std::string toString() const;
 
 	void aimToHex(const BattleHex & destination);

+ 5 - 3
lib/gameState/CGameState.cpp

@@ -404,7 +404,7 @@ void CGameState::preInit(Services * services)
 	this->services = services;
 }
 
-void CGameState::init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap)
+void CGameState::init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap)
 {
 	preInitAuto();
 	logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
@@ -416,7 +416,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
 	switch(scenarioOps->mode)
 	{
 	case StartInfo::NEW_GAME:
-		initNewGame(mapService, allowSavingRandomMap);
+		initNewGame(mapService, allowSavingRandomMap, progressTracking);
 		break;
 	case StartInfo::CAMPAIGN:
 		initCampaign();
@@ -535,7 +535,7 @@ void CGameState::preInitAuto()
 	}
 }
 
-void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap)
+void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking)
 {
 	if(scenarioOps->createRandomMap())
 	{
@@ -544,8 +544,10 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
 
 		// Gen map
 		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, scenarioOps->seedToBeUsed);
+		progressTracking.include(mapGenerator);
 
 		std::unique_ptr<CMap> randomMap = mapGenerator.generate();
+		progressTracking.exclude(mapGenerator);
 
 		if(allowSavingRandomMap)
 		{

+ 3 - 2
lib/gameState/CGameState.h

@@ -11,6 +11,7 @@
 
 #include "bonuses/CBonusSystemNode.h"
 #include "IGameCallback.h"
+#include "LoadProgress.h"
 
 namespace boost
 {
@@ -89,7 +90,7 @@ public:
 
 	void preInit(Services * services);
 
-	void init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap = false);
+	void init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator &, bool allowSavingRandomMap = false);
 	void updateOnLoad(StartInfo * si);
 
 	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
@@ -166,7 +167,7 @@ public:
 private:
 	// ----- initialization -----
 	void preInitAuto();
-	void initNewGame(const IMapService * mapService, bool allowSavingRandomMap);
+	void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking);
 	void checkMapChecksum();
 	void initGlobalBonuses();
 	void initGrailPosition();

+ 1 - 1
lib/logging/CLogger.cpp

@@ -288,7 +288,7 @@ std::string CLogFormatter::format(const LogRecord & record) const
 	boost::algorithm::replace_first(message, "%m", record.message);
 	boost::algorithm::replace_first(message, "%c", boost::posix_time::to_simple_string(record.timeStamp));
 
-	//return boost::to_string (boost::format("%d %d %d[%d] - %d") % dateStream.str() % level % record.domain.getName() % record.threadId % record.message);
+	//return boost::str (boost::format("%d %d %d[%d] - %d") % dateStream.str() % level % record.domain.getName() % record.threadId % record.message);
 
 	return message;
 }

+ 1 - 1
lib/mapObjects/CGCreature.cpp

@@ -69,7 +69,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
 			ms.appendLocalString(EMetaText::GENERAL_TXT,243);
 			break;
 		default: //decision = cost in gold
-			ms.appendRawString(boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision));
+			ms.appendRawString(boost::str(boost::format(VLC->generaltexth->allTexts[244]) % decision));
 			break;
 		}
 

部分文件因为文件数量过多而无法显示