1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Add files for rebased serializer refactoring done by Ivan

This commit is contained in:
Ivan Savenko 2016-09-10 03:28:11 +03:00 committed by Arseniy Shestakov
parent 15b4774076
commit 3d1b1f4ba8
14 changed files with 2254 additions and 0 deletions

View File

@ -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>(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<boost::filesystem::ifstream>(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!");
}

View File

@ -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 <boost/mpl/for_each.hpp>
#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<typename Variant, typename Source>
struct VariantLoaderHelper
{
Source & source;
std::vector<std::function<Variant()>> funcs;
VariantLoaderHelper(Source & source):
source(source)
{
boost::mpl::for_each<typename Variant::types>(std::ref(*this));
}
template<typename Type>
void operator()(Type)
{
funcs.push_back([&]() -> Variant
{
Type obj;
source.load(obj);
return Variant(obj);
});
}
};
template<typename Ser,typename T>
struct LoadIfStackInstance
{
static bool invoke(Ser &s, T &data)
{
return false;
}
};
template<typename Ser>
struct LoadIfStackInstance<Ser, CStackInstance *>
{
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<CGHeroInstance *>(armedObj);
assert(hero);
assert(hero->commander);
data = hero->commander;
}
return true;
}
};
template <typename T, typename Enable = void>
struct ClassObjectCreator
{
static T *invoke()
{
static_assert(!std::is_abstract<T>::value, "Cannot call new upon abstract classes!");
return new T();
}
};
template<typename T>
struct ClassObjectCreator<T, typename std::enable_if<std::is_abstract<T>::value>::type>
{
static T *invoke()
{
throw std::runtime_error("Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std::string(typeid(T).name()));
}
};
#define READ_CHECK_U32(x) \
ui32 length; \
load(length); \
if(length > 500000) \
{ \
logGlobal->warnStream() << "Warning: very big length: " << length;\
reader->reportState(logGlobal); \
};
template <typename T> 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<typename T> static CBasicPointerLoader *getApplier(const T * t=nullptr)
{
return new CPointerLoader<T>();
}
};
template <typename T> 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<BinaryDeserializer&>(ar);
T *&ptr = *static_cast<T**>(data);
//create new object under pointer
typedef typename std::remove_pointer<T>::type npT;
ptr = ClassObjectCreator<npT>::invoke(); //does new npT or throws for abstract classes
s.ptrAllocated(ptr, pid);
//T is most derived known type, it's time to call actual serialize
ptr->serialize(s,s.fileVersion);
return &typeid(T);
}
};
CApplier<CBasicPointerLoader> 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<ui32, void*> loadedPointers;
std::map<ui32, const std::type_info*> loadedPointersTypes;
std::map<const void*, boost::any> loadedSharedPointers;
bool smartPointerSerialization;
bool saving;
BinaryDeserializer(IBinaryReader * r): CLoaderBase(r)
{
saving = false;
fileVersion = 0;
smartPointerSerialization = true;
reverseEndianess = false;
}
template<class T>
BinaryDeserializer & operator&(T & t)
{
this->load(t);
return * this;
}
template < class T, typename std::enable_if < std::is_fundamental<T>::value && !std::is_same<T, bool>::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<BinaryDeserializer, T>::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<T>::type nonConstT;
nonConstT &hlp = const_cast<nonConstT&>(data);
hlp.serialize(*this,fileVersion);
}
template < typename T, typename std::enable_if < std::is_array<T>::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<T>::value, int >::type = 0 >
void load(T &data)
{
si32 read;
load( read );
data = static_cast<T>(read);
}
template < typename T, typename std::enable_if < std::is_same<T, bool>::value, int >::type = 0 >
void load(T &data)
{
ui8 read;
load( read );
data = static_cast<bool>(read);
}
template < typename T, typename std::enable_if < std::is_same<T, std::vector<bool> >::value, int >::type = 0 >
void load(T & data)
{
std::vector<ui8> convData;
load(convData);
convData.resize(data.size());
range::copy(convData, data.begin());
}
template <typename T>
void load(std::vector<T> &data, typename std::enable_if < !std::is_same<T, bool >::value, int >::type = 0)
{
READ_CHECK_U32(length);
data.resize(length);
for(ui32 i=0;i<length;i++)
load( data[i]);
}
template < typename T, typename std::enable_if < std::is_pointer<T>::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<typename std::remove_pointer<T>::type>::type TObjectType; //eg: const CGHeroInstance * => CGHeroInstance
typedef typename VectorizedTypeFor<TObjectType>::type VType; //eg: CGHeroInstance -> CGobjectInstance
typedef typename VectorizedIDType<TObjectType>::type IDType;
if(const auto *info = reader->getVectorizedTypeInfo<VType, IDType>())
{
IDType id;
load(id);
if(id != IDType(-1))
{
data = static_cast<T>(reader->getVectorItemFromId<VType, IDType>(*info, id));
return;
}
}
}
if(reader->sendStackInstanceByIds)
{
bool gotLoaded = LoadIfStackInstance<BinaryDeserializer,T>::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<ui32, void*>::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<T>(typeList.castRaw(i->second, loadedPointersTypes.at(pid), &typeid(typename std::remove_const<typename std::remove_pointer<T>::type>::type)));
return;
}
}
//get type id
ui16 tid;
load( tid );
if(!tid)
{
typedef typename std::remove_pointer<T>::type npT;
typedef typename std::remove_const<npT>::type ncpT;
data = ClassObjectCreator<ncpT>::invoke();
ptrAllocated(data, pid);
load(*data);
}
else
{
auto typeInfo = applier.getApplier(tid)->loadPtr(*this,&data, pid);
data = reinterpret_cast<T>(typeList.castRaw((void*)data, typeInfo, &typeid(typename std::remove_const<typename std::remove_pointer<T>::type>::type)));
}
}
template <typename T>
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<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
applier.registerType(b, d);
}
template <typename T>
void load(std::shared_ptr<T> &data)
{
typedef typename std::remove_const<T>::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<T>();
if(*actualType == *typeWeNeedToReturn)
{
// No casting needed, just unpack already stored shared_ptr and return it
data = boost::any_cast<std::shared_ptr<T>>(itr->second);
}
else
{
// We need to perform series of casts
auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn);
data = boost::any_cast<std::shared_ptr<T>>(ret);
}
}
catch(std::exception &e)
{
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<T>).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<NonConstT>(internalPtr);
data = hlp; //possibly adds const
loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
}
}
else
data.reset();
}
template <typename T>
void load(std::unique_ptr<T> &data)
{
T *internalPtr;
load( internalPtr );
data.reset(internalPtr);
}
template <typename T, size_t N>
void load(std::array<T, N> &data)
{
for(ui32 i = 0; i < N; i++)
load( data[i] );
}
template <typename T>
void load(std::set<T> &data)
{
READ_CHECK_U32(length);
data.clear();
T ins;
for(ui32 i=0;i<length;i++)
{
load( ins );
data.insert(ins);
}
}
template <typename T, typename U>
void load(std::unordered_set<T, U> &data)
{
READ_CHECK_U32(length);
data.clear();
T ins;
for(ui32 i=0;i<length;i++)
{
load(ins);
data.insert(ins);
}
}
template <typename T>
void load(std::list<T> &data)
{
READ_CHECK_U32(length);
data.clear();
T ins;
for(ui32 i=0;i<length;i++)
{
load(ins);
data.push_back(ins);
}
}
template <typename T1, typename T2>
void load(std::pair<T1,T2> &data)
{
load(data.first);
load(data.second);
}
template <typename T1, typename T2>
void load(std::map<T1,T2> &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<T1, T2>(std::move(key), std::move(value)));
}
}
template <typename T1, typename T2>
void load(std::multimap<T1, T2> &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<T1, T2>(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 <BOOST_VARIANT_ENUM_PARAMS(typename T)>
void load(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> &data)
{
typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> TVariant;
VariantLoaderHelper<TVariant, BinaryDeserializer> loader(*this);
si32 which;
load( which );
assert(which < loader.funcs.size());
data = loader.funcs.at(which)();
}
template <typename T>
void load(boost::optional<T> & data)
{
ui8 present;
load( present );
if(present)
{
T t;
load(t);
}
else
{
data = boost::optional<T>();
}
}
};
class DLL_LINKAGE CLoadFile : public IBinaryReader
{
public:
BinaryDeserializer serializer;
std::string fName;
std::unique_ptr<boost::filesystem::ifstream> 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<class T>
CLoadFile & operator>>(T &t)
{
serializer & t;
return * this;
}
};

View File

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

View File

@ -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 <typename Handler>
struct VariantVisitorSaver : boost::static_visitor<>
{
Handler &h;
VariantVisitorSaver(Handler &H):h(H)
{
}
template <typename T>
void operator()(const T &t)
{
h & t;
}
};
template<typename Ser,typename T>
struct SaveIfStackInstance
{
static bool invoke(Ser &s, const T &data)
{
return false;
}
};
template<typename Ser>
struct SaveIfStackInstance<Ser, CStackInstance *>
{
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 <typename T> class CPointerSaver;
class CBasicPointerSaver
{
public:
virtual void savePtr(CSaverBase &ar, const void *data) const =0;
virtual ~CBasicPointerSaver(){}
template<typename T> static CBasicPointerSaver *getApplier(const T * t=nullptr)
{
return new CPointerSaver<T>();
}
};
template <typename T>
class CPointerSaver : public CBasicPointerSaver
{
public:
void savePtr(CSaverBase &ar, const void *data) const override
{
BinarySerializer &s = static_cast<BinarySerializer&>(ar);
const T *ptr = static_cast<const T*>(data);
//T is most derived known type, it's time to call actual serialize
const_cast<T*>(ptr)->serialize(s,version);
}
};
CApplier<CBasicPointerSaver> applier;
public:
std::map<const void*, ui32> savedPointers;
bool smartPointerSerialization;
bool saving;
BinarySerializer(IBinaryWriter * w): CSaverBase(w)
{
saving=true;
smartPointerSerialization = true;
}
template<typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
applier.registerType(b, d);
}
template<class T>
BinarySerializer & operator&(const T & t)
{
this->save(t);
return * this;
}
template < typename T, typename std::enable_if < std::is_same<T, bool>::value, int >::type = 0 >
void save(const T &data)
{
ui8 writ = static_cast<ui8>(data);
save(writ);
}
template < typename T, typename std::enable_if < std::is_same<T, std::vector<bool> >::value, int >::type = 0 >
void save(const T &data)
{
std::vector<ui8> convData;
std::copy(data.begin(), data.end(), std::back_inserter(convData));
save(convData);
}
template < class T, typename std::enable_if < std::is_fundamental<T>::value && !std::is_same<T, bool>::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<T>::value, int >::type = 0 >
void save(const T &data)
{
si32 writ = static_cast<si32>(data);
*this & writ;
}
template < typename T, typename std::enable_if < std::is_array<T>::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<T>::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<typename std::remove_pointer<T>::type>::type TObjectType;
typedef typename VectorizedTypeFor<TObjectType>::type VType;
typedef typename VectorizedIDType<TObjectType>::type IDType;
if(const auto *info = writer->getVectorizedTypeInfo<VType, IDType>())
{
IDType id = writer->getIdFromVectorItem<VType>(*info, data);
save(id);
if(id != IDType(-1)) //vector id is enough
return;
}
}
if(writer->sendStackInstanceByIds)
{
const bool gotSaved = SaveIfStackInstance<BinarySerializer,T>::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<const void*,ui32>::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<BinarySerializer, T>::value, int >::type = 0 >
void save(const T &data)
{
const_cast<T&>(data).serialize(*this,version);
}
template <typename T>
void save(const std::shared_ptr<T> &data)
{
T *internalPtr = data.get();
save(internalPtr);
}
template <typename T>
void save(const std::unique_ptr<T> &data)
{
T *internalPtr = data.get();
save(internalPtr);
}
template <typename T>
void save(const std::vector<T> &data)
{
ui32 length = data.size();
*this & length;
for(ui32 i=0;i<length;i++)
save(data[i]);
}
template <typename T, size_t N>
void save(const std::array<T, N> &data)
{
for(ui32 i=0; i < N; i++)
save(data[i]);
}
template <typename T>
void save(const std::set<T> &data)
{
std::set<T> &d = const_cast<std::set<T> &>(data);
ui32 length = d.size();
save(length);
for(typename std::set<T>::iterator i=d.begin();i!=d.end();i++)
save(*i);
}
template <typename T, typename U>
void save(const std::unordered_set<T, U> &data)
{
std::unordered_set<T, U> &d = const_cast<std::unordered_set<T, U> &>(data);
ui32 length = d.size();
*this & length;
for(typename std::unordered_set<T, U>::iterator i=d.begin();i!=d.end();i++)
save(*i);
}
template <typename T>
void save(const std::list<T> &data)
{
std::list<T> &d = const_cast<std::list<T> &>(data);
ui32 length = d.size();
*this & length;
for(typename std::list<T>::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 <typename T1, typename T2>
void save(const std::pair<T1,T2> &data)
{
save(data.first);
save(data.second);
}
template <typename T1, typename T2>
void save(const std::map<T1,T2> &data)
{
*this & ui32(data.size());
for(typename std::map<T1,T2>::const_iterator i=data.begin();i!=data.end();i++)
{
save(i->first);
save(i->second);
}
}
template <typename T1, typename T2>
void save(const std::multimap<T1, T2> &data)
{
*this & ui32(data.size());
for(typename std::map<T1, T2>::const_iterator i = data.begin(); i != data.end(); i++)
{
save(i->first);
save(i->second);
}
}
template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
void save(const boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> &data)
{
si32 which = data.which();
save(which);
VariantVisitorSaver<BinarySerializer> visitor(*this);
boost::apply_visitor(visitor, data);
}
template <typename T>
void save(const boost::optional<T> &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<FileStream> 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<class T>
CSaveFile & operator<<(const T &t)
{
serializer & t;
return * this;
}
};

View File

@ -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<CLoadFile>(primaryFileName, minimalVersion);
controlFile = make_unique<CLoadFile>(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<ui8> 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<CLoadFile> 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);
}

View File

@ -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<CLoadFile> 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<CLoadFile> decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore
};

View File

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

View File

@ -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<ui8> 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 <typename T>
static std::unique_ptr<T> deepCopy(const T &data)
{
CMemorySerializer mem;
mem.oser & &data;
std::unique_ptr<T> ret;
mem.iser & ret;
return ret;
}
};

