Serializer refactoring

Hi. Right now I’m trying to figure out how serialization works and refactor Connection.h code to make it more easy to debug - SXX suspects that it has bugs lurking somewhere in it.

We do have wiki page describing serialization - wiki.vcmi.eu/index.php?title=Serialization but it mostly has high-level description of serializer. It does not explains its internals and purpose of its classes thus not helping much with debugging.

What I have already done:

  • Refactoring of CISer/COSer. Now these classes don’t have long call chains causing unmanageable stack traces in debugging. IIRC there were ~10 calls inside serializer between two calls to SomeClass::serialize(). Now in same situation there will be at most 3 internal calls (can be reduced to 2)
  • Refactor Connection.h into several single-purpose files.

What I still want to do:

  • Investigate type list to see if that structure can be simplified to avoid 1G+ memory usage by gcc and remove reliance on type_info which does not works properly with dynamic libraries - libVCMI & AI.

  • Document all classes in Connection.h

  • Provide some ways to debug saves & client-server syncronization. Possible approaches:
    ** object-specific hashes. Regulary send hashes of all game objects to or from server, in case of mismatch - investigate desync.
    ** some debug info that can be used to compare client & server saves and in case of mismatch - locate its origin.
    ** serialization as text, e.g. JSON so in case of problems save can be opened and checked manually.

Now, in order to implement any of above properly I’m going to change how our serialize() method works in objects. Currently it looks like this:

void serialize( Handler & h, const int version )
{
    h & a & b & c;
}

What I’d like to receive is:

void serialize( Visitor & v, const int version )
{
    VCMI_SERIALIZE(v, a);
    VCMI_SERIALIZE(v, b);
    VCMI_SERIALIZE(v, c);
}

While this is less compact than current code it also allows me to:
a) use it for something that is not save or load but (for example) hash calculator.
b) expand macro to v.visit( “a”, a) thus allowing to save as text in key-value form.
c) expand macro to v.visit( a, “CGHeroInstance.cpp:546”) allowing to show origin of mismatching values for saves comparator ( can be combined with “b”).

  • currently CISer & COser still have operator & and void load()/void save(). I’d like to remove operator and create unified visit() method. This will break all serialize() method but if I’m introducing macro mentioned above this is not really relevant and this will allow me to remove one more serializer call from call stack.

I’m going to upload curent code on github tomorrow(Friday). I have tested it with gcc & clang but I am not sure if MSVC will accept all my changes in template magic so it would be great if somebody with MSVC would check my branch and (if necessary) post build log so I can be sure that all my changes are OK.

@Ivan Should github.com/vcmi/vcmi/pull/132 be now consider irrelevant or I should merge it?

Still relevant. And I should have used it as base for my branch - a bit hack-ish but working type list is a better start than broken one.

Will merge it with both develop and my branch later today.

I have thought of a way to accomplish this, but it requires that all types that should be serializeable through pointers to implement some interface (ISerializeable), and all serializer objects to have a common base (ISerializer). Then it is possible to rely on virtual calls rather than type_info and chains of dynamic_cast. If you are interested, I can tell you more, and maybe also implement it.

BTW I tested this branch few days ago a lot and looks like it’s works fine at least in single player without mods. Sadly can’t test it with MSVC. :neutral_face: