소스 검색

Merge pull request #3997 from IvanSavenko/serialization_refactor

[1.6] Serialization refactor
Ivan Savenko 1 년 전
부모
커밋
5ecb527252
64개의 변경된 파일413개의 추가작업 그리고 672개의 파일을 삭제
  1. 0 8
      AI/EmptyAI/CEmptyAI.cpp
  2. 0 3
      AI/EmptyAI/CEmptyAI.h
  3. 0 21
      AI/Nullkiller/AIGateway.cpp
  4. 0 22
      AI/Nullkiller/AIGateway.h
  5. 0 14
      AI/Nullkiller/AIUtility.h
  6. 0 6
      AI/Nullkiller/Goals/CGoal.h
  7. 0 14
      AI/VCAI/AIUtility.h
  8. 0 17
      AI/VCAI/Goals/AbstractGoal.h
  9. 0 6
      AI/VCAI/Goals/CGoal.h
  10. 5 5
      AI/VCAI/Goals/Explore.cpp
  11. 0 14
      AI/VCAI/ResourceManager.h
  12. 0 25
      AI/VCAI/VCAI.cpp
  13. 0 97
      AI/VCAI/VCAI.h
  14. 0 14
      client/CPlayerInterface.cpp
  15. 0 4
      client/CPlayerInterface.h
  16. 2 130
      client/Client.cpp
  17. 2 4
      client/Client.h
  18. 0 27
      client/PlayerLocalState.h
  19. 2 1
      lib/CArtHandler.h
  20. 2 1
      lib/CCreatureSet.h
  21. 1 1
      lib/CGameInfoCallback.cpp
  22. 0 24
      lib/CGameInterface.cpp
  23. 0 6
      lib/CGameInterface.h
  24. 20 13
      lib/CGeneralTextHandler.h
  25. 1 0
      lib/CMakeLists.txt
  26. 13 1
      lib/CPlayerState.h
  27. 2 1
      lib/CRandomGenerator.h
  28. 2 2
      lib/IGameCallback.cpp
  29. 2 1
      lib/StartInfo.h
  30. 4 2
      lib/battle/CObstacleInstance.h
  31. 2 1
      lib/bonuses/Bonus.h
  32. 3 3
      lib/bonuses/CBonusSystemNode.h
  33. 3 2
      lib/bonuses/Limiters.h
  34. 4 2
      lib/bonuses/Propagators.h
  35. 3 2
      lib/bonuses/Updaters.h
  36. 2 1
      lib/campaign/CampaignState.h
  37. 4 5
      lib/gameState/CGameState.cpp
  38. 1 1
      lib/gameState/CGameState.h
  39. 2 1
      lib/gameState/CGameStateCampaign.h
  40. 3 1
      lib/gameState/TavernHeroesPool.h
  41. 2 1
      lib/mapObjectConstructors/CBankInstanceConstructor.h
  42. 1 1
      lib/mapObjects/CGCreature.cpp
  43. 1 1
      lib/mapObjects/CGCreature.h
  44. 2 1
      lib/mapObjects/CGObjectInstance.h
  45. 10 9
      lib/mapObjects/CQuest.h
  46. 4 3
      lib/mapObjects/IObjectInterface.h
  47. 1 1
      lib/mapObjects/MiscObjects.h
  48. 0 48
      lib/mapObjects/ObjectTemplate.cpp
  49. 2 5
      lib/mapObjects/ObjectTemplate.h
  50. 3 2
      lib/mapping/CMapHeader.h
  51. 3 1
      lib/mapping/CMapInfo.h
  52. 2 1
      lib/networkPacks/NetPacksBase.h
  53. 3 3
      lib/networkPacks/NetPacksLib.cpp
  54. 2 2
      lib/pathfinder/PathfinderUtil.h
  55. 2 1
      lib/rewardable/Limiter.h
  56. 2 1
      lib/rmg/CMapGenOptions.h
  57. 137 45
      lib/serializer/BinaryDeserializer.h
  58. 120 32
      lib/serializer/BinarySerializer.h
  59. 0 6
      lib/serializer/CSerializer.cpp
  60. 6 1
      lib/serializer/ESerializationVersion.h
  61. 21 0
      lib/serializer/Serializeable.h
  62. 1 1
      lib/spells/AdventureSpellMechanics.cpp
  63. 2 2
      server/CGameHandler.cpp
  64. 1 1
      server/processors/PlayerMessageProcessor.cpp

+ 0 - 8
AI/EmptyAI/CEmptyAI.cpp

@@ -14,14 +14,6 @@
 #include "../../lib/CStack.h"
 #include "../../lib/battle/BattleAction.h"
 
-void CEmptyAI::saveGame(BinarySerializer & h)
-{
-}
-
-void CEmptyAI::loadGame(BinaryDeserializer & h)
-{
-}
-
 void CEmptyAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB)
 {
 	cb = CB;

+ 0 - 3
AI/EmptyAI/CEmptyAI.h

@@ -19,9 +19,6 @@ class CEmptyAI : public CGlobalAI
 	std::shared_ptr<CCallback> cb;
 
 public:
-	void saveGame(BinarySerializer & h) override;
-	void loadGame(BinaryDeserializer & h) override;
-
 	void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
 	void yourTurn(QueryID queryID) override;
 	void yourTacticPhase(const BattleID & battleID, int distance) override;

+ 0 - 21
AI/Nullkiller/AIGateway.cpp

@@ -772,27 +772,6 @@ void AIGateway::showMapObjectSelectDialog(QueryID askID, const Component & icon,
 	requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
 }
 
-void AIGateway::saveGame(BinarySerializer & h)
-{
-	NET_EVENT_HANDLER;
-	nullkiller->memory->removeInvisibleObjects(myCb.get());
-
-	CAdventureAI::saveGame(h);
-	serializeInternal(h);
-}
-
-void AIGateway::loadGame(BinaryDeserializer & h)
-{
-	//NET_EVENT_HANDLER;
-
-	#if 0
-	//disabled due to issue 2890
-	registerGoals(h);
-	#endif // 0
-	CAdventureAI::loadGame(h);
-	serializeInternal(h);
-}
-
 bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
 {
 	if(!obj)

+ 0 - 22
AI/Nullkiller/AIGateway.h

@@ -60,15 +60,6 @@ public:
 	void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
 	void receivedAnswerConfirmation(int answerRequestID, int result);
 	void heroVisit(const CGObjectInstance * obj, bool started);
-
-
-	template<typename Handler> void serialize(Handler & h)
-	{
-		h & battle;
-		h & remainingQueries;
-		h & requestToQueryID;
-		h & havingTurn;
-	}
 };
 
 // The gateway is responsible for AI events handling. Copied from VCAI.h and refined a bit
@@ -120,8 +111,6 @@ public:
 	void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
-	void saveGame(BinarySerializer & h) override; //saving
-	void loadGame(BinaryDeserializer & h) override; //loading
 	void finish() override;
 
 	void availableCreaturesChanged(const CGDwelling * town) override;
@@ -203,17 +192,6 @@ public:
 	void answerQuery(QueryID queryID, int selection);
 	//special function that can be called ONLY from game events handling thread and will send request ASAP
 	void requestActionASAP(std::function<void()> whatToDo);
-
-	template<typename Handler> void serializeInternal(Handler & h)
-	{
-		h & nullkiller->memory->knownTeleportChannels;
-		h & nullkiller->memory->knownSubterraneanGates;
-		h & destinationTeleport;
-		h & nullkiller->memory->visitableObjs;
-		h & nullkiller->memory->alreadyVisited;
-		h & status;
-		h & battlename;
-	}
 };
 
 }

+ 0 - 14
AI/Nullkiller/AIUtility.h

@@ -111,14 +111,6 @@ public:
 	const CGHeroInstance * get(bool doWeExpectNull = false) const;
 	const CGHeroInstance * get(const CPlayerSpecificInfoCallback * cb, bool doWeExpectNull = false) const;
 	bool validAndSet() const;
-
-
-	template<typename Handler> void serialize(Handler & handler)
-	{
-		handler & h;
-		handler & hid;
-		handler & name;
-	}
 };
 
 enum BattleState
@@ -143,12 +135,6 @@ struct ObjectIdRef
 	ObjectIdRef(const CGObjectInstance * obj);
 
 	bool operator<(const ObjectIdRef & rhs) const;
-
-
-	template<typename Handler> void serialize(Handler & h)
-	{
-		h & id;
-	}
 };
 
 template<Obj::Type id>

+ 0 - 6
AI/Nullkiller/Goals/CGoal.h

@@ -36,12 +36,6 @@ namespace Goals
 		{
 			return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
 		}
-		template<typename Handler> void serialize(Handler & h)
-		{
-			h & static_cast<AbstractGoal &>(*this);
-			//h & goalType & isElementar & isAbstract & priority;
-			//h & value & resID & objid & aid & tile & hero & town & bid;
-		}
 
 		bool operator==(const AbstractGoal & g) const override
 		{

+ 0 - 14
AI/VCAI/AIUtility.h

@@ -68,14 +68,6 @@ public:
 
 	const CGHeroInstance * get(bool doWeExpectNull = false) const;
 	bool validAndSet() const;
-
-
-	template<typename Handler> void serialize(Handler & h)
-	{
-		h & this->h;
-		h & hid;
-		h & name;
-	}
 };
 
 enum BattleState
@@ -100,12 +92,6 @@ struct ObjectIdRef
 	ObjectIdRef(const CGObjectInstance * obj);
 
 	bool operator<(const ObjectIdRef & rhs) const;
-
-
-	template<typename Handler> void serialize(Handler & h)
-	{
-		h & id;
-	}
 };
 
 struct TimeCheck

+ 0 - 17
AI/VCAI/Goals/AbstractGoal.h

@@ -73,7 +73,6 @@ namespace Goals
 	public:
 		bool operator==(const TSubgoal & rhs) const;
 		bool operator<(const TSubgoal & rhs) const;
-		//TODO: serialize?
 	};
 
 	using TGoalVec = std::vector<TSubgoal>;
@@ -175,21 +174,5 @@ namespace Goals
 //		{
 //			return !(*this == g);
 //		}
-
-		template<typename Handler> void serialize(Handler & h)
-		{
-			h & goalType;
-			h & isElementar;
-			h & isAbstract;
-			h & priority;
-			h & value;
-			h & resID;
-			h & objid;
-			h & aid;
-			h & tile;
-			h & hero;
-			h & town;
-			h & bid;
-		}
 	};
 }

+ 0 - 6
AI/VCAI/Goals/CGoal.h

@@ -69,12 +69,6 @@ namespace Goals
 
 			return ptr;
 		}
-		template<typename Handler> void serialize(Handler & h)
-		{
-			h & static_cast<AbstractGoal &>(*this);
-			//h & goalType & isElementar & isAbstract & priority;
-			//h & value & resID & objid & aid & tile & hero & town & bid;
-		}
 
 		bool operator==(const AbstractGoal & g) const override
 		{

+ 5 - 5
AI/VCAI/Goals/Explore.cpp

@@ -53,7 +53,7 @@ namespace Goals
 		{
 			int3 tile = int3(0, 0, ourPos.z);
 
-			const auto & slice = (*(ts->fogOfWarMap))[ourPos.z];
+			const auto & slice = ts->fogOfWarMap[ourPos.z];
 
 			for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
 			{
@@ -81,13 +81,13 @@ namespace Goals
 
 			foreach_tile_pos([&](const int3 & pos)
 			{
-				if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y])
+				if(ts->fogOfWarMap[pos.z][pos.x][pos.y])
 				{
 					bool hasInvisibleNeighbor = false;
 
 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
 					{
-						if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
+						if(!ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
 						{
 							hasInvisibleNeighbor = true;
 						}
@@ -171,7 +171,7 @@ namespace Goals
 			{
 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
 				{
-					if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
+					if(ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
 					{
 						out.push_back(neighbour);
 					}
@@ -184,7 +184,7 @@ namespace Goals
 			int ret = 0;
 			int3 npos = int3(0, 0, pos.z);
 
-			const auto & slice = (*(ts->fogOfWarMap))[pos.z];
+			const auto & slice = ts->fogOfWarMap[pos.z];
 
 			for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
 			{

+ 0 - 14
AI/VCAI/ResourceManager.h

@@ -26,13 +26,6 @@ struct DLL_EXPORT ResourceObjective
 
 	TResources resources; //how many resoures do we need
 	Goals::TSubgoal goal; //what for (build, gather army etc...)
-
-	 //TODO: register?
-	template<typename Handler> void serializeInternal(Handler & h)
-	{
-		h & resources;
-		//h & goal; //FIXME: goal serialization is broken
-	}
 };
 
 class DLL_EXPORT IResourceManager //: public: IAbstractManager
@@ -103,11 +96,4 @@ private:
 	boost::heap::binomial_heap<ResourceObjective> queue;
 
 	void dumpToLog() const;
-
-	//TODO: register?
-	template<typename Handler> void serializeInternal(Handler & h)
-	{
-		h & saving;
-		h & queue;
-	}
 };

+ 0 - 25
AI/VCAI/VCAI.cpp

@@ -747,31 +747,6 @@ void VCAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, cons
 	requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
 }
 
-void VCAI::saveGame(BinarySerializer & h)
-{
-	NET_EVENT_HANDLER;
-	validateVisitableObjs();
-
-	#if 0
-	//disabled due to issue 2890
-	registerGoals(h);
-	#endif // 0
-	CAdventureAI::saveGame(h);
-	serializeInternal(h);
-}
-
-void VCAI::loadGame(BinaryDeserializer & h)
-{
-	//NET_EVENT_HANDLER;
-
-	#if 0
-	//disabled due to issue 2890
-	registerGoals(h);
-	#endif // 0
-	CAdventureAI::loadGame(h);
-	serializeInternal(h);
-}
-
 void makePossibleUpgrades(const CArmedInstance * obj)
 {
 	if(!obj)

+ 0 - 97
AI/VCAI/VCAI.h

@@ -66,15 +66,6 @@ public:
 	void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
 	void receivedAnswerConfirmation(int answerRequestID, int result);
 	void heroVisit(const CGObjectInstance * obj, bool started);
-
-
-	template<typename Handler> void serialize(Handler & h)
-	{
-		h & battle;
-		h & remainingQueries;
-		h & requestToQueryID;
-		h & havingTurn;
-	}
 };
 
 class DLL_EXPORT VCAI : public CAdventureAI
@@ -152,8 +143,6 @@ public:
 	void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
 	void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
-	void saveGame(BinarySerializer & h) override; //saving
-	void loadGame(BinaryDeserializer & h) override; //loading
 	void finish() override;
 
 	void availableCreaturesChanged(const CGDwelling * town) override;
@@ -273,92 +262,6 @@ public:
 	void answerQuery(QueryID queryID, int selection);
 	//special function that can be called ONLY from game events handling thread and will send request ASAP
 	void requestActionASAP(std::function<void()> whatToDo);
-
-	#if 0
-	//disabled due to issue 2890
-	template<typename Handler> void registerGoals(Handler & h)
-	{
-		//h.template registerType<Goals::AbstractGoal, Goals::BoostHero>();
-		h.template registerType<Goals::AbstractGoal, Goals::Build>();
-		h.template registerType<Goals::AbstractGoal, Goals::BuildThis>();
-		//h.template registerType<Goals::AbstractGoal, Goals::CIssueCommand>();
-		h.template registerType<Goals::AbstractGoal, Goals::ClearWayTo>();
-		h.template registerType<Goals::AbstractGoal, Goals::CollectRes>();
-		h.template registerType<Goals::AbstractGoal, Goals::Conquer>();
-		h.template registerType<Goals::AbstractGoal, Goals::DigAtTile>();
-		h.template registerType<Goals::AbstractGoal, Goals::Explore>();
-		h.template registerType<Goals::AbstractGoal, Goals::FindObj>();
-		h.template registerType<Goals::AbstractGoal, Goals::GatherArmy>();
-		h.template registerType<Goals::AbstractGoal, Goals::GatherTroops>();
-		h.template registerType<Goals::AbstractGoal, Goals::GetArtOfType>();
-		h.template registerType<Goals::AbstractGoal, Goals::VisitObj>();
-		h.template registerType<Goals::AbstractGoal, Goals::Invalid>();
-		//h.template registerType<Goals::AbstractGoal, Goals::NotLose>();
-		h.template registerType<Goals::AbstractGoal, Goals::RecruitHero>();
-		h.template registerType<Goals::AbstractGoal, Goals::VisitHero>();
-		h.template registerType<Goals::AbstractGoal, Goals::VisitTile>();
-		h.template registerType<Goals::AbstractGoal, Goals::Win>();
-	}
-	#endif
-
-	template<typename Handler> void serializeInternal(Handler & h)
-	{
-		h & knownTeleportChannels;
-		h & knownSubterraneanGates;
-		h & destinationTeleport;
-		h & townVisitsThisWeek;
-
-		#if 0
-		//disabled due to issue 2890
-		h & lockedHeroes;
-		#else
-		{
-			ui32 length = 0;
-			h & length;
-			if(!h.saving)
-			{
-				std::set<ui32> loadedPointers;
-				lockedHeroes.clear();
-				for(ui32 index = 0; index < length; index++)
-				{
-					HeroPtr ignored1;
-					h & ignored1;
-
-					ui8 flag = 0;
-					h & flag;
-
-					if(flag)
-					{
-						ui32 pid = 0xffffffff;
-						h & pid;
-
-						if(!vstd::contains(loadedPointers, pid))
-						{
-							loadedPointers.insert(pid);
-
-							ui16 typeId = 0;
-							//this is the problem requires such hack
-							//we have to explicitly ignore invalid goal class type id
-							h & typeId;
-							Goals::AbstractGoal ignored2;
-							ignored2.serialize(h);
-						}
-					}
-				}
-			}
-		}
-		#endif
-
-		h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
-		h & visitableObjs;
-		h & alreadyVisited;
-		h & reservedObjs;
-		h & status;
-		h & battlename;
-		h & heroesUnableToExplore;
-
-		//myCB is restored after load by init call
-	}
 };
 
 class cannotFulfillGoalException : public std::exception

+ 0 - 14
client/CPlayerInterface.cpp

@@ -142,7 +142,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player):
 	makingTurn = false;
 	showingDialog = new CondSh<bool>(false);
 	cingconsole = new CInGameConsole();
-	firstCall = 1; //if loading will be overwritten in serialize
 	autosaveCount = 0;
 	isAutoFightOn = false;
 	isAutoFightEndBattle = false;
@@ -1201,19 +1200,6 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
 	}
 }
 
-void CPlayerInterface::saveGame( BinarySerializer & h )
-{
-	EVENT_HANDLER_CALLED_BY_CLIENT;
-	localState->serialize(h);
-}
-
-void CPlayerInterface::loadGame( BinaryDeserializer & h )
-{
-	EVENT_HANDLER_CALLED_BY_CLIENT;
-	localState->serialize(h);
-	firstCall = -1;
-}
-
 void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
 {
 	assert(h);

+ 0 - 4
client/CPlayerInterface.h

@@ -61,8 +61,6 @@ class CPlayerInterface : public CGameInterface, public IUpdateable
 	bool ignoreEvents;
 	size_t numOfMovedArts;
 
-	// -1 - just loaded game; 1 - just started game; 0 otherwise
-	int firstCall;
 	int autosaveCount;
 
 	std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
@@ -146,8 +144,6 @@ protected: // Call-ins from server, should not be called directly, but only via
 	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
 	void playerEndsTurn(PlayerColor player) override;
-	void saveGame(BinarySerializer & h) override; //saving
-	void loadGame(BinaryDeserializer & h) override; //loading
 	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
 
 	//for battles

+ 2 - 130
client/Client.cpp

@@ -203,137 +203,9 @@ void CClient::loadGame(CGameState * initializedGameState)
 	reinitScripting();
 
 	initPlayerEnvironments();
-	
-	// Loading of client state - disabled for now
-	// Since client no longer writes or loads its own state and instead receives it from server
-	// client state serializer will serialize its own copies of all pointers, e.g. heroes/towns/objects
-	// and on deserialize will create its own copies (instead of using copies from state received from server)
-	// Potential solutions:
-	// 1) Use server gamestate to deserialize pointers, so any pointer to same object will point to server instance and not our copy
-	// 2) Remove all serialization of pointers with instance ID's and restore them on load (including AI deserializer code)
-	// 3) Completely remove client savegame and send all information, like hero paths and sleeping status to server (either in form of hero properties or as some generic "client options" message
-#ifdef BROKEN_CLIENT_STATE_SERIALIZATION_HAS_BEEN_FIXED
-	// try to deserialize client data including sleepingHeroes
-	try
-	{
-		boost::filesystem::path clientSaveName = *CResourceHandler::get()->getResourceName(ResourcePath(CSH->si->mapname, EResType::CLIENT_SAVEGAME));
-
-		if(clientSaveName.empty())
-			throw std::runtime_error("Cannot open client part of " + CSH->si->mapname);
-
-		std::unique_ptr<CLoadFile> loader (new CLoadFile(clientSaveName));
-		serialize(loader->serializer, loader->serializer.version);
-
-		logNetwork->info("Client data loaded.");
-	}
-	catch(std::exception & e)
-	{
-		logGlobal->info("Cannot load client data for game %s. Error: %s", CSH->si->mapname, e.what());
-	}
-#endif
-
 	initPlayerInterfaces();
 }
 
-void CClient::serialize(BinarySerializer & h)
-{
-	assert(h.saving);
-	ui8 players = static_cast<ui8>(playerint.size());
-	h & players;
-
-	for(auto i = playerint.begin(); i != playerint.end(); i++)
-	{
-		logGlobal->trace("Saving player %s interface", i->first);
-		assert(i->first == i->second->playerID);
-		h & i->first;
-		h & i->second->dllName;
-		h & i->second->human;
-		i->second->saveGame(h);
-	}
-
-#if SCRIPTING_ENABLED
-	JsonNode scriptsState;
-	clientScripts->serializeState(h.saving, scriptsState);
-	h & scriptsState;
-#endif
-}
-
-void CClient::serialize(BinaryDeserializer & h)
-{
-	assert(!h.saving);
-	ui8 players = 0;
-	h & players;
-
-	for(int i = 0; i < players; i++)
-	{
-		std::string dllname;
-		PlayerColor pid;
-		bool isHuman = false;
-		auto prevInt = LOCPLINT;
-
-		h & pid;
-		h & dllname;
-		h & isHuman;
-		assert(dllname.length() == 0 || !isHuman);
-		if(pid == PlayerColor::NEUTRAL)
-		{
-			logGlobal->trace("Neutral battle interfaces are not serialized.");
-			continue;
-		}
-
-		logGlobal->trace("Loading player %s interface", pid);
-		std::shared_ptr<CGameInterface> nInt;
-		if(dllname.length())
-			nInt = CDynLibHandler::getNewAI(dllname);
-		else
-			nInt = std::make_shared<CPlayerInterface>(pid);
-
-		nInt->dllName = dllname;
-		nInt->human = isHuman;
-		nInt->playerID = pid;
-
-		bool shouldResetInterface = true;
-		// Client no longer handle this player at all
-		if(!vstd::contains(CSH->getAllClientPlayers(CSH->logicConnection->connectionID), pid))
-		{
-			logGlobal->trace("Player %s is not belong to this client. Destroying interface", pid);
-		}
-		else if(isHuman && !vstd::contains(CSH->getHumanColors(), pid))
-		{
-			logGlobal->trace("Player %s is no longer controlled by human. Destroying interface", pid);
-		}
-		else if(!isHuman && vstd::contains(CSH->getHumanColors(), pid))
-		{
-			logGlobal->trace("Player %s is no longer controlled by AI. Destroying interface", pid);
-		}
-		else
-		{
-			installNewPlayerInterface(nInt, pid);
-			shouldResetInterface = false;
-		}
-
-		// loadGame needs to be called after initGameInterface to load paths correctly
-		// initGameInterface is called in installNewPlayerInterface
-		nInt->loadGame(h);
-
-		if (shouldResetInterface)
-		{
-			nInt.reset();
-			LOCPLINT = prevInt;
-		}
-	}
-
-#if SCRIPTING_ENABLED
-	{
-		JsonNode scriptsState;
-		h & scriptsState;
-		clientScripts->serializeState(h.saving, scriptsState);
-	}
-#endif
-
-	logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff());
-}
-
 void CClient::save(const std::string & fname)
 {
 	if(!gs->currentBattles.empty())
@@ -460,7 +332,7 @@ void CClient::initPlayerInterfaces()
 	logNetwork->trace("Initialized player interfaces %d ms", CSH->th->getDiff());
 }
 
-std::string CClient::aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman)
+std::string CClient::aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman) const
 {
 	if(ps.name.size())
 	{
@@ -472,7 +344,7 @@ std::string CClient::aiNameForPlayer(const PlayerSettings & ps, bool battleAI, b
 	return aiNameForPlayer(battleAI, alliedToHuman);
 }
 
-std::string CClient::aiNameForPlayer(bool battleAI, bool alliedToHuman)
+std::string CClient::aiNameForPlayer(bool battleAI, bool alliedToHuman) const
 {
 	const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
 	std::string goodAdventureAI = alliedToHuman ? settings["server"]["alliedAI"].String() : settings["server"]["playerAI"].String();

+ 2 - 4
client/Client.h

@@ -131,8 +131,6 @@ public:
 
 	void newGame(CGameState * gameState);
 	void loadGame(CGameState * gameState);
-	void serialize(BinarySerializer & h);
-	void serialize(BinaryDeserializer & h);
 
 	void save(const std::string & fname);
 	void endGame();
@@ -140,8 +138,8 @@ public:
 	void initMapHandler();
 	void initPlayerEnvironments();
 	void initPlayerInterfaces();
-	std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman); //empty means no AI -> human
-	std::string aiNameForPlayer(bool battleAI, bool alliedToHuman);
+	std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman) const; //empty means no AI -> human
+	std::string aiNameForPlayer(bool battleAI, bool alliedToHuman) const;
 	void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
 	void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
 

+ 0 - 27
client/PlayerLocalState.h

@@ -45,15 +45,6 @@ public:
 		int spellbookLastPageAdvmap = 0;
 		int spellbookLastTabBattle = 4;
 		int spellbookLastTabAdvmap = 4;
-
-		template<typename Handler>
-		void serialize(Handler & h)
-		{
-			h & spellbookLastPageBattle;
-			h & spellbookLastPageAdvmap;
-			h & spellbookLastTabBattle;
-			h & spellbookLastTabAdvmap;
-		}
 	} spellbookSettings;
 
 	explicit PlayerLocalState(CPlayerInterface & owner);
