Browse Source

Improved serializer. See: http://forum.vcmi.eu/viewtopic.php?p=11562#11562
Save format changed, removed compatibility workarounds.

Michał W. Urbańczyk 11 năm trước cách đây
mục cha
commit
1e555a8ee3
10 tập tin đã thay đổi với 727 bổ sung327 xóa
  1. 10 0
      client/CPreGame.cpp
  2. 16 0
      client/Client.cpp
  3. 58 58
      lib/CGameState.cpp
  4. 3 14
      lib/CModHandler.h
  5. 35 0
      lib/CObjectHandler.h
  6. 88 10
      lib/Connection.cpp
  7. 258 33
      lib/Connection.h
  8. 7 3
      lib/NetPacks.h
  9. 240 209
      lib/RegisterTypes.h
  10. 12 0
      server/CGameHandler.cpp

+ 10 - 0
client/CPreGame.cpp

@@ -209,6 +209,16 @@ public:
 	}
 };
 
+template <> class CApplyOnPG<CPack> : public CBaseForPGApply
+{
+public:
+	void applyOnPG(CSelectionScreen *selScr, void *pack) const
+	{
+			logGlobal->errorStream() << "Cannot apply on PG plain CPack!";
+			assert(0);
+	}
+};
+
 static CApplier<CBaseForPGApply> *applier = nullptr;
 
 static CPicture* createPicture(const JsonNode& config)

+ 16 - 0
client/Client.cpp

@@ -79,6 +79,22 @@ public:
 	}
 };
 
+template <> class CApplyOnCL<CPack> : public CBaseForCLApply
+{
+public:
+	void applyOnClAfter(CClient *cl, void *pack) const
+	{
+		logGlobal->errorStream() << "Cannot apply on CL plain CPack!";
+		assert(0);
+	}
+	void applyOnClBefore(CClient *cl, void *pack) const
+	{
+		logGlobal->errorStream() << "Cannot apply on CL plain CPack!";
+		assert(0);
+	}
+};
+
+
 static CApplier<CBaseForCLApply> *applier = nullptr;
 
 void CClient::init()

+ 58 - 58
lib/CGameState.cpp

@@ -75,61 +75,61 @@ public:
 
 static CApplier<CBaseForGSApply> *applierGs = nullptr;
 
-class IObjectCaller
-{
-public:
-	virtual ~IObjectCaller(){};
-	virtual void preInit()=0;
-	virtual void postInit()=0;
-};
-
-template <typename T>
-class CObjectCaller : public IObjectCaller
-{
-public:
-	void preInit()
-	{
-		//T::preInit();
-	}
-	void postInit()
-	{
-		//T::postInit();
-	}
-};
-
-class CObjectCallersHandler
-{
-public:
-	std::vector<IObjectCaller*> apps;
-
-	template<typename T> void registerType(const T * t=nullptr)
-	{
-		apps.push_back(new CObjectCaller<T>);
-	}
-
-	CObjectCallersHandler()
-	{
-		registerTypes1(*this);
-	}
-
-	~CObjectCallersHandler()
-	{
-		for (auto & elem : apps)
-			delete elem;
-	}
-
-	void preInit()
-	{
-// 		for (size_t i = 0; i < apps.size(); i++)
-// 			apps[i]->preInit();
-	}
-
-	void postInit()
-	{
-	//for (size_t i = 0; i < apps.size(); i++)
-	//apps[i]->postInit();
-	}
-} *objCaller = nullptr;
+// class IObjectCaller
+// {
+// public:
+// 	virtual ~IObjectCaller(){};
+// 	virtual void preInit()=0;
+// 	virtual void postInit()=0;
+// };
+// 
+// template <typename T>
+// class CObjectCaller : public IObjectCaller
+// {
+// public:
+// 	void preInit()
+// 	{
+// 		//T::preInit();
+// 	}
+// 	void postInit()
+// 	{
+// 		//T::postInit();
+// 	}
+// };
+
+// class CObjectCallersHandler
+// {
+// public:
+// 	std::vector<IObjectCaller*> apps;
+// 
+// 	template<typename T> void registerType(const T * t=nullptr)
+// 	{
+// 		apps.push_back(new CObjectCaller<T>);
+// 	}
+// 
+// 	CObjectCallersHandler()
+// 	{
+// 		registerTypes1(*this);
+// 	}
+// 
+// 	~CObjectCallersHandler()
+// 	{
+// 		for (auto & elem : apps)
+// 			delete elem;
+// 	}
+// 
+// 	void preInit()
+// 	{
+// // 		for (size_t i = 0; i < apps.size(); i++)
+// // 			apps[i]->preInit();
+// 	}
+// 
+// 	void postInit()
+// 	{
+// 	//for (size_t i = 0; i < apps.size(); i++)
+// 	//apps[i]->postInit();
+// 	}
+// } *objCaller = nullptr;
 
 void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
 {
@@ -730,7 +730,7 @@ CGameState::CGameState()
 	mx = new boost::shared_mutex();
 	applierGs = new CApplier<CBaseForGSApply>;
 	registerTypes2(*applierGs);
-	objCaller = new CObjectCallersHandler;
+	//objCaller = new CObjectCallersHandler;
 	globalEffects.setDescription("Global effects");
 }
 
@@ -742,7 +742,7 @@ CGameState::~CGameState()
 	//delete scenarioOps; //TODO: fix for loading ind delete
 	//delete initialOpts;
 	delete applierGs;
-	delete objCaller;
+	//delete objCaller;
 
 	for(auto ptr : hpool.heroesPool) // clean hero pool
 		ptr.second.dellNull();
@@ -1838,7 +1838,7 @@ void CGameState::initTowns()
 void CGameState::initMapObjects()
 {
 	logGlobal->debugStream() << "\tObject initialization";
-	objCaller->preInit();
+//	objCaller->preInit();
 	for(CGObjectInstance *obj : map->objects)
 	{
 		if(obj)

+ 3 - 14
lib/CModHandler.h

@@ -45,13 +45,7 @@ class CIdentifierStorage
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			if(version >= 744)
-				h & id & scope;
-			else if(h.saving)
-			{
-				logGlobal->warnStream() << "Save compatibility, making object data with id -1 (can this happen?)";
-				id = -1;
-			}
+			h & id & scope;
 		}
 	};
 
@@ -87,8 +81,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		if(version >= 744)
-			h & registeredObjects;
+		h & registeredObjects;
 	}
 };
 
@@ -266,10 +259,6 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & allMods & activeMods & settings & modules;
-		if(version >= 744)
-			h & identifiers;
-		else
-			logGlobal->warnStream() << "Savegame compatibility mode, omitting identifiers in modhandler. Related bugs will persist.";
+		h & allMods & activeMods & settings & modules & identifiers;
 	}
 };

+ 35 - 0
lib/CObjectHandler.h

@@ -128,6 +128,11 @@ public:
 
 	static void preInit(); //called before objs receive their initObj
 	static void postInit();//called after objs receive their initObj
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		logGlobal->errorStream() << "IObjectInterface serialized, unexpected, should not happen!";
+	}
 };
 
 class DLL_LINKAGE IBoatGenerator
@@ -136,6 +141,8 @@ public:
 	const CGObjectInstance *o;
 
 	IBoatGenerator(const CGObjectInstance *O);
+	virtual ~IBoatGenerator() {}
+
 	virtual int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
 	virtual void getOutOffsets(std::vector<int3> &offsets) const =0; //offsets to obj pos when we boat can be placed
 	int3 bestLocation() const; //returns location when the boat should be placed
@@ -143,16 +150,28 @@ public:
 	enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER};
 	EGeneratorState shipyardStatus() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water
 	void getProblemText(MetaString &out, const CGHeroInstance *visitor = nullptr) const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & o;
+	}
 };
 
 class DLL_LINKAGE IShipyard : public IBoatGenerator
 {
 public:
 	IShipyard(const CGObjectInstance *O);
+	virtual ~IShipyard() {}
+
 	virtual void getBoatCost(std::vector<si32> &cost) const;
 
 	static const IShipyard *castFrom(const CGObjectInstance *obj);
 	static IShipyard *castFrom(CGObjectInstance *obj);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<IBoatGenerator&>(*this);
+	}
 };
 
 class DLL_LINKAGE IMarket
@@ -161,6 +180,8 @@ public:
 	const CGObjectInstance *o;
 
 	IMarket(const CGObjectInstance *O);
+	virtual ~IMarket() {}
+
 	virtual int getMarketEfficiency() const =0;
 	virtual bool allowsTrade(EMarketMode::EMarketMode mode) const;
 	virtual int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
@@ -170,6 +191,11 @@ public:
 	std::vector<EMarketMode::EMarketMode> availableModes() const;
 
 	static const IMarket *castFrom(const CGObjectInstance *obj, bool verbose = true);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & o;
+	}
 };
 
 class DLL_LINKAGE CGObjectInstance : public IObjectInterface
@@ -653,6 +679,8 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGDwelling&>(*this);
+		h & static_cast<IShipyard&>(*this);
+		h & static_cast<IMarket&>(*this);
 		h & name & builded & destroyed & identifier;
 		h & garrisonHero & visitingHero;
 		h & alignment & forbiddenBuildings & builtBuildings & bonusValue
@@ -1333,6 +1361,12 @@ public:
 	void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
 	CGShipyard();
 	void onHeroVisit(const CGHeroInstance * h) const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+		h & static_cast<IShipyard&>(*this);
+	}
 };
 
 class DLL_LINKAGE CGMagi : public CGObjectInstance
@@ -1417,6 +1451,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);
+		h & static_cast<IMarket&>(*this);
 	}
 };
 

+ 88 - 10
lib/Connection.cpp

@@ -440,27 +440,104 @@ CTypeList::CTypeList()
 	registerTypes(*this);
 }
 
-ui16 CTypeList::registerType( const std::type_info *type )
+CTypeList::TypeInfoPtr CTypeList::registerType( const std::type_info *type )
 {
-	TTypeMap::const_iterator i = types.find(type);
-	if(i != types.end())
-		return i->second; //type found, return ID
+	if(auto typeDescr = getTypeDescriptor(type, false))
+		return typeDescr;  //type found, return ptr to structure
 
 	//type not found - add it to the list and return given ID
-	ui16 id = types.size() + 1;
-	types.insert(std::make_pair(type,id));
-	return id;
+	auto newType = make_shared<TypeDescriptor>();
+	newType->typeID = typeInfos.size() + 1;
+	newType->name = type->name();
+	typeInfos[type] = newType;
+
+	return newType;
 }
 
 ui16 CTypeList::getTypeID( const std::type_info *type )
 {
-	TTypeMap::const_iterator i = types.find(type);
-	if(i != types.end())
-		return i->second;
+	auto i = typeInfos.find(type);
+	if(i != typeInfos.end())
+		return i->second->typeID;
 	else
 		return 0;
 }
 
+std::vector<CTypeList::TypeInfoPtr> CTypeList::castSequence(TypeInfoPtr from, TypeInfoPtr to)
+{
+	if(from == to)
+		return std::vector<CTypeList::TypeInfoPtr>();
+
+	// Perform a simple BFS in the class hierarchy.
+
+	auto BFS = [&](bool upcast)
+	{
+		std::map<TypeInfoPtr, TypeInfoPtr> previous;
+		std::queue<TypeInfoPtr> q;
+		q.push(to);
+		while(q.size())
+		{
+			auto typeNode = q.front();
+			q.pop();
+			for(auto &nodeBase : upcast ? typeNode->parents : typeNode->children)
+			{
+				if(!previous.count(nodeBase))
+				{
+					previous[nodeBase] = typeNode;
+					q.push(nodeBase);
+				}
+			}
+		}
+		
+		std::vector<TypeInfoPtr> ret;
+
+		if(!previous.count(from))
+			return ret;
+
+		ret.push_back(from);
+		TypeInfoPtr ptr = from;
+		do
+		{
+			ptr = previous.at(ptr);
+			ret.push_back(ptr);
+		} while(ptr != to);
+
+		return ret;
+	};
+
+	// Try looking both up and down.
+	auto ret = BFS(true);
+	if(ret.empty())
+		ret = BFS(false);
+
+	if(ret.empty())
+		THROW_FORMAT("Cannot find relation between types %s and %s. Were they (and all classes between them) properly registered?", from->name % to->name);
+
+	return ret;
+}
+
+std::vector<CTypeList::TypeInfoPtr> CTypeList::castSequence(const std::type_info *from, const std::type_info *to)
+{
+	//This additional if is needed because getTypeDescriptor might fail if type is not registered
+	// (and if casting is not needed, then registereing should no  be required)
+	if(*from == *to)
+		return std::vector<CTypeList::TypeInfoPtr>();
+
+	return castSequence(getTypeDescriptor(from), getTypeDescriptor(to));
+}
+
+CTypeList::TypeInfoPtr CTypeList::getTypeDescriptor(const std::type_info *type, bool throws)
+{
+	auto i = typeInfos.find(type);
+	if(i != typeInfos.end())
+		return i->second; //type found, return ptr to structure	
+
+	if(!throws)
+		return nullptr;
+
+	THROW_FORMAT("Cannot find type descriptor for type %s. Was it registered?", type->name());
+}
+
  std::ostream & operator<<(std::ostream &str, const CConnection &cpc)
  {
  	return str << "Connection with " << cpc.name << " (ID: " << cpc.connectionID << /*", " << (cpc.host ? "host" : "guest") <<*/ ")";
@@ -536,6 +613,7 @@ int CLoadIntegrityValidator::read( void * data, unsigned size )
 unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
 {
 	primaryFile->loadedPointers = this->loadedPointers;
+	primaryFile->loadedPointersTypes = this->loadedPointersTypes;
 	return std::move(primaryFile);
 }
 

+ 258 - 33
lib/Connection.h

@@ -28,8 +28,8 @@
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 
-const ui32 version = 744;
-const ui32 minSupportedVersion = 743;
+const ui32 version = 745;
+const ui32 minSupportedVersion = version;
 
 class CConnection;
 class CGObjectInstance;
@@ -86,23 +86,174 @@ struct TypeComparer
 	}
 };
 