View File

@ -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<CGObjectInstance, ObjectInstanceID>(&gs->map->objects,
[](const CGObjectInstance &obj){ return obj.id; });
registerVectoredType<CHero, HeroTypeID>(&lib->heroh->heroes,
[](const CHero &h){ return h.ID; });
registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->map->allHeroes,
[](const CGHeroInstance &h){ return h.type->ID; });
registerVectoredType<CCreature, CreatureID>(&lib->creh->creatures,
[](const CCreature &cre){ return cre.idNumber; });
registerVectoredType<CArtifact, ArtifactID>(&lib->arth->artifacts,
[](const CArtifact &art){ return art.id; });
registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
[](const CArtifactInstance &artInst){ return artInst.id; });
registerVectoredType<CQuest, si32>(&gs->map->quests,
[](const CQuest &q){ return q.qid; });
smartVectorMembersSerialization = true;
}

View File

@ -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 <typename ObjType, typename IdType>
struct VectorizedObjectInfo
{
const std::vector<ConstTransitivePtr<ObjType> > *vector; //pointer to the appropriate vector
std::function<IdType(const ObjType &)> idRetriever;
VectorizedObjectInfo(const std::vector< ConstTransitivePtr<ObjType> > *Vector, std::function<IdType(const ObjType &)> IdGetter)
:vector(Vector), idRetriever(IdGetter)
{
}
};
/// Base class for serializers capable of reading or writing data
class DLL_LINKAGE CSerializer
{
template<typename T>
static si32 idToNumber(const T &t, typename boost::enable_if<boost::is_convertible<T,si32> >::type * dummy = 0)
{
return t;
}
template<typename T, typename NT>
static NT idToNumber(const BaseForID<T, NT> &t)
{
return t.getNum();
}
template <typename T, typename U>
void registerVectoredType(const std::vector<T*> *Vector, const std::function<U(const T&)> &idRetriever)
{
vectors[&typeid(T)] = VectorizedObjectInfo<T, U>(Vector, idRetriever);
}
template <typename T, typename U>
void registerVectoredType(const std::vector<ConstTransitivePtr<T> > *Vector, const std::function<U(const T&)> &idRetriever)
{
vectors[&typeid(T)] = VectorizedObjectInfo<T, U>(Vector, idRetriever);
}
typedef std::map<const std::type_info *, boost::any, TypeComparer> 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 <typename T, typename U>
const VectorizedObjectInfo<T, U> *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<T, U>));
VectorizedObjectInfo<T, U> *ret = &(boost::any_cast<VectorizedObjectInfo<T, U>&>(i->second));
return ret;
}
}
template <typename T, typename U>
T* getVectorItemFromId(const VectorizedObjectInfo<T, U> &oInfo, U id) const
{
si32 idAsNumber = idToNumber(id);
assert(oInfo.vector);
assert(static_cast<si32>(oInfo.vector->size()) > idAsNumber);
return const_cast<T*>((*oInfo.vector)[idAsNumber].get());
}
template <typename T, typename U>
U getIdFromVectorItem(const VectorizedObjectInfo<T, U> &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<class S, class T>
struct is_serializeable
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U * data, S* arg1 = 0,
typename std::enable_if<std::is_void<
decltype(data->serialize(*arg1, int(0)))
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(is_serializeable::test((typename std::remove_reference<typename std::remove_cv<T>::type>::type*)0));
};
template <typename T> //metafunction returning CGObjectInstance if T is its derivate or T elsewise
struct VectorizedTypeFor
{
typedef typename
//if
boost::mpl::eval_if<std::is_same<CGHeroInstance,T>,
boost::mpl::identity<CGHeroInstance>,
//else if
boost::mpl::eval_if<std::is_base_of<CGObjectInstance,T>,
boost::mpl::identity<CGObjectInstance>,
//else
boost::mpl::identity<T>
> >::type type;
};
template <typename U>
struct VectorizedIDType
{
typedef typename
//if
boost::mpl::eval_if<std::is_same<CArtifact,U>,
boost::mpl::identity<ArtifactID>,
//else if
boost::mpl::eval_if<std::is_same<CCreature,U>,
boost::mpl::identity<CreatureID>,
//else if
boost::mpl::eval_if<std::is_same<CHero,U>,
boost::mpl::identity<HeroTypeID>,
//else if
boost::mpl::eval_if<std::is_same<CArtifactInstance,U>,
boost::mpl::identity<ArtifactInstanceID>,
//else if
boost::mpl::eval_if<std::is_same<CGHeroInstance,U>,
boost::mpl::identity<HeroTypeID>,
//else if
boost::mpl::eval_if<std::is_base_of<CGObjectInstance,U>,
boost::mpl::identity<ObjectInstanceID>,
//else
boost::mpl::identity<si32>
> > > > > >::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;
};

