{"id":924,"date":"2018-05-21T16:06:14","date_gmt":"2018-05-21T16:06:14","guid":{"rendered":"http:\/\/smartfoxserver.com\/blog\/?p=924"},"modified":"2018-09-22T08:32:18","modified_gmt":"2018-09-22T08:32:18","slug":"best-of-both-worlds-sfs2x-server-side-unity-for-realtime-games-p2","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/best-of-both-worlds-sfs2x-server-side-unity-for-realtime-games-p2\/","title":{"rendered":"Best of both worlds: SFS2X + server side Unity for realtime games (p2)"},"content":{"rendered":"<p>In\u00a0part one\u00a0of this article series we took a bird&#8217;s eye look at various client-server strategies for action multiplayer games.\u00a0We then highlighted the advantages of running an hybrid solution with\u00a0SmartFoxServer 2X\u00a0and Unity on the server side to\u00a0combine the best of both worlds.<\/p>\n<p>In this second part we&#8217;ll be looking at the details of implementing such a solution, the potential difficulties and how to overcome them.<\/p>\n<p>If you have skipped the first article <a href=\"http:\/\/smartfoxserver.com\/blog\/best-of-both-worlds-sfs2x-server-side-unity-for-realtime-games-p1\/\">we highly recommend to go back and read it<\/a>, before you proceed.<!--more--><\/p>\n<h2>\u00bb The basic architecture<\/h2>\n<p>To get started\u00a0let&#8217;s review the basic architecture diagram\u00a0from the previous article:<\/p>\n<p><img loading=\"lazy\" class=\"wp-image-909 aligncenter\" src=\"http:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo.png\" alt=\"\" width=\"412\" height=\"415\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo.png 888w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo-150x150.png 150w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo-298x300.png 298w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo-768x773.png 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-combo-624x628.png 624w\" sizes=\"(max-width: 412px) 100vw, 412px\" \/><\/p>\n<p>In this solution SmartFoxServer 2X acts as the main connection point for all clients, managing the session&#8217;s life cycle, providing the lobby and match making services etc., and orchestrating the creation and destruction of Unity servers.<\/p>\n<p>Unity\u00a0engines are attached to game Rooms to handle their game logic and removed when the match is over and the relative room is destroyed. In some cases, like for virtual worlds and MMOs, Unity engines\u00a0can handle a larger portions of the world, based on the characteristics and size of the game map. In that case we will map a larger portion of the virtual world to as single Room.<\/p>\n<p>In order to start a new game we create a Room and join all the players inside. Then\u00a0 when clients are ready to start, we&#8217;ll spawn a new headless Unity server and tell each player to connect to it. This means that\u00a0clients will\u00a0use a maximum of 2 simultaneous connections, one toward SFS2X and the other toward the Unity server. With this approach\u00a0everyone will be able to receive events from SFS2X (lobby updates, chat\/buddy messages etc.) while the game is running.<\/p>\n<h2>\u00bb\u00a0Headless Unity<\/h2>\n<p>Running Unity as a server is a bit uncommon compared to the normal use cases but it&#8217;s actually a very powerful solution for multiplayer games. In particular, the ability to create a server that runs on the same engine as the client is very convenient as we can share assets (e.g. game maps) and work with the same language and\u00a0environment\u00a0used for both sides.<\/p>\n<p>In our use case we&#8217;ll be running &#8220;headless&#8221; Unity servers which means that Unity instances will not create a new\u00a0desktop window and thus perform no rendering at all, as it is not needed on the server side.<\/p>\n<p>To do this we just need to build our project as an executable for the OS used on the server side, and add a few parameters\u00a0when launching it from\u00a0command line.<\/p>\n<h3>How do I build my client\/server Unity game exactly?<\/h3>\n<p>In this article we&#8217;re not going to develop a specific game.\u00a0It would be beyond the scope of the tutorial and you can find innumerable videos and resources to learn how to create multiplayer games in Unity.<\/p>\n<p>What we&#8217;re going to do, however, is we will describe how the Unity client and server are built and the server part is integrated with SmartFoxServer 2X. For the sake of example we can imagine to be building a multiplayer air-hockey game: we want it to be a 2D top down view that supports the browser via WebGL.<\/p>\n<p>In order to write the client and server\u00a0portions of the game we can use two different approaches:<\/p>\n<p><strong>1) Client and server logic in the same project<\/strong><\/p>\n<p><img loading=\"lazy\" class=\"size-full wp-image-928 aligncenter\" src=\"http:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-one-project.png\" alt=\"\" width=\"452\" height=\"204\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-one-project.png 452w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-one-project-300x135.png 300w\" sizes=\"(max-width: 452px) 100vw, 452px\" \/><\/p>\n<p>We can code both the client and server portions in the same Unity project and then <em>activate\u00a0<\/em>one\u00a0side via an external parameter. This is done by passing a command line argument to the Unity executable\u00a0that is\u00a0launched from the SFS2X side.<\/p>\n<p><strong>PROS<\/strong>:<\/p>\n<ul>\n<li>we can manage all the code in one place<\/li>\n<li>we can build both targets from the same project<\/li>\n<li>code that is common to client and server can be reused without duplications<\/li>\n<li>best for small or medium size projects<\/li>\n<\/ul>\n<p><strong>CONS<\/strong>:<\/p>\n<ul>\n<li>there&#8217;s a potential for confusion and extra bugs when mixing server and\u00a0client code<\/li>\n<li>the app has to split itself into two so it can become cumbersome in large projects<\/li>\n<\/ul>\n<p><strong>2) Separate projects for client and server sides<\/strong><\/p>\n<p><img loading=\"lazy\" class=\"size-full wp-image-929 aligncenter\" src=\"http:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-two-projects.png\" alt=\"\" width=\"441\" height=\"227\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-two-projects.png 441w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/unity-two-projects-300x154.png 300w\" sizes=\"(max-width: 441px) 100vw, 441px\" \/><\/p>\n<p>This is probably the best approach for medium and large sized games as you can keep concerns and resources separated.<\/p>\n<p><strong>PROS<\/strong>:<\/p>\n<ul>\n<li>More secure, as no server side code is present on the client side, so hackers can&#8217;t see what the server side does<\/li>\n<li>Separation of concerns and resources<\/li>\n<li>Easier to work with when there are teams of people dedicated to each side<\/li>\n<\/ul>\n<p><strong>CONS<\/strong>:<\/p>\n<ul>\n<li>Some code duplication may occur, although shared libraries can usually mitigate the issue.<\/li>\n<\/ul>\n<h2>\u00bb\u00a0Managing Unity engines<\/h2>\n<p>Now that we have a clearer picture of how to work with the client and server portions in Unity, it&#8217;s time to see how we can put it all together from the SmartFoxServer side.<\/p>\n<p>In our example game we want to use a one-Unity-per-Room approach so that every game is managed by a\u00a0separate Unity engine. To do this we can attach a custom Extension to each SFS2X game Room that will take care of running the Unity server.<\/p>\n<p><img loading=\"lazy\" class=\"size-full wp-image-930 aligncenter\" src=\"http:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/roomsunity.png\" alt=\"\" width=\"417\" height=\"258\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/roomsunity.png 417w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/roomsunity-300x186.png 300w\" sizes=\"(max-width: 417px) 100vw, 417px\" \/>To be able to run Unity from SmartFoxServer we will need to deploy the Unity executable to our Extension folder, for example like this:<\/p>\n<p><img loading=\"lazy\" class=\"size-full wp-image-931 aligncenter\" src=\"http:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-deploy.png\" alt=\"\" width=\"428\" height=\"194\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-deploy.png 428w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2018\/05\/sfs2x-unity-deploy-300x136.png 300w\" sizes=\"(max-width: 428px) 100vw, 428px\" \/><\/p>\n<p>Under our <em>AirHockeyGame\/<\/em> extension folder we deploy\u00a0the Extension file (.jar file) and we create a <em>Unity\/\u00a0<\/em>subdirectory were we can deploy the server side Unity engine. In our case we&#8217;re working with <strong>macOS<\/strong> where both the executable and\u00a0relative assets are bundled in a single file.<\/p>\n<p>Under <strong>Windows<\/strong> and <strong>Linux<\/strong> the build\u00a0is comprised of two elements: the executable and\u00a0a folder\u00a0containing all the necessary assets. Make sure to copy both under the <em>Unity\/<\/em>\u00a0directory.<\/p>\n<p><strong>Running multiple Unity on the same machine<\/strong><\/p>\n<p>For the sake of simplicity we will run the game servers on the same machine where SFS2X runs.\u00a0This, in turn, requires to allocate a different TCP (or UDP) port number for each instance\u00a0as it&#8217;s not possible for two apps to listen on the same port.<\/p>\n<p>To do this we&#8217;ll use a simple <strong>PortManager<\/strong> class that allocates and deallocates a port value\u00a0so we\u00a0can pass it to the Unity engine itself.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage sfs2x.extension.unity;\r\n\r\npublic class PortManager\r\n{\r\n\tprivate int portBaseValue = 7000;\r\n\tprivate int maxPorts = 100;\r\n\t\r\n\tprivate boolean [] ports = new boolean [maxPorts];\r\n\t\r\n\tpublic PortManager()\r\n\t{\r\n\t\tinitPorts();\r\n\t}\r\n\t\r\n\t\r\n\tprotected synchronized void initPorts()\r\n\t{\r\n\t\tfor (int i = 0; i &lt; maxPorts; i++)\r\n\t\t{\r\n\t\t\tports[i] = false;\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic synchronized int getAvailablePort()\r\n\t{\r\n\t\tfor (int i = 0; i &lt; maxPorts; i++)\r\n\t\t{\r\n\t\t\tif (ports[i] == false)\r\n\t\t\t{\r\n\t\t\t\tports[i] = true;\r\n\t\t\t\treturn portBaseValue + i;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tthrow new RuntimeException(&quot;All ports are currently taken. Cannot start a new server-side Unity instance&quot;);\r\n\t}\r\n\r\n\tpublic synchronized void releasePort(int value)\r\n\t{\r\n\t\tports[value - portBaseValue] = false;\r\n\t}\r\n}\r\n<\/pre>\n<p>The code\u00a0should be\u00a0straightforward:\u00a0we keeps track of a series of numeric &#8220;slots&#8221; starting at 7000 (our initial port number) that can be taken\u00a0and returned when the we&#8217;ve finished with the job.\u00a0Notice the\u00a0use of the\u00a0<em>synchronized<\/em>\u00a0mutator\u00a0to make sure the class works with concurrent requests. We instantiate this class in the main <strong>Zone Extension<\/strong> of the game so that all Rooms can work with the same <strong>PortManager<\/strong>.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage sfs2x.extension.unity;\r\n\r\nimport com.smartfoxserver.v2.extensions.SFSExtension;\r\n\r\npublic class AirHockeyZoneExtension extends SFSExtension\r\n{\r\n\tprivate PortManager pManager;\r\n\t\r\n\t@Override\r\n\tpublic void init()\r\n\t{\r\n\t\tpManager = new PortManager();\r\n\t}\r\n\t\r\n\tpublic PortManager getPortManager()\r\n\t{\r\n\t\treturn pManager;\r\n\t}\r\n}\r\n<\/pre>\n<p><strong>Launching server side processes<\/strong><\/p>\n<p>Next we can move onto the core of the solution which is launching the Unity engines from our <strong>Room Extension:<\/strong><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage sfs2x.extension.unity;\r\n\r\nimport java.io.File;\r\nimport java.io.IOException;\r\n\r\nimport com.smartfoxserver.v2.extensions.SFSExtension;\r\nimport com.smartfoxserver.v2.entities.User;\r\nimport com.smartfoxserver.v2.entities.data.ISFSObject;\r\n\r\npublic class AirHockeyRoomExtension extends SFSExtension\r\n{\r\n\tString unityExe = &quot;OSXBuild&quot;;\r\n\tString workingDir = new File(&quot;extensions\/AirHockey\/Unity\/OSXBuild.app\/Contents\/MacOS\/&quot;).getAbsolutePath();\r\n\tThread launcherThread;\r\n\t\r\n\tprivate class UnityRunner implements Runnable\r\n\t{\r\n\t\tprivate int tcpPort;\r\n\t\t\r\n\t\tpublic UnityRunner(int port)\r\n\t\t{\r\n\t\t\tthis.tcpPort = port;\r\n\t\t\t\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tgetParentRoom().setVariable(new SFSRoomVariable(&quot;port&quot;, port));\r\n\t\t\t}\r\n\t\t\tcatch(Exception ex)\r\n\t\t\t{\r\n\t\t\t\ttrace(ex);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\t@Override\r\n\t\tpublic void run()\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tString cmd = workingDir + &quot;\/&quot; + unityExe;\r\n\r\n\t\t\t\tProcessBuilder processBuilder = new ProcessBuilder(cmd, &quot;-batchmode&quot;, &quot;-logFile&quot;,  workingDir + &quot;\/unity.log&quot;, &quot;--serverPort&quot;, String.valueOf(tcpPort)); \r\n\t\t\t\tprocessBuilder.directory(new File(workingDir));\r\n\t\t\t\t\r\n\t\t\t\tProcess proc = processBuilder.start();\r\n\t\t\t\tint exit = proc.waitFor();\r\n\t\t\t\ttrace(&quot;Unity process terminated with code: &quot; + exit);\r\n\t\t\t}\r\n\t\t\tcatch(IOException ex)\r\n\t\t\t{\r\n\t\t\t\ttrace(&quot;Error launching Unity executable: &quot; + ex);\r\n\t\t\t\tex.printStackTrace();\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex) \r\n\t\t\t{\r\n\t\t\t\ttrace(&quot;Unexpected exceptions: &quot; + ex);\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\t((AirHockeyZoneExtension) getParentZone().getExtension()).getPortManager().releasePort(tcpPort);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void init()\r\n\t{\r\n\t\tAirHockeyZoneExtension ext = (AirHockeyZoneExtension) getParentZone().getExtension();\r\n\t\tint tcpPort = ext.getPortManager().getAvailablePort();\r\n\t\t\r\n\t\tlauncherThread = new Thread(new UnityRunner(tcpPort), &quot;unity-worker:&quot; + tcpPort);\r\n\t\tlauncherThread.start();\r\n\r\n\t\ttrace(&quot;UnityLauncher started&quot;);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void destroy()\r\n\t{\r\n\t\tsuper.destroy();\r\n\t\t\r\n\t\tif (launcherThread.isAlive())\r\n\t\t\tlauncherThread.interrupt();\r\n\t\t\r\n\t\ttrace(&quot;UnityLauncher stopped&quot;);\r\n\t}\r\n}\r\n<\/pre>\n<p>at the top of the class we define the path to our Unity executable relative to the <em>SFS2X\/<\/em> top folder. Under <strong>Windows<\/strong> and <strong>Linux<\/strong> you just have to point to the path of the executable but under <strong>macOS<\/strong> things are a little bit different, due to the bundling system we mentioned before.<\/p>\n<p>To recap:<\/p>\n<ul>\n<li><strong>Windows \/\u00a0Linux<\/strong>: point to the folder where the executable was copied to, in other words under <em>Unity\/<\/em><\/li>\n<li><strong>macOS<\/strong>:\u00a0you have to point to the bundle file as if were a directory (which it is) plus two extra steps. In other words <em>Unity\/&lt;Name&gt;.app\/Content\/MacOS\/\u00a0<\/em>where &lt;Name&gt; is the name of your build<\/li>\n<\/ul>\n<p>Next we define an internal private class where the &#8220;magic&#8221; happens. The <strong>UnityRunner<\/strong> class implements <em>Runnable<\/em>\u00a0and it\u00a0is\u00a0invoked by a separate thread that waits for the Process to complete its\u00a0job and do some clean up before shutting down.<\/p>\n<p>In the <strong>UnityRunner<\/strong> constructor we obtain a port value for the Unity instance and store it in the Room&#8217;s variables. Normally we would need to call <strong>getApi().setRoomVariables()<\/strong> method to update all clients, but we&#8217;re still in the <strong>init()<\/strong> method of our Extension where the server is not even accepting connections. In this situation it is fine to bypass the API and add variables directly (in any other situation, you would use the API instead).<\/p>\n<p>The variable is going to be readable by clients when they join the Room, and it will be used to connect to the game server.<\/p>\n<p>To run the Unity executable we use the convenient <a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/api\/java\/lang\/ProcessBuilder.html\" target=\"_blank\" rel=\"noopener noreferrer\">ProcessBuilder<\/a> class from the JDK which allows to run system commands with any number of parameters. Let&#8217;s see them in detail:<\/p>\n<ul>\n<li><strong>-batchmode<\/strong>: this instructs Unity to run headless, with no rendering<\/li>\n<li><strong>-logFile<\/strong>: this allows us to specify where we want Unity to save a log file of all\u00a0Unity&#8217;s runtime activity. This is particularly useful to debug\u00a0unexpected\u00a0issues, especially since we&#8217;ll have no visual output whatsoever<\/li>\n<li><strong>&#8211;serverPort:\u00a0<\/strong>this is a custom command,\u00a0one\u00a0that it&#8217;s not recognized by Unity, and that we&#8217;ll be able to\u00a0read it from inside\u00a0the Unity server\u00a0to obtain the port number assigned by our Extension<\/li>\n<\/ul>\n<p>If we need to pass more parameters to the Unity server we can keep adding more custom commands preceded by a double dash (&#8211;) or any other similar convention. Just keep in mind that a single dash (-) is already used by the internal Unity commands and should be avoided.<\/p>\n<p>Reading the command line parameters from Unity\u00a0can be\u00a0done via the <strong>System.Environment<\/strong> object:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\tprivate int readServerPortFromCommandLine()\r\n\t{\r\n\t\tstring[] args = System.Environment.GetCommandLineArgs();\r\n\r\n\t\tfor (int i = 0; i &lt; args.Length; i++) \r\n\t\t{\r\n\t\t\tif (args[i] ==&quot;--serverPort&quot;  &amp;&amp; args.Length - 1) \r\n\t\t\t{\r\n\t\t\t\treturn int.Parse(args[i + 1]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn -1;\r\n\t}\r\n<\/pre>\n<p>Back to our Extension\u00a0code we\u00a0launch the process by calling the <strong>start()<\/strong> method on the <strong>ProcessBuilder<\/strong> instance and finally we call <strong>waitFor()<\/strong> to keep the new thread in pause until the process (i.e. Unity engine)\u00a0is\u00a0done.<\/p>\n<p>The <em>try\/catch<\/em> block takes care of potential runtime issues, such as a missing executable, in case we made some mistake with the deployment, and in the <em>finally<\/em> block takes care of releasing the port number before returning.<\/p>\n<div class=\"dottedYellowBox\"><strong>Deployment Notes:<\/strong><br \/>\n<br \/>\nTo simplify the deployment of this example we would recommend copying the extension&#8217;s jar in the <strong>extensions\/__lib__\/<\/strong> folder. This enables all classes to be loaded in the same classloader and make cross-extension communication easier.\n<\/div>\n<p><strong>Life cycle of a Unity engine<\/strong><\/p>\n<p>In this example we use the<strong> init()<\/strong> method of our Extension to bootstrap the Unity server instance so that it is ready as soon as the Room is created. An alternative to this approach would be to create the Room without starting the Unity server and wait for all the players to join before we launch it.<\/p>\n<p>Both ways are perfectly valid and it all comes down to how our game works. For instance, a game that doesn&#8217;t need to wait for all players will use the first method and viceversa.<\/p>\n<p>Similarly there can be different strategies to stop the Unity engine:<\/p>\n<ul>\n<li>we wait for all players to leave the game server and then call <strong>Application.Quit()\u00a0<\/strong>from Unity<\/li>\n<li>we wait for all players to leave the SFS2X Room and the shut down the Unity process by calling <strong>Process.destroy()<\/strong> from our Extension<\/li>\n<\/ul>\n<h2>\u00bb\u00a0Performance and scalability<\/h2>\n<p>Now that we have explored the basics of creating and managing Unity instances from a SmartFoxServer Extension, you may be wondering how resource intensive this could be, especially when we want to run hundreds of games simultaneously.<\/p>\n<p>Unfortunately there is\u00a0no clear-cut answer. The amount of resources (CPU and RAM especially) is heavily dependent on the complexity and type of game: in particular the number of players per room, the size and complexity of the game world, the physics, the amount of updates per second, they all contribute to the overall usage of resources.<\/p>\n<p>Additionally there are the technical specifications of the machine running this system, which can also be highly variable.<\/p>\n<p>As usual the best way to estimate the\u00a0performance of a system is to benchmark it. In our case we can\u00a0run one or more\u00a0Unity servers and monitor the usage of CPU, RAM and network. Then we can extrapolate how many Unity engines\u00a0can be run on our dedicated machine.<\/p>\n<p>Supposing\u00a0the tests\u00a0give us a\u00a0limit of approximately\u00a050 engines per server (=\u00a050 game rooms), how can we scale up our system to deal with a higher demands?<\/p>\n<p>As you may expect the answer is to run multiple servers to host more Unity\u00a0engines.<br \/>\nWe can explore\u00a0a couple of ways to do this:<\/p>\n<ul>\n<li><strong>Multi-game\u00a0servers<\/strong>: we run a new dedicated server that will host a maximum of N Unity engines, based on our estimates.\u00a0In our example we calculated 50 game Rooms,\u00a0 which means\u00a0each dedicated machine will host a max of 50 games, then a new machine needs to be setup or spawned<\/li>\n<li><strong>Cloud based micro servers<\/strong>: instead of running multiple games per machine we spawn a small dedicated server (e.g. like a <a href=\"https:\/\/aws.amazon.com\/ec2\/instance-types\/\" target=\"_blank\" rel=\"noopener noreferrer\">t2.nano<\/a> under AWS EC2) and run a single game per micro-server<\/li>\n<\/ul>\n<p>Both approaches can be cloud-based and since most cloud providers offer Java API to automate operations in their environment, it can be convenient to integrate such API in the server side Extension to be able to\u00a0manage\u00a0on-demand servers.<\/p>\n<p>This means that we can directly manage instances in our Extension code, creating new servers via the cloud API and removing them when they are no longer needed. This approach may sound complicated but in actuality most cloud\u00a0vendors\u00a0provide high level API that can add or remove a new server with a couple of lines of code.<\/p>\n<h2>\u00bb Next steps<\/h2>\n<p>We have explored the fundamental concepts to build an hybrid game server that takes advantage of both SmartFoxServer and Unity.<\/p>\n<p>If you have any questions about what we have discussed so far, feel free to post your comments <a href=\"https:\/\/www.smartfoxserver.com\/forums\/index.php\" target=\"_blank\" rel=\"noopener noreferrer\">in our support board<\/a>.<\/p>\n<p>As a final note, we are planning to release a new API for SFS2X that will help you integrate Unity on the server side as we have illustrated, manage the Unity servers, load balancing etc.<\/p>\n<p>Stay tuned for more.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In\u00a0part one\u00a0of this article series we took a bird&#8217;s eye look at various client-server strategies for action multiplayer games.\u00a0We then highlighted the advantages of running an hybrid solution with\u00a0SmartFoxServer 2X\u00a0and Unity on the server side to\u00a0combine the best of both worlds. In this second part we&#8217;ll be looking at the details of implementing such a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23],"tags":[31,99,7,42],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/924"}],"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=924"}],"version-history":[{"count":32,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/924\/revisions"}],"predecessor-version":[{"id":972,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/924\/revisions\/972"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=924"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=924"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=924"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}