1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +02:00

Significant changes to saving system. Now both client and server store their lib part.

Desync detection upon loading. Fixed many desyncs. (more remain)
Monsters won't have creature count 0 even if that is set as creature properties.
This commit is contained in:
Michał W. Urbańczyk
2013-02-18 22:37:22 +00:00
parent 2e385375b7
commit d45a554fec
27 changed files with 502 additions and 231 deletions

View File

@ -246,7 +246,7 @@ void CCallback::setSelection(const CArmedInstance * obj)
cl->calculatePaths(static_cast<const CGHeroInstance *>(obj));
//nasty workaround. TODO: nice workaround
cl->gs->getPlayer(*player)->currentSelection = obj->id;
//cl->gs->getPlayer(*player)->currentSelection = obj->id;
}
}

View File

@ -905,7 +905,7 @@ void CSelectionScreen::startScenario()
overWrite += boost::bind(&CCallback::save, LOCPLINT->cb, saveGameName);
overWrite += bind(&CGuiHandler::popIntTotally, &GH, this);
if(CResourceHandler::get()->existsResource(ResourceID(saveGameName, EResType::LIB_SAVEGAME)))
if(CResourceHandler::get()->existsResource(ResourceID(saveGameName, EResType::CLIENT_SAVEGAME)))
{
std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?
boost::algorithm::replace_first(hlp, "%s", sel->txt->text);
@ -1124,13 +1124,13 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
try
{
CLoadFile lf(CResourceHandler::get()->getResourceName(files[i]));
ui8 sign[8];
lf >> sign;
if(std::memcmp(sign,"VCMISVG",7))
{
throw std::runtime_error("not a correct savefile!");
}
lf.checkMagicBytes(SAVEGAME_MAGIC);
// ui8 sign[8];
// lf >> sign;
// if(std::memcmp(sign,"VCMISVG",7))
// {
// throw std::runtime_error("not a correct savefile!");
// }
// Create the map info object
CMapInfo mapInfo;
@ -1207,7 +1207,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
case CMenuScreen::loadGame:
case CMenuScreen::saveGame:
parseGames(getFiles("Saves/", EResType::LIB_SAVEGAME), MultiPlayer);
parseGames(getFiles("Saves/", EResType::CLIENT_SAVEGAME), MultiPlayer);
if(tabType == CMenuScreen::loadGame)
{
positions = 18;
@ -1343,7 +1343,7 @@ void SelectionTab::select( int position )
if(txt)
{
std::string filename = CResourceHandler::get()->getResourceName(
ResourceID(curItems[py]->fileURI, EResType::LIB_SAVEGAME));
ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
txt->setTxt(CFileInfo(filename).getBaseName());
}
@ -1476,7 +1476,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
else
{
name = CFileInfo(CResourceHandler::get()->getResourceName(
ResourceID(currentItem->fileURI, EResType::LIB_SAVEGAME))).getBaseName();
ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
}
//print name

View File

@ -232,29 +232,27 @@ void CClient::loadGame( const std::string & fname )
CStopWatch tmh;
{
char sig[8];
CMapHeader dum;
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
StartInfo *si;
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
lf >> sig >> dum >> si;
tlog0 <<"Reading save signature: "<<tmh.getDiff()<<std::endl;
lf >> *VLC;
const_cast<CGameInfo*>(CGI)->setFromLib();
tlog0 <<"Reading handlers: "<<tmh.getDiff()<<std::endl;
lf >> gs;
tlog0 <<"Reading gamestate: "<<tmh.getDiff()<<std::endl;
auto clientSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
auto controlServerSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME));
unique_ptr<CLoadFile> loader;
{
CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName);
loadCommonState(checkingLoader);
loader = checkingLoader.decay();
}
tlog0 << "Loaded common part of save " << tmh.getDiff()<<std::endl;
const_cast<CGameInfo*>(CGI)->state = gs;
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1));
CGI->mh->init();
tlog0 <<"Initing maphandler: "<<tmh.getDiff()<<std::endl;
*loader >> *this;
tlog0 << "Loaded client part of save " << tmh.getDiff()<<std::endl;
}
serv = sh.connectToServer();
serv->addStdVecItems(gs);
@ -280,10 +278,6 @@ void CClient::loadGame( const std::string & fname )
serv->enableStackSendingByID();
serv->disableSmartPointerSerialization();
{
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)));
lf >> *this;
}
}
void CClient::newGame( CConnection *con, StartInfo *si )

View File

@ -191,6 +191,7 @@ public:
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE {};
void removeArtifact(const ArtifactLocation &al) OVERRIDE {};
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE {return false;};
void synchronizeArtifactHandlerLists() OVERRIDE {};
void showCompInfo(ShowInInfobox * comp) OVERRIDE {};
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {};

View File

@ -781,12 +781,32 @@ void SaveGame::applyCl(CClient *cl)
try
{
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
cl->saveCommonState(save);
save << *cl;
}
catch(std::exception &e)
{
tlog1 << "Failed to save game:" << e.what() << std::endl;
}
// try
// {
// auto clientPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME));
// auto libPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME));
// CLoadIntegrityValidator checker(clientPart, libPart);
//
// CMapHeader mh;
// StartInfo *si;
// LibClasses *lib;
// CGameState *game;
//
// checker.checkMagicBytes(SAVEGAME_MAGIC);
// checker >> mh >> si >> lib >> game;
// }
// catch(...)
// {
// tlog1 << "Desync!!!\n";
// }
}
void PlayerMessage::applyCl(CClient *cl)

View File

@ -646,7 +646,7 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
else //blit normal object
{
const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages;
SDL_Surface *bitmap = ourImages[(anim+obj->animPhaseShift)%ourImages.size()].bitmap;
SDL_Surface *bitmap = ourImages[(anim+getPhaseShift(obj))%ourImages.size()].bitmap;
//setting appropriate flag color
if(color < 8 || color==255)
@ -1101,6 +1101,19 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
out = CGI->generaltexth->terrainNames[t.terType];
}
ui8 CMapHandler::getPhaseShift(const CGObjectInstance *object) const
{
auto i = animationPhase.find(object);
if(i == animationPhase.end())
{
ui8 ret = rand() % 255;
animationPhase[object] = ret;
return ret;
}
return i->second;
}
TerrainTile2::TerrainTile2()
:terbitmap(0)
{}

View File

@ -99,6 +99,8 @@ public:
std::vector<std::vector<std::vector<ui8> > > hideBitmap; //specifies number of graphic that should be used to fully hide a tile
mutable std::map<const CGObjectInstance*, ui8> animationPhase;
static const bool MARK_BLOCKED_POSITIONS;
static const bool MARK_VISITABLE_POSITIONS;
@ -106,6 +108,7 @@ public:
~CMapHandler(); //d-tor
std::pair<SDL_Surface *, bool> getVisBitmap(const int3 & pos, const std::vector< std::vector< std::vector<ui8> > > & visibilityMap) const; //returns appropriate bitmap and info if alpha blitting is necessary
ui8 getPhaseShift(const CGObjectInstance *object) const;
std::vector< std::string > getObjDescriptions(int3 pos); //returns desriptions of objects blocking given position
void getTerrainDescr(const int3 &pos, std::string & out, bool terName); //if tername == false => empty string when tile is clear

View File

