This page has been translated automatically.
UnigineScript
The Language
Core Library
Engine Library
Node-Related Classes
GUI-Related Classes
Plugins Library
High-Level Systems
Samples
C++ API
API Reference
Integration Samples
C++ Plugins
Content Creation
Materials
Unigine Material Library
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Serialization

Unigine API allows creating of objects whose states can be saved into a binary file and later restored from it.

See also

An example can be found in <UnigineSDK>/source/samples/Api/Scripts/Serialization/ directory.

Serialization Guidelines

Saving and restoring of the object state is done by using a binary serialization mechanism of Unigine. In order to be compatible with this mechanism,a class must implement methods that allow you to store states of the class objects into the Unigine::Stream and restore them from it.

These methods can be grouped as follows:

  • Methods for saving/restoring of states. The states are used for objects created and handled purely in UnigineScript. It means, such object implemented on the C++ side is created in script via new operator and deleted via delete. It is not passed between UnigineScript and any other system.
    • saveState() - saves a state of an object created on the UnigineScript side.
    • restoreState() — restores a state of an object created on the UnigineScript side. This function implies that a class has a default constructor that creates an empty object.
  • Methods for saving/restoring of pointers. The pointers are used for objects that are created in a C++ part of the application and will be deleted there as well. The script receives it, but is not responsible for managing them. For example, this is the case when a C++ function that creates an object is called from the script.
    • savePointer() — a static method which is used to save a state of an object created on C++ side and handled by a script.
    • restorePointer() — a static method which is used to restore a state of an object created on C++ side and handled by a script.

If an object is going to be created in script, as well as created on C++ side, all four functions should be implemented.

Notice
If the above methods are implemented incorrectly, the objects will not be saved, and memory-related errors will appear.

Step 1. Implement Object Saving and Restoring

By default, the following implementation is used (see the include/UnigineInterpreter.h header file):

Source code (C++)
// functor to save a state of an object constructed in scripts
template <class Class>
void ExternClassSaveState(const StreamPtr &stream,Class *object) {
	object->saveState(stream);
}

// functor to restore a state of an object constructed in scripts
template <class Class>
Class *ExternClassRestoreState(const StreamPtr &stream) {
	Class *object = new Class();
	object->restoreState(stream);
	return object;
}

// functor to save a state of an object created on C++ side and handled by a script
template <class Class>
	void ExternClassSavePointer(const StreamPtr &stream,Class *object) {
	Class::savePointer(stream,object);
}

// functor to restore a state of an object created on C++ side and handled by a script
template <class Class>
Class *ExternClassRestorePointer(const StreamPtr &stream) {
	return Class::restorePointer(stream);
}

Step 2. Export Class

You need to export classes whose instances are going to be serialized. One of the following functions can be used for that:

  • MakeExternClass() function. Instances of classes exported by using this function are non-restorable, that is, they should be manually re-created. If you try to restore an instance of such the class, this instance will be restored to null.
  • MakeExternClassSaveRestoreState() function, which allows you to save and restore instances created within UnigineScript.
  • MakeExternClassSaveRestorePointer() function, which allows you to save and restore objects that were created in C++ code and exported into UnigineScript.
  • MakeExternClassSaveRestoreStatePointer() function, which combines two previous possibilities, that is, objects created on both the UnigineScript and C++ sides can be saved and restored.

Changing Serialization Behaviour

After exporting, it is still possible to change serialization behavior in run-time. For that you can use two UnigineScript functions.

Notice
It is completely safe only if both state and pointer saving/restoring is implemented.
  • class_append() attaches the object to the script that will handle it (save, restore and delete it).
  • class_remove() removes the object from the script.
Notice
UnigineScript does not destroy objects that were created on C++ side and were not appended to the script.

Serialization Example

Here is an example of exporting a C++ class that fully supports serialization into UnigineScript.

C++ Side

Source code (C++)
#include <Unigine.h>
#include <UnigineInterpreter.h>

/*
 */
using namespace Unigine;

/******************************************************************************\
*
* User defined class
*
\******************************************************************************/

/*
 */
class MyObject : public Base {
		
	public:
		
		MyObject() : mass(0.0f) {
			Log::warning("MyObject::MyObject(): called\n");
		}
		MyObject(const vec3 &size,float mass) : size(size), mass(mass) {
			Log::warning("MyObject::MyObject((%g,%g,%g),%g): called\n",size.x,size.y,size.z,mass);
		}
		~MyObject() {
			Log::warning("MyObject::~MyObject(): called\n");
		}
		
		// size
		void setSize(const vec3 &s) {
			Log::warning("MyObject::setSize((%g,%g,%g)): called\n",s.x,s.y,s.z);
			size = s;
		}
		const vec3 &getSize() const {
			return size;
		}
		
		// mass
		void setMass(float m) {
			Log::warning("MyObject::setMass(%g): called\n",m);
			mass = m;
		}
		float getMass() const {
			return mass;
		}
		
