What is the serialization about

From Rigs of Rods Wiki

Jump to:navigation, search

THIS PAGE IS OBSOLETE

The serializer is the component in RoR that can load beam models (trucks, airplanes, ...) from their truck file format (and later other formats) into the game. It has by design support for saving back on the fly.


Why do we need it

the previous truck loading implementation was in a single method. Their drawbacks were:

  • hard to maintain (horrible code)
  • hard to extend
  • hard to verify if something is working
  • impossible to save data
  • mixmash of structure filling and 3d data loading. Impossible to do background loading.

So all these points should be better with the serializer.


General Overview

The serializer consists of two main classes:

you can find both classes in source/serializer/serializer.h.

So for example, lets take a simple section from the truck file description: Globals. In order to get parsed by the new serializer, you need to:

1. derive a new class from RoRSerializationModule

and define the initData, deserialize (truck loading), serialize (truck saving) and initResources functions.

The example class definition for the globals section:

#ifndef GLOBALSSERIALIZER_H__
#define GLOBALSSERIALIZER_H__
 
#include "string.h"
#include "RoRPrerequisites.h"
#include "BeamData.h"
#include "RoRSerializationModule.h"
#include "RoRSerializer.h"
 
class GlobalsSerializer : public RoRSerializationModule
{
public:
	GlobalsSerializer(RoRSerializer *s);
	void initData(rig_t *rig);
	int deserialize(char *line, rig_t *rig, std::string activeSection = std::string());
	int serialize(char *line, rig_t *rig);
	int initResources(Ogre::SceneManager *manager, Ogre::SceneNode *node, rig_t *rig);
};
 
#endif //GLOBALSSERIALIZER_H__

(should be defined in a file in the modules directory, i.e. modules/GlobalsSerializer.h)

2. implement that class

#include "GlobalsSerializer.h"
#include "BeamData.h"
 
#include "OgreLogManager.h"
#include "Ogre.h"
 
using namespace Ogre;
 
// format description:
// http://wiki.rigsofrods.com/pages/Truck_Description_File
 
GlobalsSerializer::GlobalsSerializer(RoRSerializer *s) : RoRSerializationModule(s)
{
	// setup the base descriptions, etc, then register ourself
	s->registerModuleSerializer(this);
	s->addSectionHandler("globals", this);
}
 
void GlobalsSerializer::initData(rig_t *rig)
{
	if(initiated) return;
	rig->globals = new globals_section_t();
	memset(rig->globals, 0, sizeof(globals_section_t));
	initiated = true;
}
 
int GlobalsSerializer::deserialize(char *line, rig_t *rig, std::string activeSection)
{
	// ignore section header
	if(!strcmp(line, "globals"))
		return 1;
 
	if(!initiated) initData(rig);
 
	// shortcuts	
	globals_section_t *g = rig->globals;
 
	return checkRes(2, sscanf(line,"%f, %f, %s", &g->truckmass, &g->loadmass, g->texname));
}
 
int GlobalsSerializer::serialize(char *line, rig_t *rig)
{
	return sprintf(line, "globals\n%f, %f, %s\n", rig->globals->truckmass, rig->globals->loadmass, rig->globals->texname);
}
 
int GlobalsSerializer::initResources(Ogre::SceneManager *manager, Ogre::SceneNode *node, rig_t *rig)
{
	return 0;
}

(should be implemented in a file in the modules directory, i.e. modules/GlobalsSerializer.cpp)

notes:

  • the sscanf line is taken from the old implementation

3. create the structures to store the data into

like those for globals:

// section structs
struct globals_section
{
	float truckmass;
	float loadmass;
	char texname[1024];
};

from BeamData.h

and

FWDCLSTRUCT(globals_section);

which extends automatically to:

typedef struct globals_section globals_section_t;

from RoRPrerequisites.h

then add this structure to the rig_t structure that represents a beam structure in the game:


struct rig
{
// ...
	// pointer to structs
	globals_section_t    *globals;
// ...
}

4. understand / implement / verify the workflow

a) a new section gets implemented (globals for us), and the module is added to the default serializer:

RoRSerializer::RoRSerializer()
{
	// register all available modules :)
	new GlobalsSerializer(this);
// ...

this class then registers itself via the constructor to the serializer (see above) and adds itself as callback for the "globals" section:

s->registerModuleSerializer(this);
s->addSectionHandler("globals", this);

b) when the serializer parses(deserializes) a new file and recognizes any sections or commands registered to that class (globals here) it will invoke the 'deserialize' function of that class.

c) the deserializer function does several things:

ignores the section header (if it does not carry information):

	// ignore section header
	if(linestr == activeSection)
		return 1;

initiated the data structures:

	if(!initiated) initData(rig);

basically, initData does create a new section struct once and resets all its data:

	if(initiated) return;
	rig->globals = new globals_section_t();
	memset(rig->globals, 0, sizeof(globals_section_t));
	initiated = true;

then, back in the deserialize function, it fills the forementioned data section with values:

	// shortcuts	
	globals_section_t *g = rig->globals;
 
	return checkRes(2, sscanf(line,"%f, %f, %s", &g->truckmass, &g->loadmass, g->texname));

The return value of deserialize is always the amount of arguments parsed from that line. 0 means an error, as no arguments parsed.

5. tell RoR how to use it

the Beam class is derived from the rig_t structure. So all you have to do is access the structure after checking for their existence. For example for props:

void Beam::updateProps()
{
	if(!props) return;
	int i;
	//the props
	for (i=0; i<props->free_prop; i++)
	{
// ...

props is actually a pointer to a props_section_t struct.

Important notes

notes for those who want to implement a new section, or port an old section to the serializer:

  • never ever do anything Ogre3d resource loading inside the deserialization. You need to split that visualization creation up and put it into the old Beam::loadTruck() method, right after loadRig(). The usage of initResources() is not recommended, please do not use it until the design of the lib is better than now. Look for the comment "// now init things that must happen here" in Beam.cpp
  • never ever memset structs that contain any non POD types. See http://stackoverflow.com/questions/146452/what-are-pod-types-in-c
  • before using another section from another section (for example if your section adds beams or nodes) make sure you init that sections data. Read up in the serializer implementation for examples. (see wheels section for example). see function RoRSerializer::getSectionModule()
  • There is a dummy module. Remove the section/command you ported from it to make the code cleaner ;)

Where to work

  • until the serialization is stable and working well, it is developed in the serialization branch
  • Things that still needs to be ported can be found here
  • look for "XXX" and "TODO" in the files.