@ -561,40 +561,10 @@ ArtifactID CArtHandler::getRandomArt(int flags)
}
ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
{
auto erasePickedArt = [&]( ArtifactID id )
{
std::vector<CArtifact*>* ptr;
CArtifact *art = artifacts[id];
switch (art->aClass)
{
case CArtifact::ART_TREASURE:
ptr = &treasures;
break;
case CArtifact::ART_MINOR:
ptr = &minors;
break;
case CArtifact::ART_MAJOR:
ptr = &majors;
break;
case CArtifact::ART_RELIC:
ptr = &relics;
break;
default: //special artifacts should not be erased
return;
}
ptr->erase (std::find(ptr->begin(), ptr->end(), art)); //remove the artifact from available list
};
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag)
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
{
if (arts->empty()) //restock available arts
{
for (int i = 0; i < allowedArtifacts.size(); ++i)
{
if (allowedArtifacts[i]->aClass == flag)
arts->push_back(allowedArtifacts[i]);
}
}
fillList(*arts, flag);
for (int i = 0; i < arts->size(); ++i)
{
@ -625,6 +595,8 @@ ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
out.resize (64);
std::fill_n (out.begin(), 64, artifacts[2]); //Give Grail - this can't be banned (hopefully)
}
tlog0 << "Treasure count: " << treasures.size() << std::endl;
};
std::vector<ConstTransitivePtr<CArtifact> > out;
@ -829,6 +801,55 @@ std::vector<bool> CArtHandler::getDefaultAllowedArtifacts() const
return allowedArtifacts;
}
void CArtHandler::erasePickedArt(ArtifactID id)
{
CArtifact *art = artifacts[id];
if(auto artifactList = listFromClass(art->aClass))
{
if(artifactList->empty())
fillList(*artifactList, art->aClass);
auto itr = vstd::find(*artifactList, art);
if(itr != artifactList->end())
{
artifactList->erase(itr);
}
else
tlog2 << "Problem: cannot erase artifact " << art->Name() << " from list, it was not present\n";
}
else
tlog2 << "Problem: cannot find list for artifact " << art->Name() << ", strange class. (special?)\n";
}
boost::optional<std::vector<CArtifact*>&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass )
{
switch(artifactClass)
{
case CArtifact::ART_TREASURE:
return treasures;
case CArtifact::ART_MINOR:
return minors;
case CArtifact::ART_MAJOR:
return majors;
case CArtifact::ART_RELIC:
return relics;
default: //special artifacts should not be erased
return 0;
}
}
void CArtHandler::fillList( std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass )
{
assert(listToBeFilled.empty());
for (int i = 0; i < allowedArtifacts.size(); ++i)
{
if (allowedArtifacts[i]->aClass == artifactClass)
listToBeFilled.push_back(allowedArtifacts[i]);
}
}
CArtifactInstance::CArtifactInstance()
{
init();

View File

@ -212,6 +212,12 @@ public:
void addBonuses();
void clear();
void clearHlpLists();
//void restockArtifactList()''
void fillList(std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects
boost::optional<std::vector<CArtifact*>&> listFromClass(CArtifact::EartClass artifactClass);
void erasePickedArt(ArtifactID id);
ArtifactID getRandomArt (int flags);
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked = false);
bool legalArtifact(ArtifactID id);

View File

@ -56,6 +56,7 @@ void foofoofoo()
registerTypes((COSer<CConnection>&)*ccc);
registerTypes((CSaveFile&)*ccc);
registerTypes((CLoadFile&)*ccc);
registerTypes((CLoadIntegrityValidator&)*ccc);
registerTypes((CTypeList&)*ccc);
}
@ -815,7 +816,7 @@ void CGameState::init(StartInfo * si)
}
break;
case CScenarioTravel::STravelBonus::ARTIFACT:
gs->giveHeroArtifact(hero, curBonus->info2);
gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
break;
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
{
@ -962,8 +963,8 @@ void CGameState::init(StartInfo * si)
day = 0;
tlog4 << "Initialization:";
tlog4 << "\tPicking grail position";
tlog4 << "Initialization:\n";
tlog4 << "\tPicking grail position\n";
//pick grail location
if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
{
@ -1003,7 +1004,7 @@ void CGameState::init(StartInfo * si)
}
//picking random factions for players
tlog4 << "\tPicking random factions for players";
tlog4 << "\tPicking random factions for players\n";
for(auto it = scenarioOps->playerInfos.begin();
it != scenarioOps->playerInfos.end(); ++it)
{
@ -1018,7 +1019,7 @@ void CGameState::init(StartInfo * si)
}
//randomizing objects
tlog4 << "\tRandomizing objects";
tlog4 << "\tRandomizing objects\n";
BOOST_FOREACH(CGObjectInstance *obj, map->objects)
{
if(!obj)
@ -1040,7 +1041,7 @@ void CGameState::init(StartInfo * si)
//std::cout<<"\tRandomizing objects: "<<th.getDif()<<std::endl;
/*********creating players entries in gs****************************************/
tlog4 << "\tCreating player entries in gs";
tlog4 << "\tCreating player entries in gs\n";
for(auto it = scenarioOps->playerInfos.begin();
it != scenarioOps->playerInfos.end(); ++it)
{
@ -1054,7 +1055,7 @@ void CGameState::init(StartInfo * si)
}
/*********give starting hero****************************************/
tlog4 << "\tGiving starting hero";
tlog4 << "\tGiving starting hero\n";
{
bool campaignGiveHero = false;
if(scenarioOps->campState)
@ -1093,7 +1094,7 @@ void CGameState::init(StartInfo * si)
}
/*************************replace hero placeholders*****************************/
tlog4 << "\tReplacing hero placeholders";
tlog4 << "\tReplacing hero placeholders\n";
std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > campHeroReplacements; //instance, id in vector
if (scenarioOps->campState)
{
@ -1186,7 +1187,7 @@ void CGameState::init(StartInfo * si)
}
/******************RESOURCES****************************************************/
tlog4 << "\tSetting up resources";
tlog4 << "\tSetting up resources\n";
const JsonNode config(ResourceID("config/startres.json"));
const JsonVector &vector = config["difficulty"].Vector();
const JsonNode &level = vector[scenarioOps->difficulty];
@ -1240,7 +1241,7 @@ void CGameState::init(StartInfo * si)
/*************************HEROES************************************************/
tlog4 << "\tSetting up heroes";
tlog4 << "\tSetting up heroes\n";
//Replace placeholders with heroes from previous missions
BOOST_FOREACH(auto obj, campHeroReplacements)
{
@ -1374,7 +1375,7 @@ void CGameState::init(StartInfo * si)
}
/*************************FOG**OF**WAR******************************************/
tlog4 << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
tlog4 << "\tFog of war\n"; //FIXME: should be initialized after all bonuses are set
for(auto k=teams.begin(); k!=teams.end(); ++k)
{
k->second.fogOfWarMap.resize(map->width);
@ -1403,7 +1404,7 @@ void CGameState::init(StartInfo * si)
}
}
tlog4 << "\tStarting bonuses";
tlog4 << "\tStarting bonuses\n";
for(auto k=players.begin(); k!=players.end(); ++k)
{
//starting bonus
@ -1445,7 +1446,7 @@ void CGameState::init(StartInfo * si)
}
}
/****************************TOWNS************************************************/
tlog4 << "\tTowns";
tlog4 << "\tTowns\n";
//campaign bonuses for towns
if (scenarioOps->mode == StartInfo::CAMPAIGN)
@ -1519,16 +1520,16 @@ void CGameState::init(StartInfo * si)
}
//town events
BOOST_FOREACH(CCastleEvent *ev, vti->events)
BOOST_FOREACH(CCastleEvent &ev, vti->events)
{
for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
if (vstd::contains(ev->buildings,(-31-i))) //if we have horde for this level
if (vstd::contains(ev.buildings,(-31-i))) //if we have horde for this level
{
ev->buildings.erase(BuildingID(-31-i));
ev.buildings.erase(BuildingID(-31-i));
if (vti->town->hordeLvl[0] == i)
ev->buildings.insert(BuildingID::HORDE_1);
ev.buildings.insert(BuildingID::HORDE_1);
if (vti->town->hordeLvl[1] == i)
ev->buildings.insert(BuildingID::HORDE_2);
ev.buildings.insert(BuildingID::HORDE_2);
}
}
//init spells
@ -1573,7 +1574,7 @@ void CGameState::init(StartInfo * si)
getPlayer(vti->getOwner())->towns.push_back(vti);
}
tlog4 << "\tObject initialization";
tlog4 << "\tObject initialization\n";
objCaller->preInit();
BOOST_FOREACH(CGObjectInstance *obj, map->objects)
{
@ -1627,7 +1628,7 @@ void CGameState::init(StartInfo * si)
}
tlog4 << "\tChecking objectives";
tlog4 << "\tChecking objectives\n";
map->checkForObjectives(); //needs to be run when all objects are properly placed
int seedAfterInit = ran();
@ -2643,7 +2644,7 @@ void CGameState::attachArmedObjects()
}
}
void CGameState::giveHeroArtifact(CGHeroInstance *h, int aid)
void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid)
{
CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);

View File

@ -394,7 +394,7 @@ public:
void randomizeObject(CGObjectInstance *cur);
std::pair<Obj,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
int pickHero(int owner);
void giveHeroArtifact(CGHeroInstance *h, int aid);
void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid);
void apply(CPack *pack);
BFieldType battleGetBattlefieldType(int3 tile) const;

View File