@@ -92,22 +83,4 @@ public:
 
 	/// Changes currently selected object
 	void setSelection(const CArmedInstance *sel);
-
-	template<typename Handler>
-	void serialize(Handler & h)
-	{
-		//WARNING: this code is broken and not used. See CClient::loadGame
-		std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest
-		if(h.saving)
-			saveHeroPaths(pathsMap);
-
-		h & pathsMap;
-
-		if(!h.saving)
-			loadHeroPaths(pathsMap);
-
-		h & ownedTowns;
-		h & wanderingHeroes;
-		h & sleepingHeroes;
-	}
 };

+ 2 - 1
lib/CArtHandler.h

@@ -16,6 +16,7 @@
 #include "bonuses/CBonusSystemNode.h"
 #include "GameConstants.h"
 #include "IHandlerBase.h"
+#include "serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -187,7 +188,7 @@ struct DLL_LINKAGE ArtSlotInfo
 	}
 };
 
-class DLL_LINKAGE CArtifactSet
+class DLL_LINKAGE CArtifactSet : public virtual Serializeable
 {
 public:
 	using ArtPlacementMap = std::map<CArtifactInstance*, ArtifactPosition>;

+ 2 - 1
lib/CCreatureSet.h

@@ -11,6 +11,7 @@
 
 #include "bonuses/Bonus.h"
 #include "bonuses/CBonusSystemNode.h"
+#include "serializer/Serializeable.h"
 #include "GameConstants.h"
 #include "CArtHandler.h"
 #include "CArtifactInstance.h"
@@ -208,7 +209,7 @@ namespace NArmyFormation
 	static const std::vector<std::string> names{ "wide", "tight" };
 }
 
-class DLL_LINKAGE CCreatureSet : public IArmyDescriptor //seven combined creatures
+class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable //seven combined creatures
 {
 	CCreatureSet(const CCreatureSet &) = delete;
 	CCreatureSet &operator=(const CCreatureSet&);

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -553,7 +553,7 @@ std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::ge
 		for(tile.x = 0; tile.x < width; tile.x++)
 			for(tile.y = 0; tile.y < height; tile.y++)
 			{
-				if ((*team->fogOfWarMap)[tile.z][tile.x][tile.y])
+				if (team->fogOfWarMap[tile.z][tile.x][tile.y])
 					(*ptr)[tile.z][tile.x][tile.y] = &gs->map->getTile(tile);
 				else
 					(*ptr)[tile.z][tile.x][tile.y] = nullptr;

+ 0 - 24
lib/CGameInterface.cpp

@@ -243,28 +243,4 @@ void CAdventureAI::yourTacticPhase(const BattleID & battleID, int distance)
 	battleAI->yourTacticPhase(battleID, distance);
 }
 
-void CAdventureAI::saveGame(BinarySerializer & h) /*saving */
-{
-	bool hasBattleAI = static_cast<bool>(battleAI);
-	h & hasBattleAI;
-	if(hasBattleAI)
-	{
-		h & battleAI->dllName;
-	}
-}
-
-void CAdventureAI::loadGame(BinaryDeserializer & h) /*loading */
-{
-	bool hasBattleAI = false;
-	h & hasBattleAI;
-	if(hasBattleAI)
-	{
-		std::string dllName;
-		h & dllName;
-		battleAI = CDynLibHandler::getNewBattleAI(dllName);
-		assert(cbc); //it should have been set by the one who new'ed us
-		battleAI->initBattleInterface(env, cbc);
-	}
-}
-
 VCMI_LIB_NAMESPACE_END

+ 0 - 6
lib/CGameInterface.h

@@ -110,9 +110,6 @@ public:
 	virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain){};
 
 	virtual std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) = 0;
-
-	virtual void saveGame(BinarySerializer & h) = 0;
-	virtual void loadGame(BinaryDeserializer & h) = 0;
 };
 
 class DLL_LINKAGE CDynLibHandler
@@ -161,9 +158,6 @@ public:
 	void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
 	void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;
 	void battleUnitsChanged(const BattleID & battleID, const std::vector<UnitChanges> & units) override;
-
-	void saveGame(BinarySerializer & h) override;
-	void loadGame(BinaryDeserializer & h) override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 20 - 13
lib/CGeneralTextHandler.h