View File

@ -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>(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<TypeDescriptor>();
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::TypeInfoPtr> CTypeList::castSequence(TypeInfoPtr from, TypeInfoPtr to) const
{
if(!strcmp(from->name, to->name))
return std::vector<CTypeList::TypeInfoPtr>();
// Perform a simple BFS in the class hierarchy.
auto BFS = [&](bool upcast)
{
std::map<TypeInfoPtr, TypeInfoPtr> previous;
std::queue<TypeInfoPtr> q;
q.push(to);
while(q.size())
{
auto typeNode = q.front();
q.pop();
for(auto &nodeBase : upcast ? typeNode->parents : typeNode->children)
{
if(!previous.count(nodeBase))
{
previous[nodeBase] = typeNode;
q.push(nodeBase);
}
}
}
std::vector<TypeInfoPtr> ret;
if(!previous.count(from))
return ret;
ret.push_back(from);
TypeInfoPtr ptr = from;
do
{
ptr = previous.at(ptr);
ret.push_back(ptr);
} while(ptr != to);
return ret;
};
// Try looking both up and down.
auto ret = BFS(true);
if(ret.empty())
ret = BFS(false);
if(ret.empty())
THROW_FORMAT("Cannot find relation between types %s and %s. Were they (and all classes between them) properly registered?", from->name % to->name);
return ret;
}
std::vector<CTypeList::TypeInfoPtr> CTypeList::castSequence(const std::type_info *from, const std::type_info *to) 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<CTypeList::TypeInfoPtr>();
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());
}

