From 3d1b1f4ba8b4e186f314ad1de5a7ba55bf713cbc Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 10 Sep 2016 03:28:11 +0300 Subject: [PATCH 01/11] Add files for rebased serializer refactoring done by Ivan --- lib/serializer/BinaryDeserializer.cpp | 104 ++++ lib/serializer/BinaryDeserializer.h | 533 +++++++++++++++++++++ lib/serializer/BinarySerializer.cpp | 74 +++ lib/serializer/BinarySerializer.h | 373 ++++++++++++++ lib/serializer/CLoadIntegrityValidator.cpp | 65 +++ lib/serializer/CLoadIntegrityValidator.h | 31 ++ lib/serializer/CMemorySerializer.cpp | 40 ++ lib/serializer/CMemorySerializer.h | 43 ++ lib/serializer/CSerializer.cpp | 49 ++ lib/serializer/CSerializer.h | 201 ++++++++ lib/serializer/CTypeList.cpp | 122 +++++ lib/serializer/CTypeList.h | 230 +++++++++ lib/serializer/Connection.cpp | 280 +++++++++++ lib/serializer/Connection.h | 109 +++++ 14 files changed, 2254 insertions(+) create mode 100644 lib/serializer/BinaryDeserializer.cpp create mode 100644 lib/serializer/BinaryDeserializer.h create mode 100644 lib/serializer/BinarySerializer.cpp create mode 100644 lib/serializer/BinarySerializer.h create mode 100644 lib/serializer/CLoadIntegrityValidator.cpp create mode 100644 lib/serializer/CLoadIntegrityValidator.h create mode 100644 lib/serializer/CMemorySerializer.cpp create mode 100644 lib/serializer/CMemorySerializer.h create mode 100644 lib/serializer/CSerializer.cpp create mode 100644 lib/serializer/CSerializer.h create mode 100644 lib/serializer/CTypeList.cpp create mode 100644 lib/serializer/CTypeList.h create mode 100644 lib/serializer/Connection.cpp create mode 100644 lib/serializer/Connection.h diff --git a/lib/serializer/BinaryDeserializer.cpp b/lib/serializer/BinaryDeserializer.cpp new file mode 100644 index 000000000..538b5b536 --- /dev/null +++ b/lib/serializer/BinaryDeserializer.cpp @@ -0,0 +1,104 @@ +#include "StdInc.h" +#include "BinaryDeserializer.h" + +#include "../registerTypes/RegisterTypes.h" + +/* + * BinaryDeserializer.cpp, 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 + * + */ + +extern template void registerTypes(BinaryDeserializer & s); + +CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/): serializer(this) +{ + registerTypes(serializer); + openNextFile(fname, minimalVersion); +} + +CLoadFile::~CLoadFile() +{ +} + +int CLoadFile::read(void * data, unsigned size) +{ + sfile->read((char*)data,size); + return size; +} + +void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalVersion) +{ + assert(!serializer.reverseEndianess); + assert(minimalVersion <= version); + + try + { + fName = fname.string(); + sfile = make_unique(fname, std::ios::binary); + sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway + + if(!(*sfile)) + THROW_FORMAT("Error: cannot open to read %s!", fName); + + //we can read + char buffer[4]; + sfile->read(buffer, 4); + if(std::memcmp(buffer,"VCMI",4)) + THROW_FORMAT("Error: not a VCMI file(%s)!", fName); + + serializer & serializer.fileVersion; + if(serializer.fileVersion < minimalVersion) + THROW_FORMAT("Error: too old file format (%s)!", fName); + + if(serializer.fileVersion > version) + { + logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % version % fName; + + auto versionptr = (char*)&serializer.fileVersion; + std::reverse(versionptr, versionptr + 4); + logGlobal->warnStream() << "Version number reversed is " << serializer.fileVersion << ", checking..."; + + if(serializer.fileVersion == version) + { + logGlobal->warnStream() << fname << " seems to have different endianness! Entering reversing mode."; + serializer.reverseEndianess = true; + } + else + THROW_FORMAT("Error: too new file format (%s)!", fName); + } + } + catch(...) + { + clear(); //if anything went wrong, we delete file and rethrow + throw; + } +} + +void CLoadFile::reportState(CLogger * out) +{ + out->debugStream() << "CLoadFile"; + if(!!sfile && *sfile) + { + out->debugStream() << "\tOpened " << fName << "\n\tPosition: " << sfile->tellg(); + } +} + +void CLoadFile::clear() +{ + sfile = nullptr; + fName.clear(); + serializer.fileVersion = 0; +} + +void CLoadFile::checkMagicBytes( const std::string &text ) +{ + std::string loaded = text; + read((void*)loaded.data(), text.length()); + if(loaded != text) + throw std::runtime_error("Magic bytes doesn't match!"); +} diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h new file mode 100644 index 000000000..20c8a8937 --- /dev/null +++ b/lib/serializer/BinaryDeserializer.h @@ -0,0 +1,533 @@ + +/* + * BinaryDeserializer.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 + +#include + +#include "CTypeList.h" +#include "../mapObjects/CGHeroInstance.h" + +class CStackInstance; + +class DLL_LINKAGE CLoaderBase +{ +protected: + IBinaryReader * reader; +public: + CLoaderBase(IBinaryReader * r): reader(r){}; + + inline int read(void * data, unsigned size) + { + return reader->read(data, size); + }; +}; + +/// Main class for deserialization of classes from binary form +/// Effectively revesed version of BinarySerializer +class DLL_LINKAGE BinaryDeserializer : public CLoaderBase +{ + template + struct VariantLoaderHelper + { + Source & source; + std::vector> funcs; + + VariantLoaderHelper(Source & source): + source(source) + { + boost::mpl::for_each(std::ref(*this)); + } + + template + void operator()(Type) + { + funcs.push_back([&]() -> Variant + { + Type obj; + source.load(obj); + return Variant(obj); + }); + } + }; + + template + struct LoadIfStackInstance + { + static bool invoke(Ser &s, T &data) + { + return false; + } + }; + + template + struct LoadIfStackInstance + { + static bool invoke(Ser &s, CStackInstance* &data) + { + CArmedInstance *armedObj; + SlotID slot; + s.load(armedObj); + s.load(slot); + if(slot != SlotID::COMMANDER_SLOT_PLACEHOLDER) + { + assert(armedObj->hasStackAtSlot(slot)); + data = armedObj->stacks[slot]; + } + else + { + auto hero = dynamic_cast(armedObj); + assert(hero); + assert(hero->commander); + data = hero->commander; + } + return true; + } + }; + + template + struct ClassObjectCreator + { + static T *invoke() + { + static_assert(!std::is_abstract::value, "Cannot call new upon abstract classes!"); + return new T(); + } + }; + + template + struct ClassObjectCreator::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())); + } + }; + +#define READ_CHECK_U32(x) \ + ui32 length; \ + load(length); \ + if(length > 500000) \ + { \ + logGlobal->warnStream() << "Warning: very big length: " << length;\ + reader->reportState(logGlobal); \ + }; + + template class CPointerLoader; + + class CBasicPointerLoader + { + public: + virtual const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const =0; //data is pointer to the ACTUAL POINTER + virtual ~CBasicPointerLoader(){} + + template static CBasicPointerLoader *getApplier(const T * t=nullptr) + { + return new CPointerLoader(); + } + }; + + template class CPointerLoader : public CBasicPointerLoader + { + public: + const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const override //data is pointer to the ACTUAL POINTER + { + BinaryDeserializer &s = static_cast(ar); + T *&ptr = *static_cast(data); + + //create new object under pointer + typedef typename std::remove_pointer::type npT; + ptr = ClassObjectCreator::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,s.fileVersion); + return &typeid(T); + } + }; + + CApplier applier; + + int write(const void * data, unsigned size); + +public: + bool reverseEndianess; //if source has different endianness than us, we reverse bytes + si32 fileVersion; + + std::map loadedPointers; + std::map loadedPointersTypes; + std::map loadedSharedPointers; + bool smartPointerSerialization; + bool saving; + + BinaryDeserializer(IBinaryReader * r): CLoaderBase(r) + { + saving = false; + fileVersion = 0; + smartPointerSerialization = true; + reverseEndianess = false; + } + + template + BinaryDeserializer & operator&(T & t) + { + this->load(t); + return * this; + } + + template < class T, typename std::enable_if < std::is_fundamental::value && !std::is_same::value, int >::type = 0 > + void load(T &data) + { + if(0) //for testing #989 + { + this->read(&data,sizeof(data)); + } + else + { + unsigned length = sizeof(data); + char* dataPtr = (char*)&data; + this->read(dataPtr,length); + if(reverseEndianess) + std::reverse(dataPtr, dataPtr + length); + } + } + + template < typename T, typename std::enable_if < is_serializeable::value, int >::type = 0 > + void load(T &data) + { + ////that const cast is evil because it allows to implicitly overwrite const objects when deserializing + typedef typename std::remove_const::type nonConstT; + nonConstT &hlp = const_cast(data); + hlp.serialize(*this,fileVersion); + } + template < typename T, typename std::enable_if < std::is_array::value, int >::type = 0 > + void load(T &data) + { + ui32 size = ARRAY_COUNT(data); + for(ui32 i = 0; i < size; i++) + load(data[i]); + } + + template < typename T, typename std::enable_if < std::is_enum::value, int >::type = 0 > + void load(T &data) + { + si32 read; + load( read ); + data = static_cast(read); + } + + template < typename T, typename std::enable_if < std::is_same::value, int >::type = 0 > + void load(T &data) + { + ui8 read; + load( read ); + data = static_cast(read); + } + + template < typename T, typename std::enable_if < std::is_same >::value, int >::type = 0 > + void load(T & data) + { + std::vector convData; + load(convData); + convData.resize(data.size()); + range::copy(convData, data.begin()); + } + + template + void load(std::vector &data, typename std::enable_if < !std::is_same::value, int >::type = 0) + { + READ_CHECK_U32(length); + data.resize(length); + for(ui32 i=0;i::value, int >::type = 0 > + void load(T &data) + { + ui8 hlp; + load( hlp ); + if(!hlp) + { + data = nullptr; + return; + } + + if(reader->smartVectorMembersSerialization) + { + typedef typename std::remove_const::type>::type TObjectType; //eg: const CGHeroInstance * => CGHeroInstance + typedef typename VectorizedTypeFor::type VType; //eg: CGHeroInstance -> CGobjectInstance + typedef typename VectorizedIDType::type IDType; + if(const auto *info = reader->getVectorizedTypeInfo()) + { + IDType id; + load(id); + if(id != IDType(-1)) + { + data = static_cast(reader->getVectorItemFromId(*info, id)); + return; + } + } + } + + if(reader->sendStackInstanceByIds) + { + bool gotLoaded = LoadIfStackInstance::invoke(* this, data); + if(gotLoaded) + return; + } + + ui32 pid = 0xffffffff; //pointer id (or maybe rather pointee id) + if(smartPointerSerialization) + { + load( pid ); //get the id + std::map::iterator i = loadedPointers.find(pid); //lookup + + if(i != loadedPointers.end()) + { + // 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(typeList.castRaw(i->second, loadedPointersTypes.at(pid), &typeid(typename std::remove_const::type>::type))); + return; + } + } + + //get type id + ui16 tid; + load( tid ); + + if(!tid) + { + typedef typename std::remove_pointer::type npT; + typedef typename std::remove_const::type ncpT; + data = ClassObjectCreator::invoke(); + ptrAllocated(data, pid); + load(*data); + } + else + { + auto typeInfo = applier.getApplier(tid)->loadPtr(*this,&data, pid); + data = reinterpret_cast(typeList.castRaw((void*)data, typeInfo, &typeid(typename std::remove_const::type>::type))); + } + } + + template + 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 + } + } + + template void registerType(const Base * b = nullptr, const Derived * d = nullptr) + { + applier.registerType(b, d); + } + + template + void load(std::shared_ptr &data) + { + typedef typename std::remove_const::type NonConstT; + NonConstT *internalPtr; + load(internalPtr); + + void *internalPtrDerived = typeList.castToMostDerived(internalPtr); + + if(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 + { + auto actualType = typeList.getTypeInfo(internalPtr); + auto typeWeNeedToReturn = typeList.getTypeInfo(); + if(*actualType == *typeWeNeedToReturn) + { + // No casting needed, just unpack already stored shared_ptr and return it + data = boost::any_cast>(itr->second); + } + else + { + // We need to perform series of casts + auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn); + data = boost::any_cast>(ret); + } + } + catch(std::exception &e) + { + logGlobal->errorStream() << e.what(); + logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME") + % itr->second.type().name() % typeid(std::shared_ptr).name(); + //TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa) + assert(0); + } + } + else + { + auto hlp = std::shared_ptr(internalPtr); + data = hlp; //possibly adds const + loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp); + } + } + else + data.reset(); + } + template + void load(std::unique_ptr &data) + { + T *internalPtr; + load( internalPtr ); + data.reset(internalPtr); + } + template + void load(std::array &data) + { + for(ui32 i = 0; i < N; i++) + load( data[i] ); + } + template + void load(std::set &data) + { + READ_CHECK_U32(length); + data.clear(); + T ins; + for(ui32 i=0;i + void load(std::unordered_set &data) + { + READ_CHECK_U32(length); + data.clear(); + T ins; + for(ui32 i=0;i + void load(std::list &data) + { + READ_CHECK_U32(length); + data.clear(); + T ins; + for(ui32 i=0;i + void load(std::pair &data) + { + load(data.first); + load(data.second); + } + + template + void load(std::map &data) + { + READ_CHECK_U32(length); + data.clear(); + T1 key; + T2 value; + for(ui32 i=0;i(std::move(key), std::move(value))); + } + } + template + void load(std::multimap &data) + { + READ_CHECK_U32(length); + data.clear(); + T1 key; + T2 value; + for(ui32 i = 0; i < length; i++) + { + load(key); + load(value); + data.insert(std::pair(std::move(key), std::move(value))); + } + } + void load(std::string &data) + { + READ_CHECK_U32(length); + data.resize(length); + this->read((void*)data.c_str(),length); + } + + template + void load(boost::variant &data) + { + typedef boost::variant TVariant; + + VariantLoaderHelper loader(*this); + + si32 which; + load( which ); + assert(which < loader.funcs.size()); + data = loader.funcs.at(which)(); + } + + template + void load(boost::optional & data) + { + ui8 present; + load( present ); + if(present) + { + T t; + load(t); + } + else + { + data = boost::optional(); + } + } +}; + +class DLL_LINKAGE CLoadFile : public IBinaryReader +{ +public: + BinaryDeserializer serializer; + + std::string fName; + std::unique_ptr sfile; + + CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws! + ~CLoadFile(); + int read(void * data, unsigned size) override; //throws! + + void openNextFile(const boost::filesystem::path & fname, int minimalVersion); //throws! + void clear(); + void reportState(CLogger * out) override; + + void checkMagicBytes(const std::string & text); + + template + CLoadFile & operator>>(T &t) + { + serializer & t; + return * this; + } +}; diff --git a/lib/serializer/BinarySerializer.cpp b/lib/serializer/BinarySerializer.cpp new file mode 100644 index 000000000..a4ac40691 --- /dev/null +++ b/lib/serializer/BinarySerializer.cpp @@ -0,0 +1,74 @@ +#include "StdInc.h" +#include "BinarySerializer.h" + +#include "../registerTypes/RegisterTypes.h" + +/* + * BinarySerializer.cpp, 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 + * + */ + +extern template void registerTypes(BinarySerializer & s); + +CSaveFile::CSaveFile(const boost::filesystem::path &fname): serializer(this) +{ + registerTypes(serializer); + openNextFile(fname); +} + +CSaveFile::~CSaveFile() +{ +} + +int CSaveFile::write( const void * data, unsigned size ) +{ + sfile->write((char *)data,size); + return size; +} + +void CSaveFile::openNextFile(const boost::filesystem::path &fname) +{ + fName = fname; + try + { + sfile = make_unique(fname, std::ios::out | std::ios::binary); + sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway + + if(!(*sfile)) + THROW_FORMAT("Error: cannot open to write %s!", fname); + + sfile->write("VCMI",4); //write magic identifier + serializer & version; //write format version + } + catch(...) + { + logGlobal->errorStream() << "Failed to save to " << fname; + clear(); + throw; + } +} + +void CSaveFile::reportState(CLogger * out) +{ + out->debugStream() << "CSaveFile"; + if(sfile.get() && *sfile) + { + out->debugStream() << "\tOpened " << fName << "\n\tPosition: " << sfile->tellp(); + } +} + +void CSaveFile::clear() +{ + fName.clear(); + sfile = nullptr; +} + +void CSaveFile::putMagicBytes( const std::string &text ) +{ + write(text.c_str(), text.length()); +} diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h new file mode 100644 index 000000000..b6f09114b --- /dev/null +++ b/lib/serializer/BinarySerializer.h @@ -0,0 +1,373 @@ + +/* + * BinarySerializer.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 + +#include "CTypeList.h" +#include "../mapObjects/CArmedInstance.h" + +class DLL_LINKAGE CSaverBase +{ +protected: + IBinaryWriter * writer; +public: + CSaverBase(IBinaryWriter * w): writer(w){}; + + inline int write(const void * data, unsigned size) + { + return writer->write(data, size); + }; +}; + +/// Main class for serialization of classes into binary form +/// Behaviour for various classes is following: +/// 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 +{ + template + struct VariantVisitorSaver : boost::static_visitor<> + { + Handler &h; + VariantVisitorSaver(Handler &H):h(H) + { + } + + template + void operator()(const T &t) + { + h & t; + } + }; + + template + struct SaveIfStackInstance + { + static bool invoke(Ser &s, const T &data) + { + return false; + } + }; + + template + struct SaveIfStackInstance + { + static bool invoke(Ser &s, const CStackInstance* const &data) + { + assert(data->armyObj); + SlotID slot; + + if(data->getNodeType() == CBonusSystemNode::COMMANDER) + slot = SlotID::COMMANDER_SLOT_PLACEHOLDER; + else + slot = data->armyObj->findStack(data); + + assert(slot != SlotID()); + s & data->armyObj & slot; + return true; + } + }; + + template class CPointerSaver; + + class CBasicPointerSaver + { + public: + virtual void savePtr(CSaverBase &ar, const void *data) const =0; + virtual ~CBasicPointerSaver(){} + + template static CBasicPointerSaver *getApplier(const T * t=nullptr) + { + return new CPointerSaver(); + } + }; + + + template + class CPointerSaver : public CBasicPointerSaver + { + public: + void savePtr(CSaverBase &ar, const void *data) const override + { + BinarySerializer &s = static_cast(ar); + const T *ptr = static_cast(data); + + //T is most derived known type, it's time to call actual serialize + const_cast(ptr)->serialize(s,version); + } + }; + + CApplier applier; + +public: + std::map savedPointers; + + bool smartPointerSerialization; + bool saving; + + BinarySerializer(IBinaryWriter * w): CSaverBase(w) + { + saving=true; + smartPointerSerialization = true; + } + + template + void registerType(const Base * b = nullptr, const Derived * d = nullptr) + { + applier.registerType(b, d); + } + + template + BinarySerializer & operator&(const T & t) + { + this->save(t); + return * this; + } + + template < typename T, typename std::enable_if < std::is_same::value, int >::type = 0 > + void save(const T &data) + { + ui8 writ = static_cast(data); + save(writ); + } + + template < typename T, typename std::enable_if < std::is_same >::value, int >::type = 0 > + void save(const T &data) + { + std::vector convData; + std::copy(data.begin(), data.end(), std::back_inserter(convData)); + save(convData); + } + + template < class T, typename std::enable_if < std::is_fundamental::value && !std::is_same::value, int >::type = 0 > + void save(const T &data) + { + // save primitive - simply dump binary data to output + this->write(&data,sizeof(data)); + } + + template < typename T, typename std::enable_if < std::is_enum::value, int >::type = 0 > + void save(const T &data) + { + si32 writ = static_cast(data); + *this & writ; + } + + template < typename T, typename std::enable_if < std::is_array::value, int >::type = 0 > + void save(const T &data) + { + ui32 size = ARRAY_COUNT(data); + for(ui32 i=0; i < size; i++) + *this & data[i]; + } + + template < typename T, typename std::enable_if < std::is_pointer::value, int >::type = 0 > + void save(const T &data) + { + //write if pointer is not nullptr + ui8 hlp = (data!=nullptr); + save(hlp); + + //if pointer is nullptr then we don't need anything more... + if(!hlp) + return; + + if(writer->smartVectorMembersSerialization) + { + typedef typename std::remove_const::type>::type TObjectType; + typedef typename VectorizedTypeFor::type VType; + typedef typename VectorizedIDType::type IDType; + + if(const auto *info = writer->getVectorizedTypeInfo()) + { + IDType id = writer->getIdFromVectorItem(*info, data); + save(id); + if(id != IDType(-1)) //vector id is enough + return; + } + } + + if(writer->sendStackInstanceByIds) + { + const bool gotSaved = SaveIfStackInstance::invoke(*this, data); + if(gotSaved) + return; + } + + if(smartPointerSerialization) + { + // 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::iterator i = savedPointers.find(actualPointer); + if(i != savedPointers.end()) + { + //this pointer has been already serialized - write only it's id + save(i->second); + return; + } + + //give id to this pointer + ui32 pid = (ui32)savedPointers.size(); + savedPointers[actualPointer] = pid; + save(pid); + } + + //write type identifier + ui16 tid = typeList.getTypeID(data); + save(tid); + + if(!tid) + save(*data); //if type is unregistered simply write all data in a standard way + else + applier.getApplier(tid)->savePtr(*this, typeList.castToMostDerived(data)); //call serializer specific for our real type + } + + template < typename T, typename std::enable_if < is_serializeable::value, int >::type = 0 > + void save(const T &data) + { + const_cast(data).serialize(*this,version); + } + + template + void save(const std::shared_ptr &data) + { + T *internalPtr = data.get(); + save(internalPtr); + } + template + void save(const std::unique_ptr &data) + { + T *internalPtr = data.get(); + save(internalPtr); + } + template + void save(const std::vector &data) + { + ui32 length = data.size(); + *this & length; + for(ui32 i=0;i + void save(const std::array &data) + { + for(ui32 i=0; i < N; i++) + save(data[i]); + } + template + void save(const std::set &data) + { + std::set &d = const_cast &>(data); + ui32 length = d.size(); + save(length); + for(typename std::set::iterator i=d.begin();i!=d.end();i++) + save(*i); + } + template + void save(const std::unordered_set &data) + { + std::unordered_set &d = const_cast &>(data); + ui32 length = d.size(); + *this & length; + for(typename std::unordered_set::iterator i=d.begin();i!=d.end();i++) + save(*i); + } + template + void save(const std::list &data) + { + std::list &d = const_cast &>(data); + ui32 length = d.size(); + *this & length; + for(typename std::list::iterator i=d.begin();i!=d.end();i++) + save(*i); + } + void save(const std::string &data) + { + save(ui32(data.length())); + this->write(data.c_str(),data.size()); + } + template + void save(const std::pair &data) + { + save(data.first); + save(data.second); + } + template + void save(const std::map &data) + { + *this & ui32(data.size()); + for(typename std::map::const_iterator i=data.begin();i!=data.end();i++) + { + save(i->first); + save(i->second); + } + } + template + void save(const std::multimap &data) + { + *this & ui32(data.size()); + for(typename std::map::const_iterator i = data.begin(); i != data.end(); i++) + { + save(i->first); + save(i->second); + } + } + template + void save(const boost::variant &data) + { + si32 which = data.which(); + save(which); + + VariantVisitorSaver visitor(*this); + boost::apply_visitor(visitor, data); + } + template + void save(const boost::optional &data) + { + if(data) + { + save((ui8)1); + save(*data); + } + else + { + save((ui8)0); + } + } +}; + +class DLL_LINKAGE CSaveFile : public IBinaryWriter +{ +public: + BinarySerializer serializer; + + boost::filesystem::path fName; + std::unique_ptr sfile; + + CSaveFile(const boost::filesystem::path &fname); //throws! + ~CSaveFile(); + int write(const void * data, unsigned size) override; + + void openNextFile(const boost::filesystem::path &fname); //throws! + void clear(); + void reportState(CLogger * out) override; + + void putMagicBytes(const std::string &text); + + template + CSaveFile & operator<<(const T &t) + { + serializer & t; + return * this; + } +}; diff --git a/lib/serializer/CLoadIntegrityValidator.cpp b/lib/serializer/CLoadIntegrityValidator.cpp new file mode 100644 index 000000000..6eb07572c --- /dev/null +++ b/lib/serializer/CLoadIntegrityValidator.cpp @@ -0,0 +1,65 @@ +#include "StdInc.h" +#include "CLoadIntegrityValidator.h" + +#include "../registerTypes/RegisterTypes.h" + +/* + * CLoadIntegrityValidator.cpp, 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 + * + */ + +CLoadIntegrityValidator::CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion /*= version*/) + : serializer(this), foundDesync(false) +{ + registerTypes(serializer); + primaryFile = make_unique(primaryFileName, minimalVersion); + controlFile = make_unique(controlFileName, minimalVersion); + + assert(primaryFile->serializer.fileVersion == controlFile->serializer.fileVersion); + serializer.fileVersion = primaryFile->serializer.fileVersion; +} + +int CLoadIntegrityValidator::read( void * data, unsigned size ) +{ + assert(primaryFile); + assert(controlFile); + + if(!size) + return size; + + std::vector controlData(size); + auto ret = primaryFile->read(data, size); + + if(!foundDesync) + { + controlFile->read(controlData.data(), size); + if(std::memcmp(data, controlData.data(), size)) + { + logGlobal->errorStream() << "Desync found! Position: " << primaryFile->sfile->tellg(); + foundDesync = true; + //throw std::runtime_error("Savegame dsynchronized!"); + } + } + return ret; +} + +std::unique_ptr CLoadIntegrityValidator::decay() +{ + primaryFile->serializer.loadedPointers = this->serializer.loadedPointers; + primaryFile->serializer.loadedPointersTypes = this->serializer.loadedPointersTypes; + return std::move(primaryFile); +} + +void CLoadIntegrityValidator::checkMagicBytes( const std::string &text ) +{ + assert(primaryFile); + assert(controlFile); + + primaryFile->checkMagicBytes(text); + controlFile->checkMagicBytes(text); +} diff --git a/lib/serializer/CLoadIntegrityValidator.h b/lib/serializer/CLoadIntegrityValidator.h new file mode 100644 index 000000000..24ef4ddb3 --- /dev/null +++ b/lib/serializer/CLoadIntegrityValidator.h @@ -0,0 +1,31 @@ + +/* + * CLoadIntegrityValidator.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 + +#include "BinaryDeserializer.h" + +/// Simple byte-to-byte saves comparator +class DLL_LINKAGE CLoadIntegrityValidator + : public IBinaryReader +{ +public: + BinaryDeserializer serializer; + std::unique_ptr primaryFile, controlFile; + bool foundDesync; + + CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = version); //throws! + + int read( void * data, unsigned size) override; //throws! + void checkMagicBytes(const std::string &text); + + std::unique_ptr decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore +}; diff --git a/lib/serializer/CMemorySerializer.cpp b/lib/serializer/CMemorySerializer.cpp new file mode 100644 index 000000000..70e9aa424 --- /dev/null +++ b/lib/serializer/CMemorySerializer.cpp @@ -0,0 +1,40 @@ +#include "StdInc.h" +#include "CMemorySerializer.h" + +#include "../registerTypes/RegisterTypes.h" + +/* + * CMemorySerializer.cpp, 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 + * + */ + +int CMemorySerializer::read(void * data, unsigned size) +{ + if(buffer.size() < readPos + size) + throw std::runtime_error(boost::str(boost::format("Cannot read past the buffer (accessing index %d, while size is %d)!") % (readPos + size - 1) % buffer.size())); + + std::memcpy(data, buffer.data() + readPos, size); + readPos += size; + return size; +} + +int CMemorySerializer::write(const void * data, unsigned size) +{ + auto oldSize = buffer.size(); //and the pos to write from + buffer.resize(oldSize + size); + std::memcpy(buffer.data() + oldSize, data, size); + return size; +} + +CMemorySerializer::CMemorySerializer(): iser(this), oser(this) +{ + readPos = 0; + registerTypes(iser); + registerTypes(oser); +} + diff --git a/lib/serializer/CMemorySerializer.h b/lib/serializer/CMemorySerializer.h new file mode 100644 index 000000000..868da3b8f --- /dev/null +++ b/lib/serializer/CMemorySerializer.h @@ -0,0 +1,43 @@ + +/* + * CMemorySerializer.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 + +#include "BinarySerializer.h" +#include "BinaryDeserializer.h" + +/// Serializer that stores objects in the dynamic buffer. Allows performing deep object copies. +class DLL_LINKAGE CMemorySerializer + : public IBinaryReader, public IBinaryWriter +{ + std::vector buffer; + + size_t readPos; //index of the next byte to be read +public: + BinaryDeserializer iser; + BinarySerializer oser; + + int read(void * data, unsigned size) override; //throws! + int write(const void * data, unsigned size) override; + + CMemorySerializer(); + + template + static std::unique_ptr deepCopy(const T &data) + { + CMemorySerializer mem; + mem.oser & &data; + + std::unique_ptr ret; + mem.iser & ret; + return ret; + } +}; diff --git a/lib/serializer/CSerializer.cpp b/lib/serializer/CSerializer.cpp new file mode 100644 index 000000000..0666633db --- /dev/null +++ b/lib/serializer/CSerializer.cpp @@ -0,0 +1,49 @@ +#include "StdInc.h" +#include "CSerializer.h" + +#include "CGameState.h" +#include "mapping/CMap.h" +#include "CHeroHandler.h" +#include "../mapObjects/CGHeroInstance.h" + +/* + * CSerializer.cpp, 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 + * + */ + +CSerializer::~CSerializer() +{ + +} + +CSerializer::CSerializer() +{ + smartVectorMembersSerialization = false; + sendStackInstanceByIds = false; +} + + +void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib) +{ + registerVectoredType(&gs->map->objects, + [](const CGObjectInstance &obj){ return obj.id; }); + registerVectoredType(&lib->heroh->heroes, + [](const CHero &h){ return h.ID; }); + registerVectoredType(&gs->map->allHeroes, + [](const CGHeroInstance &h){ return h.type->ID; }); + registerVectoredType(&lib->creh->creatures, + [](const CCreature &cre){ return cre.idNumber; }); + registerVectoredType(&lib->arth->artifacts, + [](const CArtifact &art){ return art.id; }); + registerVectoredType(&gs->map->artInstances, + [](const CArtifactInstance &artInst){ return artInst.id; }); + registerVectoredType(&gs->map->quests, + [](const CQuest &q){ return q.qid; }); + + smartVectorMembersSerialization = true; +} diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h new file mode 100644 index 000000000..e6eefc914 --- /dev/null +++ b/lib/serializer/CSerializer.h @@ -0,0 +1,201 @@ + +/* + * CSerializer.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 + +#include "../ConstTransitivePtr.h" +#include "../GameConstants.h" + +const ui32 version = 761; +const ui32 minSupportedVersion = 753; +const std::string SAVEGAME_MAGIC = "VCMISVG"; + +class CHero; +class CGHeroInstance; +class CGObjectInstance; + +class CGameState; +class LibClasses; +extern DLL_LINKAGE LibClasses * VLC; + +struct TypeComparer +{ + bool operator()(const std::type_info *a, const std::type_info *b) const + { + //#ifndef __APPLE__ + // return a->before(*b); + //#else + return strcmp(a->name(), b->name()) < 0; + //#endif + } +}; + +template +struct VectorizedObjectInfo +{ + const std::vector > *vector; //pointer to the appropriate vector + std::function idRetriever; + + VectorizedObjectInfo(const std::vector< ConstTransitivePtr > *Vector, std::function IdGetter) + :vector(Vector), idRetriever(IdGetter) + { + } +}; + +/// Base class for serializers capable of reading or writing data +class DLL_LINKAGE CSerializer +{ + template + static si32 idToNumber(const T &t, typename boost::enable_if >::type * dummy = 0) + { + return t; + } + + template + static NT idToNumber(const BaseForID &t) + { + return t.getNum(); + } + + template + void registerVectoredType(const std::vector *Vector, const std::function &idRetriever) + { + vectors[&typeid(T)] = VectorizedObjectInfo(Vector, idRetriever); + } + template + void registerVectoredType(const std::vector > *Vector, const std::function &idRetriever) + { + vectors[&typeid(T)] = VectorizedObjectInfo(Vector, idRetriever); + } + + typedef std::map TTypeVecMap; + TTypeVecMap vectors; //entry must be a pointer to vector containing pointers to the objects of key type + +public: + bool smartVectorMembersSerialization; + bool sendStackInstanceByIds; + + CSerializer(); + ~CSerializer(); + + virtual void reportState(CLogger * out){}; + + template + const VectorizedObjectInfo *getVectorizedTypeInfo() + { + const std::type_info *myType = nullptr; + + myType = &typeid(T); + + TTypeVecMap::iterator i = vectors.find(myType); + if(i == vectors.end()) + return nullptr; + else + { + assert(!i->second.empty()); + assert(i->second.type() == typeid(VectorizedObjectInfo)); + VectorizedObjectInfo *ret = &(boost::any_cast&>(i->second)); + return ret; + } + } + + template + T* getVectorItemFromId(const VectorizedObjectInfo &oInfo, U id) const + { + si32 idAsNumber = idToNumber(id); + + assert(oInfo.vector); + assert(static_cast(oInfo.vector->size()) > idAsNumber); + return const_cast((*oInfo.vector)[idAsNumber].get()); + } + + template + U getIdFromVectorItem(const VectorizedObjectInfo &oInfo, const T* obj) const + { + if(!obj) + return U(-1); + + return oInfo.idRetriever(*obj); + } + + void addStdVecItems(CGameState *gs, LibClasses *lib = VLC); +}; + +/// Helper to detect classes with user-provided serialize(S&, int version) method +template +struct is_serializeable +{ + typedef char (&Yes)[1]; + typedef char (&No)[2]; + + template + static Yes test(U * data, S* arg1 = 0, + typename std::enable_ifserialize(*arg1, int(0))) + >::value>::type * = 0); + static No test(...); + static const bool value = sizeof(Yes) == sizeof(is_serializeable::test((typename std::remove_reference::type>::type*)0)); +}; + +template //metafunction returning CGObjectInstance if T is its derivate or T elsewise +struct VectorizedTypeFor +{ + typedef typename + //if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else + boost::mpl::identity + > >::type type; +}; +template +struct VectorizedIDType +{ + typedef typename + //if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else if + boost::mpl::eval_if, + boost::mpl::identity, + //else + boost::mpl::identity + > > > > > >::type type; +}; + +/// Base class for deserializers +class IBinaryReader : public virtual CSerializer +{ +public: + virtual int read(void * data, unsigned size) = 0; +}; + +/// Base class for serializers +class IBinaryWriter : public virtual CSerializer +{ +public: + virtual int write(const void * data, unsigned size) = 0; +}; diff --git a/lib/serializer/CTypeList.cpp b/lib/serializer/CTypeList.cpp new file mode 100644 index 000000000..ad8f28d7f --- /dev/null +++ b/lib/serializer/CTypeList.cpp @@ -0,0 +1,122 @@ +#include "StdInc.h" +#include "CTypeList.h" + +#include "../registerTypes/RegisterTypes.h" + +/* + * CTypeList.cpp, 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 + * + */ + +extern template void registerTypes(CTypeList & s); + +CTypeList typeList; + +CTypeList::CTypeList() +{ + registerTypes(*this); +} + +CTypeList::TypeInfoPtr CTypeList::registerType(const std::type_info *type) +{ + 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 + auto newType = std::make_shared(); + newType->typeID = typeInfos.size() + 1; + newType->name = type->name(); + typeInfos[type] = newType; + + return newType; +} + +ui16 CTypeList::getTypeID(const std::type_info *type, bool throws) const +{ + auto descriptor = getTypeDescriptor(type, throws); + if (descriptor == nullptr) + { + return 0; + } + return descriptor->typeID; +} + +std::vector CTypeList::castSequence(TypeInfoPtr from, TypeInfoPtr to) const +{ + if(!strcmp(from->name, to->name)) + return std::vector(); + + // Perform a simple BFS in the class hierarchy. + + auto BFS = [&](bool upcast) + { + std::map previous; + std::queue 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 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::castSequence(const std::type_info *from, const std::type_info *to) const +{ + //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(!strcmp(from->name(), to->name())) + return std::vector(); + + return castSequence(getTypeDescriptor(from), getTypeDescriptor(to)); +} + +CTypeList::TypeInfoPtr CTypeList::getTypeDescriptor(const std::type_info *type, bool throws) const +{ + 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()); +} diff --git a/lib/serializer/CTypeList.h b/lib/serializer/CTypeList.h new file mode 100644 index 000000000..a68a8fb94 --- /dev/null +++ b/lib/serializer/CTypeList.h @@ -0,0 +1,230 @@ + +/* + * CTypeList.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 + +#include "CSerializer.h" + +struct IPointerCaster +{ + virtual boost::any castRawPtr(const boost::any &ptr) const = 0; // takes From*, returns To* + virtual boost::any castSharedPtr(const boost::any &ptr) const = 0; // takes std::shared_ptr, performs dynamic cast, returns std::shared_ptr + virtual boost::any castWeakPtr(const boost::any &ptr) const = 0; // takes std::weak_ptr, performs dynamic cast, returns std::weak_ptr. The object under poitner must live. + //virtual boost::any castUniquePtr(const boost::any &ptr) const = 0; // takes std::unique_ptr, performs dynamic cast, returns std::unique_ptr +}; + +template +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(ptr); + To * ret = static_cast(from); + return (void*)ret; + } + + // Helper function performing casts between smart pointers + template + boost::any castSmartPtr(const boost::any &ptr) const + { + try + { + auto from = boost::any_cast(ptr); + auto ret = std::static_pointer_cast(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>(ptr); + } + virtual boost::any castWeakPtr(const boost::any &ptr) const override + { + auto from = boost::any_cast>(ptr); + return castSmartPtr>(from.lock()); + } +}; + +/// Class that implements basic reflection-like mechanisms +/// For every type registered via registerType() generates inheritance tree +/// Rarely used directly - usually used as part of CApplier +class DLL_LINKAGE CTypeList: public boost::noncopyable +{ +//public: + struct TypeDescriptor; + typedef std::shared_ptr TypeInfoPtr; + struct TypeDescriptor + { + ui16 typeID; + const char *name; + std::vector children, parents; + }; + typedef boost::shared_mutex TMutex; + typedef boost::unique_lock TUniqueLock; + typedef boost::shared_lock TSharedLock; +private: + mutable TMutex mx; + + std::map typeInfos; + std::map, std::unique_ptr> casters; //for each pair we provide a caster (each registered relations creates a single entry here) + + /// 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 castSequence(TypeInfoPtr from, TypeInfoPtr to) const; + std::vector castSequence(const std::type_info *from, const std::type_info *to) const; + + template + boost::any castHelper(boost::any inputPtr, const std::type_info *fromArg, const std::type_info *toArg) const + { + TSharedLock lock(mx); + auto typesSequence = castSequence(fromArg, toArg); + + boost::any ptr = inputPtr; + for(int i = 0; i < static_cast(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 not have operator->* ..? + } + + return ptr; + } + CTypeList &operator=(CTypeList &) + { + // As above. + assert(0); + return *this; + } + + TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true) const; //if not throws, failure returns nullptr + TypeInfoPtr registerType(const std::type_info *type); + +public: + + CTypeList(); + + template + void registerType(const Base * b = nullptr, const Derived * d = nullptr) + { + TUniqueLock lock(mx); + static_assert(std::is_base_of::value, "First registerType template parameter needs to ba a base class of the second one."); + static_assert(std::has_virtual_destructor::value, "Base class needs to have a virtual destructor."); + static_assert(!std::is_same::value, "Parameters of registerTypes should be two different types."); + auto bt = getTypeInfo(b); + auto dt = getTypeInfo(d); //obtain std::type_info + auto bti = registerType(bt); + auto 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>(); + casters[std::make_pair(dti, bti)] = make_unique>(); + } + + ui16 getTypeID(const std::type_info *type, bool throws = false) const; + + template + ui16 getTypeID(const T * t = nullptr, bool throws = false) const + { + return getTypeID(getTypeInfo(t), throws); + } + + template + void * castToMostDerived(const TInput * inputPtr) const + { + auto &baseType = typeid(typename std::remove_cv::type); + auto derivedType = getTypeInfo(inputPtr); + + if (strcmp(baseType.name(), derivedType->name()) == 0) + { + return const_cast(reinterpret_cast(inputPtr)); + } + + return boost::any_cast(castHelper<&IPointerCaster::castRawPtr>( + const_cast(reinterpret_cast(inputPtr)), &baseType, + derivedType)); + } + + template + boost::any castSharedToMostDerived(const std::shared_ptr inputPtr) const + { + auto &baseType = typeid(typename std::remove_cv::type); + auto derivedType = getTypeInfo(inputPtr.get()); + + if (!strcmp(baseType.name(), derivedType->name())) + return inputPtr; + + return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, &baseType, derivedType); + } + + void * castRaw(void *inputPtr, const std::type_info *from, const std::type_info *to) const + { + return boost::any_cast(castHelper<&IPointerCaster::castRawPtr>(inputPtr, from, to)); + } + boost::any castShared(boost::any inputPtr, const std::type_info *from, const std::type_info *to) const + { + return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, from, to); + } + + template const std::type_info * getTypeInfo(const T * t = nullptr) const + { + if(t) + return &typeid(*t); + else + return &typeid(T); + } +}; + +extern DLL_LINKAGE CTypeList typeList; + +/// Wrapper over CTypeList. Allows execution of templated class T for any type +/// that was resgistered for this applier +template +class CApplier +{ + std::map> apps; + + template + void addApplier(ui16 ID) + { + if(!apps.count(ID)) + { + RegisteredType * rtype = nullptr; + apps[ID].reset(T::getApplier(rtype)); + } + } + +public: + T * getApplier(ui16 ID) + { + assert(apps.count(ID)); + return apps[ID].get(); + } + + template + void registerType(const Base * b = nullptr, const Derived * d = nullptr) + { + typeList.registerType(b, d); + addApplier(typeList.getTypeID(b)); + addApplier(typeList.getTypeID(d)); + } +}; diff --git a/lib/serializer/Connection.cpp b/lib/serializer/Connection.cpp new file mode 100644 index 000000000..0a898b9cb --- /dev/null +++ b/lib/serializer/Connection.cpp @@ -0,0 +1,280 @@ +#include "StdInc.h" +#include "Connection.h" + +#include "registerTypes/RegisterTypes.h" +#include "mapping/CMap.h" +#include "CGameState.h" + +#include + +/* + * Connection.cpp, 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 + * + */ + +using namespace boost; +using namespace boost::asio::ip; + +#if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MISPEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) +#define BIG_ENDIAN +#else +#define LIL_ENDIAN +#endif + + +void CConnection::init() +{ + boost::asio::ip::tcp::no_delay option(true); + socket->set_option(option); + + enableSmartPointerSerializatoin(); + disableStackSendingByID(); + registerTypes(iser); + registerTypes(oser); +#ifdef LIL_ENDIAN + myEndianess = true; +#else + myEndianess = false; +#endif + connected = true; + std::string pom; + //we got connection + oser & std::string("Aiya!\n") & name & myEndianess; //identify ourselves + iser & pom & pom & contactEndianess; + logNetwork->infoStream() << "Established connection with "<errorStream() << "Problem with resolving: \n" << error; + goto connerror1; + } + pom = endpoint_iterator; + if(pom != end) + logNetwork->infoStream()<<"Found endpoints:"; + else + { + logNetwork->errorStream() << "Critical problem: No endpoints found!"; + goto connerror1; + } + i=0; + while(pom != end) + { + logNetwork->infoStream() << "\t" << i << ": " << (boost::asio::ip::tcp::endpoint&)*pom; + pom++; + } + i=0; + while(endpoint_iterator != end) + { + logNetwork->infoStream() << "Trying connection to " << (boost::asio::ip::tcp::endpoint&)*endpoint_iterator << " (" << i++ << ")"; + socket->connect(*endpoint_iterator, error); + if(!error) + { + init(); + return; + } + else + { + logNetwork->errorStream() << "Problem with connecting: " << error; + } + endpoint_iterator++; + } + + //we shouldn't be here - error handling +connerror1: + logNetwork->errorStream() << "Something went wrong... checking for error info"; + if(error) + logNetwork->errorStream() << error; + else + logNetwork->errorStream() << "No error info. "; + delete io_service; + //delete socket; + throw std::runtime_error("Can't establish connection :("); +} +CConnection::CConnection(TSocket * Socket, std::string Name ) + :iser(this), oser(this), socket(Socket),io_service(&Socket->get_io_service()), name(Name)//, send(this), rec(this) +{ + init(); +} +CConnection::CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name) +: iser(this), oser(this), name(Name)//, send(this), rec(this) +{ + boost::system::error_code error = asio::error::host_not_found; + socket = new tcp::socket(*io_service); + acceptor->accept(*socket,error); + if (error) + { + logNetwork->errorStream() << "Error on accepting: " << error; + delete socket; + throw std::runtime_error("Can't establish connection :("); + } + init(); +} +int CConnection::write(const void * data, unsigned size) +{ + try + { + int ret; + ret = asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size))); + return ret; + } + catch(...) + { + //connection has been lost + connected = false; + throw; + } +} +int CConnection::read(void * data, unsigned size) +{ + try + { + int ret = asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size))); + return ret; + } + catch(...) + { + //connection has been lost + connected = false; + throw; + } +} +CConnection::~CConnection(void) +{ + if(handler) + handler->join(); + + delete handler; + + close(); + delete io_service; + delete wmx; + delete rmx; +} + +template +CConnection & CConnection::operator&(const T &t) { +// throw std::exception(); +//XXX this is temporaly ? solution to fix gcc (4.3.3, other?) compilation +// problem for more details contact t0@czlug.icis.pcz.pl or impono@gmail.com +// do not remove this exception it shoudnt be called + return *this; +} + +void CConnection::close() +{ + if(socket) + { + socket->close(); + delete socket; + socket = nullptr; + } +} + +bool CConnection::isOpen() const +{ + return socket && connected; +} + +void CConnection::reportState(CLogger * out) +{ + out->debugStream() << "CConnection"; + if(socket && socket->is_open()) + { + out->debugStream() << "\tWe have an open and valid socket"; + out->debugStream() << "\t" << socket->available() <<" bytes awaiting"; + } +} + +CPack * CConnection::retreivePack() +{ + CPack *ret = nullptr; + boost::unique_lock lock(*rmx); + logNetwork->traceStream() << "Listening... "; + iser & ret; + logNetwork->traceStream() << "\treceived server message of type " << typeid(*ret).name() << ", data: " << ret; + return ret; +} + +void CConnection::sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID) +{ + boost::unique_lock lock(*wmx); + logNetwork->traceStream() << "Sending to server a pack of type " << typeid(pack).name(); + oser & player & requestID & &pack; //packs has to be sent as polymorphic pointers! +} + +void CConnection::disableStackSendingByID() +{ + CSerializer::sendStackInstanceByIds = false; +} + +void CConnection::enableStackSendingByID() +{ + CSerializer::sendStackInstanceByIds = true; +} + +void CConnection::disableSmartPointerSerialization() +{ + iser.smartPointerSerialization = oser.smartPointerSerialization = false; +} + +void CConnection::enableSmartPointerSerializatoin() +{ + iser.smartPointerSerialization = oser.smartPointerSerialization = true; +} + +void CConnection::prepareForSendingHeroes() +{ + iser.loadedPointers.clear(); + oser.savedPointers.clear(); + disableSmartVectorMemberSerialization(); + enableSmartPointerSerializatoin(); + disableStackSendingByID(); +} + +void CConnection::enterPregameConnectionMode() +{ + iser.loadedPointers.clear(); + oser.savedPointers.clear(); + disableSmartVectorMemberSerialization(); + disableSmartPointerSerialization(); +} + +void CConnection::disableSmartVectorMemberSerialization() +{ + CSerializer::smartVectorMembersSerialization = false; +} + +void CConnection::enableSmartVectorMemberSerializatoin() +{ + CSerializer::smartVectorMembersSerialization = true; +} + +std::ostream & operator<<(std::ostream &str, const CConnection &cpc) + { + return str << "Connection with " << cpc.name << " (ID: " << cpc.connectionID << /*", " << (cpc.host ? "host" : "guest") <<*/ ")"; + } diff --git a/lib/serializer/Connection.h b/lib/serializer/Connection.h new file mode 100644 index 000000000..4e326f85d --- /dev/null +++ b/lib/serializer/Connection.h @@ -0,0 +1,109 @@ + +/* + * Connection.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 + +#include "BinaryDeserializer.h" +#include "BinarySerializer.h" + +struct CPack; + +namespace boost +{ + namespace asio + { + namespace ip + { + class tcp; + } + class io_service; + + template class stream_socket_service; + template + class basic_stream_socket; + + template class socket_acceptor_service; + template + class basic_socket_acceptor; + } + class mutex; +} + +typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service > TSocket; +typedef boost::asio::basic_socket_acceptor > TAcceptor; + +/// Main class for network communication +/// Allows establishing connection and bidirectional read-write +class DLL_LINKAGE CConnection + : public IBinaryReader, public IBinaryWriter +{ + CConnection(void); + + void init(); + void reportState(CLogger * out) override; +public: + BinaryDeserializer iser; + BinarySerializer oser; + + boost::mutex *rmx, *wmx; // read/write mutexes + TSocket * socket; + bool logging; + bool connected; + bool myEndianess, contactEndianess; //true if little endian, if endianness is different we'll have to revert received multi-byte vars + boost::asio::io_service *io_service; + std::string name; //who uses this connection + + int connectionID; + boost::thread *handler; + + bool receivedStop, sendStop; + + CConnection(std::string host, std::string port, std::string Name); + CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name); + CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket + + int write(const void * data, unsigned size) override; + int read(void * data, unsigned size) override; + void close(); + bool isOpen() const; + template + CConnection &operator&(const T&); + virtual ~CConnection(void); + + CPack *retreivePack(); //gets from server next pack (allocates it with new) + void sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID); + + void disableStackSendingByID(); + void enableStackSendingByID(); + void disableSmartPointerSerialization(); + void enableSmartPointerSerializatoin(); + void disableSmartVectorMemberSerialization(); + void enableSmartVectorMemberSerializatoin(); + + void prepareForSendingHeroes(); //disables sending vectorized, enables smart pointer serialization, clears saved/loaded ptr cache + void enterPregameConnectionMode(); + + template + CConnection & operator>>(T &t) + { + iser & t; + return * this; + } + + template + CConnection & operator<<(const T &t) + { + oser & t; + return * this; + } +}; + +DLL_LINKAGE std::ostream &operator<<(std::ostream &str, const CConnection &cpc); From 5b76c3f4eb96caf18a52e7338ccd20f9f77b8fd7 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 10 Sep 2016 03:32:40 +0300 Subject: [PATCH 02/11] Rebase of codebase changes for refactored serializer Some of newer fixes not yet merged there and save compatibility a bit off. --- AI/StupidAI/StupidAI.cpp | 4 ++-- AI/StupidAI/StupidAI.h | 4 ++-- AI/VCAI/AIUtility.h | 3 ++- AI/VCAI/VCAI.cpp | 8 +++++--- AI/VCAI/VCAI.h | 5 ++--- CCallback.cpp | 1 - Global.h | 5 ++++- client/CMT.cpp | 3 ++- client/CPlayerInterface.cpp | 8 +++++--- client/CPlayerInterface.h | 4 ++-- client/CPreGame.cpp | 5 +++-- client/Client.cpp | 16 +++++++++------- client/Client.h | 12 ++++++------ client/NetPacksClient.cpp | 3 ++- lib/CGameInterface.cpp | 19 ++++++++++--------- lib/CGameInterface.h | 12 ++++++------ lib/CGameState.cpp | 7 ++++--- lib/CMakeLists.txt | 9 ++++++++- lib/Connection.cpp | 4 ++-- lib/IGameCallback.cpp | 23 ++++++++++++++--------- lib/registerTypes/RegisterTypes.cpp | 12 ++++++++---- lib/registerTypes/RegisterTypes.h | 9 ++++++--- lib/registerTypes/TypesClientPacks1.cpp | 8 ++++++-- lib/registerTypes/TypesClientPacks2.cpp | 8 ++++++-- lib/registerTypes/TypesMapObjects1.cpp | 8 ++++++-- lib/registerTypes/TypesMapObjects2.cpp | 8 ++++++-- lib/registerTypes/TypesMapObjects3.cpp | 8 ++++++-- lib/registerTypes/TypesPregamePacks.cpp | 9 +++++++-- lib/registerTypes/TypesServerPacks.cpp | 8 ++++++-- server/CGameHandler.cpp | 8 +++++--- server/CGameHandler.h | 1 - server/CVCMIServer.cpp | 3 ++- server/NetPacksServer.cpp | 1 + 33 files changed, 155 insertions(+), 91 deletions(-) diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 7f74bf433..0777a5e1a 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -327,13 +327,13 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination) } } -void CStupidAI::saveGame(COSer & h, const int version) +void CStupidAI::saveGame(BinarySerializer & h, const int version) { //TODO to be implemented with saving/loading during the battles assert(0); } -void CStupidAI::loadGame(CISer & h, const int version) +void CStupidAI::loadGame(BinaryDeserializer & h, const int version) { //TODO to be implemented with saving/loading during the battles assert(0); diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index a8a96e33e..439a1d3cf 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -36,8 +36,8 @@ public: BattleAction goTowards(const CStack * stack, BattleHex hex ); - virtual void saveGame(COSer & h, const int version) override; - virtual void loadGame(CISer & h, const int version) override; + virtual void saveGame(BinarySerializer & h, const int version) override; + virtual void loadGame(BinaryDeserializer & h, const int version) override; }; diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index abf6a3b47..bbe96f897 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -5,8 +5,9 @@ #include "../../lib/CCreatureHandler.h" #include "../../lib/CTownHandler.h" #include "../../lib/spells/CSpellHandler.h" -#include "../../lib/Connection.h" #include "../../lib/CStopWatch.h" +#include "../../lib/mapObjects/CObjectHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" /* * AIUtility.h, part of VCMI engine diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 3162b2ef1..841c21383 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -9,7 +9,9 @@ #include "../../lib/CModHandler.h" #include "../../lib/CGameState.h" #include "../../lib/NetPacks.h" - +#include "../../lib/serializer/CTypeList.h" +#include "../../lib/serializer/BinarySerializer.h" +#include "../../lib/serializer/BinaryDeserializer.h" /* * VCAI.cpp, part of VCMI engine @@ -705,7 +707,7 @@ void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *do }); } -void VCAI::saveGame(COSer & h, const int version) +void VCAI::saveGame(BinarySerializer & h, const int version) { LOG_TRACE_PARAMS(logAi, "version '%i'", version); NET_EVENT_HANDLER; @@ -716,7 +718,7 @@ void VCAI::saveGame(COSer & h, const int version) serializeInternal(h, version); } -void VCAI::loadGame(CISer & h, const int version) +void VCAI::loadGame(BinaryDeserializer & h, const int version) { LOG_TRACE_PARAMS(logAi, "version '%i'", version); NET_EVENT_HANDLER; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 136ce7488..f82b8c037 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -14,7 +14,6 @@ #include "../../lib/CTownHandler.h" #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/spells/CSpellHandler.h" -#include "../../lib/Connection.h" #include "../../lib/CondSh.h" struct QuestInfo; @@ -189,8 +188,8 @@ public: virtual void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. virtual 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 virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; - virtual void saveGame(COSer & h, const int version) override; //saving - virtual void loadGame(CISer & h, const int version) override; //loading + virtual void saveGame(BinarySerializer & h, const int version) override; //saving + virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading virtual void finish() override; virtual void availableCreaturesChanged(const CGDwelling *town) override; diff --git a/CCallback.cpp b/CCallback.cpp index 84d55d946..1b965ebd4 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -12,7 +12,6 @@ #include "lib/mapObjects/CObjectClassesHandler.h" #include "lib/CGeneralTextHandler.h" #include "lib/CHeroHandler.h" -#include "lib/Connection.h" #include "lib/NetPacks.h" #include "client/mapHandler.h" #include "lib/spells/CSpellHandler.h" diff --git a/Global.h b/Global.h index 3dabeba83..b661afa91 100644 --- a/Global.h +++ b/Global.h @@ -108,6 +108,10 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); # define STRONG_INLINE inline #endif +#define TO_STRING_HELPER(x) #x +#define TO_STRING(x) TO_STRING_HELPER(x) +#define LINE_IN_FILE __FILE__ ":" TO_STRING(__LINE__) + #define _USE_MATH_DEFINES #include @@ -222,7 +226,6 @@ typedef boost::lock_guard TLockGuardRec; # define DLL_IMPORT __attribute__ ((visibility("default"))) # define DLL_EXPORT __attribute__ ((visibility("default"))) # define ELF_VISIBILITY __attribute__ ((visibility("default"))) -# define ELF_VISIBILITY __attribute__ ((visibility("default"))) # endif #endif diff --git a/client/CMT.cpp b/client/CMT.cpp index de65277c6..1a27d974b 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -27,7 +27,8 @@ #include "Graphics.h" #include "Client.h" #include "../lib/CConfigHandler.h" -#include "../lib/Connection.h" +#include "../lib/serializer/BinaryDeserializer.h" +#include "../lib/serializer/BinarySerializer.h" #include "../lib/VCMI_Lib.h" #include "../lib/VCMIDirs.h" #include "../lib/NetPacks.h" diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 35bffe24a..6d3f79846 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -22,7 +22,9 @@ #include "../lib/CArtHandler.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" -#include "../lib/Connection.h" +#include "../lib/serializer/CTypeList.h" +#include "../lib/serializer/BinaryDeserializer.h" +#include "../lib/serializer/BinarySerializer.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/CTownHandler.h" #include "../lib/mapObjects/CObjectClassesHandler.h" // For displaying correct UI when interacting with objects @@ -1310,13 +1312,13 @@ template void CPlayerInterface::serializeTempl( Handler &h, c h & spellbookSettings; } -void CPlayerInterface::saveGame( COSer & h, const int version ) +void CPlayerInterface::saveGame( BinarySerializer & h, const int version ) { EVENT_HANDLER_CALLED_BY_CLIENT; serializeTempl(h,version); } -void CPlayerInterface::loadGame( CISer & h, const int version ) +void CPlayerInterface::loadGame( BinaryDeserializer & h, const int version ) { EVENT_HANDLER_CALLED_BY_CLIENT; serializeTempl(h,version); diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 4bff5d61a..53c259b08 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -192,8 +192,8 @@ public: void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override; void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox - void saveGame(COSer & h, const int version) override; //saving - void loadGame(CISer & h, const int version) override; //loading + void saveGame(BinarySerializer & h, const int version) override; //saving + void loadGame(BinaryDeserializer & h, const int version) override; //loading void showWorldViewEx(const std::vector & objectPositions) override; //for battles diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 8b9ef86d6..a9522fa73 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -17,7 +17,8 @@ #include "CMusicHandler.h" #include "CVideoHandler.h" #include "Graphics.h" -#include "../lib/Connection.h" +#include "../lib/serializer/Connection.h" +#include "../lib/serializer/CTypeList.h" #include "../lib/VCMIDirs.h" #include "../lib/mapping/CMap.h" #include "windows/GUIClasses.h" @@ -1011,7 +1012,7 @@ void CSelectionScreen::processPacks() { CPackForSelectionScreen *pack = upcomingPacks.front(); upcomingPacks.pop_front(); - CBaseForPGApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier + CBaseForPGApply *apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier apply->applyOnPG(this, pack); delete pack; } diff --git a/client/Client.cpp b/client/Client.cpp index 9bf2f1425..0e175b54a 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -19,7 +19,9 @@ #include "../lib/CTownHandler.h" #include "../lib/CBuildingHandler.h" #include "../lib/spells/CSpellHandler.h" -#include "../lib/Connection.h" +#include "../lib/serializer/CTypeList.h" +#include "../lib/serializer/Connection.h" +#include "../lib/serializer/CLoadIntegrityValidator.h" #ifndef VCMI_ANDROID #include "../lib/Interprocess.h" #endif @@ -510,7 +512,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) // } } -void CClient::serialize(COSer & h, const int version) +void CClient::serialize(BinarySerializer & h, const int version) { assert(h.saving); h & hotSeat; @@ -528,7 +530,7 @@ void CClient::serialize(COSer & h, const int version) } } -void CClient::serialize(CISer & h, const int version) +void CClient::serialize(BinaryDeserializer & h, const int version) { assert(!h.saving); h & hotSeat; @@ -579,7 +581,7 @@ void CClient::serialize(CISer & h, const int version) } } -void CClient::serialize(COSer & h, const int version, const std::set & playerIDs) +void CClient::serialize(BinarySerializer & h, const int version, const std::set & playerIDs) { assert(h.saving); h & hotSeat; @@ -597,7 +599,7 @@ void CClient::serialize(COSer & h, const int version, const std::set & playerIDs) +void CClient::serialize(BinaryDeserializer & h, const int version, const std::set & playerIDs) { assert(!h.saving); h & hotSeat; @@ -653,12 +655,12 @@ void CClient::serialize(CISer & h, const int version, const std::seterror("Dropping nullptr CPack! You should check whether client and server ABI matches."); return; } - CBaseForCLApply *apply = applier->apps[typeList.getTypeID(pack)]; //find the applier + CBaseForCLApply *apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier if(apply) { boost::unique_lock guiLock(*LOCPLINT->pim); diff --git a/client/Client.h b/client/Client.h index 8d250321e..ef7c55836 100644 --- a/client/Client.h +++ b/client/Client.h @@ -32,8 +32,8 @@ struct SharedMem; class CClient; class CScriptingModule; struct CPathsInfo; -class CISer; -class COSer; +class BinaryDeserializer; +class BinarySerializer; namespace boost { class thread; } /// structure to handle running server and connecting to it @@ -238,10 +238,10 @@ public: ////////////////////////////////////////////////////////////////////////// - void serialize(COSer &h, const int version); - void serialize(CISer &h, const int version); + void serialize(BinarySerializer & h, const int version); + void serialize(BinaryDeserializer & h, const int version); - void serialize(COSer &h, const int version, const std::set& playerIDs); - void serialize(CISer &h, const int version, const std::set& playerIDs); + void serialize(BinarySerializer & h, const int version, const std::set& playerIDs); + void serialize(BinaryDeserializer & h, const int version, const std::set& playerIDs); void battleFinished(); }; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 3149fdbcd..742564977 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -7,7 +7,8 @@ #include "Client.h" #include "CPlayerInterface.h" #include "CGameInfo.h" -#include "../lib/Connection.h" +#include "../lib/serializer/Connection.h" +#include "../lib/serializer/BinarySerializer.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/VCMI_Lib.h" diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 8d301b394..977c548b1 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -9,7 +9,8 @@ #else #include #endif -#include "Connection.h" +#include "serializer/BinaryDeserializer.h" +#include "serializer/BinarySerializer.h" /* * CGameInterface.cpp, part of VCMI engine @@ -243,29 +244,29 @@ void CAdventureAI::yourTacticPhase(int distance) battleAI->yourTacticPhase(distance); } -void CAdventureAI::saveGame(COSer & h, const int version) /*saving */ +void CAdventureAI::saveGame(BinarySerializer & h, const int version) /*saving */ { LOG_TRACE_PARAMS(logAi, "version '%i'", version); CGlobalAI::saveGame(h, version); bool hasBattleAI = static_cast(battleAI); - h << hasBattleAI; + h & hasBattleAI; if(hasBattleAI) { - h << std::string(battleAI->dllName); + h & std::string(battleAI->dllName); battleAI->saveGame(h, version); } } -void CAdventureAI::loadGame(CISer & h, const int version) /*loading */ +void CAdventureAI::loadGame(BinaryDeserializer & h, const int version) /*loading */ { LOG_TRACE_PARAMS(logAi, "version '%i'", version); CGlobalAI::loadGame(h, version); bool hasBattleAI = false; - h >> hasBattleAI; + h & hasBattleAI; if(hasBattleAI) { std::string dllName; - h >> dllName; + h & dllName; battleAI = CDynLibHandler::getNewBattleAI(dllName); assert(cbc); //it should have been set by the one who new'ed us battleAI->init(cbc); @@ -273,10 +274,10 @@ void CAdventureAI::loadGame(CISer & h, const int version) /*loading */ } } -void CBattleGameInterface::saveGame(COSer & h, const int version) +void CBattleGameInterface::saveGame(BinarySerializer & h, const int version) { } -void CBattleGameInterface::loadGame(CISer & h, const int version) +void CBattleGameInterface::loadGame(BinaryDeserializer & h, const int version) { } diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index bba6027c9..08e293a63 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -54,8 +54,8 @@ struct CPathsInfo; class CCreature; class CLoadFile; class CSaveFile; -class CISer; -class COSer; +class BinaryDeserializer; +class BinarySerializer; struct ArtifactLocation; class CScriptingModule; @@ -73,8 +73,8 @@ public: virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack virtual void yourTacticPhase(int distance){}; //called when interface has opportunity to use Tactics skill -> use cb->battleMakeTacticAction from this function - virtual void saveGame(COSer &h, const int version); - virtual void loadGame(CISer &h, const int version); + virtual void saveGame(BinarySerializer & h, const int version); + virtual void loadGame(BinaryDeserializer & h, const int version); }; @@ -150,6 +150,6 @@ public: virtual void battleEnd(const BattleResult *br) override; virtual void battleStacksHealedRes(const std::vector > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override; - virtual void saveGame(COSer & h, const int version) override; //saving - virtual void loadGame(CISer & h, const int version) override; //loading + virtual void saveGame(BinarySerializer & h, const int version) override; //saving + virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 7d6100c46..3de4d4c4a 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -11,7 +11,6 @@ #include "CHeroHandler.h" #include "mapObjects/CObjectHandler.h" #include "CModHandler.h" -#include "Connection.h" #include "mapping/CMap.h" #include "mapping/CMapService.h" #include "StartInfo.h" @@ -25,6 +24,8 @@ #include "rmg/CMapGenerator.h" #include "CStopWatch.h" #include "mapping/CMapEditManager.h" +#include "serializer/CTypeList.h" +#include "serializer/CMemorySerializer.h" #ifdef min #undef min @@ -871,7 +872,7 @@ void CGameState::initDuel() else { CLoadFile lf(scenarioOps->mapname); - lf.serializer >> dp; + lf.serializer & dp; } } catch(...) @@ -2059,7 +2060,7 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col void CGameState::apply(CPack *pack) { ui16 typ = typeList.getTypeID(pack); - applierGs->apps[typ]->applyOnGS(this,pack); + applierGs->getApplier(typ)->applyOnGS(this,pack); } void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bd4508242..02ce6b2a6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -59,6 +59,14 @@ set(lib_SRCS rmg/CRmgTemplateStorage.cpp rmg/CZoneGraphGenerator.cpp rmg/CZonePlacer.cpp + + serializer/BinaryDeserializer.cpp + serializer/BinarySerializer.cpp + serializer/CLoadIntegrityValidator.cpp + serializer/CMemorySerializer.cpp + serializer/Connection.cpp + serializer/CSerializer.cpp + serializer/CTypeList.cpp spells/CSpellHandler.cpp spells/ISpellMechanics.cpp @@ -102,7 +110,6 @@ set(lib_SRCS CGameInfoCallback.cpp CPathfinder.cpp CGameState.cpp - Connection.cpp NetPacksLib.cpp serializer/JsonSerializer.cpp diff --git a/lib/Connection.cpp b/lib/Connection.cpp index 91081fa16..47833e325 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -18,8 +18,8 @@ * */ -extern template void registerTypes(CISer & s); -extern template void registerTypes(COSer & s); +extern template void registerTypes(BinaryDeserializer & s); +extern template void registerTypes(BinarySerializer & s); extern template void registerTypes(CTypeList & s); CTypeList typeList; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index bc0b57581..ee0c4206e 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -17,7 +17,12 @@ #include "CBonusTypeHandler.h" #include "CModHandler.h" -#include "Connection.h" // for SAVEGAME_MAGIC +#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC +#include "serializer/BinaryDeserializer.h" +#include "serializer/BinarySerializer.h" +#include "serializer/CLoadIntegrityValidator.h" +#include "rmg/CMapGenOptions.h" +#include "mapping/CCampaignHandler.h" #include "mapObjects/CObjectClassesHandler.h" #include "StartInfo.h" #include "CGameState.h" @@ -157,16 +162,16 @@ void CPrivilagedInfoCallback::loadCommonState(Loader &in) StartInfo *si; logGlobal->infoStream() <<"\tReading header"; - in.serializer >> dum; + in.serializer & dum; logGlobal->infoStream() << "\tReading options"; - in.serializer >> si; + in.serializer & si; logGlobal->infoStream() <<"\tReading handlers"; - in.serializer >> *VLC; + in.serializer & *VLC; logGlobal->infoStream() <<"\tReading gamestate"; - in.serializer >> gs; + in.serializer & gs; } template @@ -175,13 +180,13 @@ void CPrivilagedInfoCallback::saveCommonState(Saver &out) const logGlobal->infoStream() << "Saving lib part of game..."; out.putMagicBytes(SAVEGAME_MAGIC); logGlobal->infoStream() <<"\tSaving header"; - out.serializer << static_cast(*gs->map); + out.serializer & static_cast(*gs->map); logGlobal->infoStream() << "\tSaving options"; - out.serializer << gs->scenarioOps; + out.serializer & gs->scenarioOps; logGlobal->infoStream() << "\tSaving handlers"; - out.serializer << *VLC; + out.serializer & *VLC; logGlobal->infoStream() << "\tSaving gamestate"; - out.serializer << gs; + out.serializer & gs; } // hardly memory usage for `-gdwarf-4` flag diff --git a/lib/registerTypes/RegisterTypes.cpp b/lib/registerTypes/RegisterTypes.cpp index 77f3ead3a..a0f116094 100644 --- a/lib/registerTypes/RegisterTypes.cpp +++ b/lib/registerTypes/RegisterTypes.cpp @@ -12,6 +12,10 @@ #include "../spells/CSpellHandler.h" #include "../mapping/CCampaignHandler.h" +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" + // For reference: peak memory usage by gcc during compilation of register type templates // registerTypesMapObjects: 1.9 Gb // registerTypes2: 2.2 Gb @@ -22,8 +26,8 @@ #define DEFINE_EXTERNAL_METHOD(METHODNAME) \ -extern template DLL_LINKAGE void METHODNAME(CISer & s); \ -extern template DLL_LINKAGE void METHODNAME(COSer & s); \ +extern template DLL_LINKAGE void METHODNAME(BinaryDeserializer & s); \ +extern template DLL_LINKAGE void METHODNAME(BinarySerializer & s); \ extern template DLL_LINKAGE void METHODNAME(CTypeList & s); \ //DEFINE_EXTERNAL_METHOD(registerTypesMapObjects) @@ -34,6 +38,6 @@ DEFINE_EXTERNAL_METHOD(registerTypesClientPacks2) DEFINE_EXTERNAL_METHOD(registerTypesServerPacks) DEFINE_EXTERNAL_METHOD(registerTypesPregamePacks) -template void registerTypes(CISer & s); -template void registerTypes(COSer & s); +template void registerTypes(BinaryDeserializer & s); +template void registerTypes(BinarySerializer & s); template void registerTypes(CTypeList & s); diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index b1982bb9a..77816278d 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -1,6 +1,5 @@ #pragma once -#include "../Connection.h" #include "../NetPacks.h" #include "../VCMI_Lib.h" #include "../CArtHandler.h" @@ -24,6 +23,10 @@ * */ +class BinarySerializer; +class BinaryDeserializer; +class CTypeList; + template void registerTypesMapObjects1(Serializer &s) { @@ -376,8 +379,8 @@ void registerTypes(Serializer &s) #ifndef INSTANTIATE_REGISTER_TYPES_HERE -extern template DLL_LINKAGE void registerTypes(CISer & s); -extern template DLL_LINKAGE void registerTypes(COSer & s); +extern template DLL_LINKAGE void registerTypes(BinaryDeserializer & s); +extern template DLL_LINKAGE void registerTypes(BinarySerializer & s); extern template DLL_LINKAGE void registerTypes(CTypeList & s); #endif diff --git a/lib/registerTypes/TypesClientPacks1.cpp b/lib/registerTypes/TypesClientPacks1.cpp index cb7471787..28986b6f8 100644 --- a/lib/registerTypes/TypesClientPacks1.cpp +++ b/lib/registerTypes/TypesClientPacks1.cpp @@ -18,7 +18,11 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" -template void registerTypesClientPacks1(CISer & s); -template void registerTypesClientPacks1(COSer & s); + +template void registerTypesClientPacks1(BinaryDeserializer & s); +template void registerTypesClientPacks1(BinarySerializer & s); template void registerTypesClientPacks1(CTypeList & s); diff --git a/lib/registerTypes/TypesClientPacks2.cpp b/lib/registerTypes/TypesClientPacks2.cpp index 6dee5e84e..cf371cd13 100644 --- a/lib/registerTypes/TypesClientPacks2.cpp +++ b/lib/registerTypes/TypesClientPacks2.cpp @@ -18,9 +18,13 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" -template void registerTypesClientPacks2(CISer & s); -template void registerTypesClientPacks2(COSer & s); + +template void registerTypesClientPacks2(BinaryDeserializer & s); +template void registerTypesClientPacks2(BinarySerializer & s); template void registerTypesClientPacks2(CTypeList & s); diff --git a/lib/registerTypes/TypesMapObjects1.cpp b/lib/registerTypes/TypesMapObjects1.cpp index 6f28f4fde..d977a69e8 100644 --- a/lib/registerTypes/TypesMapObjects1.cpp +++ b/lib/registerTypes/TypesMapObjects1.cpp @@ -18,8 +18,12 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" -template void registerTypesMapObjects1(CISer & s); -template void registerTypesMapObjects1(COSer & s); +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" + +template void registerTypesMapObjects1(BinaryDeserializer & s); +template void registerTypesMapObjects1(BinarySerializer & s); template void registerTypesMapObjects1(CTypeList & s); diff --git a/lib/registerTypes/TypesMapObjects2.cpp b/lib/registerTypes/TypesMapObjects2.cpp index 676e29700..67897b7a2 100644 --- a/lib/registerTypes/TypesMapObjects2.cpp +++ b/lib/registerTypes/TypesMapObjects2.cpp @@ -18,8 +18,12 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" -template void registerTypesMapObjects2(CISer & s); -template void registerTypesMapObjects2(COSer & s); + +template void registerTypesMapObjects2(BinaryDeserializer & s); +template void registerTypesMapObjects2(BinarySerializer & s); template void registerTypesMapObjects2(CTypeList & s); diff --git a/lib/registerTypes/TypesMapObjects3.cpp b/lib/registerTypes/TypesMapObjects3.cpp index 12bd4d818..3dcb08ba2 100644 --- a/lib/registerTypes/TypesMapObjects3.cpp +++ b/lib/registerTypes/TypesMapObjects3.cpp @@ -18,6 +18,10 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" -template void registerTypesMapObjectTypes(CISer & s); -template void registerTypesMapObjectTypes(COSer & s); +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" + +template void registerTypesMapObjectTypes(BinaryDeserializer & s); +template void registerTypesMapObjectTypes(BinarySerializer & s); template void registerTypesMapObjectTypes(CTypeList & s); diff --git a/lib/registerTypes/TypesPregamePacks.cpp b/lib/registerTypes/TypesPregamePacks.cpp index 7f6aeb46a..2fba9ac9e 100644 --- a/lib/registerTypes/TypesPregamePacks.cpp +++ b/lib/registerTypes/TypesPregamePacks.cpp @@ -17,8 +17,13 @@ #include "../mapping/CCampaignHandler.h" #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" +#include "../rmg/CMapGenOptions.h" -template void registerTypesPregamePacks(CISer & s); -template void registerTypesPregamePacks(COSer & s); +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" + +template void registerTypesPregamePacks(BinaryDeserializer & s); +template void registerTypesPregamePacks(BinarySerializer & s); template void registerTypesPregamePacks(CTypeList & s); diff --git a/lib/registerTypes/TypesServerPacks.cpp b/lib/registerTypes/TypesServerPacks.cpp index c46a0c628..291014c0b 100644 --- a/lib/registerTypes/TypesServerPacks.cpp +++ b/lib/registerTypes/TypesServerPacks.cpp @@ -18,6 +18,10 @@ #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" -template void registerTypesServerPacks(CISer & s); -template void registerTypesServerPacks(COSer & s); +#include "../serializer/BinaryDeserializer.h" +#include "../serializer/BinarySerializer.h" +#include "../serializer/CTypeList.h" + +template void registerTypesServerPacks(BinaryDeserializer & s); +template void registerTypesServerPacks(BinarySerializer & s); template void registerTypesServerPacks(CTypeList & s); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f5307ed9c..291ee01b4 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -20,6 +20,7 @@ #include "../lib/NetPacks.h" #include "../lib/VCMI_Lib.h" #include "../lib/mapping/CMap.h" +#include "../lib/rmg/CMapGenOptions.h" #include "../lib/VCMIDirs.h" #include "../lib/ScopeGuard.h" #include "../lib/CSoundBase.h" @@ -29,6 +30,8 @@ #include "../lib/CThreadHelper.h" #include "../lib/GameConstants.h" #include "../lib/registerTypes/RegisterTypes.h" +#include "../lib/serializer/CTypeList.h" +#include "../lib/serializer/Connection.h" /* * CGameHandler.cpp, part of VCMI engine @@ -957,9 +960,8 @@ void CGameHandler::handleConnection(std::set players, CConnection & boost::unique_lock lock(*c.wmx); c << &applied; }; - - CBaseForGHApply *apply = applier->apps[packType]; //and appropriate applier object - if (isBlockedByQueries(pack, player)) + CBaseForGHApply *apply = applier->getApplier(packType); //and appropriate applier object + if(isBlockedByQueries(pack, player)) { sendPackageResponse(false); } diff --git a/server/CGameHandler.h b/server/CGameHandler.h index a707bbfe2..2f0ba6172 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -2,7 +2,6 @@ #include "../lib/FunctionList.h" -#include "../lib/Connection.h" #include "../lib/IGameCallback.h" #include "../lib/BattleAction.h" #include "CQuery.h" diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index ea7c4f329..e62dc0215 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -5,7 +5,7 @@ #include "../lib/filesystem/Filesystem.h" #include "../lib/mapping/CCampaignHandler.h" #include "../lib/CThreadHelper.h" -#include "../lib/Connection.h" +#include "../lib/serializer/Connection.h" #include "../lib/CModHandler.h" #include "../lib/CArtHandler.h" #include "../lib/CGeneralTextHandler.h" @@ -18,6 +18,7 @@ #include "CVCMIServer.h" #include "../lib/StartInfo.h" #include "../lib/mapping/CMap.h" +#include "../lib/rmg/CMapGenOptions.h" #ifndef VCMI_ANDROID #include "../lib/Interprocess.h" #endif diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 7938e8ae1..998ab86cc 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -7,6 +7,7 @@ #include "../lib/CGameState.h" #include "../lib/BattleState.h" #include "../lib/BattleAction.h" +#include "../lib/serializer/Connection.h" #define PLAYER_OWNS(id) (gh->getPlayerAt(c)==gh->getOwner(id)) From 8f349b5105622825105864d6c181d0bff056e9ec Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 10 Sep 2016 04:23:13 +0300 Subject: [PATCH 03/11] CTypeList: Apply changes from a50a32405de5c76f8876e5f479425f71d8e04c73 Use weak_ptr for TypeDescriptor cross-links. Fixed total TypeDescriptor memory leak. --- lib/serializer/CTypeList.cpp | 3 ++- lib/serializer/CTypeList.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/serializer/CTypeList.cpp b/lib/serializer/CTypeList.cpp index ad8f28d7f..63366c4a9 100644 --- a/lib/serializer/CTypeList.cpp +++ b/lib/serializer/CTypeList.cpp @@ -62,8 +62,9 @@ std::vector CTypeList::castSequence(TypeInfoPtr from, Ty { auto typeNode = q.front(); q.pop(); - for(auto &nodeBase : upcast ? typeNode->parents : typeNode->children) + for(auto & weakNode : (upcast ? typeNode->parents : typeNode->children) ) { + auto nodeBase = weakNode.lock(); if(!previous.count(nodeBase)) { previous[nodeBase] = typeNode; diff --git a/lib/serializer/CTypeList.h b/lib/serializer/CTypeList.h index a68a8fb94..62e800207 100644 --- a/lib/serializer/CTypeList.h +++ b/lib/serializer/CTypeList.h @@ -66,11 +66,12 @@ class DLL_LINKAGE CTypeList: public boost::noncopyable //public: struct TypeDescriptor; typedef std::shared_ptr TypeInfoPtr; + typedef std::weak_ptr WeakTypeInfoPtr; struct TypeDescriptor { ui16 typeID; const char *name; - std::vector children, parents; + std::vector children, parents; }; typedef boost::shared_mutex TMutex; typedef boost::unique_lock TUniqueLock; From 5f1254a29e696c268d4dbe9726873faf3acc43a0 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Thu, 27 Oct 2016 18:39:17 +0300 Subject: [PATCH 04/11] Apply some of Vadim's changes I intentionally didn't transfer changes in CLoadIntegrityValidator since not really agree with them. --- lib/serializer/BinaryDeserializer.h | 9 ++++++++- lib/serializer/Connection.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 20c8a8937..26869046e 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -314,7 +314,14 @@ public: } else { - auto typeInfo = applier.getApplier(tid)->loadPtr(*this,&data, pid); + auto app = applier.getApplier(tid); + if(app == nullptr) + { + logGlobal->error("load %d %d - no loader exists", tid, pid); + data = nullptr; + return; + } + auto typeInfo = app->loadPtr(*this,&data, pid); data = reinterpret_cast(typeList.castRaw((void*)data, typeInfo, &typeid(typename std::remove_const::type>::type))); } } diff --git a/lib/serializer/Connection.cpp b/lib/serializer/Connection.cpp index 0a898b9cb..1f01e09d1 100644 --- a/lib/serializer/Connection.cpp +++ b/lib/serializer/Connection.cpp @@ -216,7 +216,7 @@ CPack * CConnection::retreivePack() boost::unique_lock lock(*rmx); logNetwork->traceStream() << "Listening... "; iser & ret; - logNetwork->traceStream() << "\treceived server message of type " << typeid(*ret).name() << ", data: " << ret; + logNetwork->traceStream() << "\treceived server message of type " << (ret? typeid(*ret).name() : "nullptr") << ", data: " << ret; return ret; } From ba1929728ac6774a14ecdfa9d01ff8b75793d843 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Thu, 27 Oct 2016 18:44:00 +0300 Subject: [PATCH 05/11] Drop old now unused serializer files --- lib/Connection.cpp | 650 ---------------- lib/Connection.h | 1757 -------------------------------------------- 2 files changed, 2407 deletions(-) delete mode 100644 lib/Connection.cpp delete mode 100644 lib/Connection.h diff --git a/lib/Connection.cpp b/lib/Connection.cpp deleted file mode 100644 index 47833e325..000000000 --- a/lib/Connection.cpp +++ /dev/null @@ -1,650 +0,0 @@ -#include "StdInc.h" -#include "Connection.h" - -#include "registerTypes/RegisterTypes.h" -#include "mapping/CMap.h" -#include "CGameState.h" -#include "filesystem/FileStream.h" - -#include - -/* - * Connection.cpp, 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 - * - */ - -extern template void registerTypes(BinaryDeserializer & s); -extern template void registerTypes(BinarySerializer & s); -extern template void registerTypes(CTypeList & s); - -CTypeList typeList; - -#define LOG(a) \ - if(logging)\ - out << a -#if defined(__hppa__) || \ - defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ - (defined(__MIPS__) && defined(__MISPEB__)) || \ - defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ - defined(__sparc__) -#define BIG_ENDIAN -#else -#define LIL_ENDIAN -#endif - - -void CConnection::init() -{ - boost::asio::ip::tcp::no_delay option(true); - socket->set_option(option); - - enableSmartPointerSerializatoin(); - disableStackSendingByID(); - registerTypes(iser); - registerTypes(oser); -#ifdef LIL_ENDIAN - myEndianess = true; -#else - myEndianess = false; -#endif - connected = true; - std::string pom; - //we got connection - oser << std::string("Aiya!\n") << name << myEndianess; //identify ourselves - iser >> pom >> pom >> contactEndianess; - logNetwork->infoStream() << "Established connection with "<errorStream() << "Problem with resolving: \n" << error; - goto connerror1; - } - pom = endpoint_iterator; - if(pom != end) - logNetwork->infoStream()<<"Found endpoints:"; - else - { - logNetwork->errorStream() << "Critical problem: No endpoints found!"; - goto connerror1; - } - i=0; - while(pom != end) - { - logNetwork->infoStream() << "\t" << i << ": " << (boost::asio::ip::tcp::endpoint&)*pom; - pom++; - } - i=0; - while(endpoint_iterator != end) - { - logNetwork->infoStream() << "Trying connection to " << (boost::asio::ip::tcp::endpoint&)*endpoint_iterator << " (" << i++ << ")"; - socket->connect(*endpoint_iterator, error); - if(!error) - { - init(); - return; - } - else - { - logNetwork->errorStream() << "Problem with connecting: " << error; - } - endpoint_iterator++; - } - - //we shouldn't be here - error handling -connerror1: - logNetwork->error("Something went wrong... checking for error info"); - if(error) - logNetwork->errorStream() << error; - else - logNetwork->error("No error info."); - delete io_service; - //delete socket; - throw std::runtime_error("Can't establish connection :("); -} -CConnection::CConnection(TSocket * Socket, std::string Name ) - :iser(this), oser(this), socket(Socket),io_service(&Socket->get_io_service()), name(Name)//, send(this), rec(this) -{ - init(); -} -CConnection::CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name) -: iser(this), oser(this), name(Name)//, send(this), rec(this) -{ - boost::system::error_code error = boost::asio::error::host_not_found; - socket = new boost::asio::ip::tcp::socket(*io_service); - acceptor->accept(*socket,error); - if (error) - { - logNetwork->errorStream() << "Error on accepting: " << error; - delete socket; - throw std::runtime_error("Can't establish connection :("); - } - init(); -} -int CConnection::write(const void * data, unsigned size) -{ - //LOG("Sending " << size << " byte(s) of data" <join(); - - delete handler; - - close(); - delete io_service; - delete wmx; - delete rmx; -} - -template -CConnection & CConnection::operator&(const T &t) { -// throw std::exception(); -//XXX this is temporaly ? solution to fix gcc (4.3.3, other?) compilation -// problem for more details contact t0@czlug.icis.pcz.pl or impono@gmail.com -// do not remove this exception it shoudnt be called - return *this; -} - -void CConnection::close() -{ - if(socket) - { - socket->close(); - delete socket; - socket = nullptr; - } -} - -bool CConnection::isOpen() const -{ - return socket && connected; -} - -void CConnection::reportState(CLogger * out) -{ - out->debugStream() << "CConnection"; - if(socket && socket->is_open()) - { - out->debugStream() << "\tWe have an open and valid socket"; - out->debugStream() << "\t" << socket->available() <<" bytes awaiting"; - } -} - -CPack * CConnection::retreivePack() -{ - CPack *ret = nullptr; - boost::unique_lock lock(*rmx); - logNetwork->traceStream() << "Listening... "; - iser >> ret; - logNetwork->traceStream() << "\treceived server message of type " << (ret? typeid(*ret).name() : "nullptr") << ", data: " << ret; - return ret; -} - -void CConnection::sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID) -{ - boost::unique_lock lock(*wmx); - logNetwork->traceStream() << "Sending to server a pack of type " << typeid(pack).name(); - oser << player << requestID << &pack; //packs has to be sent as polymorphic pointers! -} - -void CConnection::disableStackSendingByID() -{ - CSerializer::sendStackInstanceByIds = false; -} - -void CConnection::enableStackSendingByID() -{ - CSerializer::sendStackInstanceByIds = true; -} - -void CConnection::disableSmartPointerSerialization() -{ - iser.smartPointerSerialization = oser.smartPointerSerialization = false; -} - -void CConnection::enableSmartPointerSerializatoin() -{ - iser.smartPointerSerialization = oser.smartPointerSerialization = true; -} - -void CConnection::prepareForSendingHeroes() -{ - iser.loadedPointers.clear(); - oser.savedPointers.clear(); - disableSmartVectorMemberSerialization(); - enableSmartPointerSerializatoin(); - disableStackSendingByID(); -} - -void CConnection::enterPregameConnectionMode() -{ - iser.loadedPointers.clear(); - oser.savedPointers.clear(); - disableSmartVectorMemberSerialization(); - disableSmartPointerSerialization(); -} - -void CConnection::disableSmartVectorMemberSerialization() -{ - CSerializer::smartVectorMembersSerialization = false; -} - -void CConnection::enableSmartVectorMemberSerializatoin() -{ - CSerializer::smartVectorMembersSerialization = true; -} - -CSaveFile::CSaveFile( const boost::filesystem::path &fname ): serializer(this) -{ - registerTypes(serializer); - openNextFile(fname); -} - -CSaveFile::~CSaveFile() -{ -} - -int CSaveFile::write( const void * data, unsigned size ) -{ - sfile->write((char *)data,size); - return size; -} - -void CSaveFile::openNextFile(const boost::filesystem::path &fname) -{ - fName = fname; - try - { - sfile = make_unique(fname, std::ios::out | std::ios::binary); - sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway - - if(!(*sfile)) - THROW_FORMAT("Error: cannot open to write %s!", fname); - - sfile->write("VCMI",4); //write magic identifier - serializer << version; //write format version - } - catch(...) - { - logGlobal->errorStream() << "Failed to save to " << fname; - clear(); - throw; - } -} - -void CSaveFile::reportState(CLogger * out) -{ - out->debugStream() << "CSaveFile"; - if(sfile.get() && *sfile) - { - out->debugStream() << "\tOpened " << fName << "\n\tPosition: " << sfile->tellp(); - } -} - -void CSaveFile::clear() -{ - fName.clear(); - sfile = nullptr; -} - -void CSaveFile::putMagicBytes( const std::string &text ) -{ - write(text.c_str(), text.length()); -} - -CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/): serializer(this) -{ - registerTypes(serializer); - openNextFile(fname, minimalVersion); -} - -CLoadFile::~CLoadFile() -{ -} - -int CLoadFile::read(void * data, unsigned size) -{ - sfile->read((char*)data,size); - return size; -} - -void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalVersion) -{ - assert(!serializer.reverseEndianess); - assert(minimalVersion <= version); - - try - { - fName = fname.string(); - sfile = make_unique(fname, std::ios::in | std::ios::binary); - sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway - - if(!(*sfile)) - THROW_FORMAT("Error: cannot open to read %s!", fName); - - //we can read - char buffer[4]; - sfile->read(buffer, 4); - if(std::memcmp(buffer,"VCMI",4)) - THROW_FORMAT("Error: not a VCMI file(%s)!", fName); - - serializer >> serializer.fileVersion; - if(serializer.fileVersion < minimalVersion) - THROW_FORMAT("Error: too old file format (%s)!", fName); - - if(serializer.fileVersion > version) - { - logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % version % fName; - - auto versionptr = (char*)&serializer.fileVersion; - std::reverse(versionptr, versionptr + 4); - logGlobal->warnStream() << "Version number reversed is " << serializer.fileVersion << ", checking..."; - - if(serializer.fileVersion == version) - { - logGlobal->warnStream() << fname << " seems to have different endianness! Entering reversing mode."; - serializer.reverseEndianess = true; - } - else - THROW_FORMAT("Error: too new file format (%s)!", fName); - } - } - catch(...) - { - clear(); //if anything went wrong, we delete file and rethrow - throw; - } -} - -void CLoadFile::reportState(CLogger * out) -{ - out->debugStream() << "CLoadFile"; - if(!!sfile && *sfile) - { - out->debugStream() << "\tOpened " << fName << "\n\tPosition: " << sfile->tellg(); - } -} - -void CLoadFile::clear() -{ - sfile = nullptr; - fName.clear(); - serializer.fileVersion = 0; -} - -void CLoadFile::checkMagicBytes( const std::string &text ) -{ - std::string loaded = text; - read((void*)loaded.data(), text.length()); - if(loaded != text) - throw std::runtime_error("Magic bytes doesn't match!"); -} - -CTypeList::CTypeList() -{ - registerTypes(*this); -} - -CTypeList::TypeInfoPtr CTypeList::registerType( const std::type_info *type ) -{ - 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 - auto newType = std::make_shared(); - newType->typeID = typeInfos.size() + 1; - newType->name = type->name(); - typeInfos[type] = newType; - - return newType; -} - -ui16 CTypeList::getTypeID( const std::type_info *type, bool throws ) const -{ - auto descriptor = getTypeDescriptor(type, throws); - if (descriptor == nullptr) - { - return 0; - } - return descriptor->typeID; -} - -std::vector CTypeList::castSequence(TypeInfoPtr from, TypeInfoPtr to) const -{ - if(!strcmp(from->name, to->name)) - return std::vector(); - - // Perform a simple BFS in the class hierarchy. - - auto BFS = [&](bool upcast) - { - std::map previous; - std::queue q; - q.push(to); - while(q.size()) - { - auto typeNode = q.front(); - q.pop(); - - for(auto & weakNode : (upcast ? typeNode->parents : typeNode->children) ) - { - auto nodeBase = weakNode.lock(); - if(!previous.count(nodeBase)) - { - previous[nodeBase] = typeNode; - q.push(nodeBase); - } - } - } - - std::vector 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::castSequence(const std::type_info *from, const std::type_info *to) const -{ - //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(!strcmp(from->name(), to->name())) - return std::vector(); - - return castSequence(getTypeDescriptor(from), getTypeDescriptor(to)); -} - -CTypeList::TypeInfoPtr CTypeList::getTypeDescriptor(const std::type_info *type, bool throws) const -{ - 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") <<*/ ")"; - } - -CSerializer::~CSerializer() -{ - -} - -CSerializer::CSerializer() -{ - smartVectorMembersSerialization = false; - sendStackInstanceByIds = false; -} - - -void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib) -{ - registerVectoredType(&gs->map->objects, - [](const CGObjectInstance &obj){ return obj.id; }); - registerVectoredType(&lib->heroh->heroes, - [](const CHero &h){ return h.ID; }); - registerVectoredType(&gs->map->allHeroes, - [](const CGHeroInstance &h){ return h.type->ID; }); - registerVectoredType(&lib->creh->creatures, - [](const CCreature &cre){ return cre.idNumber; }); - registerVectoredType(&lib->arth->artifacts, - [](const CArtifact &art){ return art.id; }); - registerVectoredType(&gs->map->artInstances, - [](const CArtifactInstance &artInst){ return artInst.id; }); - registerVectoredType(&gs->map->quests, - [](const CQuest &q){ return q.qid; }); - - smartVectorMembersSerialization = true; -} - -CLoadIntegrityValidator::CLoadIntegrityValidator( const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion /*= version*/ ) - : serializer(this), foundDesync(false) -{ - registerTypes(serializer); - primaryFile = make_unique(primaryFileName, minimalVersion); - controlFile = make_unique(controlFileName, minimalVersion); - - assert(primaryFile->serializer.fileVersion == controlFile->serializer.fileVersion); - serializer.fileVersion = primaryFile->serializer.fileVersion; -} - -int CLoadIntegrityValidator::read( void * data, unsigned size ) -{ - assert(primaryFile); - assert(controlFile); - - if(!size) - return size; - - std::vector controlData(size); - auto ret = primaryFile->read(data, size); - - if(!foundDesync) - { - controlFile->read(controlData.data(), size); - if(std::memcmp(data, controlData.data(), size)) - { - logGlobal->error("Save game format mismatch detected! Position: %d", - primaryFile->sfile->tellg()); - foundDesync = true; - //throw std::runtime_error("Savegame dsynchronized!"); - } - } - return ret; -} - -std::unique_ptr CLoadIntegrityValidator::decay() -{ - primaryFile->serializer.loadedPointers = this->serializer.loadedPointers; - primaryFile->serializer.loadedPointersTypes = this->serializer.loadedPointersTypes; - return std::move(primaryFile); -} - -void CLoadIntegrityValidator::checkMagicBytes( const std::string &text ) -{ - assert(primaryFile); - assert(controlFile); - - primaryFile->checkMagicBytes(text); - controlFile->checkMagicBytes(text); -} - -int CMemorySerializer::read(void * data, unsigned size) -{ - if(buffer.size() < readPos + size) - throw std::runtime_error(boost::str(boost::format("Cannot read past the buffer (accessing index %d, while size is %d)!") % (readPos + size - 1) % buffer.size())); - - std::memcpy(data, buffer.data() + readPos, size); - readPos += size; - return size; -} - -int CMemorySerializer::write(const void * data, unsigned size) -{ - auto oldSize = buffer.size(); //and the pos to write from - buffer.resize(oldSize + size); - std::memcpy(buffer.data() + oldSize, data, size); - return size; -} - -CMemorySerializer::CMemorySerializer(): iser(this), oser(this) -{ - readPos = 0; - registerTypes(iser); - registerTypes(oser); -} diff --git a/lib/Connection.h b/lib/Connection.h deleted file mode 100644 index 884a3acc1..000000000 --- a/lib/Connection.h +++ /dev/null @@ -1,1757 +0,0 @@ - -/* - * Connection.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 - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "ConstTransitivePtr.h" -#include "CCreatureSet.h" //for CStackInstance -#include "mapObjects/CGHeroInstance.h" -#include "mapping/CCampaignHandler.h" //for CCampaignState -#include "rmg/CMapGenerator.h" // for CMapGenOptions - -const ui32 version = 761; -const ui32 minSupportedVersion = 753; - -class CISer; -class COSer; -class CConnection; -class CGObjectInstance; -class CStackInstance; -class CGameState; -class CCreature; -class LibClasses; -class CHero; -class FileStream; -struct CPack; -extern DLL_LINKAGE LibClasses * VLC; -namespace mpl = boost::mpl; - -const std::string SAVEGAME_MAGIC = "VCMISVG"; - -namespace boost -{ - namespace asio - { - namespace ip - { - class tcp; - } - class io_service; - - template class stream_socket_service; - template - class basic_stream_socket; - - template class socket_acceptor_service; - template - class basic_socket_acceptor; - } - class mutex; -} - -enum SerializationLvl -{ - Wrong=0, - Boolean, - Primitive, - Array, - Pointer, - Enum, - Serializable, - BooleanVector -}; - - -struct TypeComparer -{ - bool operator()(const std::type_info *a, const std::type_info *b) const - { - #ifndef __APPLE__ - return a->before(*b); - #else - return strcmp(a->name(), b->name()) < 0; - #endif - } -}; - -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, performs dynamic cast, returns std::shared_ptr - virtual boost::any castWeakPtr(const boost::any &ptr) const = 0; // takes std::weak_ptr, performs dynamic cast, returns std::weak_ptr. The object under poitner must live. - //virtual boost::any castUniquePtr(const boost::any &ptr) const = 0; // takes std::unique_ptr, performs dynamic cast, returns std::unique_ptr -}; - -template -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(ptr); - To * ret = dynamic_cast(from); - if (ret == nullptr) - { - // Last resort when RTTI goes mad - ret = static_cast(from); - } - return (void*)ret; - } - - // Helper function performing casts between smart pointers using dynamic_pointer_cast - template - boost::any castSmartPtr(const boost::any &ptr) const - { - try - { - auto from = boost::any_cast(ptr); - auto ret = std::dynamic_pointer_cast(from); - if (!ret) - { - // Last resort when RTTI goes mad - ret = std::static_pointer_cast(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>(ptr); - } - virtual boost::any castWeakPtr(const boost::any &ptr) const override - { - auto from = boost::any_cast>(ptr); - return castSmartPtr>(from.lock()); - } -// virtual boost::any castUniquePtr(const boost::any &ptr) const override -// { -// return castSmartPtr>(ptr); -// } -}; - -class DLL_LINKAGE CTypeList: public boost::noncopyable -{ -public: - struct TypeDescriptor; - typedef std::shared_ptr TypeInfoPtr; - typedef std::weak_ptr WeakTypeInfoPtr; - struct TypeDescriptor - { - ui16 typeID; - const char *name; - std::vector children, parents; - }; - typedef boost::shared_mutex TMutex; - typedef boost::unique_lock TUniqueLock; - typedef boost::shared_lock TSharedLock; -private: - mutable TMutex mx; - - std::map typeInfos; - std::map, std::unique_ptr> casters; //for each pair we provide a caster (each registered relations creates a single entry here) - - /// 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 castSequence(TypeInfoPtr from, TypeInfoPtr to) const; - std::vector castSequence(const std::type_info *from, const std::type_info *to) const; - - - template - boost::any castHelper(boost::any inputPtr, const std::type_info *fromArg, const std::type_info *toArg) const - { - TSharedLock lock(mx); - auto typesSequence = castSequence(fromArg, toArg); - - boost::any ptr = inputPtr; - for(int i = 0; i < static_cast(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 std::unique_ptr not have operator->* ..? - } - - return ptr; - } - - TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true) const; //if not throws, failure returns nullptr - - TypeInfoPtr registerType(const std::type_info *type); -public: - CTypeList(); - - template - void registerType(const Base * b = nullptr, const Derived * d = nullptr) - { - TUniqueLock lock(mx); - static_assert(std::is_base_of::value, "First registerType template parameter needs to ba a base class of the second one."); - static_assert(std::has_virtual_destructor::value, "Base class needs to have a virtual destructor."); - static_assert(!std::is_same::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>(); - casters[std::make_pair(dti, bti)] = make_unique>(); - } - - ui16 getTypeID(const std::type_info *type, bool throws = false) const; - - template - ui16 getTypeID(const T * t = nullptr, bool throws = false) const - { - return getTypeID(getTypeInfo(t), throws); - } - - template - void * castToMostDerived(const TInput * inputPtr) const - { - auto &baseType = typeid(typename std::remove_cv::type); - auto derivedType = getTypeInfo(inputPtr); - - if (!strcmp(baseType.name(), derivedType->name())) - { - return const_cast(reinterpret_cast(inputPtr)); - } - - return boost::any_cast(castHelper<&IPointerCaster::castRawPtr>( - const_cast(reinterpret_cast(inputPtr)), &baseType, - derivedType)); - } - - template - boost::any castSharedToMostDerived(const std::shared_ptr inputPtr) const - { - auto &baseType = typeid(typename std::remove_cv::type); - auto derivedType = getTypeInfo(inputPtr.get()); - - if (!strcmp(baseType.name(), derivedType->name())) - return inputPtr; - - return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, &baseType, derivedType); - } - - void * castRaw(void *inputPtr, const std::type_info *from, const std::type_info *to) const - { - return boost::any_cast(castHelper<&IPointerCaster::castRawPtr>(inputPtr, from, to)); - } - boost::any castShared(boost::any inputPtr, const std::type_info *from, const std::type_info *to) const - { - return castHelper<&IPointerCaster::castSharedPtr>(inputPtr, from, to); - } - - template const std::type_info * getTypeInfo(const T * t = nullptr) const - { - if(t) - return &typeid(*t); - else - return &typeid(T); - } -}; - -extern DLL_LINKAGE CTypeList typeList; - -template -struct VariantLoaderHelper -{ - Source & source; - std::vector> funcs; - - VariantLoaderHelper(Source & source): - source(source) - { - mpl::for_each(std::ref(*this)); - } - - template - void operator()(Type) - { - funcs.push_back([&]() -> Variant - { - Type obj; - source >> obj; - return Variant(obj); - }); - } -}; - -template -struct SerializationLevel -{ - typedef mpl::integral_c_tag tag; - typedef - typename mpl::eval_if< - boost::is_same, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_same >, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_fundamental, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_enum, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_class, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_array, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_pointer, - mpl::int_, - //else - typename mpl::eval_if< - boost::is_enum, - mpl::int_, - //else - mpl::int_ - > - > - > - > - > - > - > - >::type type; - static const int value = SerializationLevel::type::value; -}; - -template -struct VectorisedObjectInfo -{ - const std::vector > *vector; //pointer to the appropriate vector - std::function idRetriever; - //const IdType ObjType::*idPtr; //pointer to the field representing the position in the vector - - VectorisedObjectInfo(const std::vector< ConstTransitivePtr > *Vector, std::function IdGetter) - :vector(Vector), idRetriever(IdGetter) - { - } -}; - -template -si32 idToNumber(const T &t, typename boost::enable_if >::type * dummy = 0) -{ - return t; -} - -template -NT idToNumber(const BaseForID &t) -{ - return t.getNum(); -} - -/// Class which is responsible for storing and loading data. -class DLL_LINKAGE CSerializer -{ -public: - typedef std::map TTypeVecMap; - TTypeVecMap vectors; //entry must be a pointer to vector containing pointers to the objects of key type - - bool smartVectorMembersSerialization; - bool sendStackInstanceByIds; - - CSerializer(); - ~CSerializer(); - - virtual void reportState(CLogger * out){}; - - template - void registerVectoredType(const std::vector *Vector, const std::function &idRetriever) - { - vectors[&typeid(T)] = VectorisedObjectInfo(Vector, idRetriever); - } - template - void registerVectoredType(const std::vector > *Vector, const std::function &idRetriever) - { - vectors[&typeid(T)] = VectorisedObjectInfo(Vector, idRetriever); - } - - template - const VectorisedObjectInfo *getVectorisedTypeInfo() - { - const std::type_info *myType = nullptr; -// -// if(boost::is_base_of::value) //ugly workaround to support also types derived from CGObjectInstance -> if we encounter one, treat it aas CGObj.. -// myType = &typeid(CGObjectInstance); -// else - myType = &typeid(T); - - TTypeVecMap::iterator i = vectors.find(myType); - if(i == vectors.end()) - return nullptr; - else - { - assert(!i->second.empty()); - assert(i->second.type() == typeid(VectorisedObjectInfo)); - VectorisedObjectInfo *ret = &(boost::any_cast&>(i->second)); - return ret; - } - } - - template - T* getVectorItemFromId(const VectorisedObjectInfo &oInfo, U id) const - { - /* if(id < 0) - return nullptr;*/ - si32 idAsNumber = idToNumber(id); - - assert(oInfo.vector); - assert(static_cast(oInfo.vector->size()) > idAsNumber); - return const_cast((*oInfo.vector)[idAsNumber].get()); - } - - template - U getIdFromVectorItem(const VectorisedObjectInfo &oInfo, const T* obj) const - { - if(!obj) - return U(-1); - - return oInfo.idRetriever(*obj); - } - - void addStdVecItems(CGameState *gs, LibClasses *lib = VLC); -}; - -class IBinaryWriter : public virtual CSerializer -{ -public: - virtual int write(const void * data, unsigned size) = 0; -}; - -class DLL_LINKAGE CSaverBase -{ -protected: - IBinaryWriter * writer; -public: - CSaverBase(IBinaryWriter * w): writer(w){}; - - inline int write(const void * data, unsigned size) - { - return writer->write(data, size); - }; -}; - -class CBasicPointerSaver -{ -public: - virtual void savePtr(CSaverBase &ar, const void *data) const =0; - virtual ~CBasicPointerSaver(){} -}; - -template //metafunction returning CGObjectInstance if T is its derivate or T elsewise -struct VectorisedTypeFor -{ - typedef typename - //if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else - mpl::identity - > >::type type; -}; -template -struct VectorizedIDType -{ - typedef typename - //if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else if - mpl::eval_if, - mpl::identity, - //else - mpl::identity - > > > > > >::type type; -}; - -template -struct VariantVisitorSaver : boost::static_visitor<> -{ - Handler &h; - VariantVisitorSaver(Handler &H):h(H) - { - } - - template - void operator()(const T &t) - { - h << t; - } -}; - -template -struct SaveIfStackInstance -{ - static bool invoke(Ser &s, const T &data) - { - return false; - } -}; - -template -struct SaveIfStackInstance -{ - static bool invoke(Ser &s, const CStackInstance* const &data) - { - assert(data->armyObj); - SlotID slot; - - if(data->getNodeType() == CBonusSystemNode::COMMANDER) - slot = SlotID::COMMANDER_SLOT_PLACEHOLDER; - else - slot = data->armyObj->findStack(data); - - assert(slot != SlotID()); - s << data->armyObj << slot; - return true; - } -}; - -template -struct LoadIfStackInstance -{ - static bool invoke(Ser &s, T &data) - { - return false; - } -}; - -template -struct LoadIfStackInstance -{ - static bool invoke(Ser &s, CStackInstance* &data) - { - CArmedInstance *armedObj; - SlotID slot; - s >> armedObj >> slot; - if(slot != SlotID::COMMANDER_SLOT_PLACEHOLDER) - { - assert(armedObj->hasStackAtSlot(slot)); - data = armedObj->stacks[slot]; - } - else - { - auto hero = dynamic_cast(armedObj); - assert(hero); - assert(hero->commander); - data = hero->commander; - } - return true; - } -}; - - -/// The class which manages saving objects. -class DLL_LINKAGE COSer : public CSaverBase -{ -public: - - struct SaveBoolean - { - static void invoke(COSer &s, const bool &data) - { - s.saveBoolean(data); - } - }; - - struct SaveBooleanVector - { - static void invoke(COSer &s, const std::vector &data) - { - s.saveBooleanVector(data); - } - }; - - template - struct SavePrimitive - { - static void invoke(COSer &s, const T &data) - { - s.savePrimitive(data); - } - }; - - template - struct SaveSerializable - { - static void invoke(COSer &s, const T &data) - { - s.saveSerializable(data); - } - }; - - template - struct SaveEnum - { - static void invoke(COSer &s, const T &data) - { - s.saveEnum(data); - } - }; - - template - struct SavePointer - { - static void invoke(COSer &s, const T &data) - { - s.savePointer(data); - } - }; - - template - struct SaveArray - { - static void invoke(COSer &s, const T &data) - { - s.saveArray(data); - } - }; - - template - struct SaveWrong - { - static void invoke(COSer &s, const T &data) - { - throw std::runtime_error("Wrong save serialization call!"); - } - }; - - template - class CPointerSaver : public CBasicPointerSaver - { - public: - void savePtr(CSaverBase &ar, const void *data) const override - { - COSer &s = static_cast(ar); - const T *ptr = static_cast(data); - //T is most derived known type, it's time to call actual serialize - const_cast(ptr)->serialize(s,version); - } - }; - - bool saving; - std::map savers; // typeID => CPointerSaver - - std::map savedPointers; - bool smartPointerSerialization; - - COSer(IBinaryWriter * w): CSaverBase(w) - { - saving=true; - smartPointerSerialization = true; - } - ~COSer() - { - std::map::iterator iter; - - for(iter = savers.begin(); iter != savers.end(); iter++) - delete iter->second; - } - - template - void addSaver(const T * t = nullptr) - { - auto ID = typeList.getTypeID(t); - if(!savers.count(ID)) - savers[ID] = new CPointerSaver; - } - - template void registerType(const Base * b = nullptr, const Derived * d = nullptr) - { - typeList.registerType(b, d); - addSaver(b); - addSaver(d); - } - - template - COSer & operator<<(const T &t) - { - this->save(t); - return * this; - } - - template - COSer & operator&(const T & t) - { - return * this << t; - } - - template - void savePrimitive(const T &data) - { - this->write(&data,sizeof(data)); - } - - template - void savePointer(const T &data) - { - //write if pointer is not nullptr - ui8 hlp = (data!=nullptr); - *this << hlp; - - //if pointer is nullptr then we don't need anything more... - if(!hlp) - return; - - if(writer->smartVectorMembersSerialization) - { - typedef typename boost::remove_const::type>::type TObjectType; - typedef typename VectorisedTypeFor::type VType; - typedef typename VectorizedIDType::type IDType; - if(const auto *info = writer->getVectorisedTypeInfo()) - { - IDType id = writer->getIdFromVectorItem(*info, data); - *this << id; - if(id != IDType(-1)) //vector id is enough - return; - } - } - - if(writer->sendStackInstanceByIds) - { - const bool gotSaved = SaveIfStackInstance::invoke(*this, data); - if(gotSaved) - return; - } - - if(smartPointerSerialization) - { - // 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::iterator i = savedPointers.find(actualPointer); - if(i != savedPointers.end()) - { - //this pointer has been already serialized - write only it's id - *this << i->second; - return; - } - - //give id to this pointer - ui32 pid = (ui32)savedPointers.size(); - savedPointers[actualPointer] = pid; - *this << pid; - } - - //write type identifier - ui16 tid = typeList.getTypeID(data); - *this << tid; - - this->savePointerHlp(tid, data); - } - - //that part of ptr serialization was extracted to allow customization of its behavior in derived classes - template - void savePointerHlp(ui16 tid, const T &data) - { - if(!tid) - *this << *data; //if type is unregistered simply write all data in a standard way - else - savers[tid]->savePtr(*this, typeList.castToMostDerived(data)); //call serializer specific for our real type - } - - template - void saveArray(const T &data) - { - ui32 size = ARRAY_COUNT(data); - for(ui32 i=0; i < size; i++) - *this << data[i]; - } - template - void save(const T &data) - { - typedef - //if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity, - //else if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity, - //else if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else - mpl::identity > - > - > - > - > - > - > - >::type typex; - typex::invoke(* this, data); - } - template - void saveSerializable(const T &data) - { - const_cast(data).serialize(*this,version); - } - template - void saveSerializable(const std::shared_ptr &data) - { - T *internalPtr = data.get(); - *this << internalPtr; - } - template - void saveSerializable(const std::unique_ptr &data) - { - T *internalPtr = data.get(); - *this << internalPtr; - } - template - void saveSerializable(const std::vector &data) - { - ui32 length = data.size(); - *this << length; - for(ui32 i=0;i - void saveSerializable(const std::array &data) - { - for(ui32 i=0; i < N; i++) - *this << data[i]; - } - template - void saveSerializable(const std::set &data) - { - std::set &d = const_cast &>(data); - ui32 length = d.size(); - *this << length; - for(typename std::set::iterator i=d.begin();i!=d.end();i++) - *this << *i; - } - template - void saveSerializable(const std::unordered_set &data) - { - std::unordered_set &d = const_cast &>(data); - ui32 length = d.size(); - *this << length; - for(typename std::unordered_set::iterator i=d.begin();i!=d.end();i++) - *this << *i; - } - template - void saveSerializable(const std::list &data) - { - std::list &d = const_cast &>(data); - ui32 length = d.size(); - *this << length; - for(typename std::list::iterator i=d.begin();i!=d.end();i++) - *this << *i; - } - void saveSerializable(const std::string &data) - { - *this << ui32(data.length()); - this->write(data.c_str(),data.size()); - } - template - void saveSerializable(const std::pair &data) - { - *this << data.first << data.second; - } - template - void saveSerializable(const std::map &data) - { - *this << ui32(data.size()); - for(typename std::map::const_iterator i=data.begin();i!=data.end();i++) - *this << i->first << i->second; - } - template - void saveSerializable(const std::multimap &data) - { - *this << ui32(data.size()); - for(typename std::map::const_iterator i = data.begin(); i != data.end(); i++) - *this << i->first << i->second; - } - template - void saveSerializable(const boost::variant &data) - { - si32 which = data.which(); - *this << which; - - VariantVisitorSaver visitor(*this); - boost::apply_visitor(visitor, data); - } - template - void saveSerializable(const boost::optional &data) - { - if(data) - { - *this << (ui8)1; - *this << *data; - } - else - { - *this << (ui8)0; - } - } - template - void saveEnum(const E &data) - { - si32 writ = static_cast(data); - *this << writ; - } - void saveBoolean(const bool & data) - { - ui8 writ = static_cast(data); - *this << writ; - } - void saveBooleanVector(const std::vector & data) - { - std::vector convData; - std::copy(data.begin(), data.end(), std::back_inserter(convData)); - saveSerializable(convData); - } -}; - -class IBinaryReader : public virtual CSerializer -{ -public: - virtual int read(void * data, unsigned size) = 0; -}; - -class DLL_LINKAGE CLoaderBase -{ -protected: - IBinaryReader * reader; -public: - CLoaderBase(IBinaryReader * r): reader(r){}; - - inline int read(void * data, unsigned size) - { - return reader->read(data, size); - }; -}; - -class CBasicPointerLoader -{ -public: - virtual const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const =0; //data is pointer to the ACTUAL POINTER - virtual ~CBasicPointerLoader(){} -}; - -template -struct ClassObjectCreator -{ - static T *invoke() - { - static_assert(!std::is_abstract::value, "Cannot call new upon abstract classes!"); - return new T(); - } -}; - -template -struct ClassObjectCreator::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())); - } -}; - - -/// The class which manages loading of objects. -class DLL_LINKAGE CISer : public CLoaderBase -{ -public: - struct LoadBoolean - { - static void invoke(CISer &s, bool &data) - { - s.loadBoolean(data); - } - }; - - struct LoadBooleanVector - { - static void invoke(CISer &s, std::vector &data) - { - s.loadBooleanVector(data); - } - }; - - template - struct LoadEnum - { - static void invoke(CISer &s, T &data) - { - s.loadEnum(data); - } - }; - - template - struct LoadPrimitive - { - static void invoke(CISer &s, T &data) - { - s.loadPrimitive(data); - } - }; - - template - struct LoadPointer - { - static void invoke(CISer &s, T &data) - { - s.loadPointer(data); - } - }; - - template - struct LoadArray - { - static void invoke(CISer &s, T &data) - { - s.loadArray(data); - } - }; - - template - struct LoadSerializable - { - static void invoke(CISer &s, T &data) - { - s.loadSerializable(data); - } - }; - - template - struct LoadWrong - { - static void invoke(CISer &s, const T &data) - { - throw std::runtime_error("Wrong load serialization call!"); - } - }; - - template class CPointerLoader : public CBasicPointerLoader - { - public: - const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const override //data is pointer to the ACTUAL POINTER - { - CISer &s = static_cast(ar); - T *&ptr = *static_cast(data); - - //create new object under pointer - typedef typename boost::remove_pointer::type npT; - ptr = ClassObjectCreator::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,s.fileVersion); - return &typeid(T); - } - }; - - bool saving; - std::map loaders; // typeID => CPointerSaver - si32 fileVersion; - bool reverseEndianess; //if source has different endianness than us, we reverse bytes - - std::map loadedPointers; - std::map loadedPointersTypes; - std::map loadedSharedPointers; - - bool smartPointerSerialization; - - CISer(IBinaryReader * r): CLoaderBase(r) - { - saving = false; - fileVersion = 0; - smartPointerSerialization = true; - reverseEndianess = false; - } - - ~CISer() - { - std::map::iterator iter; - - for(iter = loaders.begin(); iter != loaders.end(); iter++) - delete iter->second; - } - - template - void addLoader(const T * t = nullptr) - { - auto ID = typeList.getTypeID(t); - if(!loaders.count(ID)) - loaders[ID] = new CPointerLoader; - } - - template void registerType(const Base * b = nullptr, const Derived * d = nullptr) - { - typeList.registerType(b, d); - addLoader(b); - addLoader(d); - } - - template - CISer & operator>>(T &t) - { - this->load(t); - return * this; - } - - template - CISer & operator&(T & t) - { - return * this >> t; - } - - int write(const void * data, unsigned size); - template - void load(T &data) - { - typedef - //if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity, - //else if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity, - //else if - typename mpl::eval_if< mpl::equal_to,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else if - typename mpl::eval_if,mpl::int_ >, - mpl::identity >, - //else - mpl::identity > - > - > - > - > - > - > - >::type typex; - typex::invoke(* this, data); - } - template - void loadPrimitive(T &data) - { - if(0) //for testing #989 - { - this->read(&data,sizeof(data)); - } - else - { - unsigned length = sizeof(data); - char* dataPtr = (char*)&data; - this->read(dataPtr,length); - if(reverseEndianess) - std::reverse(dataPtr, dataPtr + length); - } - } - - template - void loadSerializableBySerializeCall(T &data) - { - ////that const cast is evil because it allows to implicitly overwrite const objects when deserializing - typedef typename boost::remove_const::type nonConstT; - nonConstT &hlp = const_cast(data); - hlp.serialize(*this,fileVersion); - //data.serialize(*this,myVersion); - } - - template - void loadSerializable(T &data) - { - loadSerializableBySerializeCall(data); - } - template - void loadArray(T &data) - { - ui32 size = ARRAY_COUNT(data); - for(ui32 i = 0; i < size; i++) - *this >> data[i]; - } - template - void loadPointer(T &data) - { - ui8 hlp; - *this >> hlp; - if(!hlp) - { - data = nullptr; - return; - } - - if(reader->smartVectorMembersSerialization) - { - typedef typename boost::remove_const::type>::type TObjectType; //eg: const CGHeroInstance * => CGHeroInstance - typedef typename VectorisedTypeFor::type VType; //eg: CGHeroInstance -> CGobjectInstance - typedef typename VectorizedIDType::type IDType; - if(const auto *info = reader->getVectorisedTypeInfo()) - { - IDType id; - *this >> id; - if(id != IDType(-1)) - { - data = static_cast(reader->getVectorItemFromId(*info, id)); - return; - } - } - } - - if(reader->sendStackInstanceByIds) - { - bool gotLoaded = LoadIfStackInstance::invoke(* this, data); - if(gotLoaded) - return; - } - - ui32 pid = 0xffffffff; //pointer id (or maybe rather pointee id) - if(smartPointerSerialization) - { - *this >> pid; //get the id - std::map::iterator i = loadedPointers.find(pid); //lookup - - if(i != loadedPointers.end()) - { - // 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(typeList.castRaw(i->second, loadedPointersTypes.at(pid), &typeid(typename boost::remove_const::type>::type))); - return; - } - } - - //get type id - ui16 tid; - *this >> tid; - this->loadPointerHlp(tid, data, pid); - } - - //that part of ptr deserialization was extracted to allow customization of its behavior in derived classes - template - void loadPointerHlp( ui16 tid, T & data, ui32 pid ) - { - if(!tid) - { - typedef typename boost::remove_pointer::type npT; - typedef typename boost::remove_const::type ncpT; - data = ClassObjectCreator::invoke(); - ptrAllocated(data, pid); - *this >> *data; - } - else - { - auto loader = loaders[tid]; - if (loader == nullptr) - { - logGlobal->error("loadPointerHlp %d %d - no loader exists", tid, pid); - data = nullptr; - return; - } - auto typeInfo = loader->loadPtr(*this, &data, pid); - data = reinterpret_cast(typeList.castRaw((void*)data, typeInfo, &typeid(typename boost::remove_const::type>::type))); - } - } - - template - 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) \ - ui32 length; \ - *this >> length; \ - if(length > 500000) \ - { \ - logGlobal->warnStream() << "Warning: very big length: " << length;\ - reader->reportState(logGlobal); \ - }; - - - template - void loadSerializable(std::shared_ptr &data) - { - typedef typename boost::remove_const::type NonConstT; - NonConstT *internalPtr; - *this >> internalPtr; - - void *internalPtrDerived = typeList.castToMostDerived(internalPtr); - - if(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 - { - auto actualType = typeList.getTypeInfo(internalPtr); - auto typeWeNeedToReturn = typeList.getTypeInfo(); - if(*actualType == *typeWeNeedToReturn) - { - // No casting needed, just unpack already stored std::shared_ptr and return it - data = boost::any_cast>(itr->second); - } - else - { - // We need to perform series of casts - auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn); - data = boost::any_cast>(ret); - } - } - catch(std::exception &e) - { - logGlobal->errorStream() << e.what(); - logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME") - % itr->second.type().name() % typeid(std::shared_ptr).name(); - //TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa) - assert(0); - } - } - else - { - auto hlp = std::shared_ptr(internalPtr); - data = hlp; //possibly adds const - loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp); - } - } - else - data.reset(); - } - template - void loadSerializable(std::unique_ptr &data) - { - T *internalPtr; - *this >> internalPtr; - data.reset(internalPtr); - } - template - void loadSerializable(std::vector &data) - { - READ_CHECK_U32(length); - data.resize(length); - for(ui32 i=0;i> data[i]; - } - template - void loadSerializable(std::array &data) - { - for(ui32 i = 0; i < N; i++) - *this >> data[i]; - } - template - void loadSerializable(std::set &data) - { - READ_CHECK_U32(length); - data.clear(); - T ins; - for(ui32 i=0;i> ins; - data.insert(ins); - } - } - template - void loadSerializable(std::unordered_set &data) - { - READ_CHECK_U32(length); - data.clear(); - T ins; - for(ui32 i=0;i> ins; - data.insert(ins); - } - } - template - void loadSerializable(std::list &data) - { - READ_CHECK_U32(length); - data.clear(); - T ins; - for(ui32 i=0;i> ins; - data.push_back(ins); - } - } - template - void loadSerializable(std::pair &data) - { - *this >> data.first >> data.second; - } - - template - void loadSerializable(std::map &data) - { - READ_CHECK_U32(length); - data.clear(); - T1 key; - T2 value; - for(ui32 i=0;i> key >> value; - data.insert(std::pair(std::move(key), std::move(value))); - } - } - template - void loadSerializable(std::multimap &data) - { - READ_CHECK_U32(length); - data.clear(); - T1 key; - T2 value; - for(ui32 i = 0; i < length; i++) - { - *this >> key >> value; - data.insert(std::pair(std::move(key), std::move(value))); - } - } - void loadSerializable(std::string &data) - { - READ_CHECK_U32(length); - data.resize(length); - this->read((void*)data.c_str(),length); - } - - template - void loadSerializable(boost::variant &data) - { - typedef boost::variant TVariant; - - VariantLoaderHelper loader(*this); - - si32 which; - *this >> which; - assert(which < loader.funcs.size()); - data = loader.funcs.at(which)(); - } - - template - void loadSerializable(boost::optional & data) - { - ui8 present; - *this >> present; - if(present) - { - T t; - *this >> t; - data = t; - } - else - { - data = boost::optional(); - } - } -// void loadSerializable(CStackInstance *&s) -// { -// if(sendStackInstanceByIds) -// { -// CArmedInstance *armed; -// SlotID slot; -// *this >> armed >> slot; -// assert(armed->hasStackAtSlot(slot)); -// s = armed->stacks[slot]; -// } -// else -// loadSerializableBySerializeCall(s); -// } - - template - void loadEnum(E &data) - { - si32 read; - *this >> read; - data = static_cast(read); - } - void loadBoolean(bool &data) - { - ui8 read; - *this >> read; - data = static_cast(read); - } - void loadBooleanVector(std::vector & data) - { - std::vector convData; - loadSerializable(convData); - convData.resize(data.size()); - range::copy(convData, data.begin()); - } -}; - -class DLL_LINKAGE CSaveFile - :public IBinaryWriter -{ -public: - - COSer serializer; - - boost::filesystem::path fName; - std::unique_ptr sfile; - - CSaveFile(const boost::filesystem::path &fname); //throws! - ~CSaveFile(); - int write(const void * data, unsigned size) override; - - void openNextFile(const boost::filesystem::path &fname); //throws! - void clear(); - void reportState(CLogger * out) override; - - void putMagicBytes(const std::string &text); - - template - CSaveFile & operator<<(const T &t) - { - serializer << t; - return * this; - } -}; - -class DLL_LINKAGE CLoadFile - : public IBinaryReader -{ -public: - CISer serializer; - - boost::filesystem::path fName; - std::unique_ptr sfile; - - CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws! - ~CLoadFile(); - int read(void * data, unsigned size) override; //throws! - - void openNextFile(const boost::filesystem::path & fname, int minimalVersion); //throws! - void clear(); - void reportState(CLogger * out) override; - - void checkMagicBytes(const std::string & text); - - template - CLoadFile & operator>>(T &t) - { - serializer >> t; - return * this; - } -}; - -class DLL_LINKAGE CLoadIntegrityValidator - : public IBinaryReader -{ -public: - CISer serializer; - std::unique_ptr primaryFile, controlFile; - bool foundDesync; - - CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = version); //throws! - - int read( void * data, unsigned size) override; //throws! - void checkMagicBytes(const std::string &text); - - std::unique_ptr decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore -}; - -typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service > TSocket; -typedef boost::asio::basic_socket_acceptor > TAcceptor; - -class DLL_LINKAGE CConnection - : public IBinaryReader, public IBinaryWriter -{ - //CGameState *gs; - CConnection(void); - - void init(); - void reportState(CLogger * out) override; -public: - CISer iser; - COSer oser; - - boost::mutex *rmx, *wmx; // read/write mutexes - TSocket * socket; - bool logging; - bool connected; - bool myEndianess, contactEndianess; //true if little endian, if endianness is different we'll have to revert received multi-byte vars - boost::asio::io_service *io_service; - std::string name; //who uses this connection - - int connectionID; - boost::thread *handler; - - bool receivedStop, sendStop; - - CConnection(std::string host, std::string port, std::string Name); - CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name); - CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket - - int write(const void * data, unsigned size) override; - int read(void * data, unsigned size) override; - void close(); - bool isOpen() const; - template - CConnection &operator&(const T&); - virtual ~CConnection(void); - - CPack *retreivePack(); //gets from server next pack (allocates it with new) - void sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID); - - void disableStackSendingByID(); - void enableStackSendingByID(); - void disableSmartPointerSerialization(); - void enableSmartPointerSerializatoin(); - void disableSmartVectorMemberSerialization(); - void enableSmartVectorMemberSerializatoin(); - - void prepareForSendingHeroes(); //disables sending vectorised, enables smart pointer serialization, clears saved/loaded ptr cache - void enterPregameConnectionMode(); - - template - CConnection & operator>>(T &t) - { - iser >> t; - return * this; - } - - template - CConnection & operator<<(const T &t) - { - oser << t; - return * this; - } -}; - -DLL_LINKAGE std::ostream &operator<<(std::ostream &str, const CConnection &cpc); - - -// Serializer that stores objects in the dynamic buffer. Allows performing deep object copies. -class DLL_LINKAGE CMemorySerializer - : public IBinaryReader, public IBinaryWriter -{ - std::vector buffer; - - size_t readPos; //index of the next byte to be read -public: - CISer iser; - COSer oser; - - int read(void * data, unsigned size) override; //throws! - int write(const void * data, unsigned size) override; - - CMemorySerializer(); - - template - static std::unique_ptr deepCopy(const T &data) - { - CMemorySerializer mem; - mem.oser << &data; - - std::unique_ptr ret; - mem.iser >> ret; - return ret; - } -}; - -template -class CApplier -{ -public: - std::map apps; - - ~CApplier() - { - typename std::map::iterator iter; - - for(iter = apps.begin(); iter != apps.end(); iter++) - delete iter->second; - } - - template - void addApplier(ui16 ID) - { - if(!apps.count(ID)) - { - RegisteredType * rtype = nullptr; - apps[ID] = T::getApplier(rtype); - } - } - - template - void registerType(const Base * b = nullptr, const Derived * d = nullptr) - { - typeList.registerType(b, d); - addApplier(typeList.getTypeID(b)); - addApplier(typeList.getTypeID(d)); - } - -}; From 3a040c1730322235ca6c68d47dc5f8c8800d1ade Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Thu, 27 Oct 2016 19:18:52 +0300 Subject: [PATCH 06/11] Updated project files --- lib/VCMI_lib.cbp | 16 ++++++++++++++-- lib/VCMI_lib.vcxproj | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp index 91391ceda..61a47f631 100644 --- a/lib/VCMI_lib.cbp +++ b/lib/VCMI_lib.cbp @@ -175,8 +175,6 @@ - - @@ -323,6 +321,20 @@ + + + + + + + + + + + + + + diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj index 310a6fa3a..818887fdc 100644 --- a/lib/VCMI_lib.vcxproj +++ b/lib/VCMI_lib.vcxproj @@ -183,7 +183,6 @@ - @@ -193,6 +192,13 @@ + + + + + + + @@ -297,7 +303,6 @@ - @@ -382,6 +387,13 @@ + + + + + + + From ba5ad286cda02670e1540aa04179cde929a751f5 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Fri, 28 Oct 2016 13:50:54 +0300 Subject: [PATCH 07/11] Serializer refactoring: fix paths to work in Visual Studio --- lib/serializer/CSerializer.cpp | 6 +++--- lib/serializer/Connection.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/serializer/CSerializer.cpp b/lib/serializer/CSerializer.cpp index 0666633db..7127f601f 100644 --- a/lib/serializer/CSerializer.cpp +++ b/lib/serializer/CSerializer.cpp @@ -1,9 +1,9 @@ #include "StdInc.h" #include "CSerializer.h" -#include "CGameState.h" -#include "mapping/CMap.h" -#include "CHeroHandler.h" +#include "../CGameState.h" +#include "../mapping/CMap.h" +#include "../CHeroHandler.h" #include "../mapObjects/CGHeroInstance.h" /* diff --git a/lib/serializer/Connection.cpp b/lib/serializer/Connection.cpp index 1f01e09d1..311e34928 100644 --- a/lib/serializer/Connection.cpp +++ b/lib/serializer/Connection.cpp @@ -1,9 +1,9 @@ #include "StdInc.h" #include "Connection.h" -#include "registerTypes/RegisterTypes.h" -#include "mapping/CMap.h" -#include "CGameState.h" +#include "../registerTypes/RegisterTypes.h" +#include "../mapping/CMap.h" +#include "../CGameState.h" #include From 256f43f4672f3f5da1c3364efbc30975b0acf9db Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 29 Oct 2016 19:52:19 +0300 Subject: [PATCH 08/11] Fixes zero-initialized fileVersion member in deserializer --- client/CPreGame.cpp | 6 +----- client/Client.cpp | 2 +- lib/serializer/BinaryDeserializer.cpp | 8 ++++---- lib/serializer/BinaryDeserializer.h | 21 ++++++++------------- lib/serializer/BinarySerializer.cpp | 2 +- lib/serializer/BinarySerializer.h | 4 ++-- lib/serializer/CLoadIntegrityValidator.h | 2 +- lib/serializer/CMemorySerializer.cpp | 1 + lib/serializer/CSerializer.h | 4 ++-- lib/serializer/Connection.cpp | 1 + lib/serializer/Connection.h | 5 +++-- server/CVCMIServer.cpp | 2 +- 12 files changed, 26 insertions(+), 32 deletions(-) diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index a9522fa73..fa5c9a115 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -724,10 +724,6 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti else if(current) { SelectMap sm(*current); - // FIXME: Super dirty hack to avoid crash on multiplayer game start. - // There is some issues with TriggeredEvent serialization that cause it. - // We'll look into them once refactored serializer fixed and merged - sm.mapInfo->mapHeader->triggeredEvents.clear(); *serv << &sm; UpdateStartOptions uso(sInfo); @@ -1148,7 +1144,7 @@ void SelectionTab::parseGames(const std::unordered_set &files, bool { try { - CLoadFile lf(*CResourceHandler::get()->getResourceName(file), minSupportedVersion); + CLoadFile lf(*CResourceHandler::get()->getResourceName(file), MINIMAL_SERIALIZATION_VERSION); lf.checkMagicBytes(SAVEGAME_MAGIC); // ui8 sign[8]; // lf >> sign; diff --git a/client/Client.cpp b/client/Client.cpp index 0e175b54a..ac51e4203 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -291,7 +291,7 @@ void CClient::loadGame(const std::string & fname, const bool server, const std:: throw std::runtime_error("Cannot open server part of " + fname); { - CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, minSupportedVersion); + CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION); loadCommonState(checkingLoader); loader = checkingLoader.decay(); } diff --git a/lib/serializer/BinaryDeserializer.cpp b/lib/serializer/BinaryDeserializer.cpp index 538b5b536..68b0f3008 100644 --- a/lib/serializer/BinaryDeserializer.cpp +++ b/lib/serializer/BinaryDeserializer.cpp @@ -34,7 +34,7 @@ int CLoadFile::read(void * data, unsigned size) void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalVersion) { assert(!serializer.reverseEndianess); - assert(minimalVersion <= version); + assert(minimalVersion <= SERIALIZATION_VERSION); try { @@ -55,15 +55,15 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV if(serializer.fileVersion < minimalVersion) THROW_FORMAT("Error: too old file format (%s)!", fName); - if(serializer.fileVersion > version) + if(serializer.fileVersion > SERIALIZATION_VERSION ) { - logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % version % fName; + logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % SERIALIZATION_VERSION % fName; auto versionptr = (char*)&serializer.fileVersion; std::reverse(versionptr, versionptr + 4); logGlobal->warnStream() << "Version number reversed is " << serializer.fileVersion << ", checking..."; - if(serializer.fileVersion == version) + if(serializer.fileVersion == SERIALIZATION_VERSION) { logGlobal->warnStream() << fname << " seems to have different endianness! Entering reversing mode."; serializer.reverseEndianess = true; diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 26869046e..8c1a43329 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -148,6 +148,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase ptr = ClassObjectCreator::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 + assert(s.fileVersion != 0); ptr->serialize(s,s.fileVersion); return &typeid(T); } @@ -185,23 +186,17 @@ public: template < class T, typename std::enable_if < std::is_fundamental::value && !std::is_same::value, int >::type = 0 > void load(T &data) { - if(0) //for testing #989 - { - this->read(&data,sizeof(data)); - } - else - { - unsigned length = sizeof(data); - char* dataPtr = (char*)&data; - this->read(dataPtr,length); - if(reverseEndianess) - std::reverse(dataPtr, dataPtr + length); - } + unsigned length = sizeof(data); + char* dataPtr = (char*)&data; + this->read(dataPtr,length); + if(reverseEndianess) + std::reverse(dataPtr, dataPtr + length); } template < typename T, typename std::enable_if < is_serializeable::value, int >::type = 0 > void load(T &data) { + assert( fileVersion != 0 ); ////that const cast is evil because it allows to implicitly overwrite const objects when deserializing typedef typename std::remove_const::type nonConstT; nonConstT &hlp = const_cast(data); @@ -521,7 +516,7 @@ public: std::string fName; std::unique_ptr sfile; - CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws! + CLoadFile(const boost::filesystem::path & fname, int minimalVersion = SERIALIZATION_VERSION ); //throws! ~CLoadFile(); int read(void * data, unsigned size) override; //throws! diff --git a/lib/serializer/BinarySerializer.cpp b/lib/serializer/BinarySerializer.cpp index a4ac40691..1c0a8afff 100644 --- a/lib/serializer/BinarySerializer.cpp +++ b/lib/serializer/BinarySerializer.cpp @@ -43,7 +43,7 @@ void CSaveFile::openNextFile(const boost::filesystem::path &fname) THROW_FORMAT("Error: cannot open to write %s!", fname); sfile->write("VCMI",4); //write magic identifier - serializer & version; //write format version + serializer & SERIALIZATION_VERSION; //write format version } catch(...) { diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index b6f09114b..0873f3eef 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -102,7 +102,7 @@ class DLL_LINKAGE BinarySerializer : public CSaverBase const T *ptr = static_cast(data); //T is most derived known type, it's time to call actual serialize - const_cast(ptr)->serialize(s,version); + const_cast(ptr)->serialize(s, SERIALIZATION_VERSION); } }; @@ -235,7 +235,7 @@ public: template < typename T, typename std::enable_if < is_serializeable::value, int >::type = 0 > void save(const T &data) { - const_cast(data).serialize(*this,version); + const_cast(data).serialize(*this, SERIALIZATION_VERSION); } template diff --git a/lib/serializer/CLoadIntegrityValidator.h b/lib/serializer/CLoadIntegrityValidator.h index 24ef4ddb3..c97ed05e7 100644 --- a/lib/serializer/CLoadIntegrityValidator.h +++ b/lib/serializer/CLoadIntegrityValidator.h @@ -22,7 +22,7 @@ public: std::unique_ptr primaryFile, controlFile; bool foundDesync; - CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = version); //throws! + CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = SERIALIZATION_VERSION); //throws! int read( void * data, unsigned size) override; //throws! void checkMagicBytes(const std::string &text); diff --git a/lib/serializer/CMemorySerializer.cpp b/lib/serializer/CMemorySerializer.cpp index 70e9aa424..341b6a9d6 100644 --- a/lib/serializer/CMemorySerializer.cpp +++ b/lib/serializer/CMemorySerializer.cpp @@ -36,5 +36,6 @@ CMemorySerializer::CMemorySerializer(): iser(this), oser(this) readPos = 0; registerTypes(iser); registerTypes(oser); + iser.fileVersion = SERIALIZATION_VERSION; } diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index e6eefc914..f65166c46 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,8 +14,8 @@ #include "../ConstTransitivePtr.h" #include "../GameConstants.h" -const ui32 version = 761; -const ui32 minSupportedVersion = 753; +const ui32 SERIALIZATION_VERSION = 761; +const ui32 MINIMAL_SERIALIZATION_VERSION = 753; const std::string SAVEGAME_MAGIC = "VCMISVG"; class CHero; diff --git a/lib/serializer/Connection.cpp b/lib/serializer/Connection.cpp index 311e34928..6971f453d 100644 --- a/lib/serializer/Connection.cpp +++ b/lib/serializer/Connection.cpp @@ -58,6 +58,7 @@ void CConnection::init() receivedStop = sendStop = false; static int cid = 1; connectionID = cid++; + iser.fileVersion = SERIALIZATION_VERSION; } CConnection::CConnection(std::string host, std::string port, std::string Name) diff --git a/lib/serializer/Connection.h b/lib/serializer/Connection.h index 4e326f85d..5eaf286a2 100644 --- a/lib/serializer/Connection.h +++ b/lib/serializer/Connection.h @@ -49,6 +49,9 @@ class DLL_LINKAGE CConnection void init(); void reportState(CLogger * out) override; + + int write(const void * data, unsigned size) override; + int read(void * data, unsigned size) override; public: BinaryDeserializer iser; BinarySerializer oser; @@ -70,8 +73,6 @@ public: CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name); CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket - int write(const void * data, unsigned size) override; - int read(void * data, unsigned size) override; void close(); bool isOpen() const; template diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index e62dc0215..285642201 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -464,7 +464,7 @@ void CVCMIServer::loadGame() c >> clients >> fname; //how many clients should be connected { - CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)), minSupportedVersion); + CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)), MINIMAL_SERIALIZATION_VERSION); gh.loadCommonState(lf); lf >> gh; } From e13f98e38e12c187aece4c5cf9cbe1567bb6470b Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sat, 29 Oct 2016 21:29:25 +0300 Subject: [PATCH 09/11] Compilation fix for VS2015 suggested by Ivan and tested by @dydzio0614 It's compiled without it by Clang / GCC, but not MSVC++ --- lib/serializer/CTypeList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serializer/CTypeList.h b/lib/serializer/CTypeList.h index 62e800207..d09bc0ce5 100644 --- a/lib/serializer/CTypeList.h +++ b/lib/serializer/CTypeList.h @@ -200,7 +200,7 @@ extern DLL_LINKAGE CTypeList typeList; /// Wrapper over CTypeList. Allows execution of templated class T for any type /// that was resgistered for this applier template -class CApplier +class CApplier : boost::noncopyable { std::map> apps; From a1cca251ecd571fd1c8d5680f8f182ca3c111076 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 30 Oct 2016 11:42:53 +0200 Subject: [PATCH 10/11] Possible fix for selection of incorrect method for serialization of vector --- lib/serializer/BinaryDeserializer.h | 4 ++-- lib/serializer/BinarySerializer.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 8c1a43329..7b082db33 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -235,8 +235,8 @@ public: range::copy(convData, data.begin()); } - template - void load(std::vector &data, typename std::enable_if < !std::is_same::value, int >::type = 0) + template ::value, int >::type = 0> + void load(std::vector &data) { READ_CHECK_U32(length); data.resize(length); diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index 0873f3eef..74949607c 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -250,7 +250,7 @@ public: T *internalPtr = data.get(); save(internalPtr); } - template + template ::value, int >::type = 0> void save(const std::vector &data) { ui32 length = data.size(); From e79c522c4b254695ea504f86523290a4639fd89d Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sun, 30 Oct 2016 15:14:59 +0300 Subject: [PATCH 11/11] Serializer refactoring: fix FileStream usage for game loading Miss this one rebase. Without this one non-English characters in paths to saves cause issues. --- lib/serializer/BinaryDeserializer.cpp | 9 +++++---- lib/serializer/BinaryDeserializer.h | 4 ++-- lib/serializer/BinarySerializer.cpp | 7 ++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/serializer/BinaryDeserializer.cpp b/lib/serializer/BinaryDeserializer.cpp index 68b0f3008..ce47c7c43 100644 --- a/lib/serializer/BinaryDeserializer.cpp +++ b/lib/serializer/BinaryDeserializer.cpp @@ -15,7 +15,8 @@ extern template void registerTypes(BinaryDeserializer & s); -CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/): serializer(this) +CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/) + : serializer(this) { registerTypes(serializer); openNextFile(fname, minimalVersion); @@ -39,7 +40,7 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV try { fName = fname.string(); - sfile = make_unique(fname, std::ios::binary); + sfile = make_unique(fname, std::ios::in | std::ios::binary); sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway if(!(*sfile)) @@ -55,7 +56,7 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV if(serializer.fileVersion < minimalVersion) THROW_FORMAT("Error: too old file format (%s)!", fName); - if(serializer.fileVersion > SERIALIZATION_VERSION ) + if(serializer.fileVersion > SERIALIZATION_VERSION) { logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % SERIALIZATION_VERSION % fName; @@ -95,7 +96,7 @@ void CLoadFile::clear() serializer.fileVersion = 0; } -void CLoadFile::checkMagicBytes( const std::string &text ) +void CLoadFile::checkMagicBytes(const std::string &text) { std::string loaded = text; read((void*)loaded.data(), text.length()); diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 7b082db33..447ba54da 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -514,9 +514,9 @@ public: BinaryDeserializer serializer; std::string fName; - std::unique_ptr sfile; + std::unique_ptr sfile; - CLoadFile(const boost::filesystem::path & fname, int minimalVersion = SERIALIZATION_VERSION ); //throws! + CLoadFile(const boost::filesystem::path & fname, int minimalVersion = SERIALIZATION_VERSION); //throws! ~CLoadFile(); int read(void * data, unsigned size) override; //throws! diff --git a/lib/serializer/BinarySerializer.cpp b/lib/serializer/BinarySerializer.cpp index 1c0a8afff..b5b7276ec 100644 --- a/lib/serializer/BinarySerializer.cpp +++ b/lib/serializer/BinarySerializer.cpp @@ -15,7 +15,8 @@ extern template void registerTypes(BinarySerializer & s); -CSaveFile::CSaveFile(const boost::filesystem::path &fname): serializer(this) +CSaveFile::CSaveFile(const boost::filesystem::path &fname) + : serializer(this) { registerTypes(serializer); openNextFile(fname); @@ -25,7 +26,7 @@ CSaveFile::~CSaveFile() { } -int CSaveFile::write( const void * data, unsigned size ) +int CSaveFile::write(const void * data, unsigned size) { sfile->write((char *)data,size); return size; @@ -68,7 +69,7 @@ void CSaveFile::clear() sfile = nullptr; } -void CSaveFile::putMagicBytes( const std::string &text ) +void CSaveFile::putMagicBytes(const std::string &text) { write(text.c_str(), text.length()); }