Browse Source

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.

Michał W. Urbańczyk 12 years ago
parent
commit
d45a554fec

+ 1 - 1
CCallback.cpp

@@ -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;
 	}
 }
 

+ 11 - 11
client/CPreGame.cpp

@@ -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

+ 14 - 20
client/Client.cpp

@@ -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 )

+ 1 - 0
client/Client.h

@@ -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 {};

+ 20 - 0
client/NetPacksClient.cpp

@@ -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)

+ 14 - 1
client/mapHandler.cpp

@@ -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)
 {}

+ 3 - 0
client/mapHandler.h

@@ -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

+ 53 - 32
lib/CArtHandler.cpp

@@ -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();

+ 6 - 0
lib/CArtHandler.h

@@ -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);

+ 22 - 21
lib/CGameState.cpp

@@ -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);

+ 1 - 1
lib/CGameState.h

@@ -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;

+ 43 - 43
lib/CObjectHandler.cpp

@@ -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
-		{
-			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
+		case ObjProperty::BANK_ADD_ARTIFACT: //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

+ 2 - 3
lib/CObjectHandler.h

@@ -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);
 
 	//////////////////////////////////////////////////////////////////////////

+ 57 - 0
lib/Connection.cpp

@@ -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);
+}

+ 22 - 2
lib/Connection.h

@@ -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;

+ 1 - 1
lib/GameConstants.h

@@ -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)

+ 45 - 0
lib/IGameCallback.cpp

@@ -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))

+ 9 - 1
lib/IGameCallback.h

@@ -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;

+ 0 - 5
lib/Mapping/CMap.cpp

@@ -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)

+ 1 - 1
lib/Mapping/CMap.h

@@ -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;

+ 25 - 25
lib/Mapping/MapFormatH3M.cpp

@@ -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);
 

+ 42 - 1
lib/NetPacks.h

@@ -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
 	};
 }
 

+ 21 - 1
lib/NetPacksLib.cpp

@@ -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);
 		}
 	}
 }

+ 3 - 0
lib/RegisterTypes.h

@@ -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>();

+ 67 - 45
server/CGameHandler.cpp

@@ -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();

+ 1 - 0
server/CGameHandler.h

@@ -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;

+ 17 - 16
server/CVCMIServer.cpp

@@ -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;
 	}