@ -257,7 +257,7 @@ int CGObjectInstance::getOwner() const
return tempOwner; //won't have owner
}
CGObjectInstance::CGObjectInstance(): animPhaseShift(rand()%0xff)
CGObjectInstance::CGObjectInstance()
{
pos = int3(-1,-1,-1);
//std::cout << "Tworze obiekt "<<this<<std::endl;
@ -3141,6 +3141,12 @@ void CGCreature::initObj()
amount = c.ammMax;
else
amount = c.ammMin + (ran() % (c.ammMax - c.ammMin));
if(!amount) //armies with 0 creatures are illegal
{
tlog2 << "Problem: stack " << nodeName() << " cannot have 0 creatures. Check properties of " << c.nodeName() << std::endl;
amount = 1;
}
}
temppower = stacks[SlotID(0)]->count * 1000;
@ -3506,13 +3512,13 @@ void CGResource::initObj()
switch(subID)
{
case 6:
amount = 500 + (rand()%6)*100;
amount = 500 + (ran()%6)*100;
break;
case 0: case 2:
amount = 6 + (rand()%5);
amount = 6 + (ran()%5);
break;
default:
amount = 3 + (rand()%3);
amount = 3 + (ran()%3);
break;
}
}
@ -5894,43 +5900,56 @@ void CBank::reset(ui16 var1) //prevents desync
void CBank::initialize() const
{
cb->setObjProperty (id, 14, ran()); //synchronous reset
cb->setObjProperty (id, ObjProperty::BANK_RESET, ran()); //synchronous reset
for (ui8 i = 0; i <= 3; i++)
{
for (ui8 n = 0; n < bc->artifacts[i]; n++) //new function using proper randomization algorithm
for (ui8 n = 0; n < bc->artifacts[i]; n++)
{
cb->setObjProperty (id, 18 + i, ran()); //synchronic artifacts
CArtifact::EartClass artClass;
switch(i)
{
case 0: artClass = CArtifact::ART_TREASURE; break;
case 1: artClass = CArtifact::ART_MINOR; break;
case 2: artClass = CArtifact::ART_MAJOR; break;
case 3: artClass = CArtifact::ART_RELIC; break;
default: assert(0); continue;
}
int artID = cb->getArtSync(ran(), artClass, true);
cb->setObjProperty(id, ObjProperty::BANK_ADD_ARTIFACT, artID);
}
}
cb->setObjProperty (id, 17, ran()); //get army
cb->setObjProperty (id, ObjProperty::BANK_INIT_ARMY, ran()); //get army
}
void CBank::setPropertyDer (ui8 what, ui32 val)
/// random values are passed as arguments and processed identically on all clients
{
switch (what)
{
case 11: //daycounter
case ObjProperty::BANK_DAYCOUNTER: //daycounter
if (val == 0)
daycounter = 1; //yes, 1
else
daycounter++;
break;
case 12: //multiplier, in percent
case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent
multiplier = val / 100.0;
break;
case 13: //bank preset
bc = VLC->objh->banksInfo[index][val];
break;
case 14:
case ObjProperty::BANK_RESET:
reset (val%100);
break;
case 15:
case ObjProperty::BANK_CLEAR_CONFIG:
bc = NULL;
break;
case 16: //remove rewards from Derelict Ship
case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship
artifacts.clear();
break;
case 17: //set ArmedInstance army
case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army
{
int upgraded = 0;
if (val%100 < bc->upgradeChance) //once again anti-desync
@ -5975,28 +5994,9 @@ void CBank::setPropertyDer (ui8 what, ui32 val)
}
}
break;
case 18: //add Artifact
case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact
{
int id = cb->getArtSync(val, CArtifact::ART_TREASURE, true);
artifacts.push_back (id);
break;
}
case 19: //add Artifact
{
int id = cb->getArtSync(val, CArtifact::ART_MINOR, true);
artifacts.push_back (id);
break;
}
case 20: //add Artifact
{
int id = cb->getArtSync(val, CArtifact::ART_MAJOR, true);
artifacts.push_back (id);
break;
}
case 21: //add Artifact
{
int id = cb->getArtSync(val, CArtifact::ART_RELIC, true);
artifacts.push_back (id);
artifacts.push_back (val);
break;
}
}
@ -6011,15 +6011,15 @@ void CBank::newTurn() const
else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries
{
initialize();
cb->setObjProperty (id, 11, 0); //daycounter 0
cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 0); //daycounter 0
if (ID == Obj::DERELICT_SHIP && cb->getDate() > 1)
{
cb->setObjProperty (id, 12, 0);//ugly hack to make derelict ships usable only once
cb->setObjProperty (id, 16, 0);
cb->setObjProperty (id, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0);
}
}
else
cb->setObjProperty (id, 11, 1); //daycounter++
cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
}
}
bool CBank::wasVisited (TPlayerColor player) const
@ -6215,7 +6215,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
cb->showInfoDialog(&iw);
cb->giveCreatures(this, h, ourArmy, false);
}
cb->setObjProperty (id, 15, 0); //bc = NULL
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); //bc = NULL
}
}
@ -6227,13 +6227,13 @@ void CGPyramid::initObj()
if (available.size())
{
bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value?
spell = (available[rand()%available.size()]);
spell = (available[ran()%available.size()]);
}
else
{
tlog1 <<"No spells available for Pyramid! Object set to empty.\n";
}
setPropertyDer (17,ran()); //set guards at game start
setPropertyDer (ObjProperty::BANK_INIT_ARMY,ran()); //set guards at game start
}
const std::string & CGPyramid::getHoverText() const
{
@ -6284,7 +6284,7 @@ void CGPyramid::endBattle (const CGHeroInstance *h, const BattleResult *result)
iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
}
cb->showInfoDialog(&iw);
cb->setObjProperty (id, 15, 0);
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0);
}
}
void CGKeys::setPropertyDer (ui8 what, ui32 val) //101-108 - enable key for player 1-8

View File

@ -172,7 +172,6 @@ public:
si32 subID; //normal subID (this one from OH3 maps ;])
ObjectInstanceID id;//number of object in map's vector
CGDefInfo * defInfo;
ui8 animPhaseShift;
TPlayerColor tempOwner;
bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile
@ -211,7 +210,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & hoverName & pos & ID & subID & id & animPhaseShift & tempOwner & blockVisit & defInfo;
h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & defInfo;
//definfo is handled by map serializer
}
protected:
@ -591,7 +590,7 @@ public:
std::vector<CGTownBuilding*> bonusingBuildings;
std::vector<SpellID> possibleSpells, obligatorySpells;
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
std::list<CCastleEvent*> events;
std::list<CCastleEvent> events;
std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond);
//////////////////////////////////////////////////////////////////////////

View File

@ -344,6 +344,11 @@ void CSaveFile::clear()
sfile = nullptr;
}
void CSaveFile::putMagicBytes( const std::string &text )
{
write(text.c_str(), text.length());
}
CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
{
registerTypes(*this);
@ -424,6 +429,14 @@ void CLoadFile::clear()
fileVersion = 0;
}
void CLoadFile::checkMagicBytes( const std::string &text )
{
std::string loaded = text;
read(loaded.c_str(), text.length());
if(loaded != text)
throw std::runtime_error("Magic bytes doesn't match!");
}
CTypeList::CTypeList()
{
registerTypes(*this);
@ -477,3 +490,47 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
registerVectoredType(&gs->map->quests, &CQuest::qid);
smartVectorMembersSerialization = true;
}
CLoadIntegrityValidator::CLoadIntegrityValidator( const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion /*= version*/ )
: foundDesync(false)
{
registerTypes(*this);
primaryFile = make_unique<CLoadFile>(primaryFileName, minimalVersion);
controlFile = make_unique<CLoadFile>(controlFileName, minimalVersion);
}
int CLoadIntegrityValidator::read( const void * data, unsigned size )
{
assert(primaryFile);
assert(controlFile);
if(!size)
return size;
std::vector<ui8> controlData(size);
auto ret = primaryFile->read(data, size);
controlFile->read(controlData.data(), size);
if(!foundDesync && std::memcmp(data, controlData.data(), size))
{
tlog1 << "Desync found! Position: " << primaryFile->sfile->tellg() << std::endl;
foundDesync = true;
//throw std::runtime_error("Savegame dsynchronized!");
}
return ret;
}
unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
{
return std::move(primaryFile);
}
void CLoadIntegrityValidator::checkMagicBytes( const std::string &text )
{
assert(primaryFile);
assert(controlFile);
primaryFile->checkMagicBytes(text);
controlFile->checkMagicBytes(text);
}

View File

@ -34,7 +34,7 @@
#include "CObjectHandler.h" //for CArmedInstance
#include "Mapping/CCampaignHandler.h" //for CCampaignState
const ui32 version = 737;
const ui32 version = 738;
class CConnection;
class CGObjectInstance;
@ -47,6 +47,8 @@ struct CPack;
extern DLL_LINKAGE LibClasses * VLC;
namespace mpl = boost::mpl;
const std::string SAVEGAME_MAGIC = "VCMISVG";
namespace boost
{
namespace asio
@ -382,7 +384,7 @@ public:
si32 idAsNumber = idToNumber(id);
assert(oInfo.vector);
assert(oInfo.vector->size() > idAsNumber);
assert(static_cast<si32>(oInfo.vector->size()) > idAsNumber);
return const_cast<T*>((*oInfo.vector)[idAsNumber].get());
}
@ -1218,6 +1220,8 @@ public:
void openNextFile(const std::string &fname); //throws!
void clear();
void reportState(CLogger &out);
void putMagicBytes(const std::string &text);
};
class DLL_LINKAGE CLoadFile
@ -1239,6 +1243,22 @@ public:
void openNextFile(const std::string &fname, int minimalVersion); //throws!
void clear();
void reportState(CLogger &out);
void checkMagicBytes(const std::string &text);
};
class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator>
{
public:
unique_ptr<CLoadFile> primaryFile, controlFile;
bool foundDesync;
CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
int read(const void * data, unsigned size); //throws!
void checkMagicBytes(const std::string &text);
unique_ptr<CLoadFile> decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore
};
typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service<boost::asio::ip::tcp> > TSocket;

View File

@ -198,7 +198,7 @@ public:
};
template<typename Der>
DLL_LINKAGE std::ostream & operator << (std::ostream & os, BaseForID<Der> id);
std::ostream & operator << (std::ostream & os, BaseForID<Der> id);
template<typename Der>
std::ostream & operator << (std::ostream & os, BaseForID<Der> id)

View File

@ -17,6 +17,9 @@
#include "CBuildingHandler.h"
#include "GameConstants.h"
#include "CModHandler.h"
#include "CDefObjInfoHandler.h"
#include "Connection.h"
/*
* IGameCallback.cpp, part of VCMI engine
@ -210,6 +213,48 @@ void CPrivilagedInfoCallback::getAllowedSpells(std::vector<SpellID> &out, ui16 l
}
}
template<typename Loader>
void CPrivilagedInfoCallback::loadCommonState(Loader &in)
{
tlog0 << "Loading lib part of game...\n";
in.checkMagicBytes(SAVEGAME_MAGIC);
CMapHeader dum;
StartInfo *si;
tlog0 <<"\tReading header"<<std::endl;
in >> dum;
tlog0 << "\tReading options"<<std::endl;
in >> si;
tlog0 <<"\tReading handlers"<<std::endl;
in >> *VLC;
tlog0 <<"\tReading gamestate"<<std::endl;
in >> gs;
}
template<typename Saver>
void CPrivilagedInfoCallback::saveCommonState(Saver &out) const
{
tlog0 << "Saving lib part of game...";
out.putMagicBytes(SAVEGAME_MAGIC);
tlog0 <<"\tSaving header"<<std::endl;
out << static_cast<CMapHeader&>(*gs->map);
tlog0 << "\tSaving options"<<std::endl;
out << gs->scenarioOps;
tlog0 << "\tSaving handlers"<<std::endl;
out << *VLC;
tlog0 << "\tSaving gamestate"<<std::endl;
out << gs;
}
template DLL_LINKAGE void CPrivilagedInfoCallback::loadCommonState<CLoadIntegrityValidator>(CLoadIntegrityValidator&);
template DLL_LINKAGE void CPrivilagedInfoCallback::loadCommonState<CLoadFile>(CLoadFile&);
template DLL_LINKAGE void CPrivilagedInfoCallback::saveCommonState<CSaveFile>(CSaveFile&) const;
inline TerrainTile * CNonConstInfoCallback::getTile( int3 pos )
{
if(!gs->map->isInTheMap(pos))

View File

@ -58,7 +58,8 @@ class CStackBasicDescriptor;
struct TeamState;
struct QuestInfo;
class CGCreature;
class CSaveFile;
class CLoadFile;
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
{
@ -176,6 +177,12 @@ public:
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked); //synchronous
void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
void getAllowedSpells(std::vector<SpellID> &out, ui16 level);
template<typename Saver>
void saveCommonState(Saver &out) const; //stores GS and VLC
template<typename Loader>
void loadCommonState(Loader &in); //loads GS and VLC
};
class DLL_LINKAGE CNonConstInfoCallback : public CPrivilagedInfoCallback
@ -235,6 +242,7 @@ public:
virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0;
virtual void removeArtifact(const ArtifactLocation &al) = 0;
virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0;
virtual void synchronizeArtifactHandlerLists() = 0;
virtual void showCompInfo(ShowInInfobox * comp)=0;
virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;

View File

@ -158,11 +158,6 @@ CMap::~CMap()
}
delete [] terrain;
}
for(std::list<ConstTransitivePtr<CMapEvent> >::iterator i = events.begin(); i != events.end(); i++)
{
i->dellNull();
}
}
void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)

View File

@ -743,7 +743,7 @@ public:
std::vector<bool> allowedAbilities;
/** list of map events */
std::list<ConstTransitivePtr<CMapEvent> > events;
std::list<CMapEvent> events;
/** specifies the position of the grail */
int3 grailPos;

View File

@ -1959,39 +1959,39 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
for(int gh = 0; gh < numberOfEvent; ++gh)
{
CCastleEvent * nce = new CCastleEvent();
nce->town = nt;
nce->name = reader.readString();
nce->message = reader.readString();
CCastleEvent nce;
nce.town = nt;
nce.name = reader.readString();
nce.message = reader.readString();
readResourses(nce->resources);
readResourses(nce.resources);
nce->players = reader.readUInt8();
nce.players = reader.readUInt8();
if(map->version > EMapFormat::AB)
{
nce->humanAffected = reader.readUInt8();
nce.humanAffected = reader.readUInt8();
}
else
{
nce->humanAffected = true;
nce.humanAffected = true;
}
nce->computerAffected = reader.readUInt8();
nce->firstOccurence = reader.readUInt16();
nce->nextOccurence = reader.readUInt8();
nce.computerAffected = reader.readUInt8();
nce.firstOccurence = reader.readUInt16();
nce.nextOccurence = reader.readUInt8();
reader.skip(17);
// New buildings
readBitmask(nce->buildings,6,48,false);
readBitmask(nce.buildings,6,48,false);
nce->buildings = convertBuildings(nce->buildings, castleID, false);
nce.buildings = convertBuildings(nce.buildings, castleID, false);
nce->creatures.resize(7);
nce.creatures.resize(7);
for(int vv = 0; vv < 7; ++vv)
{
nce->creatures[vv] = reader.readUInt16();
nce.creatures[vv] = reader.readUInt16();
}
reader.skip(4);
nt->events.push_back(nce);
@ -2072,23 +2072,23 @@ void CMapLoaderH3M::readEvents()
int numberOfEvents = reader.readUInt32();
for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo)
{
CMapEvent * ne = new CMapEvent();
ne->name = reader.readString();
ne->message = reader.readString();
CMapEvent ne;
ne.name = reader.readString();
ne.message = reader.readString();
readResourses(ne->resources);
ne->players = reader.readUInt8();
readResourses(ne.resources);
ne.players = reader.readUInt8();
if(map->version > EMapFormat::AB)
{
ne->humanAffected = reader.readUInt8();
ne.humanAffected = reader.readUInt8();
}
else
{
ne->humanAffected = true;
ne.humanAffected = true;
}
ne->computerAffected = reader.readUInt8();
ne->firstOccurence = reader.readUInt16();
ne->nextOccurence = reader.readUInt8();
ne.computerAffected = reader.readUInt8();
ne.firstOccurence = reader.readUInt16();
ne.nextOccurence = reader.readUInt8();
reader.skip(17);

View File

@ -577,6 +577,44 @@ struct PrepareForAdvancingCampaign : public CPackForClient //122
}
};
struct UpdateArtHandlerLists : public CPackForClient //123
{
UpdateArtHandlerLists(){type = 123;};
std::vector<CArtifact*> treasures, minors, majors, relics;
DLL_LINKAGE void applyGs(CGameState *gs);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & treasures & minors & majors & relics;
}
};
struct UpdateMapEvents : public CPackForClient //124
{
UpdateMapEvents(){type = 124;}
std::list<CMapEvent> events;
DLL_LINKAGE void applyGs(CGameState *gs);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & events;
}
};
struct UpdateCastleEvents : public CPackForClient //125
{
UpdateCastleEvents(){type = 125;}
ObjectInstanceID town;
std::list<CCastleEvent> events;
DLL_LINKAGE void applyGs(CGameState *gs);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & town & events;
}
};
struct RemoveObject : public CPackForClient //500
{
RemoveObject(){type = 500;};
@ -1133,8 +1171,11 @@ namespace ObjProperty
//town-specific
STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state
BONUS_VALUE_FIRST, BONUS_VALUE_SECOND //used in Rampart for special building that generates resources (storing resource type and quantity)
BONUS_VALUE_FIRST, BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity)
//creature-bank specific
BANK_DAYCOUNTER, BANK_CLEAR_ARTIFACTS, BANK_ADD_ARTIFACT, BANK_MULTIPLIER, BANK_CONFIG_PRESET,
BANK_CLEAR_CONFIG, BANK_INIT_ARMY, BANK_RESET
};
}

