mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-27 22:49:25 +02:00
#1409 should not crash anymore.
Fixed crash on serializing empty path. [How did it got there...?] operator<< for boost::optional.
This commit is contained in:
@@ -907,6 +907,7 @@ void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
|
|||||||
{
|
{
|
||||||
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
|
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
|
||||||
NET_EVENT_HANDLER;
|
NET_EVENT_HANDLER;
|
||||||
|
validateVisitableObjs();
|
||||||
CAdventureAI::saveGame(h, version);
|
CAdventureAI::saveGame(h, version);
|
||||||
serializeInternal(h, version);
|
serializeInternal(h, version);
|
||||||
}
|
}
|
||||||
@@ -1018,6 +1019,11 @@ void VCAI::makeTurnInternal()
|
|||||||
boost::sort (hero.second, isCloser);
|
boost::sort (hero.second, isCloser);
|
||||||
for (auto obj : hero.second)
|
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()));
|
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)
|
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);
|
pickBestCreatures (t->visitingHero, t);
|
||||||
}
|
}
|
||||||
@@ -1110,6 +1116,13 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t)
|
|||||||
|
|
||||||
bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * source)
|
bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * source)
|
||||||
{ //TODO: merge with pickBestCreatures
|
{ //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};
|
const CArmedInstance *armies[] = {army, source};
|
||||||
int armySize = 0;
|
int armySize = 0;
|
||||||
//we calculate total strength for each creature type available in armies
|
//we calculate total strength for each creature type available in armies
|
||||||
@@ -1438,6 +1451,7 @@ void VCAI::wander(HeroPtr h)
|
|||||||
{
|
{
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
|
validateVisitableObjs();
|
||||||
std::vector <ObjectIdRef> dests;
|
std::vector <ObjectIdRef> dests;
|
||||||
range::copy(reservedHeroesMap[h], std::back_inserter(dests));
|
range::copy(reservedHeroesMap[h], std::back_inserter(dests));
|
||||||
if (!dests.size())
|
if (!dests.size())
|
||||||
@@ -1598,21 +1612,41 @@ void VCAI::reserveObject(HeroPtr h, const CGObjectInstance *obj)
|
|||||||
{
|
{
|
||||||
reservedObjs.push_back(obj);
|
reservedObjs.push_back(obj);
|
||||||
reservedHeroesMap[h].push_back(obj);
|
reservedHeroesMap[h].push_back(obj);
|
||||||
|
logAi->debugStream() << "reserved object id=" << obj->id << "; address=" << (int)obj << "; name=" << obj->getHoverText();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::validateVisitableObjs()
|
void VCAI::validateVisitableObjs()
|
||||||
{
|
{
|
||||||
std::vector<const CGObjectInstance *> hlp;
|
std::vector<const CGObjectInstance *> hlp;
|
||||||
retreiveVisitableObjs(hlp, true);
|
retreiveVisitableObjs(hlp, true);
|
||||||
erase_if(visitableObjs, [&](const CGObjectInstance *obj) -> bool
|
|
||||||
|
std::string errorMsg;
|
||||||
|
auto shouldBeErased = [&](const CGObjectInstance *obj) -> bool
|
||||||
{
|
{
|
||||||
if(!vstd::contains(hlp, obj))
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
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
|
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)
|
for(auto &p : reservedHeroesMap)
|
||||||
erase_if(p.second, matchesId);
|
erase_if(p.second, matchesId);
|
||||||
|
|
||||||
|
erase_if(reservedObjs, matchesId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
Global.h
10
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
|
namespace vstd
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -1183,7 +1183,12 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
|
|||||||
if(h.saving)
|
if(h.saving)
|
||||||
{
|
{
|
||||||
for(auto &p : paths)
|
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;
|
h & pathsMap;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -290,6 +290,15 @@ void CClient::loadGame( const std::string & fname )
|
|||||||
serv->enableStackSendingByID();
|
serv->enableStackSendingByID();
|
||||||
serv->disableSmartPointerSerialization();
|
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 )
|
void CClient::newGame( CConnection *con, StartInfo *si )
|
||||||
|
|||||||
@@ -300,9 +300,9 @@ DLL_LINKAGE void RemoveBonus::applyGs( CGameState *gs )
|
|||||||
|
|
||||||
DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
|
DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
|
||||||
{
|
{
|
||||||
logGlobal->debugStream() << "removing object oid=" << id;
|
|
||||||
|
|
||||||
CGObjectInstance *obj = gs->getObjInstance(id);
|
CGObjectInstance *obj = gs->getObjInstance(id);
|
||||||
|
logGlobal->debugStream() << "removing object id=" << id << "; address=" << (int)obj << "; name=" << obj->getHoverText();
|
||||||
//unblock tiles
|
//unblock tiles
|
||||||
if(obj->defInfo)
|
if(obj->defInfo)
|
||||||
{
|
{
|
||||||
@@ -594,7 +594,10 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs )
|
|||||||
gs->map->addBlockVisTiles(o);
|
gs->map->addBlockVisTiles(o);
|
||||||
o->initObj();
|
o->initObj();
|
||||||
assert(o->defInfo);
|
assert(o->defInfo);
|
||||||
|
|
||||||
|
logGlobal->debugStream() << "added object id=" << id << "; address=" << (int)o << "; name=" << o->getHoverText();
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void NewArtifact::applyGs( CGameState *gs )
|
DLL_LINKAGE void NewArtifact::applyGs( CGameState *gs )
|
||||||
{
|
{
|
||||||
assert(!vstd::contains(gs->map->artInstances, art));
|
assert(!vstd::contains(gs->map->artInstances, art));
|
||||||
|
|||||||
Reference in New Issue
Block a user