1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +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)); cl->calculatePaths(static_cast<const CGHeroInstance *>(obj));
//nasty workaround. TODO: nice workaround //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 += boost::bind(&CCallback::save, LOCPLINT->cb, saveGameName);
overWrite += bind(&CGuiHandler::popIntTotally, &GH, this); 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? std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?
boost::algorithm::replace_first(hlp, "%s", sel->txt->text); boost::algorithm::replace_first(hlp, "%s", sel->txt->text);
@ -1124,13 +1124,13 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
try try
{ {
CLoadFile lf(CResourceHandler::get()->getResourceName(files[i])); CLoadFile lf(CResourceHandler::get()->getResourceName(files[i]));
lf.checkMagicBytes(SAVEGAME_MAGIC);
ui8 sign[8]; // ui8 sign[8];
lf >> sign; // lf >> sign;
if(std::memcmp(sign,"VCMISVG",7)) // if(std::memcmp(sign,"VCMISVG",7))
{ // {
throw std::runtime_error("not a correct savefile!"); // throw std::runtime_error("not a correct savefile!");
} // }
// Create the map info object // Create the map info object
CMapInfo mapInfo; CMapInfo mapInfo;
@ -1207,7 +1207,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
case CMenuScreen::loadGame: case CMenuScreen::loadGame:
case CMenuScreen::saveGame: case CMenuScreen::saveGame:
parseGames(getFiles("Saves/", EResType::LIB_SAVEGAME), MultiPlayer); parseGames(getFiles("Saves/", EResType::CLIENT_SAVEGAME), MultiPlayer);
if(tabType == CMenuScreen::loadGame) if(tabType == CMenuScreen::loadGame)
{ {
positions = 18; positions = 18;
@ -1343,7 +1343,7 @@ void SelectionTab::select( int position )
if(txt) if(txt)
{ {
std::string filename = CResourceHandler::get()->getResourceName( 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()); txt->setTxt(CFileInfo(filename).getBaseName());
} }
@ -1476,7 +1476,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
else else
{ {
name = CFileInfo(CResourceHandler::get()->getResourceName( name = CFileInfo(CResourceHandler::get()->getResourceName(
ResourceID(currentItem->fileURI, EResType::LIB_SAVEGAME))).getBaseName(); ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
} }
//print name //print name

View File

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

View File

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

View File

@ -781,12 +781,32 @@ void SaveGame::applyCl(CClient *cl)
try try
{ {
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME))); CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
cl->saveCommonState(save);
save << *cl; save << *cl;
} }
catch(std::exception &e) catch(std::exception &e)
{ {
tlog1 << "Failed to save game:" << e.what() << std::endl; 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) 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 else //blit normal object
{ {
const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages; 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 //setting appropriate flag color
if(color < 8 || color==255) 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]; 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() TerrainTile2::TerrainTile2()
:terbitmap(0) :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 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_BLOCKED_POSITIONS;
static const bool MARK_VISITABLE_POSITIONS; static const bool MARK_VISITABLE_POSITIONS;
@ -106,6 +108,7 @@ public:
~CMapHandler(); //d-tor ~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 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 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 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) ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
{ {
auto erasePickedArt = [&]( ArtifactID id ) auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
{
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)
{ {
if (arts->empty()) //restock available arts if (arts->empty()) //restock available arts
{ fillList(*arts, flag);
for (int i = 0; i < allowedArtifacts.size(); ++i)
{
if (allowedArtifacts[i]->aClass == flag)
arts->push_back(allowedArtifacts[i]);
}
}
for (int i = 0; i < arts->size(); ++i) for (int i = 0; i < arts->size(); ++i)
{ {
@ -625,6 +595,8 @@ ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
out.resize (64); out.resize (64);
std::fill_n (out.begin(), 64, artifacts[2]); //Give Grail - this can't be banned (hopefully) 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; std::vector<ConstTransitivePtr<CArtifact> > out;
@ -829,6 +801,55 @@ std::vector<bool> CArtHandler::getDefaultAllowedArtifacts() const
return allowedArtifacts; 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() CArtifactInstance::CArtifactInstance()
{ {
init(); init();

View File

@ -212,6 +212,12 @@ public:
void addBonuses(); void addBonuses();
void clear(); void clear();
void clearHlpLists(); 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 getRandomArt (int flags);
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked = false); ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked = false);
bool legalArtifact(ArtifactID id); bool legalArtifact(ArtifactID id);

View File

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

View File

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

View File

@ -257,7 +257,7 @@ int CGObjectInstance::getOwner() const
return tempOwner; //won't have owner return tempOwner; //won't have owner
} }
CGObjectInstance::CGObjectInstance(): animPhaseShift(rand()%0xff) CGObjectInstance::CGObjectInstance()
{ {
pos = int3(-1,-1,-1); pos = int3(-1,-1,-1);
//std::cout << "Tworze obiekt "<<this<<std::endl; //std::cout << "Tworze obiekt "<<this<<std::endl;
@ -3141,6 +3141,12 @@ void CGCreature::initObj()
amount = c.ammMax; amount = c.ammMax;
else else
amount = c.ammMin + (ran() % (c.ammMax - c.ammMin)); 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; temppower = stacks[SlotID(0)]->count * 1000;
@ -3506,13 +3512,13 @@ void CGResource::initObj()
switch(subID) switch(subID)
{ {
case 6: case 6:
amount = 500 + (rand()%6)*100; amount = 500 + (ran()%6)*100;
break; break;
case 0: case 2: case 0: case 2:
amount = 6 + (rand()%5); amount = 6 + (ran()%5);
break; break;
default: default:
amount = 3 + (rand()%3); amount = 3 + (ran()%3);
break; break;
} }
} }
@ -5894,43 +5900,56 @@ void CBank::reset(ui16 var1) //prevents desync
void CBank::initialize() const 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 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) void CBank::setPropertyDer (ui8 what, ui32 val)
/// random values are passed as arguments and processed identically on all clients /// random values are passed as arguments and processed identically on all clients
{ {
switch (what) switch (what)
{ {
case 11: //daycounter case ObjProperty::BANK_DAYCOUNTER: //daycounter
if (val == 0) if (val == 0)
daycounter = 1; //yes, 1 daycounter = 1; //yes, 1
else else
daycounter++; daycounter++;
break; break;
case 12: //multiplier, in percent case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent
multiplier = val / 100.0; multiplier = val / 100.0;
break; break;
case 13: //bank preset case 13: //bank preset
bc = VLC->objh->banksInfo[index][val]; bc = VLC->objh->banksInfo[index][val];
break; break;
case 14: case ObjProperty::BANK_RESET:
reset (val%100); reset (val%100);
break; break;
case 15: case ObjProperty::BANK_CLEAR_CONFIG:
bc = NULL; bc = NULL;
break; break;
case 16: //remove rewards from Derelict Ship case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship
artifacts.clear(); artifacts.clear();
break; break;
case 17: //set ArmedInstance army case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army
{ {
int upgraded = 0; int upgraded = 0;
if (val%100 < bc->upgradeChance) //once again anti-desync if (val%100 < bc->upgradeChance) //once again anti-desync
@ -5975,28 +5994,9 @@ void CBank::setPropertyDer (ui8 what, ui32 val)
} }
} }
break; break;
case 18: //add Artifact case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact
{ {
int id = cb->getArtSync(val, CArtifact::ART_TREASURE, true); artifacts.push_back (val);
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);
break; break;
} }
} }
@ -6011,15 +6011,15 @@ void CBank::newTurn() const
else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries
{ {
initialize(); 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) 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, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once
cb->setObjProperty (id, 16, 0); cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0);
} }
} }
else else
cb->setObjProperty (id, 11, 1); //daycounter++ cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
} }
} }
bool CBank::wasVisited (TPlayerColor player) const bool CBank::wasVisited (TPlayerColor player) const
@ -6215,7 +6215,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
cb->giveCreatures(this, h, ourArmy, false); 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()) if (available.size())
{ {
bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value? bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value?
spell = (available[rand()%available.size()]); spell = (available[ran()%available.size()]);
} }
else else
{ {
tlog1 <<"No spells available for Pyramid! Object set to empty.\n"; 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 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)); iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
} }
cb->showInfoDialog(&iw); 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 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 ;]) si32 subID; //normal subID (this one from OH3 maps ;])
ObjectInstanceID id;//number of object in map's vector ObjectInstanceID id;//number of object in map's vector
CGDefInfo * defInfo; CGDefInfo * defInfo;
ui8 animPhaseShift;
TPlayerColor tempOwner; TPlayerColor tempOwner;
bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile 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) 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 //definfo is handled by map serializer
} }
protected: protected:
@ -591,7 +590,7 @@ public:
std::vector<CGTownBuilding*> bonusingBuildings; std::vector<CGTownBuilding*> bonusingBuildings;
std::vector<SpellID> possibleSpells, obligatorySpells; std::vector<SpellID> possibleSpells, obligatorySpells;
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild 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); 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; sfile = nullptr;
} }
void CSaveFile::putMagicBytes( const std::string &text )
{
write(text.c_str(), text.length());
}
CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/) CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
{ {
registerTypes(*this); registerTypes(*this);
@ -424,6 +429,14 @@ void CLoadFile::clear()
fileVersion = 0; 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() CTypeList::CTypeList()
{ {
registerTypes(*this); registerTypes(*this);
@ -477,3 +490,47 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
registerVectoredType(&gs->map->quests, &CQuest::qid); registerVectoredType(&gs->map->quests, &CQuest::qid);
smartVectorMembersSerialization = true; 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 "CObjectHandler.h" //for CArmedInstance
#include "Mapping/CCampaignHandler.h" //for CCampaignState #include "Mapping/CCampaignHandler.h" //for CCampaignState
const ui32 version = 737; const ui32 version = 738;
class CConnection; class CConnection;
class CGObjectInstance; class CGObjectInstance;
@ -47,6 +47,8 @@ struct CPack;
extern DLL_LINKAGE LibClasses * VLC; extern DLL_LINKAGE LibClasses * VLC;
namespace mpl = boost::mpl; namespace mpl = boost::mpl;
const std::string SAVEGAME_MAGIC = "VCMISVG";
namespace boost namespace boost
{ {
namespace asio namespace asio
@ -382,7 +384,7 @@ public:
si32 idAsNumber = idToNumber(id); si32 idAsNumber = idToNumber(id);
assert(oInfo.vector); assert(oInfo.vector);
assert(oInfo.vector->size() > idAsNumber); assert(static_cast<si32>(oInfo.vector->size()) > idAsNumber);
return const_cast<T*>((*oInfo.vector)[idAsNumber].get()); return const_cast<T*>((*oInfo.vector)[idAsNumber].get());
} }
@ -1218,6 +1220,8 @@ public:
void openNextFile(const std::string &fname); //throws! void openNextFile(const std::string &fname); //throws!
void clear(); void clear();
void reportState(CLogger &out); void reportState(CLogger &out);
void putMagicBytes(const std::string &text);
}; };
class DLL_LINKAGE CLoadFile class DLL_LINKAGE CLoadFile
@ -1239,6 +1243,22 @@ public:
void openNextFile(const std::string &fname, int minimalVersion); //throws! void openNextFile(const std::string &fname, int minimalVersion); //throws!
void clear(); void clear();
void reportState(CLogger &out); 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; 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> 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> template<typename Der>
std::ostream & operator << (std::ostream & os, BaseForID<Der> id) std::ostream & operator << (std::ostream & os, BaseForID<Der> id)

View File

@ -17,6 +17,9 @@
#include "CBuildingHandler.h" #include "CBuildingHandler.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "CModHandler.h" #include "CModHandler.h"
#include "CDefObjInfoHandler.h"
#include "Connection.h"
/* /*
* IGameCallback.cpp, part of VCMI engine * 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 ) inline TerrainTile * CNonConstInfoCallback::getTile( int3 pos )
{ {
if(!gs->map->isInTheMap(pos)) if(!gs->map->isInTheMap(pos))

View File

@ -58,7 +58,8 @@ class CStackBasicDescriptor;
struct TeamState; struct TeamState;
struct QuestInfo; struct QuestInfo;
class CGCreature; class CGCreature;
class CSaveFile;
class CLoadFile;
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
{ {
@ -176,6 +177,12 @@ public:
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked); //synchronous 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 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); 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 class DLL_LINKAGE CNonConstInfoCallback : public CPrivilagedInfoCallback
@ -235,6 +242,7 @@ public:
virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0; virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0;
virtual void removeArtifact(const ArtifactLocation &al) = 0; virtual void removeArtifact(const ArtifactLocation &al) = 0;
virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0; virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0;
virtual void synchronizeArtifactHandlerLists() = 0;
virtual void showCompInfo(ShowInInfobox * comp)=0; virtual void showCompInfo(ShowInInfobox * comp)=0;
virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;

View File

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

View File

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

View File

@ -1959,39 +1959,39 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
for(int gh = 0; gh < numberOfEvent; ++gh) for(int gh = 0; gh < numberOfEvent; ++gh)
{ {
CCastleEvent * nce = new CCastleEvent(); CCastleEvent nce;
nce->town = nt; nce.town = nt;
nce->name = reader.readString(); nce.name = reader.readString();
nce->message = 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) if(map->version > EMapFormat::AB)
{ {
nce->humanAffected = reader.readUInt8(); nce.humanAffected = reader.readUInt8();
} }
else else
{ {
nce->humanAffected = true; nce.humanAffected = true;
} }
nce->computerAffected = reader.readUInt8(); nce.computerAffected = reader.readUInt8();
nce->firstOccurence = reader.readUInt16(); nce.firstOccurence = reader.readUInt16();
nce->nextOccurence = reader.readUInt8(); nce.nextOccurence = reader.readUInt8();
reader.skip(17); reader.skip(17);
// New buildings // 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) for(int vv = 0; vv < 7; ++vv)
{ {
nce->creatures[vv] = reader.readUInt16(); nce.creatures[vv] = reader.readUInt16();
} }
reader.skip(4); reader.skip(4);
nt->events.push_back(nce); nt->events.push_back(nce);
@ -2072,23 +2072,23 @@ void CMapLoaderH3M::readEvents()
int numberOfEvents = reader.readUInt32(); int numberOfEvents = reader.readUInt32();
for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo) for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo)
{ {
CMapEvent * ne = new CMapEvent(); CMapEvent ne;
ne->name = reader.readString(); ne.name = reader.readString();
ne->message = reader.readString(); ne.message = reader.readString();
readResourses(ne->resources); readResourses(ne.resources);
ne->players = reader.readUInt8(); ne.players = reader.readUInt8();
if(map->version > EMapFormat::AB) if(map->version > EMapFormat::AB)
{ {
ne->humanAffected = reader.readUInt8(); ne.humanAffected = reader.readUInt8();
} }
else else
{ {
ne->humanAffected = true; ne.humanAffected = true;
} }
ne->computerAffected = reader.readUInt8(); ne.computerAffected = reader.readUInt8();
ne->firstOccurence = reader.readUInt16(); ne.firstOccurence = reader.readUInt16();
ne->nextOccurence = reader.readUInt8(); ne.nextOccurence = reader.readUInt8();
reader.skip(17); 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 struct RemoveObject : public CPackForClient //500
{ {
RemoveObject(){type = 500;}; RemoveObject(){type = 500;};
@ -1133,8 +1171,11 @@ namespace ObjProperty
//town-specific //town-specific
STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state 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"; 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 ) DLL_LINKAGE void HeroVisitCastle::applyGs( CGameState *gs )
{ {
CGHeroInstance *h = gs->getHero(hid); CGHeroInstance *h = gs->getHero(hid);
@ -794,7 +814,7 @@ DLL_LINKAGE void MoveArtifact::applyGs( CGameState *gs )
{ {
CGHeroInstance *h = *hPtr; CGHeroInstance *h = *hPtr;
if(h && !h->hasSpellbook()) 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<RemoveBonus>();
s.template registerType<UpdateCampaignState>(); s.template registerType<UpdateCampaignState>();
s.template registerType<PrepareForAdvancingCampaign>(); s.template registerType<PrepareForAdvancingCampaign>();
s.template registerType<UpdateArtHandlerLists>();
s.template registerType<UpdateMapEvents>();
s.template registerType<UpdateCastleEvents>();
s.template registerType<RemoveObject>(); s.template registerType<RemoveObject>();
s.template registerType<TryMoveHero>(); s.template registerType<TryMoveHero>();
//s.template registerType<SetGarrisons>(); //s.template registerType<SetGarrisons>();

View File

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

View File

@ -159,6 +159,7 @@ public:
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE; void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE;
void removeArtifact(const ArtifactLocation &al) OVERRIDE; void removeArtifact(const ArtifactLocation &al) OVERRIDE;
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE; bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE;
void synchronizeArtifactHandlerLists() OVERRIDE;
void showCompInfo(ShowInInfobox * comp) OVERRIDE; void showCompInfo(ShowInInfobox * comp) OVERRIDE;
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) 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 c >> clients >> fname; //how many clients should be connected - TODO: support more than one
{ // {
char sig[8]; // char sig[8];
CMapHeader dum; // CMapHeader dum;
StartInfo *si; // StartInfo *si;
//
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME))); // CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
lf >> sig >> dum >> si; // lf >> sig >> dum >> si;
tlog0 <<"Reading save signature"<<std::endl; // tlog0 <<"Reading save signature"<<std::endl;
//
lf >> *VLC; // lf >> *VLC;
tlog0 <<"Reading handlers"<<std::endl; // tlog0 <<"Reading handlers"<<std::endl;
//
lf >> (gh.gs); // lf >> (gh.gs);
c.addStdVecItems(gh.gs); // c.addStdVecItems(gh.gs);
tlog0 <<"Reading gamestate"<<std::endl; // tlog0 <<"Reading gamestate"<<std::endl;
} // }
{ {
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME))); CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
gh.loadCommonState(lf);
lf >> gh; lf >> gh;
} }