Object serialization between static and dynamic languages

Among the advanced features provided in the SmartFoxServer SDK is the ability to exchange any class instance between client and server with minimal coding intervention. This is particularly useful for statically typed languages such as Java, C# and Actionscript, while dynamic languages such as Javascript are not directly supported.

In this article we’re going to discuss an alternative way to implement custom serialization for classes that are needed from both client and server side to implement our game logic.

If you’re not entirely familiar with the topic of class serialization in SFS2X we recommend reading this article from our documentation

» The Basics

SmartFoxServer provides two fundamental classes, SFSObject and SFSArray, on both client and server side that are used to exchange data between the two end points.

SFSObject and SFSArray represent platform-neutral, high level objects that abstract the data transport between client and server. They can be used to represent data in form of a Map/Dictionary or List/Array respectively, supporting multiple data types and nested objects to create complex data structures.

Both classes also support two generic getClass()/putClass() methods that allow to serialize custom class instances provided they implement a specific interface. This is particularly useful when we need to exchange specific game entities such as characters, NPCs, weapons etc. between client and server (if you need more info on this please refer to the documentation here).

As alluded in this article intro, not all languages are supported: for example Javascript with its dynamic structure and classless OOP foundations doesn’t work well with class serialization.
So what options do we have if we need to exchange custom objects between client and servers?

» Custom serialization

One simple and elegant solution is to add a couple of specialized methods to our game entities, to serialize/deserialize them before we send them over the network.

To demonstrate this approach we can jump straight into an example: imagine we have a weapon entity that we need to send from server to client and viceversa.

public class GammaRayGun
{
	private int reloadTime;
	private int magazineSize;
	private double damagePerSecond;
	private double accuracy;
	
	public GammaRayGun()
	{
	
	}
	
	// ...
	// getters and setters
	// ...
	
	public ISFSObject toSFSObject()
	{
		ISFSObject sfso = new SFSObject();
		sfso.putInt("rt", reloadTime);
		sfso.putInt("ms", magazineSize);
		sfso.putDouble("ds", damagePerSecond);
		sfso.putDouble("ac", accuracy);
		
		return sfso;
	}
	
	public static GammaRayGun fromSFSObject(ISFSObject sfso)
	{
		GammaRayGun gun = new GammaRayGun();
		
		gun.setReloadTime( sfso.getInt("rt") );
		gun.setMagazineSize( sfso.getInt("ms") );
		gun.setDamagePerSecond( sfso.getDouble("ds") );
		gun.setAccuracy( sfso.getDouble("ac") );		
		
		return gun;
	}
}

This is the server side implementation in Java. The toSFSObject() method wraps the properties we need to exchange with the client in an SFSObject ready to be sent alongside other data, if needed. The fromSFSObject() works in the opposite direction, by taking an entity’s serialized data and rebuilding the original object.

Now we can take a look at the relative implementation in Javascript:

var GammaRayGun = function() 
{
	this.reloadTime = 0
	this.magazineSize = 0
	this.damagePerSecond = 0
	this.accuracy = 0
}

GammaRayGun.prototype.toSFSObject = function()
{
	var sfso = new SFS2X.SFSObject()
	sfso.putInt("rt", this.reloadTime)
	sfso.putInt("ms", this.magazineSize)
	sfso.putDouble("ds", this.damagePerSecond)
	sfso.putDouble("ac", this.accuracy)
	
	return sfso
}

GammaRayGun.fromSFSObject = function(sfso)
{
	var gun = new GammaRayGun()

	gun.reloadTime = sfso.getInt("rt")
	gun.magazineSize = sfso.getInt("ms")
	gun.damagePerSecond = sfso.getDouble("ds")
	gun.accuracy = sfso.getDouble("ac")

	return gun
}

The code mirrors the server side version in Javascript, using the relative SmartFoxServer 2X JS API.

Even if it requires a bit of extra code, this solutions allows you to fine tune which field should be transmitted and which data types should be used by the network serializer. In our example we are using double for floating point values, but if you need less precision you could use float instead and save some bandwidth. Similarly with the int values you may opt for a smaller data type, such as short or byte, and trim the packet size to a minimum.

Finally you may also consider this approach for other client-side languages that fully support class serialization. The reason for this is that class serialization requires extra data to be sent for each entity to identify its class, and it isn’t as flexible in fine tuning each data type. If small packet size is a priority in your game, this approach will yield better overall results.

In conclusion you can experiment with both strategies if you’re using statically typed languages on the client side, or use the alternative approach we have outlined here, if using a language not supported by our class serialization.

If you want to discuss this topic further or you have other questions you can reach us in our support forums.