@@ -196,24 +196,31 @@ public:
 	template <typename Handler>
 	void serialize(Handler & h)
 	{
-		std::string key;
-		auto sz = stringsLocalizations.size();
-		h & sz;
-		if(h.saving)
+		if (h.version >= Handler::Version::SIMPLE_TEXT_CONTAINER_SERIALIZATION)
 		{
-			for(auto s : stringsLocalizations)
-			{
-				key = s.first;
-				h & key;
-				h & s.second;
-			}
+			h & stringsLocalizations;
 		}
 		else
 		{
-			for(size_t i = 0; i < sz; ++i)
+			std::string key;
+			int64_t sz = stringsLocalizations.size();
+			h & sz;
+			if(h.saving)
+			{
+				for(auto s : stringsLocalizations)
+				{
+					key = s.first;
+					h & key;
+					h & s.second;
+				}
+			}
+			else
 			{
-				h & key;
-				h & stringsLocalizations[key];
+				for(size_t i = 0; i < sz; ++i)
+				{
+					h & key;
+					h & stringsLocalizations[key];
+				}
 			}
 		}
 	}

+ 1 - 0
lib/CMakeLists.txt

@@ -602,6 +602,7 @@ set(lib_MAIN_HEADERS
 	serializer/JsonUpdater.h
 	serializer/Cast.h
 	serializer/ESerializationVersion.h
+	serializer/Serializeable.h
 
 	spells/AbilityCaster.h
 	spells/AdventureSpellMechanics.h

+ 13 - 1
lib/CPlayerState.h

@@ -128,7 +128,7 @@ public:
 	TeamID id; //position in gameState::teams
 	std::set<PlayerColor> players; // members of this team
 	//TODO: boost::array, bool if possible
-	std::unique_ptr<boost::multi_array<ui8, 3>> fogOfWarMap; //[z][x][y] true - visible, false - hidden
+	boost::multi_array<ui8, 3> fogOfWarMap; //[z][x][y] true - visible, false - hidden
 
 	TeamState();
 
@@ -136,6 +136,18 @@ public:
 	{
 		h & id;
 		h & players;
+		if (h.version < Handler::Version::REMOVE_FOG_OF_WAR_POINTER)
+		{
+			struct Helper : public Serializeable
+			{
+				void serialize(Handler &h)
+				{}
+			};
+			Helper helper;
+			auto ptrHelper = &helper;
+			h & ptrHelper;
+		}
+
 		h & fogOfWarMap;
 		h & static_cast<CBonusSystemNode&>(*this);
 	}

+ 2 - 1
lib/CRandomGenerator.h

@@ -11,6 +11,7 @@
 #pragma once
 
 #include <vstd/RNG.h>
+#include "serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -27,7 +28,7 @@ using TRandI = std::function<int()>;
 /// The random generator randomly generates integers and real numbers("doubles") between
 /// a given range. This is a header only class and mainly a wrapper for
 /// convenient usage of the standard random API. An instance of this RNG is not thread safe.
-class DLL_LINKAGE CRandomGenerator : public vstd::RNG, boost::noncopyable
+class DLL_LINKAGE CRandomGenerator : public vstd::RNG, boost::noncopyable, public Serializeable
 {
 public:
 	/// Seeds the generator by default with the product of the current time in milliseconds and the

+ 2 - 2
lib/IGameCallback.cpp

@@ -103,8 +103,8 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 				if(distance <= radious)
 				{
 					if(!player
-						|| (mode == ETileVisibility::HIDDEN  && (*team->fogOfWarMap)[pos.z][xd][yd] == 0)
-						|| (mode == ETileVisibility::REVEALED && (*team->fogOfWarMap)[pos.z][xd][yd] == 1)
+						|| (mode == ETileVisibility::HIDDEN  && team->fogOfWarMap[pos.z][xd][yd] == 0)
+						|| (mode == ETileVisibility::REVEALED && team->fogOfWarMap[pos.z][xd][yd] == 1)
 					)
 						tiles.insert(int3(xd,yd,pos.z));
 				}

+ 2 - 1
lib/StartInfo.h

@@ -15,6 +15,7 @@
 #include "TurnTimerInfo.h"
 #include "ExtraOptionsInfo.h"
 #include "campaign/CampaignConstants.h"
+#include "serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -107,7 +108,7 @@ enum class EStartMode : int32_t
 };
 
 /// Struct which describes the difficulty, the turn time,.. of a heroes match.
-struct DLL_LINKAGE StartInfo
+struct DLL_LINKAGE StartInfo : public Serializeable
 {
 	EStartMode mode;
 	ui8 difficulty; //0=easy; 4=impossible

+ 4 - 2
lib/battle/CObstacleInstance.h

@@ -9,9 +9,11 @@
  */
 #pragma once
 #include "BattleHex.h"
+
+#include "../constants/EntityIdentifiers.h"
 #include "../filesystem/ResourcePath.h"
 #include "../networkPacks/BattleChanges.h"
-#include "../constants/EntityIdentifiers.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -20,7 +22,7 @@ class ObstacleChanges;
 class JsonSerializeFormat;
 class SpellID;
 
-struct DLL_LINKAGE CObstacleInstance
+struct DLL_LINKAGE CObstacleInstance : public Serializeable
 {
 	enum EObstacleType : ui8
 	{

+ 2 - 1
lib/bonuses/Bonus.h

@@ -13,6 +13,7 @@
 #include "BonusCustomTypes.h"
 #include "../constants/VariantIdentifier.h"
 #include "../constants/EntityIdentifiers.h"
+#include "../serializer/Serializeable.h"
 #include "../MetaString.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -55,7 +56,7 @@ public:
 #define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving && h.smartPointerSerialization) deserializationFix();
 
 /// Struct for handling bonuses of several types. Can be transferred to any hero
-struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
+struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>, public Serializeable
 {
 	BonusDuration::Type duration = BonusDuration::PERMANENT; //uses BonusDuration values
 	si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK

+ 3 - 3
lib/bonuses/CBonusSystemNode.h

@@ -9,11 +9,11 @@
  */
 #pragma once
 
-#include "GameConstants.h"
-
 #include "BonusList.h"
 #include "IBonusBearer.h"
 
+#include "../serializer/Serializeable.h"
+
 VCMI_LIB_NAMESPACE_BEGIN
 
 using TNodes = std::set<CBonusSystemNode *>;
@@ -21,7 +21,7 @@ using TCNodes = std::set<const CBonusSystemNode *>;
 using TNodesVector = std::vector<CBonusSystemNode *>;
 using TCNodesVector = std::vector<const CBonusSystemNode *>;
 
-class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::noncopyable
+class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public virtual Serializeable, public boost::noncopyable
 {
 public:
 	enum ENodeTypes

+ 3 - 2
lib/bonuses/Limiters.h

@@ -10,8 +10,9 @@
 
 #include "Bonus.h"
 
-#include "../GameConstants.h"
 #include "../battle/BattleHex.h"
+#include "../serializer/Serializeable.h"
+#include "../constants/Enumerations.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -27,7 +28,7 @@ struct BonusLimitationContext
 	const BonusList & stillUndecided;
 };
 
-class DLL_LINKAGE ILimiter
+class DLL_LINKAGE ILimiter : public Serializeable
 {
 public:
 	enum class EDecision : uint8_t {ACCEPT, DISCARD, NOT_SURE};

+ 4 - 2
lib/bonuses/Propagators.h

@@ -12,11 +12,13 @@
 #include "Bonus.h"
 #include "CBonusSystemNode.h"
 
+#include "../serializer/Serializeable.h"
+
 VCMI_LIB_NAMESPACE_BEGIN
 
 extern DLL_LINKAGE const std::map<std::string, TPropagatorPtr> bonusPropagatorMap;
 
-class DLL_LINKAGE IPropagator
+class DLL_LINKAGE IPropagator : public Serializeable
 {
 public:
 	virtual ~IPropagator() = default;
@@ -42,4 +44,4 @@ public:
 	}
 };
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 3 - 2
lib/bonuses/Updaters.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "Bonus.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -17,7 +18,7 @@ extern DLL_LINKAGE const std::map<std::string, TUpdaterPtr> bonusUpdaterMap;
 
 // observers for updating bonuses based on certain events (e.g. hero gaining level)
 
-class DLL_LINKAGE IUpdater
+class DLL_LINKAGE IUpdater : public Serializeable
 {
 public:
 	virtual ~IUpdater() = default;
@@ -114,4 +115,4 @@ public:
 	JsonNode toJsonNode() const override;
 };
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 2 - 1
lib/campaign/CampaignState.h

@@ -12,6 +12,7 @@
 #include "../GameConstants.h"
 #include "../MetaString.h"
 #include "../filesystem/ResourcePath.h"
+#include "../serializer/Serializeable.h"
 #include "../CGeneralTextHandler.h"
 #include "CampaignConstants.h"
 #include "CampaignScenarioPrologEpilog.h"
@@ -214,7 +215,7 @@ struct DLL_LINKAGE CampaignScenario
 };
 
 /// Class that represents loaded campaign information
-class DLL_LINKAGE Campaign : public CampaignHeader
+class DLL_LINKAGE Campaign : public CampaignHeader, public Serializeable
 {
 	friend class CampaignHandler;
 

+ 4 - 5
lib/gameState/CGameState.cpp

@@ -693,8 +693,8 @@ void CGameState::initFogOfWar()
 	for(auto & elem : teams)
 	{
 		auto & fow = elem.second.fogOfWarMap;
-		fow->resize(boost::extents[layers][map->width][map->height]);
-		std::fill(fow->data(), fow->data() + fow->num_elements(), 0);
+		fow.resize(boost::extents[layers][map->width][map->height]);
+		std::fill(fow.data(), fow.data() + fow.num_elements(), 0);
 
 		for(CGObjectInstance *obj : map->objects)
 		{
@@ -704,7 +704,7 @@ void CGameState::initFogOfWar()
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::HIDDEN, obj->tempOwner);
 			for(const int3 & tile : tiles)
 			{
-				(*elem.second.fogOfWarMap)[tile.z][tile.x][tile.y] = 1;
+				elem.second.fogOfWarMap[tile.z][tile.x][tile.y] = 1;
 			}
 		}
 	}
@@ -1326,7 +1326,7 @@ bool CGameState::isVisible(int3 pos, const std::optional<PlayerColor> & player)
 	if(player->isSpectator())
 		return true;
 
-	return (*getPlayerTeam(*player)->fogOfWarMap)[pos.z][pos.x][pos.y];
+	return getPlayerTeam(*player)->fogOfWarMap[pos.z][pos.x][pos.y];
 }
 
 bool CGameState::isVisible(const CGObjectInstance * obj, const std::optional<PlayerColor> & player) const
@@ -1957,7 +1957,6 @@ bool RumorState::update(int id, int extra)
 TeamState::TeamState()
 {
 	setNodeType(TEAM);
-	fogOfWarMap = std::make_unique<boost::multi_array<ui8, 3>>();
 }
 
 CRandomGenerator & CGameState::getRandomGenerator()

+ 1 - 1
lib/gameState/CGameState.h

@@ -79,7 +79,7 @@ class BattleInfo;
 
 DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
 
-class DLL_LINKAGE CGameState : public CNonConstInfoCallback
+class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeable
 {
 	friend class CGameStateCampaign;
 

+ 2 - 1
lib/gameState/CGameStateCampaign.h

@@ -11,6 +11,7 @@
 
 #include "../GameConstants.h"
 #include "../campaign/CampaignConstants.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -28,7 +29,7 @@ struct CampaignHeroReplacement
 	std::vector<ArtifactPosition> transferrableArtifacts;
 };
 
-class CGameStateCampaign
+class CGameStateCampaign : public Serializeable
 {
 	CGameState * gameState;
 

+ 3 - 1
lib/gameState/TavernHeroesPool.h

@@ -11,6 +11,8 @@
 
 #include "../GameConstants.h"
 #include "TavernSlot.h"
+#include "../serializer/Serializeable.h"
+
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -21,7 +23,7 @@ class CHeroClass;
 class CGameState;
 class CSimpleArmy;
 
-class DLL_LINKAGE TavernHeroesPool
+class DLL_LINKAGE TavernHeroesPool : public Serializeable
 {
 	struct TavernSlot
 	{

+ 2 - 1
lib/mapObjectConstructors/CBankInstanceConstructor.h

@@ -16,10 +16,11 @@
 #include "../ResourceSet.h"
 #include "../json/JsonNode.h"
 #include "../mapObjects/CBank.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-struct BankConfig
+struct BankConfig : public Serializeable
 {
 	ui32 chance = 0; //chance for this level being chosen
 	std::vector<CStackBasicDescriptor> guards; //creature ID, amount

+ 1 - 1
lib/mapObjects/CGCreature.cpp

@@ -270,7 +270,7 @@ void CGCreature::initObj(CRandomGenerator & rand)
 		}
 	}
 
-	temppower = stacks[SlotID(0)]->count * static_cast<ui64>(1000);
+	temppower = stacks[SlotID(0)]->count * static_cast<int64_t>(1000);
 	refusedJoining = false;
 }
 

+ 1 - 1
lib/mapObjects/CGCreature.h

@@ -35,7 +35,7 @@ public:
 	ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
 	bool neverFlees = false; //if true, the troops will never flee
 	bool notGrowingTeam = false; //if true, number of units won't grow
-	ui64 temppower = 0; //used to handle fractional stack growth for tiny stacks
+	int64_t temppower = 0; //used to handle fractional stack growth for tiny stacks
 
 	bool refusedJoining = false;
 

+ 2 - 1
lib/mapObjects/CGObjectInstance.h

@@ -10,10 +10,11 @@
 #pragma once
 
 #include "IObjectInterface.h"
+
+#include "../bonuses/BonusEnum.h"
 #include "../constants/EntityIdentifiers.h"
 #include "../filesystem/ResourcePath.h"
 #include "../int3.h"
-#include "../bonuses/BonusEnum.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 10 - 9
lib/mapObjects/CQuest.h

@@ -12,6 +12,7 @@
 #include "CRewardableObject.h"
 #include "../ResourceSet.h"
 #include "../MetaString.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -36,7 +37,7 @@ enum class EQuestMission {
 	HOTA_REACH_DATE = 13,
 };
 
-class DLL_LINKAGE CQuest final
+class DLL_LINKAGE CQuest final : public Serializeable
 {
 public:
 
@@ -74,13 +75,13 @@ public:
 	CQuest(); //TODO: Remove constructor
 
 	static bool checkMissionArmy(const CQuest * q, const CCreatureSet * army);
-	virtual bool checkQuest(const CGHeroInstance * h) const; //determines whether the quest is complete or not
-	virtual void getVisitText(IGameCallback * cb, MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
-	virtual void getCompletionText(IGameCallback * cb, MetaString &text) const;
-	virtual void getRolloverText (IGameCallback * cb, MetaString &text, bool onHover) const; //hover or quest log entry
-	virtual void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
-	virtual void addTextReplacements(IGameCallback * cb, MetaString &out, std::vector<Component> & components) const;
-	virtual void addKillTargetReplacements(MetaString &out) const;
+	bool checkQuest(const CGHeroInstance * h) const; //determines whether the quest is complete or not
+	void getVisitText(IGameCallback * cb, MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
+	void getCompletionText(IGameCallback * cb, MetaString &text) const;
+	void getRolloverText (IGameCallback * cb, MetaString &text, bool onHover) const; //hover or quest log entry
+	void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
+	void addTextReplacements(IGameCallback * cb, MetaString &out, std::vector<Component> & components) const;
+	void addKillTargetReplacements(MetaString &out) const;
 	void defineQuestName();
 
 	bool operator== (const CQuest & quest) const
@@ -114,7 +115,7 @@ public:
 	void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName);
 };
 
-class DLL_LINKAGE IQuestObject
+class DLL_LINKAGE IQuestObject : public virtual Serializeable
 {
 public:
 	CQuest * quest = new CQuest();

+ 4 - 3
lib/mapObjects/IObjectInterface.h

@@ -9,10 +9,11 @@
  */
 #pragma once
 
+#include "../GameCallbackHolder.h"
+#include "../constants/EntityIdentifiers.h"
 #include "../networkPacks/EInfoWindowMode.h"
 #include "../networkPacks/ObjProperty.h"
-#include "../constants/EntityIdentifiers.h"
-#include "../GameCallbackHolder.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -29,7 +30,7 @@ class int3;
 class MetaString;
 class PlayerColor;
 
-class DLL_LINKAGE IObjectInterface : public GameCallbackHolder
+class DLL_LINKAGE IObjectInterface : public GameCallbackHolder, public virtual Serializeable
 {
 public:
 	using GameCallbackHolder::GameCallbackHolder;

+ 1 - 1
lib/mapObjects/MiscObjects.h

@@ -185,7 +185,7 @@ protected:
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
-struct DLL_LINKAGE TeleportChannel
+struct DLL_LINKAGE TeleportChannel : public Serializeable
 {
 	enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE};
 

+ 0 - 48
lib/mapObjects/ObjectTemplate.cpp

@@ -59,54 +59,6 @@ ObjectTemplate::ObjectTemplate():
 {
 }
 
-ObjectTemplate::ObjectTemplate(const ObjectTemplate& other):
-	visitDir(other.visitDir),
-	allowedTerrains(other.allowedTerrains),
-	id(other.id),
-	subid(other.subid),
-	printPriority(other.printPriority),
-	animationFile(other.animationFile),
-	editorAnimationFile(other.editorAnimationFile),
-	stringID(other.stringID),
-	width(other.width),
-	height(other.height),
-	visitable(other.visitable),
-	blockedOffsets(other.blockedOffsets),
-	blockMapOffset(other.blockMapOffset),
-	visitableOffset(other.visitableOffset)
-{
-	//default copy constructor is failing with usedTiles this for unknown reason
-
-	usedTiles.resize(other.usedTiles.size());
-	for(size_t i = 0; i < usedTiles.size(); i++)
-		std::copy(other.usedTiles[i].begin(), other.usedTiles[i].end(), std::back_inserter(usedTiles[i]));
-}
-
-ObjectTemplate & ObjectTemplate::operator=(const ObjectTemplate & rhs)
-{
-	visitDir = rhs.visitDir;
-	allowedTerrains = rhs.allowedTerrains;
-	id = rhs.id;
-	subid = rhs.subid;
-	printPriority = rhs.printPriority;
-	animationFile = rhs.animationFile;
-	editorAnimationFile = rhs.editorAnimationFile;
-	stringID = rhs.stringID;
-	width = rhs.width;
-	height = rhs.height;
-	visitable = rhs.visitable;
-	blockedOffsets = rhs.blockedOffsets;
-	blockMapOffset = rhs.blockMapOffset;
-	visitableOffset = rhs.visitableOffset;
-
-	usedTiles.clear();
-	usedTiles.resize(rhs.usedTiles.size());
-	for(size_t i = 0; i < usedTiles.size(); i++)
-		std::copy(rhs.usedTiles[i].begin(), rhs.usedTiles[i].end(), std::back_inserter(usedTiles[i]));
-
-	return *this;
-}
-
 void ObjectTemplate::afterLoadFixup()
 {
 	if(id == Obj::EVENT)

+ 2 - 5
lib/mapObjects/ObjectTemplate.h

@@ -12,6 +12,7 @@
 #include "../GameConstants.h"
 #include "../int3.h"
 #include "../filesystem/ResourcePath.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -20,7 +21,7 @@ class CLegacyConfigParser;
 class JsonNode;
 class int3;
 
-class DLL_LINKAGE ObjectTemplate
+class DLL_LINKAGE ObjectTemplate : public Serializeable
 {
 	enum EBlockMapBits
 	{
@@ -122,10 +123,6 @@ public:
 	bool canBePlacedAt(TerrainId terrain) const;
 
 	ObjectTemplate();
-	//custom copy constructor is required
-	ObjectTemplate(const ObjectTemplate & other);
-
-	ObjectTemplate& operator=(const ObjectTemplate & rhs);
 
 	void readTxt(CLegacyConfigParser & parser);
 	void readMsk();

+ 3 - 2
lib/mapping/CMapHeader.h

@@ -13,7 +13,8 @@
 #include "../constants/EntityIdentifiers.h"
 #include "../constants/Enumerations.h"
 #include "../constants/VariantIdentifier.h"
-#include "../modding/CModInfo.h"
+#include "../modding/ModVerificationInfo.h"
+#include "../serializer/Serializeable.h"
 #include "../LogicalExpression.h"
 #include "../int3.h"
 #include "../MetaString.h"
@@ -202,7 +203,7 @@ enum class EMapDifficulty : uint8_t
 };
 
 /// The map header holds information about loss/victory condition,map format, version, players, height, width,...
-class DLL_LINKAGE CMapHeader
+class DLL_LINKAGE CMapHeader: public Serializeable
 {
 	void setupEvents();
 public:

+ 3 - 1
lib/mapping/CMapInfo.h

@@ -9,6 +9,8 @@
  */
 #pragma once
 
+#include "../serializer/Serializeable.h"
+
 VCMI_LIB_NAMESPACE_BEGIN
 
 struct StartInfo;
@@ -21,7 +23,7 @@ class ResourcePath;
  * A class which stores the count of human players and all players, the filename,
  * scenario options, the map header information,...
  */
-class DLL_LINKAGE CMapInfo
+class DLL_LINKAGE CMapInfo : public Serializeable
 {
 public:
 	std::unique_ptr<CMapHeader> mapHeader; //may be nullptr if campaign

+ 2 - 1
lib/networkPacks/NetPacksBase.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "../constants/EntityIdentifiers.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -18,7 +19,7 @@ class CConnection;
 
 class ICPackVisitor;
 
-struct DLL_LINKAGE CPack
+struct DLL_LINKAGE CPack : public Serializeable
 {
 	/// Pointer to connection that pack received from
 	/// Only set & used on server

+ 3 - 3
lib/networkPacks/NetPacksLib.cpp

@@ -963,7 +963,7 @@ void FoWChange::applyGs(CGameState *gs)
 	TeamState * team = gs->getPlayerTeam(player);
 	auto & fogOfWarMap = team->fogOfWarMap;
 	for(const int3 & t : tiles)
-		(*fogOfWarMap)[t.z][t.x][t.y] = mode != ETileVisibility::HIDDEN;
+		fogOfWarMap[t.z][t.x][t.y] = mode != ETileVisibility::HIDDEN;
 
 	if (mode == ETileVisibility::HIDDEN) //do not hide too much
 	{
@@ -986,7 +986,7 @@ void FoWChange::applyGs(CGameState *gs)
 			}
 		}
 		for(const int3 & t : tilesRevealed) //probably not the most optimal solution ever
-			(*fogOfWarMap)[t.z][t.x][t.y] = 1;
+			fogOfWarMap[t.z][t.x][t.y] = 1;
 	}
 }
 
@@ -1325,7 +1325,7 @@ void TryMoveHero::applyGs(CGameState *gs)
 
 	auto & fogOfWarMap = gs->getPlayerTeam(h->getOwner())->fogOfWarMap;
 	for(const int3 & t : fowRevealed)
-		(*fogOfWarMap)[t.z][t.x][t.y] = 1;
+		fogOfWarMap[t.z][t.x][t.y] = 1;
 }
 
 void NewStructures::applyGs(CGameState *gs)

+ 2 - 2
lib/pathfinder/PathfinderUtil.h

@@ -19,13 +19,13 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 namespace PathfinderUtil
 {
-	using FoW = std::unique_ptr<boost::multi_array<ui8, 3>>;
+	using FoW = boost::multi_array<ui8, 3>;
 	using ELayer = EPathfindingLayer;
 
 	template<EPathfindingLayer::Type layer>
 	EPathAccessibility evaluateAccessibility(const int3 & pos, const TerrainTile & tinfo, const FoW & fow, const PlayerColor player, const CGameState * gs)
 	{
-		if(!(*fow)[pos.z][pos.x][pos.y])
+		if(!fow[pos.z][pos.x][pos.y])
 			return EPathAccessibility::BLOCKED;
 
 		switch(layer)

+ 2 - 1
lib/rewardable/Limiter.h

@@ -12,6 +12,7 @@
 
 #include "../GameConstants.h"
 #include "../ResourceSet.h"
+#include "../serializer/Serializeable.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -26,7 +27,7 @@ using LimitersList = std::vector<std::shared_ptr<Rewardable::Limiter>>;
 
 /// Limiters of rewards. Rewards will be granted to hero only if he satisfies requirements
 /// Note: for this is only a test - it won't remove anything from hero (e.g. artifacts or creatures)
-struct DLL_LINKAGE Limiter final
+struct DLL_LINKAGE Limiter final : public Serializeable
 {
 	/// day of week, unused if 0, 1-7 will test for current day of week
 	si32 dayOfWeek;

+ 2 - 1
lib/rmg/CMapGenOptions.h

@@ -11,6 +11,7 @@
 #pragma once
 
 #include "../GameConstants.h"
+#include "../serializer/Serializeable.h"
 #include "CRmgTemplate.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -26,7 +27,7 @@ enum class EPlayerType
 
 /// The map gen options class holds values about general map generation settings
 /// e.g. the size of the map, the count of players,...
-class DLL_LINKAGE CMapGenOptions
+class DLL_LINKAGE CMapGenOptions : public Serializeable
 {
 public:
 	/// The player settings class maps the player color, starting town and human player flag.

+ 137 - 45
lib/serializer/BinaryDeserializer.h

@@ -35,7 +35,7 @@ public:
 
 /// Main class for deserialization of classes from binary form
 /// Effectively revesed version of BinarySerializer
-class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
+class BinaryDeserializer : public CLoaderBase
 {
 	template<typename Ser,typename T>
 	struct LoadIfStackInstance
@@ -101,9 +101,9 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 		}
 	};
 
-	STRONG_INLINE ui32 readAndCheckLength()
+	STRONG_INLINE uint32_t readAndCheckLength()
 	{
-		ui32 length;
+		uint32_t length;
 		load(length);
 		//NOTE: also used for h3m's embedded in campaigns, so it may be quite large in some cases (e.g. XXL maps with multiple objects)
 		if(length > 1000000)
@@ -119,7 +119,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 	class IPointerLoader
 	{
 	public:
-		virtual void * loadPtr(CLoaderBase &ar, IGameCallback * cb, ui32 pid) const =0; //data is pointer to the ACTUAL POINTER
+		virtual Serializeable * loadPtr(CLoaderBase &ar, IGameCallback * cb, uint32_t pid) const =0; //data is pointer to the ACTUAL POINTER
 		virtual ~IPointerLoader() = default;
 
 		template<typename Type> static IPointerLoader *getApplier(const Type * t = nullptr)
@@ -132,7 +132,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 	class CPointerLoader : public IPointerLoader
 	{
 	public:
-		void * loadPtr(CLoaderBase &ar, IGameCallback * cb, ui32 pid) const override //data is pointer to the ACTUAL POINTER
+		Serializeable * loadPtr(CLoaderBase &ar, IGameCallback * cb, uint32_t pid) const override //data is pointer to the ACTUAL POINTER
 		{
 			auto & s = static_cast<BinaryDeserializer &>(ar);
 
@@ -142,7 +142,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
 
 			ptr->serialize(s);
 
-			return static_cast<void*>(ptr);
+			return static_cast<Serializeable*>(ptr);
 		}
 	};
 
@@ -156,13 +156,19 @@ public:
 	bool reverseEndianness; //if source has different endianness than us, we reverse bytes
 	Version version;
 
-	std::map<ui32, void*> loadedPointers;
-	std::map<const void*, std::shared_ptr<void>> loadedSharedPointers;
+	std::vector<std::string> loadedStrings;
+	std::map<uint32_t, Serializeable*> loadedPointers;
+	std::map<const Serializeable*, std::shared_ptr<Serializeable>> loadedSharedPointers;
 	IGameCallback * cb = nullptr;
 	bool smartPointerSerialization;
 	bool saving;
 
-	BinaryDeserializer(IBinaryReader * r);
+	bool hasFeature(Version what) const
+	{
+		return version >= what;
+	};
+
+	DLL_LINKAGE BinaryDeserializer(IBinaryReader * r);
 
 	template<class T>
 	BinaryDeserializer & operator&(T & t)
@@ -171,12 +177,56 @@ public:
 		return * this;
 	}
 
-	template < class T, typename std::enable_if_t < std::is_fundamental_v<T> && !std::is_same_v<T, bool>, int  > = 0 >
+	int64_t loadEncodedInteger()
+	{
+		uint64_t valueUnsigned = 0;
+		uint_fast8_t offset = 0;
+
+		for (;;)
+		{
+			uint8_t byteValue;
+			load(byteValue);
+
+			if ((byteValue & 0x80) != 0)
+			{
+				valueUnsigned |= (byteValue & 0x7f) << offset;
+				offset += 7;
+			}
+			else
+			{
+				valueUnsigned |= (byteValue & 0x3f) << offset;
+				bool isNegative = (byteValue & 0x40) != 0;
+				if (isNegative)
+					return -static_cast<int64_t>(valueUnsigned);
+				else
+					return valueUnsigned;
+			}
+		}
+	}
+
+	template < class T, typename std::enable_if_t < std::is_floating_point_v<T>, int  > = 0 >
 	void load(T &data)
 	{
 		this->read(static_cast<void *>(&data), sizeof(data), reverseEndianness);
 	}
 
+	template < class T, typename std::enable_if_t < std::is_integral_v<T> && !std::is_same_v<T, bool>, int  > = 0 >
+	void load(T &data)
+	{
+		if constexpr (sizeof(T) == 1)
+		{
+			this->read(static_cast<void *>(&data), sizeof(data), reverseEndianness);
+		}
+		else
+		{
+			static_assert(!std::is_same_v<uint64_t, T>, "Serialization of unsigned 64-bit value may not work in some cases");
+			if (hasFeature(Version::COMPACT_INTEGER_SERIALIZATION))
+				data = loadEncodedInteger();
+			else
+				this->read(static_cast<void *>(&data), sizeof(data), reverseEndianness);
+		}
+	}
+
 	template < typename T, typename std::enable_if_t < is_serializeable<BinaryDeserializer, T>::value, int  > = 0 >
 	void load(T &data)
 	{
@@ -188,15 +238,20 @@ public:
 	template < typename T, typename std::enable_if_t < std::is_array_v<T>, int  > = 0 >
 	void load(T &data)
 	{
-		ui32 size = std::size(data);
-		for(ui32 i = 0; i < size; i++)
+		uint32_t size = std::size(data);
+		for(uint32_t i = 0; i < size; i++)
 			load(data[i]);
 	}
 
+	void load(Version &data)
+	{
+		this->read(static_cast<void *>(&data), sizeof(data), reverseEndianness);
+	}
+
 	template < typename T, typename std::enable_if_t < std::is_enum_v<T>, int  > = 0 >
 	void load(T &data)
 	{
-		si32 read;
+		int32_t read;
 		load( read );
 		data = static_cast<T>(read);
 	}
@@ -204,7 +259,7 @@ public:
 	template < typename T, typename std::enable_if_t < std::is_same_v<T, bool>, int > = 0 >
 	void load(T &data)
 	{
-		ui8 read;
+		uint8_t read;
 		load( read );
 		data = static_cast<bool>(read);
 	}
@@ -212,18 +267,18 @@ public:
 	template <typename T, typename std::enable_if_t < !std::is_same_v<T, bool >, int  > = 0>
 	void load(std::vector<T> &data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.resize(length);
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 			load( data[i]);
 	}
 
 	template <typename T, typename std::enable_if_t < !std::is_same_v<T, bool >, int  > = 0>
 	void load(std::deque<T> & data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.resize(length);
-		for(ui32 i = 0; i < length; i++)
+		for(uint32_t i = 0; i < length; i++)
 			load(data[i]);
 	}
 
@@ -281,7 +336,7 @@ public:
 				return;
 		}
 
-		ui32 pid = 0xffffffff; //pointer id (or maybe rather pointee id)
+		uint32_t pid = 0xffffffff; //pointer id (or maybe rather pointee id)
 		if(smartPointerSerialization)
 		{
 			load( pid ); //get the id
@@ -291,12 +346,12 @@ public:
 			{
 				// We already got this pointer
 				// Cast it in case we are loading it to a non-first base pointer
-				data = static_cast<T>(i->second);
+				data = dynamic_cast<T>(i->second);
 				return;
 			}
 		}
 		//get type id
-		ui16 tid;
+		uint16_t tid;
 		load( tid );
 
 		if(!tid)
@@ -316,15 +371,15 @@ public:
 				data = nullptr;
 				return;
 			}
-			data = static_cast<T>(app->loadPtr(*this, cb, pid));
+			data = dynamic_cast<T>(app->loadPtr(*this, cb, pid));
 		}
 	}
 
 	template <typename T>
-	void ptrAllocated(const T *ptr, ui32 pid)
+	void ptrAllocated(T *ptr, uint32_t pid)
 	{
 		if(smartPointerSerialization && pid != 0xffffffff)
-			loadedPointers[pid] = (void*)ptr; //add loaded pointer to our lookup map; cast is to avoid errors with const T* pt
+			loadedPointers[pid] = const_cast<Serializeable*>(dynamic_cast<const Serializeable*>(ptr)); //add loaded pointer to our lookup map; cast is to avoid errors with const T* pt
 	}
 
 	template<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
@@ -339,7 +394,7 @@ public:
 		NonConstT *internalPtr;
 		load(internalPtr);
 
-		void * internalPtrDerived = static_cast<void*>(internalPtr);
+		const auto * internalPtrDerived = static_cast<Serializeable*>(internalPtr);
 
 		if(internalPtr)
 		{
@@ -354,7 +409,7 @@ public:
 			{
 				auto hlp = std::shared_ptr<NonConstT>(internalPtr);
 				data = hlp;
-				loadedSharedPointers[internalPtrDerived] = std::static_pointer_cast<void>(hlp);
+				loadedSharedPointers[internalPtrDerived] = std::static_pointer_cast<Serializeable>(hlp);
 			}
 		}
 		else
@@ -386,16 +441,16 @@ public:
 	template <typename T, size_t N>
 	void load(std::array<T, N> &data)
 	{
-		for(ui32 i = 0; i < N; i++)
+		for(uint32_t i = 0; i < N; i++)
 			load( data[i] );
 	}
 	template <typename T>
 	void load(std::set<T> &data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.clear();
 		T ins;
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 		{
 			load( ins );
 			data.insert(ins);
@@ -404,10 +459,10 @@ public:
 	template <typename T, typename U>
 	void load(std::unordered_set<T, U> &data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.clear();
 		T ins;
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 		{
 			load(ins);
 			data.insert(ins);
@@ -416,10 +471,10 @@ public:
 	template <typename T>
 	void load(std::list<T> &data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.clear();
 		T ins;
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 		{
 			load(ins);
 			data.push_back(ins);
@@ -432,13 +487,26 @@ public:
 		load(data.second);
 	}
 
+	template <typename T1, typename T2>
+	void load(std::unordered_map<T1,T2> &data)
+	{
+		uint32_t length = readAndCheckLength();
+		data.clear();
+		T1 key;
+		for(uint32_t i=0;i<length;i++)
+		{
+			load(key);
+			load(data[key]);
+		}
+	}
+
 	template <typename T1, typename T2>
 	void load(std::map<T1,T2> &data)
 	{
-		ui32 length = readAndCheckLength();
+		uint32_t length = readAndCheckLength();
 		data.clear();
 		T1 key;
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 		{
 			load(key);
 			load(data[key]);
@@ -446,15 +514,39 @@ public:
 	}
 	void load(std::string &data)
 	{
-		ui32 length = readAndCheckLength();
-		data.resize(length);
-		this->read(static_cast<void *>(data.data()), length, false);
+		if (hasFeature(Version::COMPACT_STRING_SERIALIZATION))
+		{
+			int32_t length;
+			load(length);
+
+			if (length < 0)
+			{
+				int32_t stringID = -length - 1; // -1, -2 ... -> 0, 1 ...
+				data = loadedStrings[stringID];
+			}
+			if (length == 0)
+			{
+				data = {};
+			}
+			if (length > 0)
+			{
+				data.resize(length);
+				this->read(static_cast<void *>(data.data()), length, false);
+				loadedStrings.push_back(data);
+			}
+		}
+		else
+		{
+			uint32_t length = readAndCheckLength();
+			data.resize(length);
+			this->read(static_cast<void *>(data.data()), length, false);
+		}
 	}
 
 	template<typename... TN>
 	void load(std::variant<TN...> & data)
 	{
-		si32 which;
+		int32_t which;
 		load( which );
 		assert(which < sizeof...(TN));
 
@@ -469,7 +561,7 @@ public:
 	template<typename T>
 	void load(std::optional<T> & data)
 	{
-		ui8 present;
+		uint8_t present;
 		load( present );
 		if(present)
 		{
@@ -487,16 +579,16 @@ public:
 	template <typename T>
 	void load(boost::multi_array<T, 3> & data)
 	{
-		ui32 length = readAndCheckLength();
-		ui32 x;
-		ui32 y;
-		ui32 z;
+		uint32_t length = readAndCheckLength();
+		uint32_t x;
+		uint32_t y;
+		uint32_t z;
 		load(x);
 		load(y);
 		load(z);
 		data.resize(boost::extents[x][y][z]);
 		assert(length == data.num_elements()); //x*y*z should be equal to number of elements
-		for(ui32 i = 0; i < length; i++)
+		for(uint32_t i = 0; i < length; i++)
 			load(data.data()[i]);
 	}
 	template <std::size_t T>

+ 120 - 32
lib/serializer/BinarySerializer.h

@@ -12,6 +12,7 @@
 #include "CSerializer.h"
 #include "CTypeList.h"
 #include "ESerializationVersion.h"
+#include "Serializeable.h"
 #include "../mapObjects/CArmedInstance.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -23,7 +24,7 @@ protected:
 public:
 	CSaverBase(IBinaryWriter * w): writer(w){};
 
-	inline void write(const void * data, unsigned size)
+	void write(const void * data, unsigned size)
 	{
 		writer->write(reinterpret_cast<const std::byte*>(data), size);
 	};
@@ -34,7 +35,7 @@ public:
 /// Primitives:    copy memory into underlying stream (defined in CSaverBase)
 /// Containers:    custom overloaded method that decouples class into primitives
 /// VCMI Classes:  recursively serialize them via ClassName::serialize( BinarySerializer &, int version) call
-class DLL_LINKAGE BinarySerializer : public CSaverBase
+class BinarySerializer : public CSaverBase
 {
 	template<typename Handler>
 	struct VariantVisitorSaver
@@ -85,7 +86,7 @@ class DLL_LINKAGE BinarySerializer : public CSaverBase
 	{
 	public:
 		virtual void savePtr(CSaverBase &ar, const void *data) const =0;
-		virtual ~CBasicPointerSaver(){}
+		virtual ~CBasicPointerSaver() = default;
 
 		template<typename T> static CBasicPointerSaver *getApplier(const T * t=nullptr)
 		{
@@ -113,13 +114,19 @@ class DLL_LINKAGE BinarySerializer : public CSaverBase
 public:
 	using Version = ESerializationVersion;
 
-	std::map<const void*, ui32> savedPointers;
+	std::map<std::string, uint32_t> savedStrings;
+	std::map<const Serializeable*, uint32_t> savedPointers;
 
 	const Version version = Version::CURRENT;
 	bool smartPointerSerialization;
 	bool saving;
 
-	BinarySerializer(IBinaryWriter * w);
+	bool hasFeature(Version what) const
+	{
+		return version >= what;
+	};
+
+	DLL_LINKAGE BinarySerializer(IBinaryWriter * w);
 
 	template<typename Base, typename Derived>
 	void registerType(const Base * b = nullptr, const Derived * d = nullptr)
@@ -134,32 +141,72 @@ public:
 		return * this;
 	}
 
+	void saveEncodedInteger(int64_t value)
+	{
+		uint64_t valueUnsigned = std::abs(value);
+
+		while (valueUnsigned > 0x3f)
+		{
+			uint8_t byteValue = (valueUnsigned & 0x7f) | 0x80;
+			valueUnsigned = valueUnsigned >> 7;
+			save(byteValue);
+		}
+
+		uint8_t lastByteValue = valueUnsigned & 0x3f;
+		if (value < 0)
+			lastByteValue |= 0x40;
+
+		save(lastByteValue);
+	}
+
 	template < typename T, typename std::enable_if_t < std::is_same_v<T, bool>, int > = 0 >
 	void save(const T &data)
 	{
-		ui8 writ = static_cast<ui8>(data);
+		uint8_t writ = static_cast<uint8_t>(data);
 		save(writ);
 	}
 
-	template < class T, typename std::enable_if_t < std::is_fundamental_v<T> && !std::is_same_v<T, bool>, int  > = 0 >
+	template < class T, typename std::enable_if_t < std::is_floating_point_v<T>, int  > = 0 >
 	void save(const T &data)
 	{
 		// save primitive - simply dump binary data to output
 		this->write(static_cast<const void *>(&data), sizeof(data));
 	}
 
+	template < class T, typename std::enable_if_t < std::is_integral_v<T> && !std::is_same_v<T, bool>, int  > = 0 >
+	void save(const T &data)
+	{
+		if constexpr (sizeof(T) == 1)
+		{
+			// save primitive - simply dump binary data to output
+			this->write(static_cast<const void *>(&data), sizeof(data));
+		}
+		else
+		{
+			if (hasFeature(Version::COMPACT_INTEGER_SERIALIZATION))
+				saveEncodedInteger(data);
+			else
+				this->write(static_cast<const void *>(&data), sizeof(data));
+		}
+	}
+
+	void save(const Version &data)
+	{
+		this->write(static_cast<const void *>(&data), sizeof(data));
+	}
+
 	template < typename T, typename std::enable_if_t < std::is_enum_v<T>, int  > = 0 >
 	void save(const T &data)
 	{
-		si32 writ = static_cast<si32>(data);
+		int32_t writ = static_cast<int32_t>(data);
 		*this & writ;
 	}
 
 	template < typename T, typename std::enable_if_t < std::is_array_v<T>, int  > = 0 >
 	void save(const T &data)
 	{
-		ui32 size = std::size(data);
-		for(ui32 i=0; i < size; i++)
+		uint32_t size = std::size(data);
+		for(uint32_t i=0; i < size; i++)
 			*this & data[i];
 	}
 
@@ -214,7 +261,7 @@ public:
 		{
 			// We might have an object that has multiple inheritance and store it via the non-first base pointer.
 			// Therefore, all pointers need to be normalized to the actual object address.
-			const void * actualPointer = static_cast<const void*>(data);
+			const auto * actualPointer = static_cast<const Serializeable*>(data);
 			auto i = savedPointers.find(actualPointer);
 			if(i != savedPointers.end())
 			{
@@ -224,7 +271,7 @@ public:
 			}
 
 			//give id to this pointer
-			ui32 pid = (ui32)savedPointers.size();
+			uint32_t pid = savedPointers.size();
 			savedPointers[actualPointer] = pid;
 			save(pid);
 		}
@@ -271,30 +318,30 @@ public:
 	template <typename T, typename std::enable_if_t < !std::is_same_v<T, bool >, int  > = 0>
 	void save(const std::vector<T> &data)
 	{
-		ui32 length = (ui32)data.size();
+		uint32_t length = data.size();
 		*this & length;
-		for(ui32 i=0;i<length;i++)
+		for(uint32_t i=0;i<length;i++)
 			save(data[i]);
 	}
 	template <typename T, typename std::enable_if_t < !std::is_same_v<T, bool >, int  > = 0>
 	void save(const std::deque<T> & data)
 	{
-		ui32 length = (ui32)data.size();
+		uint32_t length = data.size();
 		*this & length;
-		for(ui32 i = 0; i < length; i++)
+		for(uint32_t i = 0; i < length; i++)
 			save(data[i]);
 	}
 	template <typename T, size_t N>
 	void save(const std::array<T, N> &data)
 	{
-		for(ui32 i=0; i < N; i++)
+		for(uint32_t i=0; i < N; i++)
 			save(data[i]);
 	}
 	template <typename T>
 	void save(const std::set<T> &data)
 	{
 		auto & d = const_cast<std::set<T> &>(data);
-		ui32 length = (ui32)d.size();
+		uint32_t length = d.size();
 		save(length);
 		for(auto i = d.begin(); i != d.end(); i++)
 			save(*i);
@@ -303,7 +350,7 @@ public:
 	void save(const std::unordered_set<T, U> &data)
 	{
 		auto & d = const_cast<std::unordered_set<T, U> &>(data);
-		ui32 length = (ui32)d.size();
+		uint32_t length = d.size();
 		*this & length;
 		for(auto i = d.begin(); i != d.end(); i++)
 			save(*i);
@@ -312,16 +359,47 @@ public:
 	void save(const std::list<T> &data)
 	{
 		auto & d = const_cast<std::list<T> &>(data);
-		ui32 length = (ui32)d.size();
+		uint32_t length = d.size();
 		*this & length;
 		for(auto i = d.begin(); i != d.end(); i++)
 			save(*i);
 	}
+
 	void save(const std::string &data)
 	{
-		save(ui32(data.length()));
-		this->write(static_cast<const void *>(data.data()), data.size());
+		if (hasFeature(Version::COMPACT_STRING_SERIALIZATION))
+		{
+			if (data.empty())
+			{
+				save(static_cast<uint32_t>(0));
+				return;
+			}
+
+			auto it = savedStrings.find(data);
+
+			if (it == savedStrings.end())
+			{
+				save(static_cast<uint32_t>(data.length()));
+				this->write(static_cast<const void *>(data.data()), data.size());
+
+				// -1, -2...
+				int32_t newStringID = -1 - savedStrings.size();
+
+				savedStrings[data] = newStringID;
+			}
+			else
+			{
+				int32_t index = it->second;
+				save(index);
+			}
+		}
+		else
+		{
+			save(static_cast<uint32_t>(data.length()));
+			this->write(static_cast<const void *>(data.data()), data.size());
+		}
 	}
+
 	template <typename T1, typename T2>
 	void save(const std::pair<T1,T2> &data)
 	{
@@ -329,9 +407,19 @@ public:
 		save(data.second);
 	}
 	template <typename T1, typename T2>
+	void save(const std::unordered_map<T1,T2> &data)
+	{
+		*this & static_cast<uint32_t>(data.size());
+		for(auto i = data.begin(); i != data.end(); i++)
+		{
+			save(i->first);
+			save(i->second);
+		}
+	}
+	template <typename T1, typename T2>
 	void save(const std::map<T1,T2> &data)
 	{
-		*this & ui32(data.size());
+		*this & static_cast<uint32_t>(data.size());
 		for(auto i = data.begin(); i != data.end(); i++)
 		{
 			save(i->first);
@@ -341,7 +429,7 @@ public:
 	template <typename T1, typename T2>
 	void save(const std::multimap<T1, T2> &data)
 	{
-		*this & ui32(data.size());
+		*this & static_cast<uint32_t>(data.size());
 		for(auto i = data.begin(); i != data.end(); i++)
 		{
 			save(i->first);
@@ -351,7 +439,7 @@ public:
 	template<typename T0, typename... TN>
 	void save(const std::variant<T0, TN...> & data)
 	{
-		si32 which = data.index();
+		int32_t which = data.index();
 		save(which);
 
 		VariantVisitorSaver<BinarySerializer> visitor(*this);
@@ -362,26 +450,26 @@ public:
 	{
 		if(data)
 		{
-			save((ui8)1);
+			save(static_cast<uint8_t>(1));
 			save(*data);
 		}
 		else
 		{
-			save((ui8)0);
+			save(static_cast<uint32_t>(0));
 		}
 	}
 
 	template <typename T>
 	void save(const boost::multi_array<T, 3> &data)
 	{
-		ui32 length = data.num_elements();
+		uint32_t length = data.num_elements();
 		*this & length;
 		auto shape = data.shape();
-		ui32 x = shape[0];
-		ui32 y = shape[1];
-		ui32 z = shape[2];
+		uint32_t x = shape[0];
+		uint32_t y = shape[1];
+		uint32_t z = shape[2];
 		*this & x & y & z;
-		for(ui32 i = 0; i < length; i++)
+		for(uint32_t i = 0; i < length; i++)
 			save(data.data()[i]);
 	}
 	template <std::size_t T>

+ 0 - 6
lib/serializer/CSerializer.cpp

@@ -25,14 +25,8 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
 {
 	registerVectoredType<CGObjectInstance, ObjectInstanceID>(&gs->map->objects,
 		[](const CGObjectInstance &obj){ return obj.id; });
-	registerVectoredType<CHero, HeroTypeID>(&lib->heroh->objects,
-		[](const CHero &h){ return h.getId(); });
 	registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->map->allHeroes,
 		[](const CGHeroInstance &h){ return h.type->getId(); });
-	registerVectoredType<CCreature, CreatureID>(&lib->creh->objects,
-		[](const CCreature &cre){ return cre.getId(); });
-	registerVectoredType<CArtifact, ArtifactID>(&lib->arth->objects,
-		[](const CArtifact &art){ return art.getId(); });
 	registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
 		[](const CArtifactInstance &artInst){ return artInst.getId(); });
 	registerVectoredType<CQuest, si32>(&gs->map->quests,

+ 6 - 1
lib/serializer/ESerializationVersion.h

@@ -44,5 +44,10 @@ enum class ESerializationVersion : int32_t
 
 	RELEASE_150 = ARTIFACT_COSTUMES, // for convenience
 
-	CURRENT = ARTIFACT_COSTUMES
+	COMPACT_STRING_SERIALIZATION, // 841 - optimized serialization of previoulsy encountered strings
+	COMPACT_INTEGER_SERIALIZATION, // 842 - serialize integers in forms similar to protobuf
+	REMOVE_FOG_OF_WAR_POINTER, // 843 - fog of war is serialized as reference instead of pointer
+	SIMPLE_TEXT_CONTAINER_SERIALIZATION, // 844 - text container is serialized using common routine instead of custom approach
+
+	CURRENT = SIMPLE_TEXT_CONTAINER_SERIALIZATION
 };

+ 21 - 0
lib/serializer/Serializeable.h

@@ -0,0 +1,21 @@
+/*
+ * Serializeable.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+// Tag class that acts as base for all classes that can be serialized by pointer
+class Serializeable
+{
+public:
+    virtual ~Serializeable() = default;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/spells/AdventureSpellMechanics.cpp

@@ -735,7 +735,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
 		{
 			ObjectPosInfo posInfo(obj);
 
-			if((*fowMap)[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0)
+			if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0)
 				pack.objectPositions.push_back(posInfo);
 		}
 	}

+ 2 - 2
server/CGameHandler.cpp

@@ -884,11 +884,11 @@ void CGameHandler::onNewTurn()
 				// find all hidden tiles
 				const auto & fow = getPlayerTeam(player)->fogOfWarMap;
 
-				auto shape = fow->shape();
+				auto shape = fow.shape();
 				for(size_t z = 0; z < shape[0]; z++)
 					for(size_t x = 0; x < shape[1]; x++)
 						for(size_t y = 0; y < shape[2]; y++)
-							if (!(*fow)[z][x][y])
+							if (!fow[z][x][y])
 								fw.tiles.insert(int3(x, y, z));
 
 				sendAndApply (&fw);

+ 1 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -367,7 +367,7 @@ void PlayerMessageProcessor::cheatMapReveal(PlayerColor player, bool reveal)
 	for(int z = 0; z < mapSize.z; z++)
 		for(int x = 0; x < mapSize.x; x++)
 			for(int y = 0; y < mapSize.y; y++)
-				if(!(*fowMap)[z][x][y] || fc.mode == ETileVisibility::HIDDEN)
+				if(!fowMap[z][x][y] || fc.mode == ETileVisibility::HIDDEN)
 					hlp_tab[lastUnc++] = int3(x, y, z);
 
 	fc.tiles.insert(hlp_tab, hlp_tab + lastUnc);