230
lib/serializer/CTypeList.h Normal file
View File

@ -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<From>, performs dynamic cast, returns std::shared_ptr<To>
virtual boost::any castWeakPtr(const boost::any &ptr) const = 0; // takes std::weak_ptr<From>, performs dynamic cast, returns std::weak_ptr<To>. The object under poitner must live.
//virtual boost::any castUniquePtr(const boost::any &ptr) const = 0; // takes std::unique_ptr<From>, performs dynamic cast, returns std::unique_ptr<To>
};
template <typename From, typename To>
struct PointerCaster : IPointerCaster
{
virtual boost::any castRawPtr(const boost::any &ptr) const override // takes void* pointing to From object, performs dynamic cast, returns void* pointing to To object
{
From * from = (From*)boost::any_cast<void*>(ptr);
To * ret = static_cast<To*>(from);
return (void*)ret;
}
// Helper function performing casts between smart pointers
template<typename SmartPt>
boost::any castSmartPtr(const boost::any &ptr) const
{
try
{
auto from = boost::any_cast<SmartPt>(ptr);
auto ret = std::static_pointer_cast<To>(from);
return ret;
}
catch(std::exception &e)
{
THROW_FORMAT("Failed cast %s -> %s. Given argument was %s. Error message: %s", typeid(From).name() % typeid(To).name() % ptr.type().name() % e.what());
}
}
virtual boost::any castSharedPtr(const boost::any &ptr) const override
{
return castSmartPtr<std::shared_ptr<From>>(ptr);
}
virtual boost::any castWeakPtr(const boost::any &ptr) const override
{
auto from = boost::any_cast<std::weak_ptr<From>>(ptr);
return castSmartPtr<std::shared_ptr<From>>(from.lock());
}
};
/// 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<TypeDescriptor> TypeInfoPtr;
struct TypeDescriptor
{
ui16 typeID;
const char *name;
std::vector<TypeInfoPtr> children, parents;
};
typedef boost::shared_mutex TMutex;
typedef boost::unique_lock<TMutex> TUniqueLock;
typedef boost::shared_lock<TMutex> TSharedLock;
private:
mutable TMutex mx;
std::map<const std::type_info *, TypeInfoPtr, TypeComparer> typeInfos;
std::map<std::pair<TypeInfoPtr, TypeInfoPtr>, std::unique_ptr<const IPointerCaster>> casters; //for each pair <Base, Der> we provide a caster (each registered relations creates a single entry here)
/// Returns sequence of types starting from "from" and ending on "to". Every next type is derived from the previous.
/// Throws if there is no link registered.
std::vector<TypeInfoPtr> castSequence(TypeInfoPtr from, TypeInfoPtr to) const;
std::vector<TypeInfoPtr> castSequence(const std::type_info *from, const std::type_info *to) const;
template<boost::any(IPointerCaster::*CastingFunction)(const boost::any &) const>
boost::any castHelper(boost::any inputPtr, const std::type_info *fromArg, const std::type_info *toArg) const
{
TSharedLock lock(mx);
auto typesSequence = castSequence(fromArg, toArg);
boost::any ptr = inputPtr;
for(int i = 0; i < static_cast<int>(typesSequence.size()) - 1; i++)
{
auto &from = typesSequence[i];
auto &to = typesSequence[i + 1];
auto castingPair = std::make_pair(from, to);
if(!casters.count(castingPair))
THROW_FORMAT("Cannot find caster for conversion %s -> %s which is needed to cast %s -> %s", from->name % to->name % fromArg->name() % toArg->name());
auto &caster = casters.at(castingPair);
ptr = (*caster.*CastingFunction)(ptr); //Why does unique_ptr 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 <typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
TUniqueLock lock(mx);
static_assert(std::is_base_of<Base, Derived>::value, "First registerType template parameter needs to ba a base class of the second one.");
static_assert(std::has_virtual_destructor<Base>::value, "Base class needs to have a virtual destructor.");
static_assert(!std::is_same<Base, Derived>::value, "Parameters of registerTypes should be two 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<const PointerCaster<Base, Derived>>();
casters[std::make_pair(dti, bti)] = make_unique<const PointerCaster<Derived, Base>>();
}
ui16 getTypeID(const std::type_info *type, bool throws = false) const;
template <typename T>
ui16 getTypeID(const T * t = nullptr, bool throws = false) const
{
return getTypeID(getTypeInfo(t), throws);
}
template<typename TInput>
void * castToMostDerived(const TInput * inputPtr) const
{
auto &baseType = typeid(typename std::remove_cv<TInput>::type);
auto derivedType = getTypeInfo(inputPtr);
if (strcmp(baseType.name(), derivedType->name()) == 0)
{
return const_cast<void*>(reinterpret_cast<const void*>(inputPtr));
}
return boost::any_cast<void*>(castHelper<&IPointerCaster::castRawPtr>(
const_cast<void*>(reinterpret_cast<const void*>(inputPtr)), &baseType,
derivedType));
}
template<typename TInput>
boost::any castSharedToMostDerived(const std::shared_ptr<TInput> inputPtr) const
{
auto &baseType = typeid(typename std::remove_cv<TInput>::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<void*>(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 <typename T> 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<typename T>
class CApplier
{
std::map<ui16, std::unique_ptr<T>> apps;
template<typename RegisteredType>
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<typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
typeList.registerType(b, d);
addApplier<Base>(typeList.getTypeID(b));
addApplier<Derived>(typeList.getTypeID(d));
}
};

View File

@ -0,0 +1,280 @@
#include "StdInc.h"
#include "Connection.h"
#include "registerTypes/RegisterTypes.h"
#include "mapping/CMap.h"
#include "CGameState.h"
#include <boost/asio.hpp>
/*
* 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 "<<pom;
wmx = new boost::mutex;
rmx = new boost::mutex;
handler = nullptr;
receivedStop = sendStop = false;
static int cid = 1;
connectionID = cid++;
}
CConnection::CConnection(std::string host, std::string port, std::string Name)
:iser(this), oser(this), io_service(new asio::io_service), name(Name)
{
int i;
boost::system::error_code error = asio::error::host_not_found;
socket = new tcp::socket(*io_service);
tcp::resolver resolver(*io_service);
tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host,port),error);
if(error)
{
logNetwork->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<class T>
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<boost::mutex> 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<boost::mutex> 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") <<*/ ")";
}

109
lib/serializer/Connection.h Normal file
View File

@ -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 <typename Protocol> class stream_socket_service;
template <typename Protocol,typename StreamSocketService>
class basic_stream_socket;
template <typename Protocol> class socket_acceptor_service;
template <typename Protocol,typename SocketAcceptorService>
class basic_socket_acceptor;
}
class mutex;
}
typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service<boost::asio::ip::tcp> > TSocket;
typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > 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<class T>
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<class T>
CConnection & operator>>(T &t)
{
iser & t;
return * this;
}
template<class T>
CConnection & operator<<(const T &t)
{
oser & t;
return * this;
}
};
DLL_LINKAGE std::ostream &operator<<(std::ostream &str, const CConnection &cpc);