View File

@ -116,6 +116,26 @@ DLL_LINKAGE void AddQuest::applyGs(CGameState *gs)
tlog2 << "Warning! Attempt to add duplicated quest\n";
}
DLL_LINKAGE void UpdateArtHandlerLists::applyGs(CGameState *gs)
{
VLC->arth->minors = minors;
VLC->arth->majors = majors;
VLC->arth->treasures = treasures;
VLC->arth->relics = relics;
}
DLL_LINKAGE void UpdateMapEvents::applyGs(CGameState *gs)
{
gs->map->events = events;
}
DLL_LINKAGE void UpdateCastleEvents::applyGs(CGameState *gs)
{
auto t = gs->getTown(town);
t->events = events;
}
DLL_LINKAGE void HeroVisitCastle::applyGs( CGameState *gs )
{
CGHeroInstance *h = gs->getHero(hid);
@ -794,7 +814,7 @@ DLL_LINKAGE void MoveArtifact::applyGs( CGameState *gs )
{
CGHeroInstance *h = *hPtr;
if(h && !h->hasSpellbook())
gs->giveHeroArtifact(h, 0);
gs->giveHeroArtifact(h, ArtifactID::SPELLBOOK);
}
}
}

View File

@ -138,6 +138,9 @@ void registerTypes2(Serializer &s)
s.template registerType<RemoveBonus>();
s.template registerType<UpdateCampaignState>();
s.template registerType<PrepareForAdvancingCampaign>();
s.template registerType<UpdateArtHandlerLists>();
s.template registerType<UpdateMapEvents>();
s.template registerType<UpdateCastleEvents>();
s.template registerType<RemoveObject>();
s.template registerType<TryMoveHero>();
//s.template registerType<SetGarrisons>();

View File

