category: Getting Started

Server applications are central to all games or applications leveraging Stormancer. Without one, clients can't establish connectivity, discover each other nor exchange messages and data.

While designing server applications, we wanted to provide a 'low ceremony' development experience to enable developers to get started fast.

In this article, we will detail how server apps are structured and how you can create one.

Application structure

A Stormancer server application is contained in a directory on the file system. In order to deploy it, you will need to create a git repository from the app directory.

In order to determine how to run the application, the Platform use a special _app.json file placed at the root of the directory.

app directory
---------- _app.json
     | 
     |-----[files]

The _app.json file contains informations interpreted by the cloud infrastructure to install, start and manage your application.

If you don't bundle a _app.json file with your app, the runtime will default to a NET45 host running the latest available version. While this behavior makes creating new apps fast and easy, beware that it could also trigger compatibility issues with your code if new versions of the runtime were to introduce breaking changes. We therefore advise you to always bundle an _app.json file with your apps.

C# Server application

For a C# .NET 4.5 server application targeting the 1.0 runtime, the _app.json file should contain the following json.

{
    "host": {
        "type": "NET45",
        "version": "1.4.*"
    }
}

The "version" property allows you to restrict the versions of the runtime that will run your app. We will automatically select the higher version compatible with your requirements. Be aware that different host versions could be incompatible with your server code. Therefore, we recommand you to restrict your application to minor updates of the runtime (ie "1.0.*" for instance). Learn more about runtime versioning

When targeting the C# .NET 4.5.1 host, your application can contain an optional bin directory. This directory will be probed by the runtime when looking for assemblies. So if your application has a dependency to external assemblies, create a bin directory and put them here.

app directory
-----|---- _app.json
     | 
     |-----[files]
     |----- bin
             |
             |----- application dlls

The packages property contains an optional list of required Nuget packages that will be copied into the application on installation.

The optional bin directory contains additionnal assemblies (.NET dll files) that will be referenced on compilation.

App.Run

The startup logic of the application is contained in the method Run(IAppBuilder builder) of a class App.

To add it into your project, just create a file named app.cs with the following code:

using Stormancer;
using Stormancer.Core;

namespace Stormancer.Test.application
{
    public class App
    {
        public void Run(IAppBuilder builder)
        {
          //Startup code goes here.
        }
    }
}

Declaring scene templates

We have seen in Stormancer concepts that scenes run all the realtime server logic required by your client-server applications (chat rooms, lobbies, game sessions and more).

To be able to start scenes in your game, you must first declare templates from which these scenes will be created.

In the simpliest Stormancer application these templates can be declared programmatically in the Run method.

public void Run(IAppBuilder builder)
{
    builder.SceneTemplate("name-of-the-template", scene => { 
            //Write your scene startup logic there. 
       });   
}

Template names must only contain alphanumeric characters, - or _ . The second parameter of the SceneTemplate method is the factory that will be executed by the runtime when building the scene. That's were you will declare server routes, add event handlers and add your logic.

Now let's add to the scene a route that broadcasts to all connected players any message sent to it.

builder.SceneTemplate("test-template", scene =>
{
    scene.AddRoute("echo.in", p =>
    {
        scene.Broadcast("echo.out", s => p.Stream.CopyTo(s), Core.PacketPriority.MEDIUM_PRIORITY, Core.PacketReliability.RELIABLE);
            });
    });

To learn more about how to send messages, read sending messages

Events

Startup and shutDown events

The OnStarting event is fired when the scene starts and before any player connection. The handler argument is a dynamic object containing the scene custom data that you can provide when creating the scene.

scene.OnStarting.Add(async arg => 
    {
        //Synchronous operation
        foo.Frob();
        //Asynchronous operation
        await foo.FrobAsync();
    });

The OnShutdown event is called when the scene shuts down. That's the right place to add any cleanup logic your scene may require. The handler argument is a ShutdownArgs object exposing the shutdown reason and an optional custom Data string. This data can be provided in the x-userdata header of the "delete scene" web request.

Listening to players' connections and disconnections

Three events allow your application to detect and interact with the connection state of players: OnConnecting, OnConnected and OnDisconnected. Each of these event provide the ISceenPeer object associated with the connection as argument.

OnConnecting is fired when an user try to connect to the scene. Throwing an exception in the handler will cancel the connection process. If the exception thrown is a ClientException object, the exception message is sent back to the client.

scene.OnConnecting.Add(peer=>{ 
   //Add here logic executed when an user try to connect to the scene.
   scene.Broadcast("User "+ peer.Id+"is trying to connect to the scene.");

   var user = peer.DeserializeUserData<User>();
   if(user.Name == "Bad guy")
   {
      throw new ClientException("Boooo!");
   }

});

OnConnected fires when the user completes the connection process.

scene.OnConnected.Add(peer=>{ 
   //Add here logic executed when an user successfully connected to the scene
   scene.Broadcast("User "+ peer.Id+"successfully connected to the application.");
});

OnDisconnected fires when the player is disconnected from the scene. The argument is a DisconnectedArgs instance containing the peer object and a string representing the reason of the disconnection.

scene.OnDisconnected.Add(args=>{ 

     //Add here disconnection logic.
     scene.Broadcast("user "+ args.Peer.Id+" disconnected from the server because "+ args.Reason);         
     });

Reason can be one of the following strings:

"CLIENT_REQUEST"      //The user called "Disconnect" on the scene object.
"CLIENT_DISCONNECTED" //The user client was disconnected (call "Disconnect" on the client object).
"CONNECTION_LOST"     //The connection was lost.

Order: 3