+struct IPointerCaster
+{
+	virtual boost::any castRawPtr(const boost::any &ptr) const = 0; // takes From*, performs dynamic cast, returns To*
+	virtual boost::any castSharedPtr(const boost::any &ptr) const = 0; // takes std::shared_ptr<From>, performs dynamic cast, returns std::shared_ptr<To>
+	virtual boost::any castWeakPtr(const boost::any &ptr) const = 0; // takes std::weak_ptr<From>, performs dynamic cast, returns std::weak_ptr<To>. The object under poitner must live.
+	//virtual boost::any castUniquePtr(const boost::any &ptr) const = 0; // takes std::unique_ptr<From>, performs dynamic cast, returns std::unique_ptr<To>
+};
+
+template <typename From, typename To>
+struct PointerCaster : IPointerCaster
+{
+	virtual boost::any castRawPtr(const boost::any &ptr) const override // takes void* pointing to From object, performs dynamic cast, returns void* pointing to To object
+	{
+		From * from = (From*)boost::any_cast<void*>(ptr);
+		To * ret = dynamic_cast<To*>(from);
+		return (void*)ret;
+	}
+
+	// Helper function performing casts between smart pointers using dynamic_pointer_cast
+	template<typename SmartPt>
+	boost::any castSmartPtr(const boost::any &ptr) const
+	{
+		try
+		{
+			auto from = boost::any_cast<SmartPt>(ptr);
+			auto ret = std::dynamic_pointer_cast<To>(from);
+			return ret;
+		}
+		catch(std::exception &e)
+		{
+			THROW_FORMAT("Failed cast %s -> %s. Given argument was %s. Error message: %s", typeid(From).name() % typeid(To).name() % ptr.type().name() % e.what());
+		}
+	}
+
+	virtual boost::any castSharedPtr(const boost::any &ptr) const override 
+	{
+		return castSmartPtr<std::shared_ptr<From>>(ptr);
+	}
+	virtual boost::any castWeakPtr(const boost::any &ptr) const override 
+	{
+		auto from = boost::any_cast<std::weak_ptr<From>>(ptr);
+		return castSmartPtr<std::shared_ptr<From>>(from.lock());
+	}
+// 	virtual boost::any castUniquePtr(const boost::any &ptr) const override
+// 	{
+// 		return castSmartPtr<std::unique_ptr<From>>(ptr);
+// 	}
+};
+
 class DLL_LINKAGE CTypeList
 {
-	typedef std::multimap<const std::type_info *,ui16,TypeComparer> TTypeMap;
-	TTypeMap types;
 public:
+	struct TypeDescriptor;
+	typedef std::shared_ptr<TypeDescriptor> TypeInfoPtr;
+	struct TypeDescriptor
+	{
+		ui16 typeID;
+		const char *name;
+		std::vector<TypeInfoPtr> children, parents;
+	};
+private:
+
+	std::map<const std::type_info *, TypeInfoPtr, TypeComparer> typeInfos;
+	std::map<std::pair<TypeInfoPtr, TypeInfoPtr>, std::unique_ptr<const IPointerCaster>> casters; //for each pair <Base, Der> we provide a caster (each registered relations creates a single entry here)
+
+	CTypeList(CTypeList &)
+	{
+		// This type is non-copyable.
+		// Unfortunately on Windows it is required for DLL_EXPORT-ed type to provide copy c-tor, so we can't =delete it.
+		assert(0); 
+	}
+	CTypeList &operator=(CTypeList &)
+	{
+		// As above.
+		assert(0);
+		return *this;
+	}
+public:
+
 	CTypeList();
-	ui16 registerType(const std::type_info *type);
-	template <typename T> ui16 registerType(const T * t = nullptr)
+
+	TypeInfoPtr registerType(const std::type_info *type);
+
+
+	template <typename Base, typename Derived> 
+	void registerType(const Base * b = nullptr, const Derived * d = nullptr)
 	{
-		return registerType(getTypeInfo(t));
+		static_assert(std::is_base_of<Base, Derived>::value, "First registerType template parameter needs to ba a base class of the second one.");
+		static_assert(std::has_virtual_destructor<Base>::value, "Base class needs to have a virtual destructor.");
+		static_assert(!std::is_same<Base, Derived>::value, "Parameters of registerTypes should be two diffrenet types.");
+		auto bt = getTypeInfo(b), dt = getTypeInfo(d); //obtain std::type_info
+		auto bti = registerType(bt), dti = registerType(dt); //obtain our TypeDescriptor
+
+		// register the relation between classes
+		bti->children.push_back(dti);
+		dti->parents.push_back(bti);
+		casters[std::make_pair(bti, dti)] = make_unique<const PointerCaster<Base, Derived>>();
+		casters[std::make_pair(dti, bti)] = make_unique<const PointerCaster<Derived, Base>>();
 	}
 
 	ui16 getTypeID(const std::type_info *type);
-	template <typename T> ui16 getTypeID(const T * t = nullptr)
+	TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true); //if not throws, failure returns nullptr
+
+	template <typename T> 
+	ui16 getTypeID(const T * t = nullptr)
 	{
 		return getTypeID(getTypeInfo(t));
 	}
+	
+
+	// Returns sequence of types starting from "from" and ending on "to". Every next type is derived from the previous.
+	// Throws if there is no link registered.
+	std::vector<TypeInfoPtr> castSequence(TypeInfoPtr from, TypeInfoPtr to);
+	std::vector<TypeInfoPtr> castSequence(const std::type_info *from, const std::type_info *to);
+
+	template<boost::any(IPointerCaster::*CastingFunction)(const boost::any &) const>
+	boost::any castHelper(boost::any inputPtr, const std::type_info *fromArg, const std::type_info *toArg)
+	{
+		auto typesSequence = castSequence(fromArg, toArg);
+
+		boost::any ptr = inputPtr;
+		for(int i = 0; i < (int)typesSequence.size() - 1; i++)
+		{
+			auto &from = typesSequence[i];
+			auto &to = typesSequence[i + 1];
+			auto castingPair = std::make_pair(from, to);
+			if(!casters.count(castingPair))
+				THROW_FORMAT("Cannot find caster for conversion %s -> %s which is needed to cast %s -> %s", from->name % to->name % fromArg->name() % toArg->name());
+
+			auto &caster = casters.at(castingPair);
+			ptr = (*caster.*CastingFunction)(ptr); //Why does unique_ptr does not have operator->* ..?
+		}
+
+		return ptr;
+	}
+
+	template<typename TInput>
+	void *castToMostDerived(const TInput *inputPtr)
+	{
+		auto &baseType = typeid(typename std::remove_cv<TInput>::type);
+		auto derivedType = getTypeInfo(inputPtr);
+
+		if(baseType == *derivedType)
+			return (void*)inputPtr;
+
+		return boost::any_cast<void*>(castHelper<&IPointerCaster::castRawPtr>((void*)inputPtr, &baseType, derivedType));
+	}
+
+	template<typename TInput>
+	boost::any castSharedToMostDerived(const std::shared_ptr<TInput> inputPtr)
+	{
+		auto &baseType = typeid(typename std::remove_cv<TInput>::type);
+		auto derivedType = getTypeInfo(inputPtr.get());
+
+		if(baseType == *derivedType)
+			return inputPtr;
+
+		return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, &baseType, derivedType);
+	}
+
+	void* castRaw(void *inputPtr, const std::type_info *from, const std::type_info *to)
+	{
+		return boost::any_cast<void*>(castHelper<&IPointerCaster::castRawPtr>(inputPtr, from, to));
+	}
+	boost::any castShared(boost::any inputPtr, const std::type_info *from, const std::type_info *to)
+	{
+		return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, from, to);
+	}
 
 
 	template <typename T> const std::type_info * getTypeInfo(const T * t = nullptr)
