In 2013, when we started the Stormancer project, we wanted to create a platform as a service available on the cloud, similar to Heroku in its usage, but for low latency network applications in general, and for videogames in particular. We wanted something that would give as much power as possible to the developer, while abstracting away all the underlaying complex infrastructure code. Developers would be able to code a complex game server on their machine, then push it to a cloud platform, where its services would be deployed transparently on many instances, where load would be distributed depending on the requirement of each service.
For both the player and the developer, everything would look like there was a single server, a “mega server” (even if this term has been somewhat worn off by marketing).
As a company, we still don’t provide a managed cloud platform for many reasons (business reasons, mainly. But we could, in the future). Instead, we went another way: Stormancer is a set of tools, software and technologies that allow developers to create this kind of cloud platform, for their game.
Stormancer is composed of several parts: The grid, the client libraries and the plugins.
The grid runs the game server of a game that uses Stormancer for its online functionalities. It’s a distributed infrastructure composed of one or many identical nodes or instances that together form a cluster. In the cluster, services are allocated using configurable policies depending on their type. Whenever a client sends a message to a service, the grid is able to locate it using the same policie then route the message to it. The grid is designed for bidirectional client-server communications, with a preference for the UDP protocol.
The grid works in a similar way as distributed databases like MongoDB, Cassandra or Elasticsearch. but instead of storing documents on different nodes of the cluster, a Stormancer grid runs logic.
This architecture allows the creation of clusters of any size out of a single image, without application specific configuration in the server images themselves. The topology of the cluster (which server instance a service runs on) is decided at runtime according to a set of rules. If an instance goes down, the grid is able to recover by reallocating the services that were running on this node on another node.
The grid suppors running several servers (applications) on the same cluster, and supports running different deployments of the same application side by side, as long as only one of them is the active one. This behaviors allows seamless server updates without service interruption, because player already connected to the old application version can still temporarly use it.
The grid manages the lifecycle of services, starting them lazily when a client tries to connect to it, and shuts them down after an inactivity timeout.
Stormancer use a custom network engine and protocols that are implemented by the client libraries. These libraries provide an high level generic, object oriented and fully non blocking abstraction layer on top of the protocols.
It is responsible of: - Networking - Connectivity management - Message routing - Scene lifecycle management (Read the [concepts] article for a definition of a scene). - P2P connectivity - Extensibility
Plugins are sets of high level functionalities that typically contain a client and a server component. They use the extensibility features of the client and server libraries to hide the inherent complexities of working with the lower level abstractions of Stormancer.
Plugins built by the Stormancer team follow a set of patterns and good practices designed to facilitate development, provide extensibility and improve robustness against network failures.
Most plugins provide Unreal engine specific wrappers and Blueprint nodes.
To scale easily, a web application should be stateless and not require server affinity. These characteristics enable decoupling load balancing from the application (no affinity), and easy replication of web servers (stateless). In a scalable web application, the only stateful part are the databases.
However, game servers run complex logic (AI, physics ) and require low latency communications between players and the service (game session) that runs the logic of interest to a set of players. To achieve a sufficient level of performance, the service must be stateful (maintain a state of the world in memory). Furthermore, players are connected to a specific game session located on a specific server instance (because of statefulness). We are in a situation of affinity.
Please note that databases have the same constraint. They are stateful (because they are storing documents) and when a client does a query, the query will be routed to an (or severale) instance(s) that contain the documents returned by the query.