Преглед на файлове

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 години
родител
ревизия
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;
 	}