@@ -583,10 +734,19 @@ public:
 			delete iter->second;
 	}
 
-	template<typename T> void registerType(const T * t=nullptr)
+	template<typename T>
+	void addSaver(const T * t = nullptr)
 	{
-		ui16 ID = typeList.registerType(t);
-		savers[ID] = new CPointerSaver<COSer<Serializer>,T>;
+		auto ID = typeList.getTypeID(t);
+		if(!savers.count(ID))
+			savers[ID] = new CPointerSaver<COSer<Serializer>, T>;
+	}
+
+	template<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
+	{
+		typeList.registerType(b, d);
+		addSaver(b);
+		addSaver(d);
 	}
 
     Serializer * This()
@@ -650,7 +810,10 @@ public:
 
 		if(smartPointerSerialization)
 		{
-			std::map<const void*,ui32>::iterator i = savedPointers.find(data);
+			// 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.
+			auto actualPointer = typeList.castToMostDerived(data);
+			std::map<const void*,ui32>::iterator i = savedPointers.find(actualPointer);
 			if(i != savedPointers.end())
 			{
 				//this pointer has been already serialized - write only it's id
@@ -660,7 +823,7 @@ public:
 
 			//give id to this pointer
 			ui32 pid = (ui32)savedPointers.size();
-			savedPointers[data] = pid;
+			savedPointers[actualPointer] = pid;
 			*this << pid;
 		}
 
@@ -678,7 +841,7 @@ public:
 		if(!tid)
 			*this << *data;	 //if type is unregistered simply write all data in a standard way
 		else
-			savers[tid]->savePtr(*this,data);  //call serializer specific for our real type
+			savers[tid]->savePtr(*this, typeList.castToMostDerived(data));  //call serializer specific for our real type
 	}
 
 	template <typename T>
@@ -855,24 +1018,44 @@ class DLL_LINKAGE CLoaderBase : public virtual CSerializer
 class CBasicPointerLoader
 {
 public:
-	virtual void loadPtr(CLoaderBase &ar, void *data, ui32 pid) const =0; //data is pointer to the ACTUAL POINTER
+	virtual const type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const =0; //data is pointer to the ACTUAL POINTER
 	virtual ~CBasicPointerLoader(){}
 };
 
+template <typename T, typename Enable = void>
+struct ClassObjectCreator
+{
+	static T *invoke()
+	{
+		static_assert(!typename std::is_abstract<T>::value, "Cannot call new upon abstract classes!");
+		return new T();
+	}
+};
+
+template<typename T>
+struct ClassObjectCreator<T, typename std::enable_if<std::is_abstract<T>::value>::type>
+{
+	static T *invoke()
+	{
+		throw std::runtime_error("Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std::string(typeid(T).name()));
+	}
+};
+
 template <typename Serializer, typename T> class CPointerLoader : public CBasicPointerLoader
 {
 public:
-	void loadPtr(CLoaderBase &ar, void *data, ui32 pid) const //data is pointer to the ACTUAL POINTER
+	const type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const //data is pointer to the ACTUAL POINTER
 	{
 		Serializer &s = static_cast<Serializer&>(ar);
 		T *&ptr = *static_cast<T**>(data);
 
 		//create new object under pointer
 		typedef typename boost::remove_pointer<T>::type npT;
-		ptr = new npT;
+		ptr = ClassObjectCreator<npT>::invoke(); //does new npT or throws for abstract classes
 		s.ptrAllocated(ptr, pid);
 		//T is most derived known type, it's time to call actual serialize
 		ptr->serialize(s,version);
+		return &typeid(T);
 	}
 };
 
@@ -882,10 +1065,11 @@ template <typename Serializer> class DLL_LINKAGE CISer : public CLoaderBase
 public:
 	bool saving;
 	std::map<ui16,CBasicPointerLoader*> loaders; // typeID => CPointerSaver<serializer,type>
-	ui32 fileVersion;
+	si32 fileVersion;
 	bool reverseEndianess; //if source has different endianess than us, we reverse bytes
 
 	std::map<ui32, void*> loadedPointers;
+	std::map<ui32, const std::type_info*> loadedPointersTypes;
 	std::map<const void*, boost::any> loadedSharedPointers;
 
 	bool smartPointerSerialization;
@@ -906,10 +1090,19 @@ public:
 			delete iter->second;
 	}
 
-	template<typename T> void registerType(const T * t=nullptr)
+	template<typename T>
+	void addLoader(const T * t = nullptr)
 	{
-		ui16 ID = typeList.registerType(t);
-		loaders[ID] = new CPointerLoader<CISer<Serializer>,T>;
+		auto ID = typeList.getTypeID(t);
+		if(!loaders.count(ID))
+			loaders[ID] = new CPointerLoader<CISer<Serializer>, T>;
+	}
+
+	template<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
+	{
+		typeList.registerType(b, d);
+		addLoader(b);
+		addLoader(d);
 	}
 
     Serializer * This()
@@ -1049,8 +1242,10 @@ public:
 
 			if(i != loadedPointers.end())
 			{
-				//we already got this pointer
-				data = static_cast<T>(i->second);
+				// We already got this pointer
+				// Cast it in case we are loading it to a non-first base pointer
+				assert(loadedPointersTypes.count(pid));
+				data = reinterpret_cast<T>(typeList.castRaw(i->second, loadedPointersTypes.at(pid), &typeid(typename boost::remove_const<typename boost::remove_pointer<T>::type>::type)));
 				return;
 			}
 		}
@@ -1069,13 +1264,14 @@ public:
 		{
 			typedef typename boost::remove_pointer<T>::type npT;
 			typedef typename boost::remove_const<npT>::type ncpT;
-			data = new ncpT;
+			data = ClassObjectCreator<ncpT>::invoke();
 			ptrAllocated(data, pid);
 			*this >> *data;
 		}
 		else
 		{
-			loaders[tid]->loadPtr(*this,&data, pid);
+			auto typeInfo = loaders[tid]->loadPtr(*this,&data, pid);
+			data = reinterpret_cast<T>(typeList.castRaw((void*)data, typeInfo, &typeid(typename boost::remove_const<typename boost::remove_pointer<T>::type>::type)));
 		}
 	}
 
@@ -1083,7 +1279,10 @@ public:
 	void ptrAllocated(const T *ptr, ui32 pid)
 	{
 		if(smartPointerSerialization && pid != 0xffffffff)
+		{
+			loadedPointersTypes[pid] = &typeid(T);
 			loadedPointers[pid] = (void*)ptr; //add loaded pointer to our lookup map; cast is to avoid errors with const T* pt
+		}
 	}
 
 #define READ_CHECK_U32(x)			\
