Sfoglia il codice sorgente

- Moved place bonus hero before normal random hero and starting hero placement -> same behaviour as in OH3
- Moved placing campaign heroes before random object generation -> same behaviour as in OH3
- Refactored pickHero into pickNextHeroType (hero generation sequence) and pickUnusedHeroTypeRandomly
- Added a SIGSEV violation handler to vcmiserver executable for logging stacktrace (for convenience only)
- Fixed Fuzzy.cpp and VCAI.h compilation on Clang
- Added a handleException function in addition to our macros (no use of macros, enables debugging support, does not re-throw, catches ...-case too)

beegee1 11 anni fa
parent
commit
b8eddcd9a8
6 ha cambiato i file con 140 aggiunte e 58 eliminazioni
  1. 2 2
      AI/VCAI/Fuzzy.cpp
  2. 1 1
      AI/VCAI/VCAI.h
  3. 22 5
      Global.h
  4. 68 46
      lib/CGameState.cpp
  5. 5 3
      lib/CGameState.h
  6. 42 1
      server/CVCMIServer.cpp

+ 2 - 2
AI/VCAI/Fuzzy.cpp

@@ -311,7 +311,7 @@ Goals::TSubgoal FuzzyHelper::chooseSolution (Goals::TGoalVec vec)
 		setPriority(g);
 	}
 
-	auto compareGoals = [&](Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
+	auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
 	{
 		return lhs->priority < rhs->priority;
 	};
@@ -467,4 +467,4 @@ float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
 void FuzzyHelper::setPriority (Goals::TSubgoal & g)
 {
 	g->setpriority(g->accept(this)); //this enforces returned value is set
-}
+}

+ 1 - 1
AI/VCAI/VCAI.h

@@ -295,7 +295,7 @@ public:
 	const CGTownInstance *findTownWithTavern() const;
 	bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
 
-	bool VCAI::canAct (HeroPtr h) const;
+	bool canAct(HeroPtr h) const;
 	std::vector<HeroPtr> getUnblockedHeroes() const;
 	HeroPtr primaryHero() const;
 	TResources freeResources() const; //owned resources minus gold reserve

+ 22 - 5
Global.h

@@ -225,6 +225,28 @@ template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
 /* ---------------------------------------------------------------------------- */
 /* VCMI standard library */
 /* ---------------------------------------------------------------------------- */
+#include "lib/logging/CLogger.h"
+
+void inline handleException()
+{
+	try
+	{
+		throw;
+	}
+	catch(const std::exception & ex)
+	{
+		logGlobal->errorStream() << ex.what();
+	}
+	catch(const std::string & ex)
+	{
+		logGlobal->errorStream() << ex;
+	}
+	catch(...)
+	{
+		logGlobal->errorStream() << "Sorry, caught unknown exception type. No more info available.";
+	}
+}
+
 template<typename T>
 std::ostream & operator<<(std::ostream & out, const boost::optional<T> & opt)
 {
@@ -601,8 +623,3 @@ namespace vstd
 }
 using vstd::operator-=;
 using vstd::make_unique;
-
-/* ---------------------------------------------------------------------------- */
-/* VCMI headers */
-/* ---------------------------------------------------------------------------- */
-#include "lib/logging/CLogger.h"

+ 68 - 46
lib/CGameState.cpp

@@ -26,6 +26,7 @@
 #include "GameConstants.h"
 #include "rmg/CMapGenerator.h"
 #include "CStopWatch.h"
+#include "mapping/CMapEditManager.h"
 
 DLL_LINKAGE std::minstd_rand ran;
 class CGObjectInstance;
@@ -444,28 +445,23 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor pl
 	return ret;
 }
 
-int CGameState::pickHero(PlayerColor owner)
+int CGameState::pickNextHeroType(PlayerColor owner) const
 {
 	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
-    if(!isUsedHero(HeroTypeID(ps.hero)) &&  ps.hero >= 0) //we haven't used selected hero
-		return ps.hero;
-
-	if(scenarioOps->mode == StartInfo::CAMPAIGN)
+	if(ps.hero >= 0 && !isUsedHero(HeroTypeID(ps.hero))) //we haven't used selected hero
 	{
-		auto bonus = scenarioOps->campState->getBonusForCurrentMap();
-		if(bonus && bonus->type == CScenarioTravel::STravelBonus::HERO && owner == PlayerColor(bonus->info1))
-		{
-			//TODO what if hero chosen as bonus is placed in the prison on map
-			if(bonus->info2 != 0xffff && !isUsedHero(HeroTypeID(bonus->info2))) //not random and not taken
-			{
-				return bonus->info2;
-			}
-		}
+		return ps.hero;
 	}
 
+	return pickUnusedHeroTypeRandomly(owner);
+}
+
+int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner) const
+{
 	//list of available heroes for this faction and others
 	std::vector<HeroTypeID> factionHeroes, otherHeroes;
 
+	const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
 	for(HeroTypeID hid : getUnusedAllowedHeroes())
 	{
 		if(VLC->heroh->heroes[hid.getNum()]->heroClass->faction == ps.castle)
@@ -507,7 +503,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
 	case Obj::RANDOM_RELIC_ART:
 		return std::make_pair(Obj::ARTIFACT, VLC->arth->getRandomArt (CArtifact::ART_RELIC));
 	case Obj::RANDOM_HERO:
-		return std::make_pair(Obj::HERO, pickHero(obj->tempOwner));
+		return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner));
 	case Obj::RANDOM_MONSTER:
 		return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(std::ref(ran)));
 	case Obj::RANDOM_MONSTER_L1:
@@ -794,11 +790,11 @@ void CGameState::init(StartInfo * si)
 
     logGlobal->debugStream() << "Initialization:";
 
+	initPlayerStates();
+	placeCampaignHeroes();
 	initGrailPosition();
 	initRandomFactionsForPlayers();
 	randomizeMapObjects();
-	initPlayerStates();
-	initHeroPlaceholders();
 	placeStartingHeroes();
 	initStartingResources();
 	initHeroes();
@@ -1095,10 +1091,31 @@ void CGameState::initPlayerStates()
 	}
 }
 
-void CGameState::initHeroPlaceholders()
+void CGameState::placeCampaignHeroes()
 {
 	if (scenarioOps->campState)
 	{
+		// place bonus hero
+		auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap();
+		bool campaignGiveHero = campaignBonus && campaignBonus.get().type == CScenarioTravel::STravelBonus::HERO;
+
+		if(campaignGiveHero)
+		{
+			auto playerColor = PlayerColor(campaignBonus->info1);
+			auto it = scenarioOps->playerInfos.find(playerColor);
+			if(it != scenarioOps->playerInfos.end())
+			{
+				auto heroTypeId = campaignBonus->info2;
+				if(heroTypeId == 0xffff) // random bonus hero
+				{
+					heroTypeId = pickUnusedHeroTypeRandomly(playerColor);
+				}
+
+				placeStartingHero(playerColor, HeroTypeID(heroTypeId), map->players[playerColor.getNum()].posOfMainTown);
+			}
+		}
+
+		// replace heroes placeholders
 		auto campaignScenario = getCampaignScenarioForCrossoverHeroes();
 
 		if(campaignScenario)
@@ -1107,14 +1124,29 @@ void CGameState::initHeroPlaceholders()
 			auto crossoverHeroes = prepareCrossoverHeroes(campaignScenario);
 
 			logGlobal->debugStream() << "\tGenerate list of hero placeholders";
-			auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
+			const auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
+
+			// remove same heroes on the map which will be added through crossover heroes
+			/*for(auto & campaignHeroReplacement : campaignHeroReplacement)
+			{
+				campaignHeroReplacement.first->subID
+			}*/
 
 			logGlobal->debugStream() << "\tReplace placeholders with heroes";
-			placeCampaignHeroes(campaignHeroReplacements);
+			replaceHeroesPlaceholders(campaignHeroReplacements);
 		}
 	}
 }
 
+void CGameState::placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos)
+{
+	townPos.x += 1;
+
+	CGHeroInstance * hero =  static_cast<CGHeroInstance*>(createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor));
+	hero->initHero();
+	map->getEditManager()->insertObject(hero, townPos);
+}
+
 const CCampaignScenario * CGameState::getCampaignScenarioForCrossoverHeroes() const
 {
 	const CCampaignScenario * campaignScenario = nullptr;
@@ -1223,36 +1255,26 @@ std::vector<CGHeroInstance *> CGameState::prepareCrossoverHeroes(const CCampaign
 void CGameState::placeStartingHeroes()
 {
 	logGlobal->debugStream() << "\tGiving starting hero";
-	bool campaignGiveHero = false;
-	if(scenarioOps->campState)
-	{
-		if(auto bonus = scenarioOps->campState->getBonusForCurrentMap())
-		{
-			campaignGiveHero =  scenarioOps->mode == StartInfo::CAMPAIGN &&
-				bonus.get().type == CScenarioTravel::STravelBonus::HERO;
-		}
-	}
 
-	for(auto it = scenarioOps->playerInfos.begin(); it != scenarioOps->playerInfos.end(); ++it)
+	for(auto & playerSettingPair : scenarioOps->playerInfos)
 	{
-		const PlayerInfo &p = map->players[it->first.getNum()];
-		bool generateHero = (p.generateHeroAtMainTown ||
-							 (it->second.playerID != PlayerSettings::PLAYER_AI && campaignGiveHero)) && p.hasMainTown;
-		if(generateHero && vstd::contains(scenarioOps->playerInfos, it->first))
+		auto playerColor = playerSettingPair.first;
+		auto & playerInfo = map->players[playerColor.getNum()];
+		if(playerInfo.generateHeroAtMainTown && playerInfo.hasMainTown)
 		{
-			int3 hpos = p.posOfMainTown;
-			hpos.x+=1;
+			// Do not place a starting hero if the hero was already placed due to a campaign bonus
+			if(scenarioOps->campState)
+			{
+				if(auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap())
+				{
+					if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1)) continue;
+				}
+			}
 
-			int h = pickHero(it->first);
-			if(it->second.hero == -1)
-				it->second.hero = h;
+			int heroTypeId = pickNextHeroType(playerColor);
+			if(playerSettingPair.second.hero == -1) playerSettingPair.second.hero = heroTypeId;
 
-			CGHeroInstance * nnn =  static_cast<CGHeroInstance*>(createObject(Obj::HERO,h,hpos,it->first));
-			nnn->id = ObjectInstanceID(map->objects.size());
-			nnn->initHero();
-			map->heroesOnMap.push_back(nnn);
-			map->objects.push_back(nnn);
-			map->addBlockVisTiles(nnn);
+			placeStartingHero(playerColor, HeroTypeID(heroTypeId), playerInfo.posOfMainTown);
 		}
 	}
 }
@@ -2781,7 +2803,7 @@ std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > CGameState::generateC
 	return campaignHeroReplacements;
 }
 
-void CGameState::placeCampaignHeroes(const std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > &campHeroReplacements)
+void CGameState::replaceHeroesPlaceholders(const std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > &campHeroReplacements)
 {
 	for(auto obj : campHeroReplacements)
 	{

+ 5 - 3
lib/CGameState.h

@@ -457,15 +457,16 @@ private:
 	void randomizeMapObjects();
 	void randomizeObject(CGObjectInstance *cur);
 	void initPlayerStates();
-	void initHeroPlaceholders();
+	void placeCampaignHeroes();
 	const CCampaignScenario * getCampaignScenarioForCrossoverHeroes() const;
 	std::vector<CGHeroInstance *> prepareCrossoverHeroes(const CCampaignScenario * campaignScenario);
 
 	// returns heroes and placeholders in where heroes will be put
 	std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > generateCampaignHeroesToReplace(std::vector<CGHeroInstance *> & crossoverHeroes);
 
-	void placeCampaignHeroes(const std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > &campHeroReplacements);
+	void replaceHeroesPlaceholders(const std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > &campHeroReplacements);
 	void placeStartingHeroes();
+	void placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos);
 	void initStartingResources();
 	void initHeroes();
 	void giveCampaignBonusToHero(CGHeroInstance * hero);
@@ -494,7 +495,8 @@ private:
 	bool isUsedHero(HeroTypeID hid) const; //looks in heroes and prisons
 	std::set<HeroTypeID> getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const;
 	std::pair<Obj,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
-	int pickHero(PlayerColor owner);
+	int pickUnusedHeroTypeRandomly(PlayerColor owner) const; // picks a unused hero type randomly
+	int pickNextHeroType(PlayerColor owner) const; // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
 
 	friend class CCallback;
 	friend class CClient;

+ 42 - 1
server/CVCMIServer.cpp

@@ -32,6 +32,10 @@
 
 #include "../lib/UnlockGuard.h"
 
+#ifdef __GNUC__
+#include <execinfo.h>
+#endif
+
 std::string NAME_AFFIX = "server";
 std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
 using namespace boost;
@@ -531,8 +535,41 @@ static void handleCommandOptions(int argc, char *argv[])
 	po::notify(cmdLineOptions);
 }
 
+#ifdef __GNUC__
+void handleLinuxSignal(int sig)
+{
+	const int STACKTRACE_SIZE = 100;
+	void * buffer[STACKTRACE_SIZE];
+	int ptrCount = backtrace(buffer, STACKTRACE_SIZE);
+	char ** strings;
+
+	logGlobal->errorStream() << "Error: signal " << sig << ":";
+	strings = backtrace_symbols(buffer, ptrCount);
+	if(strings == nullptr)
+	{
+		logGlobal->errorStream() << "There are no symbols.";
+	}
+	else
+	{
+		for(int i = 0; i < ptrCount; ++i)
+		{
+			logGlobal->errorStream() << strings[i];
+		}
+		free(strings);
+	}
+
+	_exit(EXIT_FAILURE);
+}
+#endif
+
 int main(int argc, char** argv)
 {
+	// Installs a sig sev segmentation violation handler
+	// to log stacktrace
+	#ifdef __GNUC__
+	signal(SIGSEGV, handleLinuxSignal);
+	#endif
+
 	console = new CConsoleHandler;
 	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Server_log.txt", console);
 	logConfig.configureDefault();
@@ -564,7 +601,11 @@ int main(int argc, char** argv)
 		{
             logNetwork->errorStream() << e.what();
 			end2 = true;
-		}HANDLE_EXCEPTION
+		}
+		catch(...)
+		{
+			handleException();
+		}
 	}
 	catch(boost::system::system_error &e)
 	{