@ -1111,9 +1111,9 @@ void CGameHandler::init(StartInfo *si)
states.addPlayer(i->first);
}
static bool evntCmp(const CMapEvent *a, const CMapEvent *b)
static bool evntCmp(const CMapEvent &a, const CMapEvent &b)
{
return a->earlierThan(*b);
return a.earlierThan(b);
}
void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false)
@ -1501,6 +1501,8 @@ void CGameHandler::newTurn()
sendAndApply(&iw);
}
}
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
}
void CGameHandler::run(bool resume)
{
@ -2306,16 +2308,18 @@ void CGameHandler::save(const std::string & filename )
try
{
{
tlog0 << "Serializing game info...\n";
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME)));
char hlp[8] = "VCMISVG";
save << hlp << static_cast<CMapHeader&>(*gs->map) << gs->scenarioOps << *VLC << gs;
}
// {
// tlog0 << "Serializing game info...\n";
// CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME)));
// // char hlp[8] = "VCMISVG";
// // save << hlp;
// saveCommonState(save);
// }
{
tlog0 << "Serializing server info...\n";
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME)));
saveCommonState(save);
tlog0 << "Saving server state\n";
save << *this;
}
tlog0 << "Game has been successfully saved!\n";
@ -4688,34 +4692,34 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c
void CGameHandler::handleTimeEvents()
{
gs->map->events.sort(evntCmp);
while(gs->map->events.size() && gs->map->events.front()->firstOccurence+1 == gs->day)
while(gs->map->events.size() && gs->map->events.front().firstOccurence+1 == gs->day)
{
CMapEvent *ev = gs->map->events.front();
CMapEvent ev = gs->map->events.front();
for(int player = 0; player < GameConstants::PLAYER_LIMIT; player++)
{
PlayerState *pinfo = gs->getPlayer(player);
if( pinfo //player exists
&& (ev->players & 1<<player) //event is enabled to this player
&& ((ev->computerAffected && !pinfo->human)
|| (ev->humanAffected && pinfo->human)
&& (ev.players & 1<<player) //event is enabled to this player
&& ((ev.computerAffected && !pinfo->human)
|| (ev.humanAffected && pinfo->human)
)
)
{
//give resources
SetResources sr;
sr.player = player;
sr.res = pinfo->resources + ev->resources;
sr.res = pinfo->resources + ev.resources;
//prepare dialog
InfoWindow iw;
iw.player = player;
iw.text << ev->message;
iw.text << ev.message;
for (int i=0; i<ev->resources.size(); i++)
for (int i=0; i<ev.resources.size(); i++)
{
if(ev->resources[i]) //if resource is changed, we add it to the dialog
iw.components.push_back(Component(Component::RESOURCE,i,ev->resources[i],0));
if(ev.resources[i]) //if resource is changed, we add it to the dialog
iw.components.push_back(Component(Component::RESOURCE,i,ev.resources[i],0));
}
if (iw.components.size())
@ -4728,59 +4732,62 @@ void CGameHandler::handleTimeEvents()
}
} //PLAYERS LOOP
if(ev->nextOccurence)
if(ev.nextOccurence)
{
gs->map->events.pop_front();
ev->firstOccurence += ev->nextOccurence;
std::list<ConstTransitivePtr<CMapEvent> >::iterator it = gs->map->events.begin();
while ( it !=gs->map->events.end() && (*it)->earlierThanOrEqual(*ev))
ev.firstOccurence += ev.nextOccurence;
auto it = gs->map->events.begin();
while ( it !=gs->map->events.end() && it->earlierThanOrEqual(ev))
it++;
gs->map->events.insert(it, ev);
}
else
{
delete ev;
gs->map->events.pop_front();
}
}
//TODO send only if changed
UpdateMapEvents ume;
ume.events = gs->map->events;
sendAndApply(&ume);
}
void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map<ObjectInstanceID, std::map<si32, si32> > &newCreas)
{
//TODO event removing desync!!!
town->events.sort(evntCmp);
while(town->events.size() && town->events.front()->firstOccurence == gs->day)
while(town->events.size() && town->events.front().firstOccurence == gs->day)
{
ui8 player = town->tempOwner;
CCastleEvent *ev = town->events.front();
CCastleEvent ev = town->events.front();
PlayerState *pinfo = gs->getPlayer(player);
if( pinfo //player exists
&& (ev->players & 1<<player) //event is enabled to this player
&& ((ev->computerAffected && !pinfo->human)
|| (ev->humanAffected && pinfo->human) ) )
&& (ev.players & 1<<player) //event is enabled to this player
&& ((ev.computerAffected && !pinfo->human)
|| (ev.humanAffected && pinfo->human) ) )
{
// dialog
InfoWindow iw;
iw.player = player;
iw.text << ev->message;
iw.text << ev.message;
if(ev->resources.nonZero())
if(ev.resources.nonZero())
{
TResources was = n.res[player];
n.res[player] += ev->resources;
n.res[player] += ev.resources;
n.res[player].amax(0);
for (int i=0; i<ev->resources.size(); i++)
if(ev->resources[i] && pinfo->resources[i] != n.res[player][i]) //if resource had changed, we add it to the dialog
for (int i=0; i<ev.resources.size(); i++)
if(ev.resources[i] && pinfo->resources[i] != n.res[player][i]) //if resource had changed, we add it to the dialog
iw.components.push_back(Component(Component::RESOURCE,i,n.res[player][i]-was[i],0));
}
BOOST_FOREACH(auto & i, ev->buildings)
BOOST_FOREACH(auto & i, ev.buildings)
{
if ( town->hasBuilt(i))
{
@ -4789,34 +4796,39 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map<
}
}
for(si32 i=0;i<ev->creatures.size();i++) //creature growths
for(si32 i=0;i<ev.creatures.size();i++) //creature growths
{
if(town->creatureDwellingLevel(i) >= 0 && ev->creatures[i])//there is dwelling
if(town->creatureDwellingLevel(i) >= 0 && ev.creatures[i])//there is dwelling
{
newCreas[town->id][i] += ev->creatures[i];
newCreas[town->id][i] += ev.creatures[i];
iw.components.push_back(Component(Component::CREATURE,
town->creatures[i].second.back(), ev->creatures[i], 0));
town->creatures[i].second.back(), ev.creatures[i], 0));
}
}
sendAndApply(&iw); //show dialog
}
if(ev->nextOccurence)
if(ev.nextOccurence)
{
town->events.pop_front();
ev->firstOccurence += ev->nextOccurence;
std::list<CCastleEvent*>::iterator it = town->events.begin();
while ( it !=town->events.end() && (*it)->earlierThanOrEqual(*ev))
ev.firstOccurence += ev.nextOccurence;
auto it = town->events.begin();
while ( it != town->events.end() && it->earlierThanOrEqual(ev))
it++;
town->events.insert(it, ev);
}
else
{
delete ev;
town->events.pop_front();
}
}
//TODO send only if changed
UpdateCastleEvents uce;
uce.town = town->id;
uce.events = town->events;
sendAndApply(&uce);
}
bool CGameHandler::complain( const std::string &problem )
@ -6182,6 +6194,16 @@ void CGameHandler::removeObstacle(const CObstacleInstance &obstacle)
sendAndApply(&obsRem);
}
void CGameHandler::synchronizeArtifactHandlerLists()
{
UpdateArtHandlerLists uahl;
uahl.treasures = VLC->arth->treasures;
uahl.minors = VLC->arth->minors;
uahl.majors = VLC->arth->majors;
uahl.relics = VLC->arth->relics;
sendAndApply(&uahl);
}
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
{
heroWithDeadCommander = ObjectInstanceID();

View File

@ -159,6 +159,7 @@ public:
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE;
void removeArtifact(const ArtifactLocation &al) OVERRIDE;
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE;
void synchronizeArtifactHandlerLists() OVERRIDE;
void showCompInfo(ShowInInfobox * comp) OVERRIDE;
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;

View File

@ -448,25 +448,26 @@ void CVCMIServer::loadGame()
c >> clients >> fname; //how many clients should be connected - TODO: support more than one
{
char sig[8];
CMapHeader dum;
StartInfo *si;
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
lf >> sig >> dum >> si;
tlog0 <<"Reading save signature"<<std::endl;
lf >> *VLC;
tlog0 <<"Reading handlers"<<std::endl;
lf >> (gh.gs);
c.addStdVecItems(gh.gs);
tlog0 <<"Reading gamestate"<<std::endl;
}
// {
// char sig[8];
// CMapHeader dum;
// StartInfo *si;
//
// CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
// lf >> sig >> dum >> si;
// tlog0 <<"Reading save signature"<<std::endl;
//
// lf >> *VLC;
// tlog0 <<"Reading handlers"<<std::endl;
//
// lf >> (gh.gs);
// c.addStdVecItems(gh.gs);
// tlog0 <<"Reading gamestate"<<std::endl;
// }
{
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
gh.loadCommonState(lf);
lf >> gh;
}