@@ -1099,19 +1298,34 @@ public:
 	template <typename T>
 	void loadSerializable(shared_ptr<T> &data)
 	{
-		T *internalPtr;
+		typedef typename boost::remove_const<T>::type NonConstT;
+		NonConstT *internalPtr;
 		*this >> internalPtr;
 		
+		void *internalPtrDerived = typeList.castToMostDerived(internalPtr);
+
 		if(internalPtr)
 		{
-			auto itr = loadedSharedPointers.find(internalPtr);
+			auto itr = loadedSharedPointers.find(internalPtrDerived);
 			if(itr != loadedSharedPointers.end())
 			{
 				// This pointers is already loaded. The "data" needs to be pointed to it, 
 				// so their shared state is actually shared.
 				try
 				{
-					data = boost::any_cast<std::shared_ptr<T>>(itr->second);
+					auto actualType = typeList.getTypeInfo(internalPtr);
+					auto typeWeNeedToReturn = typeList.getTypeInfo<T>();
+					if(*actualType == *typeWeNeedToReturn)
+					{
+						// No casting needed, just unpack already stored shared_ptr and return it
+						data = boost::any_cast<std::shared_ptr<T>>(itr->second);
+					}
+					else
+					{
+						// We need to perform series of casts
+						auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn);
+						data = boost::any_cast<std::shared_ptr<T>>(ret);
+					}
 				}
 				catch(std::exception &e)
 				{
@@ -1124,8 +1338,9 @@ public:
 			}
 			else
 			{
-				data = std::shared_ptr<T>(internalPtr);
-				loadedSharedPointers[internalPtr] = data;
+				auto hlp = std::shared_ptr<NonConstT>(internalPtr);
+				data = hlp; //possibly adds const
+				loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
 			}
 		}
 		else
@@ -1435,10 +1650,20 @@ public:
 		for(iter = apps.begin(); iter != apps.end(); iter++)
 			delete iter->second;
 	}
-	template<typename U> void registerType(const U * t=nullptr)
+
+	template<typename RegisteredType>
+	void addApplier(ui16 ID)
+	{
+		if(!apps.count(ID))
+			apps[ID] = T::getApplier<RegisteredType>();
+	}
+
+	template<typename Base, typename Derived> 
+	void registerType(const Base * b = nullptr, const Derived * d = nullptr)
 	{
-		ui16 ID = typeList.registerType(t);
-		apps[ID] = T::getApplier(t);
+		typeList.registerType(b, d);
+		addApplier<Base>(typeList.getTypeID(b));
+		addApplier<Derived>(typeList.getTypeID(d));
 	}
 
 };

+ 7 - 3
lib/NetPacks.h

@@ -47,9 +47,9 @@ struct CPackForClient : public CPack
 
 	CGameState* GS(CClient *cl);
 	void applyFirstCl(CClient *cl)//called before applying to gs
-	{};
+	{}
 	void applyCl(CClient *cl)//called after applying to gs
-	{};
+	{}
 };
 
 struct CPackForServer : public CPack
@@ -64,7 +64,11 @@ struct CPackForServer : public CPack
 		type = 2;
 	}
 
-	bool applyGh(CGameHandler *gh);//called after applying to gs
+	bool applyGh(CGameHandler *gh) //called after applying to gs
+	{
+		logGlobal->errorStream() << "Should not happen... applying plain CPackForServer";
+		return false;
+	}
 };
 
 

+ 240 - 209
lib/RegisterTypes.h

