{"id":522,"date":"2016-07-22T15:35:30","date_gmt":"2016-07-22T15:35:30","guid":{"rendered":"http:\/\/smartfoxserver.com\/blog\/?p=522"},"modified":"2016-07-22T15:37:15","modified_gmt":"2016-07-22T15:37:15","slug":"serialization-strategies","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/serialization-strategies\/","title":{"rendered":"Game state serialization strategies"},"content":{"rendered":"<p>In this article we&#8217;re going to analyze a few strategies to serialize game state data between client and server, and discuss the pros and cons of each approach.<!--more--><\/p>\n<h2>\u00bb Serialization basics<\/h2>\n<p>In computer terms data serialization is the process of converting data structures into a format that can be stored or transmitted over the network.\u00a0SmartFoxServer 2X abstracts this process via two main objects, <a title=\"SFSObject and SFSArray\" href=\"http:\/\/docs2x.smartfoxserver.com\/DevelopmentBasics\/sfsobject-sfsarray\" target=\"_blank\">SFSObject<\/a> and<a title=\"SFSObject and SFSArray\" href=\"http:\/\/docs2x.smartfoxserver.com\/DevelopmentBasics\/sfsobject-sfsarray\" target=\"_blank\"> SFSArray<\/a>, which are used pervasively throughout the server and client API.<\/p>\n<p>These objects\u00a0abstract the data transport between client and server and\u00a0provide fine-grained control of\u00a0the\u00a0data sent over the network, handled by the SFS2X binary protocol.<\/p>\n<p>This is an example of a custom message sent from client to server:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nISFSObject item = new SFSObject();\r\nitem.putUtfString(&quot;name&quot;, &quot;Golden Sword&quot;);\r\nitem.putInt(&quot;damagePoints&quot;, 120);\r\nitem.putInt(&quot;durability&quot;, 40);\r\nitem.putBool(&quot;isMagic&quot;, true);\r\n\r\nsend(item, user);\r\n<\/pre>\n<p>SFSObjects and SFSArrays support 20 basic types, including \u00a0typed collections, and deal with the low level details of serialization to binary data. This is what we usually call <strong>low level serialization<\/strong>: in other words the process of turning object data to a binary format.<\/p>\n<p>In this article, however, we&#8217;ll focus on and discuss<strong>\u00a0high level serialization<\/strong> which is the process of converting your game data to and from SFSObjects.<\/p>\n<h2>\u00bb Serializing custom objects<\/h2>\n<p>Since every game keeps state (be it a simple high score list or a complex inventory with hundreds of items) there will be custom objects in our\u00a0game modeling such data. Serialization comes into play when we\u00a0need to transmit\u00a0game state updates to clients when changes occur.<\/p>\n<p>We need to take custom data\u00a0from\u00a0our server Java Extension and send them to clients which may be even running on an entirely different language and platform, such as Unity, HTML 5, Flash\u00a0etc&#8230;<\/p>\n<p>For the sake of simplicity let&#8217;s say we have this Java class describing an element of \u00a0game state:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class SpaceShip\r\n{\r\n  byte type; \r\n\r\n  short x;\r\n  short y;\r\n\r\n  int shieldPoints;\r\n  int damagePoints;\r\n}\r\n\r\nSpaceShip ships[]\u00a0= new SpaceShip[10];\r\n<\/pre>\n<p>Let&#8217;s say we want to update clients about the spaceships contained in the array. We have essentially three options.<\/p>\n<h3>#1 Inline serialization<\/h3>\n<p>We can build a series of SFSObject, one per spaceship instance, inline in our code, add them to an SFSArray and finally send the whole thing:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nISFSArray shipArray = new SFSArray();\r\n\r\nfor (SpaceShip ship : spaceShips)\r\n{\r\n\tISFSObject shipObj = new SFSObject();\r\n\r\n\tshipObj.putByte(&quot;t&quot;, ship.type);\r\n\tshipObj.putShort(&quot;x&quot;, ship.x);\r\n\tshipObj.putShort(&quot;y&quot;, ship.y);\r\n\tshipObj.putInt(&quot;sp&quot;, ship.shieldPoints);\r\n\tshipObj.putInt(&quot;dp&quot;, ship.damagePoints);\r\n\r\n\tshipArray.addSFSObject(shipObj);\r\n}\r\n\r\nISFSObject packet = new SFSObject();\r\npacket.putSFSArray(&quot;ships&quot;, shipArray);\r\n\r\nsend(&quot;cmd&quot;, packet, someUser);\r\n<\/pre>\n<p>While this approach is essentially correct and works fine,\u00a0it could quickly pollute your code with large blocks of Class\u00a0&lt;&#8211;&gt; SFSObject inter-conversions, causing also a lot of code repetition and general untidiness. A far better approach to this type of conversion would be\u00a0to retrofit our\u00a0game classes with a couple of useful methods.<\/p>\n<h3>#2 Encapsulated\u00a0serialization<\/h3>\n<p>Keeping the same idea of approach #1, we&#8217;re going to refactor our initial SpaceShip class\u00a0adding a <strong>toSFSObject<\/strong>() and <strong>fromSFSObject<\/strong>() methods and replacing string literals with constants, to avoid duplication.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class SpaceShip\r\n{\r\n\tstatic final String KEY_TYPE = &quot;t&quot;;\r\n\tstatic final String KEY_X = &quot;x&quot;;\r\n\tstatic final String KEY_Y = &quot;y&quot;;\r\n\tstatic final String KEY_SHIELD = &quot;sp&quot;;\r\n\tstatic final String KEY_DAMAGE = &quot;dp&quot;;\r\n\r\n\t\/\/ ...\r\n\r\n\tbyte type; \r\n\r\n\tshort x;\r\n\tshort y;\r\n\r\n\tint shieldPoints;\r\n\tint damagePoints;\r\n\r\n\t\/\/ ...\r\n\r\n\tpublic static SpaceShip fromSFSObject(ISFSObject shipObj)\r\n\t{\r\n\t\tSpaceShip ship = new SpaceShip();\r\n\r\n\t\tship.type = shipObj.getByte(KEY_TYPE);\r\n\t\tship.x = shipObj.getShort(KEY_X);\r\n\t\tship.y = shipObj.getShort(KEY_Y);\r\n\t\tship.sp = shipObj.getInt(KEY_SHIELD);\r\n\t\tship.dp = shipObj.getInt(KEY_DAMAGE);\r\n\r\n\t\treturn ship;\r\n\t}\r\n\r\n\tpublic ISFSObject toSFSObject()\r\n\t{\r\n\t\tISFSObject shipObj = new SFSObject();\r\n\r\n\t\tshipObj.putByte(&quot;t&quot;, KEY_TYPE);\r\n\t\tshipObj.putShort(&quot;x&quot;, KEY_X);\r\n\t\tshipObj.putShort(&quot;y&quot;, KEY_Y);\r\n\t\tshipObj.putInt(&quot;sp&quot;, KEY_SHIELD);\r\n\t\tshipObj.putInt(&quot;dp&quot;, KEY_DAMAGE);\r\n\r\n\t\treturn shipObj;\r\n\t}\r\n}\r\n<\/pre>\n<p>Notice how the <strong>fromSFSObject()<\/strong> method is marked as <strong>static<\/strong> since it acts as an alternate constructor, where we pass the SFSObject representing the data obtained from the other end of the communication (in this case it is the client, but it could be also the server).<\/p>\n<p>Our code for sending the array of SpaceShip items\u00a0becomes much cleaner now:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nISFSArray shipArray = new SFSArray();\r\n\r\nfor (SpaceShip ship : spaceShips)\r\n{\r\n\tshipArray.addSFSObject(ship.toSFSObject());\r\n}\r\n\r\nISFSObject packet = new SFSObject();\r\npacket.putSFSArray(&quot;ships&quot;, shipArray);\r\n\r\nsend(&quot;cmd&quot;, packet, someUser);\r\n<\/pre>\n<p>Also every time we need to send an instance of a SpaceShip object we can directly invoke the <strong>toSFSObject()<\/strong> method, without resorting to more inline serialization code.<\/p>\n<div class=\"dottedYellowBox\"><strong>NOTE<\/strong>: on the client side we will need to create a specular version of the same SpaceShip class so\u00a0we can exchange the same data in both ways, which is usually needed.<\/div>\n<h3>#3 Reflection based serialization<\/h3>\n<p>SFSObject and SFSArray allow to serialize custom classes\u00a0provided that they implement the <strong>SerializableSFSType<\/strong>. This in turn allow for a selection of native types to be automagically serialized via <a title=\"Reflection definition\" href=\"https:\/\/en.wikipedia.org\/wiki\/Reflection_(computer_programming)\" target=\"_blank\">reflection<\/a>.<\/p>\n<p>Here is how the SpaceShip class would look like in this case:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class SpaceShip implements SerializableSFSType\r\n{\r\n\tbyte type; \r\n\r\n\tshort x;\r\n\tshort y;\r\n\r\n\tint shieldPoints;\r\n\tint damagePoints;\r\n}\r\n<\/pre>\n<p>Pretty straightforward. We don&#8217;t need any manual serialization method, just marking the class as SerializableSFSType. The interface doesn&#8217;t require any method to be implemented but allows the internal serializer to manage\u00a0types via reflection.<\/p>\n<p>The code to serialize the SpaceShip array looks like this:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nISFSArray shipArray = new SFSArray();\r\n\r\nfor (SpaceShip ship : spaceShips)\r\n{\r\n\tshipArray.addClass(ship);\r\n}\r\n\r\nISFSObject packet = new SFSObject();\r\npacket.putSFSArray(&quot;ships&quot;, shipArray);\r\n\r\nsend(&quot;cmd&quot;, packet, someUser);\r\n<\/pre>\n<p>This looks very similar to what we have done in the previous example with the notable difference that we&#8217;re using the SFSArray&#8217;s <strong>addClass()<\/strong> instead of <strong>addSFSObject()<\/strong>.<\/p>\n<p>On the client side we need to create a specular class that must:<\/p>\n<ul>\n<li>have the same exact name<\/li>\n<li>be located in\u00a0the same package \/ namespace<\/li>\n<li>use the same exact field names of the same\u00a0type (or equivalent, as explained in the docs)<\/li>\n<\/ul>\n<p>Naturally there are a number of caveats to keep in mind when using this approach:<\/p>\n<ul>\n<li>Reflection is more demanding, if you plan to serialize 1000s of objects you may incur in performance issues both on server side and on clients with limited CPU resources (e.g. low end mobile devices)<\/li>\n<li>Not all types are serializable, we provide a <a title=\"serialization docs\" href=\"http:\/\/docs2x.smartfoxserver.com\/AdvancedTopics\/class-serialization\" target=\"_blank\">table of what is available here<\/a>.<\/li>\n<li>Not all client technologies support reflection (e.g. HTML 5) and some have limited reflection capabilities (Actionscript 3)<\/li>\n<li>Inheritance is not possible with this approach, so complex class hierarchies will not work<\/li>\n<li>Classes are serialized slightly less efficiently, in terms of size<\/li>\n<\/ul>\n<p>For more details and examples on how reflection based serialization works you <a title=\"serialization docs\" href=\"http:\/\/docs2x.smartfoxserver.com\/AdvancedTopics\/class-serialization\" target=\"_blank\">can check our documentation<\/a>.<\/p>\n<h2>\u00bb\u00a0Best approach and conclusions<\/h2>\n<p>Manually encapsulated serialization (approach #2) is usually\u00a0the best and safest choice: \u00a0it provides flexibility, control and very little performance costs. It can be easily maintained when state objects are changed and it allows to filter out data that doesn&#8217;t need to be transmitted.<\/p>\n<p>On the other hand reflection based serialization (approach #3) can also help\u00a0in situations where clients use strong typed languages, such as Java and C#, and the use of serialization is lightweight. The advantage here is that you don&#8217;t have to write any extra code for it to work.<\/p>\n<p>Finally we&#8217;re not proponents of the first inline serialization method because it usually leads to more messy code and repetition, making model\u00a0changes harder to track down and maintain.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article we&#8217;re going to analyze a few strategies to serialize game state data between client and server, and discuss the pros and cons of each approach.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23],"tags":[72,7],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/522"}],"collection":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/comments?post=522"}],"version-history":[{"count":24,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/522\/revisions"}],"predecessor-version":[{"id":546,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/522\/revisions\/546"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=522"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=522"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=522"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}