		// save state
		void saveState(StreamPtr &stream) const {
			Log::warning("MyObject::saveState(): called\n");
			stream->writeVec3(size);
			stream->writeFloat(mass);
		}
		
		// restore state
		void restoreState(StreamPtr &stream) {
			Log::warning("MyObject::restoreState(): called\n");
			size = stream->readVec3();
			mass = stream->readFloat();
		}
		
		// save pointer
		static void savePointer(StreamPtr &stream,MyObject *object) {
			Log::warning("MyObject::savePointer(): called\n");
			stream->writeVec3(object->size);
			stream->writeFloat(object->mass);
		}
		
		// restore pointer
		static MyObject *restorePointer(StreamPtr &stream) {
			MyObject *object = new MyObject();
			Log::warning("MyObject::restorePointer(): called\n");
			object->size = stream->readVec3();
			object->mass = stream->readFloat();
			return object;
		}
		
	private:
		
		vec3 size;
		float mass;
};

/*
 */
MyObject *MakeMyObject(const vec3 &size,float mass) {
	return new MyObject(size,mass);
}

void DeleteMyObject(MyObject *object) {
	delete object;
}

/******************************************************************************\
*
* Main
*
\******************************************************************************/

/*
 */
int main(int argc,char **argv) {
	
	// export class with serialization
	ExternClass<MyObject> *my_object = MakeExternClassSaveRestoreStatePointer<MyObject>();
	my_object->addConstructor<const vec3&,float>();
	my_object->addFunction("setSize",&MyObject::setSize);
	my_object->addFunction("getSize",&MyObject::getSize);
	my_object->addFunction("setMass",&MyObject::setMass);
	my_object->addFunction("getMass",&MyObject::getMass);
	Interpreter::addExternClass("MyObject",my_object);
	
	// export functions
	Interpreter::addExternFunction("MakeMyObject",MakeExternFunction(&MakeMyObject));
	Interpreter::addExternFunction("DeleteMyObject",MakeExternFunction(&DeleteMyObject));
	
	// init engine
	Engine *engine = Engine::init(UNIGINE_VERSION,argc,argv);
	
	// enter main loop
	engine->main();
	
	// shutdown engine
	Engine::shutdown();
	
	return 0;
}

Unigine Script Side

And here is how the exported class can be used in UnigineScript (see the description of the yield control statement for better understanding of the example):

Source code (UnigineScript)
/*
 */
MyObject object_0;
MyObject object_1;

/*
 */
void object_info(MyObject object) {
	
	// object parameters
	vec3 size = object.getSize();
	float mass = object.getMass();
	
	log.message("size is: (%g,%g,%g), mass is: %g\n",size.x,size.y,size.z,mass);
}

/*
 */
int init() {
	
	/////////////////////////////////
	
	log.message("\n");
	
	// make script constructed object
	object_0 = new MyObject(vec3(1.0f,2.0f,3.0f),10.0f);
	
	// make extern constructed object
	object_1 = MakeMyObject(vec3(4.0f,5.0f,6.0f),100.0f);
	
	/////////////////////////////////
	
	// show console
	engine.console.setActivity(1);
	
	return 1;
}

/*
 */
int shutdown() {
	
	// delete external constructed object
	DeleteMyObject(object_1);
	
	return 1;
}

/*
 */
int update() {
	
	/////////////////////////////////
	// first update
	/////////////////////////////////
	
	log.message("\n");
	
	// parameters
	object_info(object_0);
	object_info(object_1);
	
	yield 1;
	
	/////////////////////////////////
	// second update
	/////////////////////////////////
	
	log.message("\n");
	
	// save and restore world state
	engine.console.run("state_save && state_restore");
	
	yield 1;
	
	/////////////////////////////////
	// third update
	/////////////////////////////////
	
	log.message("\n");
	
	// parameters
	object_info(object_0);
	object_info(object_1);
	
	yield 1;
	
	/////////////////////////////////
	
	return 1;
}

Output

The following result will be printed into the console:

Output
MyObject::MyObject((1,2,3),10): called
MyObject::MyObject((4,5,6),100): called

size is: (1,2,3), mass is: 10
size is: (4,5,6), mass is: 100

Unigine~# state_save && state_restore
Saving "data/serialization" world state to "save/quicksave.save" file

MyObject::saveState(): called
MyObject::savePointer(): called
MyObject::~MyObject(): called
MyObject::~MyObject(): called

Restoring "data/serialization" world state from "save/quicksave.save" file

MyObject::MyObject(): called
MyObject::restoreState(): called
MyObject::MyObject(): called
MyObject::restorePointer(): called

size is: (1,2,3), mass is: 10
size is: (4,5,6), mass is: 100

Unigine~# quit

MyObject::~MyObject(): called
MyObject::~MyObject(): called
Last update: 2017-07-03
Build: ()