@@ -23,237 +23,268 @@
 template<typename Serializer>
 void registerTypes1(Serializer &s)
 {
-	//map objects
-	s.template registerType<CGHeroPlaceholder>();
-	s.template registerType<CGHeroInstance>();
-	s.template registerType<CGTownInstance>();
-	s.template registerType<CTownBonus>();
-	s.template registerType<CGPandoraBox>();
-	s.template registerType<CGEvent>();
-	s.template registerType<CGDwelling>();
-	s.template registerType<CGVisitableOPH>();
-	s.template registerType<CGVisitableOPW>();
-	s.template registerType<CGTeleport>();
-	s.template registerType<CGPickable>();
-	s.template registerType<CGCreature>();
-	s.template registerType<CGSignBottle>();
-	s.template registerType<CQuest>();
-	s.template registerType<IQuestObject>();
-	s.template registerType<CGSeerHut>();
-	s.template registerType<CGQuestGuard>();
-	s.template registerType<CGWitchHut>();
-	s.template registerType<CGScholar>();
-	s.template registerType<CGGarrison>();
-	s.template registerType<CGArtifact>();
-	s.template registerType<CGResource>();
-	s.template registerType<CGMine>();
-	s.template registerType<CGShrine>();
-	s.template registerType<CGBonusingObject>();
-	s.template registerType<CGMagicSpring>();
-	s.template registerType<CGMagicWell>();
-	s.template registerType<CGObservatory>();
-	s.template registerType<CGKeys>();
-	s.template registerType<CGKeymasterTent>();
-	s.template registerType<CGBorderGuard>();
-	s.template registerType<CGBorderGate>();
-	s.template registerType<CGBoat>();
-	s.template registerType<CGMagi>();
-	s.template registerType<CGSirens>();
-	s.template registerType<CGOnceVisitable>();
-	s.template registerType<CBank>();
-	s.template registerType<CGPyramid>();
-	s.template registerType<CGShipyard>();
-	s.template registerType<CCartographer>();
-	s.template registerType<CGObjectInstance>();
-	s.template registerType<COPWBonus>();
-	s.template registerType<CGDenOfthieves>();
-	s.template registerType<CGObelisk>();
-	s.template registerType<CGLighthouse>();
-	s.template registerType<CGMarket>();
-	s.template registerType<CGBlackMarket>();
-	s.template registerType<CGUniversity>();
-	//end of objects
-	s.template registerType<IPropagator>();
-	s.template registerType<CPropagatorNodeType>();
+	//////////////////////////////////////////////////////////////////////////
+	// Adventure map objects (and related)
+	////////////////////////////////////////////////////////////////////////// 
+	s.template registerType<IObjectInterface, CGObjectInstance>();
+
+	// Non-armed objects
+	s.template registerType<CGObjectInstance, CGTeleport>();
+	s.template registerType<CGObjectInstance, CGPickable>();
+	s.template registerType<CGObjectInstance, CGSignBottle>();
+	s.template registerType<CGObjectInstance, CGScholar>();
+	s.template registerType<CGObjectInstance, CGBonusingObject>();
+	s.template registerType<CGObjectInstance, CGMagicWell>();
+	s.template registerType<CGObjectInstance, CGObservatory>();
+	s.template registerType<CGObjectInstance, CGKeys>();
+		s.template registerType<CGKeys, CGKeymasterTent>();
+		s.template registerType<CGKeys, CGBorderGuard>(); s.template registerType<IQuestObject, CGBorderGuard>();
+			s.template registerType<CGBorderGuard, CGBorderGate>();
+	s.template registerType<CGObjectInstance, CGBoat>();
+	s.template registerType<CGObjectInstance, CGMagi>();
+	s.template registerType<CGObjectInstance, CGSirens>();
+	s.template registerType<CGObjectInstance, CGShipyard>(); s.template registerType<IShipyard, CGShipyard>();
+	s.template registerType<CGObjectInstance, CGDenOfthieves>();
+	s.template registerType<CGObjectInstance, CGLighthouse>();
+	s.template registerType<CGObjectInstance, CGMarket>(); s.template registerType<IMarket, CGMarket>();
+		s.template registerType<CGMarket, CGBlackMarket>();
+		s.template registerType<CGMarket, CGUniversity>();
+	s.template registerType<CGObjectInstance, CGHeroPlaceholder>();
+
+	s.template registerType<CGObjectInstance, CArmedInstance>(); s.template registerType<CBonusSystemNode, CArmedInstance>(); s.template registerType<CCreatureSet, CArmedInstance>();
+
+	// Armed objects
+	s.template registerType<CArmedInstance, CGHeroInstance>(); s.template registerType<IBoatGenerator, CGHeroInstance>(); s.template registerType<CArtifactSet, CGHeroInstance>();
+	s.template registerType<CArmedInstance, CGDwelling>();
+		s.template registerType<CGDwelling, CGTownInstance>(); s.template registerType<IShipyard, CGTownInstance>(); s.template registerType<IMarket, CGTownInstance>();
+	s.template registerType<CArmedInstance, CGPandoraBox>();
+		s.template registerType<CGPandoraBox, CGEvent>();
+	s.template registerType<CArmedInstance, CGCreature>();
+	s.template registerType<CArmedInstance, CGGarrison>();
+	s.template registerType<CArmedInstance, CGArtifact>();
+	s.template registerType<CArmedInstance, CGResource>();
+	s.template registerType<CArmedInstance, CGMine>();
+	s.template registerType<CArmedInstance, CBank>();
+		s.template registerType<CBank, CGPyramid>();
+	s.template registerType<CArmedInstance, CGSeerHut>(); s.template registerType<IQuestObject, CGSeerHut>();
+	s.template registerType<CGSeerHut, CGQuestGuard>();
+
 
-	s.template registerType<ILimiter>();
-	s.template registerType<LimiterList>();
-	s.template registerType<CCreatureTypeLimiter>();
-	s.template registerType<HasAnotherBonusLimiter>();
-	s.template registerType<CreatureNativeTerrainLimiter>();
-	s.template registerType<CreatureFactionLimiter>();
-	s.template registerType<CreatureAlignmentLimiter>();
-	s.template registerType<RankRangeLimiter>();
-	s.template registerType<StackOwnerLimiter>();
+	//Other object-related
+	s.template registerType<IObjectInterface, CGTownBuilding>();
+		s.template registerType<CGTownBuilding, CTownBonus>();
+			s.template registerType<CGTownBuilding, COPWBonus>();
 
-	s.template registerType<CModInfo>();
 
-	s.template registerType<CBonusSystemNode>();
-	s.template registerType<CArtifact>();
-	s.template registerType<CGrowingArtifact>();
-	s.template registerType<CCreature>();
-	s.template registerType<CStackInstance>();
-	s.template registerType<CCommanderInstance>();
-	s.template registerType<PlayerState>();
-	s.template registerType<TeamState>();
-	s.template registerType<CGameState>();
-	s.template registerType<CGHeroInstance::HeroSpecial>();
-	s.template registerType<CArmedInstance>();
-	s.template registerType<CStack>();
-	s.template registerType<BattleInfo>();
-	s.template registerType<QuestInfo>();
-	s.template registerType<CArtifactInstance>();
-	s.template registerType<CCombinedArtifactInstance>();
+	s.template registerType<CGObjectInstance, CGVisitableOPH>();
 
-	s.template registerType<CObstacleInstance>();
-	s.template registerType<MoatObstacle>();
-	s.template registerType<SpellCreatedObstacle>();
+	s.template registerType<CGObjectInstance, CGVisitableOPW>();
+	s.template registerType<CGVisitableOPW, CGMagicSpring>();
+
+	s.template registerType<CGObjectInstance, CPlayersVisited>();
+		s.template registerType<CPlayersVisited, CGWitchHut>();
+		s.template registerType<CPlayersVisited, CGShrine>();
+		s.template registerType<CPlayersVisited, CGOnceVisitable>();
+		s.template registerType<CPlayersVisited, CCartographer>();
+		s.template registerType<CPlayersVisited, CGObelisk>();
+
+	//s.template registerType<CQuest>();
+	//s.template registerType<IQuestObject>();
+
+	//end of objects
 
-	//s.template registerType<CCreatureArtifactInstance>();
-	//s.template registerType<ArtSlotInfo>();
-	//s.template registerType<ArtifactLocation>();
-	//s.template registerType<StackLocation>();
+	//////////////////////////////////////////////////////////////////////////
+	// Bonus system
+	////////////////////////////////////////////////////////////////////////// 
+	//s.template registerType<IPropagator>();
+	s.template registerType<IPropagator, CPropagatorNodeType>();
+
+	// Limiters
+	//s.template registerType<ILimiter>();
+	s.template registerType<ILimiter, LimiterList>();
+	s.template registerType<ILimiter, CCreatureTypeLimiter>();
+	s.template registerType<ILimiter, HasAnotherBonusLimiter>();
+	s.template registerType<ILimiter, CreatureNativeTerrainLimiter>();
+	s.template registerType<ILimiter, CreatureFactionLimiter>();
+	s.template registerType<ILimiter, CreatureAlignmentLimiter>();
+	s.template registerType<ILimiter, RankRangeLimiter>();
+	s.template registerType<ILimiter, StackOwnerLimiter>();
+	
+//	s.template registerType<CBonusSystemNode>();
+	s.template registerType<CBonusSystemNode, CArtifact>();
+	s.template registerType<CArtifact, CGrowingArtifact>();
+	s.template registerType<CBonusSystemNode, CCreature>();
+	s.template registerType<CBonusSystemNode, CStackInstance>();
+	s.template registerType<CStackInstance, CCommanderInstance>();
+	s.template registerType<CBonusSystemNode, PlayerState>();
+	s.template registerType<CBonusSystemNode, TeamState>();
+	//s.template registerType<CGameState>(); //TODO
+	s.template registerType<CBonusSystemNode, CGHeroInstance::HeroSpecial>();
+	//s.template registerType<CArmedInstance>();
+	s.template registerType<CBonusSystemNode, CStack>();
+	s.template registerType<CBonusSystemNode, BattleInfo>();
+	//s.template registerType<QuestInfo>();
+	s.template registerType<CBonusSystemNode, CArtifactInstance>();
+	s.template registerType<CArtifactInstance, CCombinedArtifactInstance>();
+
+	//s.template registerType<CObstacleInstance>();
+		s.template registerType<CObstacleInstance, MoatObstacle>();
+		s.template registerType<CObstacleInstance, SpellCreatedObstacle>();
 }
 
 template<typename Serializer>
 void registerTypes2(Serializer &s)
 {
-	s.template registerType<PackageApplied>();
-	s.template registerType<SystemMessage>();
-	s.template registerType<PlayerBlocked>();
-	s.template registerType<YourTurn>();
-	s.template registerType<SetResource>();
-	s.template registerType<SetResources>();
-	s.template registerType<SetPrimSkill>();
-	s.template registerType<SetSecSkill>();
-	s.template registerType<HeroVisitCastle>();
-	s.template registerType<ChangeSpells>();
-	s.template registerType<SetMana>();
-	s.template registerType<SetMovePoints>();
-	s.template registerType<FoWChange>();
-	s.template registerType<SetAvailableHeroes>();
-	s.template registerType<GiveBonus>();
-	s.template registerType<ChangeObjPos>();
-	s.template registerType<PlayerEndsGame>();
-	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>();
-	s.template registerType<NewStructures>();
-	s.template registerType<RazeStructures>();
-	s.template registerType<SetAvailableCreatures>();
-	s.template registerType<SetHeroesInTown>();
-	//s.template registerType<SetHeroArtifacts>();
-	s.template registerType<HeroRecruited>();
-	s.template registerType<GiveHero>();
-	s.template registerType<NewTurn>();
-	s.template registerType<InfoWindow>();
-	s.template registerType<SetObjectProperty>();
-	s.template registerType<SetHoverName>();
-	s.template registerType<HeroLevelUp>();
-	s.template registerType<CommanderLevelUp>();
-	s.template registerType<SetCommanderProperty>();
-	s.template registerType<BlockingDialog>();
-	s.template registerType<GarrisonDialog>();
-	s.template registerType<ExchangeDialog>();
-	s.template registerType<BattleStart>();
-	s.template registerType<BattleNextRound>();
-	s.template registerType<BattleSetActiveStack>();
-	s.template registerType<BattleResult>();
-	s.template registerType<BattleStackMoved>();
-	s.template registerType<BattleStackAttacked>();
-	s.template registerType<BattleAttack>();
-	s.template registerType<StartAction>();
-	s.template registerType<EndAction>();
-	s.template registerType<BattleSpellCast>();
-	s.template registerType<SetStackEffect>();
-	s.template registerType<BattleTriggerEffect>();
-	s.template registerType<BattleObstaclePlaced>();
-	s.template registerType<BattleSetStackProperty>();
-	s.template registerType<StacksInjured>();
-	s.template registerType<BattleResultsApplied>();
-	s.template registerType<StacksHealedOrResurrected>();
-	s.template registerType<ObstaclesRemoved>();
-	s.template registerType<CatapultAttack>();
-	s.template registerType<BattleStacksRemoved>();
-	s.template registerType<BattleStackAdded>();
-	s.template registerType<ShowInInfobox>();
-	s.template registerType<AdvmapSpellCast>();
-	s.template registerType<OpenWindow>();
-	s.template registerType<NewObject>();
-	s.template registerType<NewArtifact>();
-	s.template registerType<AddQuest>();
-	s.template registerType<ChangeStackCount>();
-	s.template registerType<SetStackType>();
-	s.template registerType<EraseStack>();
-	s.template registerType<SwapStacks>();
-	s.template registerType<InsertNewStack>();
-	s.template registerType<RebalanceStacks>();
-	s.template registerType<SetAvailableArtifacts>();
-	s.template registerType<PutArtifact>();
-	s.template registerType<EraseArtifact>();
-	s.template registerType<MoveArtifact>();
-	s.template registerType<AssembledArtifact>();
-	s.template registerType<DisassembledArtifact>();
-	s.template registerType<HeroVisit>();
+	s.template registerType<CPack, CPackForClient>();
+
+	s.template registerType<CPackForClient, PackageApplied>();
+	s.template registerType<CPackForClient, SystemMessage>();
+	s.template registerType<CPackForClient, PlayerBlocked>();
+	s.template registerType<CPackForClient, YourTurn>();
+	s.template registerType<CPackForClient, SetResource>();
+	s.template registerType<CPackForClient, SetResources>();
+	s.template registerType<CPackForClient, SetPrimSkill>();
+	s.template registerType<CPackForClient, SetSecSkill>();
+	s.template registerType<CPackForClient, HeroVisitCastle>();
+	s.template registerType<CPackForClient, ChangeSpells>();
+	s.template registerType<CPackForClient, SetMana>();
+	s.template registerType<CPackForClient, SetMovePoints>();
+	s.template registerType<CPackForClient, FoWChange>();
+	s.template registerType<CPackForClient, SetAvailableHeroes>();
+	s.template registerType<CPackForClient, GiveBonus>();
+	s.template registerType<CPackForClient, ChangeObjPos>();
+	s.template registerType<CPackForClient, PlayerEndsGame>();
+	s.template registerType<CPackForClient, RemoveBonus>();
+	s.template registerType<CPackForClient, UpdateCampaignState>();
+	s.template registerType<CPackForClient, PrepareForAdvancingCampaign>();
+	s.template registerType<CPackForClient, UpdateArtHandlerLists>();
+	s.template registerType<CPackForClient, UpdateMapEvents>();
+	s.template registerType<CPackForClient, UpdateCastleEvents>();
+	s.template registerType<CPackForClient, RemoveObject>();
+	s.template registerType<CPackForClient, TryMoveHero>();
+	//s.template registerType<CPackForClient, SetGarrisons>();
+	s.template registerType<CPackForClient, NewStructures>();
+	s.template registerType<CPackForClient, RazeStructures>();
+	s.template registerType<CPackForClient, SetAvailableCreatures>();
+	s.template registerType<CPackForClient, SetHeroesInTown>();
+	//s.template registerType<CPackForClient, SetHeroArtifacts>();
+	s.template registerType<CPackForClient, HeroRecruited>();
+	s.template registerType<CPackForClient, GiveHero>();
+	s.template registerType<CPackForClient, NewTurn>();
+	s.template registerType<CPackForClient, InfoWindow>();
+	s.template registerType<CPackForClient, SetObjectProperty>();
+	s.template registerType<CPackForClient, SetHoverName>();
+	s.template registerType<CPackForClient, BattleStart>();
+	s.template registerType<CPackForClient, BattleNextRound>();
+	s.template registerType<CPackForClient, BattleSetActiveStack>();
+	s.template registerType<CPackForClient, BattleResult>();
+	s.template registerType<CPackForClient, BattleStackMoved>();
+	s.template registerType<CPackForClient, BattleStackAttacked>();
+	s.template registerType<CPackForClient, BattleAttack>();
+	s.template registerType<CPackForClient, StartAction>();
+	s.template registerType<CPackForClient, EndAction>();
+	s.template registerType<CPackForClient, BattleSpellCast>();
+	s.template registerType<CPackForClient, SetStackEffect>();
+	s.template registerType<CPackForClient, BattleTriggerEffect>();
+	s.template registerType<CPackForClient, BattleObstaclePlaced>();
+	s.template registerType<CPackForClient, BattleSetStackProperty>();
+	s.template registerType<CPackForClient, StacksInjured>();
+	s.template registerType<CPackForClient, BattleResultsApplied>();
+	s.template registerType<CPackForClient, StacksHealedOrResurrected>();
+	s.template registerType<CPackForClient, ObstaclesRemoved>();
+	s.template registerType<CPackForClient, CatapultAttack>();
+	s.template registerType<CPackForClient, BattleStacksRemoved>();
+	s.template registerType<CPackForClient, BattleStackAdded>();
+	s.template registerType<CPackForClient, ShowInInfobox>();
+	s.template registerType<CPackForClient, AdvmapSpellCast>();
+	s.template registerType<CPackForClient, OpenWindow>();
+	s.template registerType<CPackForClient, NewObject>();
+	s.template registerType<CPackForClient, NewArtifact>();
+	s.template registerType<CPackForClient, AddQuest>();
+	s.template registerType<CPackForClient, SetAvailableArtifacts>();
+	s.template registerType<CPackForClient, CenterView>();
+	s.template registerType<CPackForClient, HeroVisit>();
+	s.template registerType<CPackForClient, SetCommanderProperty>();
+
+	s.template registerType<CPackForClient, Query>();
+	s.template registerType<Query, HeroLevelUp>();
+	s.template registerType<Query, CommanderLevelUp>();
+	s.template registerType<Query, BlockingDialog>();
+	s.template registerType<Query, GarrisonDialog>();
+	s.template registerType<Query, ExchangeDialog>();
 
-	s.template registerType<SaveGame>();
-	s.template registerType<SetSelection>();
-	s.template registerType<PlayerMessage>();
-	s.template registerType<CenterView>();
+	s.template registerType<CPackForClient, CGarrisonOperationPack>();
+	s.template registerType<CGarrisonOperationPack, ChangeStackCount>();
+	s.template registerType<CGarrisonOperationPack, SetStackType>();
+	s.template registerType<CGarrisonOperationPack, EraseStack>();
+	s.template registerType<CGarrisonOperationPack, SwapStacks>();
+	s.template registerType<CGarrisonOperationPack, InsertNewStack>();
+	s.template registerType<CGarrisonOperationPack, RebalanceStacks>();
+
+	s.template registerType<CPackForClient, CArtifactOperationPack>();
+	s.template registerType<CArtifactOperationPack, PutArtifact>();
+	s.template registerType<CArtifactOperationPack, EraseArtifact>();
+	s.template registerType<CArtifactOperationPack, MoveArtifact>();
+	s.template registerType<CArtifactOperationPack, AssembledArtifact>();
+	s.template registerType<CArtifactOperationPack, DisassembledArtifact>();
+
+	s.template registerType<CPackForClient, SaveGame>();
+	s.template registerType<CPackForClient, SetSelection>();
+	s.template registerType<CPackForClient, PlayerMessage>();
 }
 
 template<typename Serializer>
 void registerTypes3(Serializer &s)
 {
-	s.template registerType<CloseServer>();
-	s.template registerType<EndTurn>();
-	s.template registerType<DismissHero>();
-	s.template registerType<MoveHero>();
-	s.template registerType<ArrangeStacks>();
-	s.template registerType<DisbandCreature>();
-	s.template registerType<BuildStructure>();
-	s.template registerType<RecruitCreatures>();
-	s.template registerType<UpgradeCreature>();
-	s.template registerType<GarrisonHeroSwap>();
-	s.template registerType<ExchangeArtifacts>();
-	s.template registerType<AssembleArtifacts>();
-	s.template registerType<BuyArtifact>();
-	s.template registerType<TradeOnMarketplace>();
-	s.template registerType<SetFormation>();
-	s.template registerType<HireHero>();
-	s.template registerType<BuildBoat>();
-	s.template registerType<QueryReply>();
-	s.template registerType<MakeAction>();
-	s.template registerType<MakeCustomAction>();
-	s.template registerType<DigWithHero>();
-	s.template registerType<CastAdvSpell>();
-	s.template registerType<CastleTeleportHero>();
+	s.template registerType<CPack, CPackForServer>();
+	s.template registerType<CPackForServer, CloseServer>();
+	s.template registerType<CPackForServer, EndTurn>();
+	s.template registerType<CPackForServer, DismissHero>();
+	s.template registerType<CPackForServer, MoveHero>();
+	s.template registerType<CPackForServer, ArrangeStacks>();
+	s.template registerType<CPackForServer, DisbandCreature>();
+	s.template registerType<CPackForServer, BuildStructure>();
+	s.template registerType<CPackForServer, RecruitCreatures>();
+	s.template registerType<CPackForServer, UpgradeCreature>();
+	s.template registerType<CPackForServer, GarrisonHeroSwap>();
+	s.template registerType<CPackForServer, ExchangeArtifacts>();
+	s.template registerType<CPackForServer, AssembleArtifacts>();
+	s.template registerType<CPackForServer, BuyArtifact>();
+	s.template registerType<CPackForServer, TradeOnMarketplace>();
+	s.template registerType<CPackForServer, SetFormation>();
+	s.template registerType<CPackForServer, HireHero>();
+	s.template registerType<CPackForServer, BuildBoat>();
+	s.template registerType<CPackForServer, QueryReply>();
+	s.template registerType<CPackForServer, MakeAction>();
+	s.template registerType<CPackForServer, MakeCustomAction>();
+	s.template registerType<CPackForServer, DigWithHero>();
+	s.template registerType<CPackForServer, CastAdvSpell>();
+	s.template registerType<CPackForServer, CastleTeleportHero>();
+	s.template registerType<CPackForServer, CommitPackage>();
 
-	s.template registerType<SaveGame>();
-	s.template registerType<CommitPackage>();
-	s.template registerType<SetSelection>();
-	s.template registerType<PlayerMessage>();
+	s.template registerType<CPackForServer, SaveGame>();
+	s.template registerType<CPackForServer, SetSelection>();
+	s.template registerType<CPackForServer, PlayerMessage>();
 }
 
 template<typename Serializer>
 void registerTypes4(Serializer &s)
 {
-	s.template registerType<ChatMessage>();
-	s.template registerType<QuitMenuWithoutStarting>();
-	s.template registerType<PlayerJoined>();
-	s.template registerType<SelectMap>();
-	s.template registerType<UpdateStartOptions>();
-	s.template registerType<PregameGuiAction>();
-	s.template registerType<RequestOptionsChange>();
-	s.template registerType<PlayerLeft>();
-	s.template registerType<PlayersNames>();
-	s.template registerType<StartWithCurrentSettings>();
+	s.template registerType<CPack, CPackForSelectionScreen>();
+	s.template registerType<CPackForSelectionScreen, CPregamePackToPropagate>();
+	s.template registerType<CPackForSelectionScreen, CPregamePackToHost>();
+
+	s.template registerType<CPregamePackToPropagate, ChatMessage>();
+	s.template registerType<CPregamePackToPropagate, QuitMenuWithoutStarting>();
+	s.template registerType<CPregamePackToPropagate, SelectMap>();
+	s.template registerType<CPregamePackToPropagate, UpdateStartOptions>();
+	s.template registerType<CPregamePackToPropagate, PregameGuiAction>();
+	s.template registerType<CPregamePackToPropagate, PlayerLeft>();
+	s.template registerType<CPregamePackToPropagate, PlayersNames>();
+	s.template registerType<CPregamePackToPropagate, StartWithCurrentSettings>();
+
+	s.template registerType<CPregamePackToHost, PlayerJoined>();
+	s.template registerType<CPregamePackToHost, RequestOptionsChange>();
 }
 
 template<typename Serializer>

+ 12 - 0
server/CGameHandler.cpp

@@ -88,6 +88,18 @@ public:
 	}
 };
 
+template <> 
+class CApplyOnGH<CPack> : public CBaseForGHApply
+{
+public:
+	bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack, PlayerColor player) const
+	{
+		logGlobal->errorStream() << "Cannot apply on GH plain CPack!";
+		assert(0);
+		return false;
+	}
+};
+
 static CApplier<CBaseForGHApply> *applier = nullptr;
 
 CMP_stack cmpst ;