Browse Source

#1409 should not crash anymore.
Fixed crash on serializing empty path. [How did it got there...?]
operator<< for boost::optional.

Michał W. Urbańczyk 12 years ago
parent
commit
3b42cff3ec
5 changed files with 69 additions and 6 deletions
  1. 40 4
      AI/VCAI/VCAI.cpp
  2. 10 0
      Global.h
  3. 6 1
      client/CPlayerInterface.cpp
  4. 9 0
      client/Client.cpp
  5. 4 1
      lib/NetPacksLib.cpp

+ 40 - 4
AI/VCAI/VCAI.cpp

@@ -907,6 +907,7 @@ void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
 {
 	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
 	NET_EVENT_HANDLER;
+	validateVisitableObjs();
 	CAdventureAI::saveGame(h, version);
 	serializeInternal(h, version);
 }
@@ -1018,6 +1019,11 @@ void VCAI::makeTurnInternal()
 			boost::sort (hero.second, isCloser);
 			for (auto obj : hero.second)
 			{
+				if(!obj || !obj->defInfo || !cb->getObj(obj->id))
+				{
+					logAi->errorStream() << "Error: there is wrong object on list for hero " << hero.first->name;
+					continue;
+				}
 				striveToGoal (CGoal(VISIT_TILE).sethero(hero.first).settile(obj->visitablePos()));
 			}
 		}
@@ -1102,7 +1108,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 
 void VCAI::moveCreaturesToHero(const CGTownInstance * t)
 {
-	if(t->visitingHero && t->armedGarrison())
+	if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner)
 	{
 		pickBestCreatures (t->visitingHero, t);
 	}
@@ -1110,6 +1116,13 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t)
 
 bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * source)
 { //TODO: merge with pickBestCreatures
+	if(army->tempOwner != source->tempOwner)
+	{
+		logAi->errorStream() << "Why are we even considering exchange between heroes from different players?";
+		return false;
+	}
+
+
 	const CArmedInstance *armies[] = {army, source};
 	int armySize = 0; 
 	//we calculate total strength for each creature type available in armies
@@ -1438,6 +1451,7 @@ void VCAI::wander(HeroPtr h)
 {
 	while(1)
 	{
+		validateVisitableObjs();
 		std::vector <ObjectIdRef> dests;
 		range::copy(reservedHeroesMap[h], std::back_inserter(dests));
 		if (!dests.size())
@@ -1598,21 +1612,41 @@ void VCAI::reserveObject(HeroPtr h, const CGObjectInstance *obj)
 {
 	reservedObjs.push_back(obj);
 	reservedHeroesMap[h].push_back(obj);
+	logAi->debugStream() << "reserved object id=" << obj->id << "; address=" << (int)obj << "; name=" << obj->getHoverText();
 }
 
 void VCAI::validateVisitableObjs()
 {
 	std::vector<const CGObjectInstance *> hlp;
 	retreiveVisitableObjs(hlp, true);
-	erase_if(visitableObjs, [&](const CGObjectInstance *obj) -> bool
+
+	std::string errorMsg;
+	auto shouldBeErased = [&](const CGObjectInstance *obj) -> bool
 	{
 		if(!vstd::contains(hlp, obj))
 		{
-            logAi->errorStream() << helperObjInfo[obj].name << " at " << helperObjInfo[obj].pos << " shouldn't be on list!";
+			logAi->errorStream() << helperObjInfo[obj].name << " at " << helperObjInfo[obj].pos << errorMsg;
 			return true;
 		}
 		return false;
-	});
+	};
+
+	//errorMsg is captured by ref so lambda will take the new text
+	errorMsg = " shouldn't be on the visitable objects list!";
+	erase_if(visitableObjs, shouldBeErased);
+
+	for(auto &p : reservedHeroesMap)
+	{
+		errorMsg = " shouldn't be on list for hero " + p.first->name + "!";
+		erase_if(p.second, shouldBeErased);
+	}
+
+	errorMsg = " shouldn't be on the reserved objs list!";
+	erase_if(reservedObjs, shouldBeErased);
+
+	//TODO overkill, hidden object should not be removed. However, we can't know if hidden object is erased from game.
+	errorMsg = " shouldn't be on the already visited objs list!";
+	erase_if(alreadyVisited, shouldBeErased);
 }
 
 void VCAI::retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned /*= false*/) const
@@ -2575,6 +2609,8 @@ void VCAI::validateObject(ObjectIdRef obj)
 
 		for(auto &p : reservedHeroesMap)
 			erase_if(p.second, matchesId);
+
+		erase_if(reservedObjs, matchesId);
 	}
 }
 

+ 10 - 0
Global.h

@@ -268,6 +268,16 @@ public:
 
 };
 
+template<typename T>
+std::ostream &operator<<(std::ostream &out, const boost::optional<T> &opt)
+{
+	if(opt)
+		return out << *opt;
+	else
+		return out<< "empty";
+}
+
+
 namespace vstd
 {
 	

+ 6 - 1
client/CPlayerInterface.cpp

@@ -1183,7 +1183,12 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
 	if(h.saving)
 	{
 		for(auto &p : paths)
-			pathsMap[p.first] = p.second.endPos();
+		{
+			if(p.second.nodes.size())
+				pathsMap[p.first] = p.second.endPos();
+			else
+				logGlobal->errorStream() << p.first->name << " has assigned an empty path! Ignoring it...";
+		}
 		h & pathsMap;
 	}
 	else

+ 9 - 0
client/Client.cpp

@@ -290,6 +290,15 @@ void CClient::loadGame( const std::string & fname )
 	serv->enableStackSendingByID();
 	serv->disableSmartPointerSerialization();
 
+// 	logGlobal->traceStream() << "Objects:";
+// 	for(int i = 0; i < gs->map->objects.size(); i++)
+// 	{
+// 		auto o = gs->map->objects[i];
+// 		if(o)
+// 			logGlobal->traceStream() << boost::format("\tindex=%5d, id=%5d; address=%5d, pos=%s, name=%s") % i % o->id % (int)o.get() % o->pos % o->getHoverText();
+// 		else
+// 			logGlobal->traceStream() << boost::format("\tindex=%5d --- nullptr") % i;
+// 	}
 }
 
 void CClient::newGame( CConnection *con, StartInfo *si )

+ 4 - 1
lib/NetPacksLib.cpp

@@ -300,9 +300,9 @@ DLL_LINKAGE void RemoveBonus::applyGs( CGameState *gs )
 
 DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
 {
-	logGlobal->debugStream() << "removing object oid=" << id;
 
 	CGObjectInstance *obj = gs->getObjInstance(id);
+	logGlobal->debugStream() << "removing object id=" << id << "; address=" << (int)obj << "; name=" << obj->getHoverText();
 	//unblock tiles
 	if(obj->defInfo)
 	{
@@ -594,7 +594,10 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs )
 	gs->map->addBlockVisTiles(o);
 	o->initObj();
 	assert(o->defInfo);
+
+	logGlobal->debugStream() << "added object id=" << id << "; address=" << (int)o << "; name=" << o->getHoverText();
 }
+
 DLL_LINKAGE void NewArtifact::applyGs( CGameState *gs )
 {
 	assert(!vstd::contains(gs->map->artInstances, art));