GAME SESSION MANAGEMENT

GAME SESSION MANAGEMENT

This chapter gives tips about how you can structure your game server when you need to handle many ongoing sessions at the same time. A session in this context is an isolated instance of the game where a set of players are playing for themselves. A specific match in an FPS game or a race in a car game are examples of sessions.

Single- and Multiple-Session Servers

There are two main ways to structure a game server with regard to sessions: having a single session per server or having multiple sessions per server.
To take the example of the FPS game again, a single-session server would have only one match going on at a time. Each new match would be handled by a separate server process, independent of the other server processes. On the contrary, a multiple-session server would have several parallel matches in the same server, so all the objects of those matches would exist in the same server scene and therefore would need to be isolated in some way.
A single-session server architecture is the most common of the two and is recommended for most games, since it is the easiest to implement and often suffices. This is the case uLink was primarily designed for and you therefore don't need to do anything special to make it work.
The main drawback of having one session per server is the overhead associated with each server process. Each session needs its own OS process, that runs its own Unity instance and uLink instance within. This results in an overhead in memory usage and also CPU usage, since the OS must perform context switching between the processes.
In contrast, having multiple sessions running in the same server process means there is no overhead per session (beyond that imposed by the game itself). This allows you to have a large number of sessions in an efficient manner. However, in order to implement this you need to write your own code to manage several sessions in a single Unity instance, which requires some extra care to be taken in how you structure the game and in how you use uLink.

Considerations

These are some of the things you should consider when choosing between a single-session and multiple-session server.
  • Players per session - Multiple-session servers are most suited for games with a low number of players per session. As the player number grows, the overhead of having a separate server per session becomes smaller per player.
  • Number of sessions - Naturally, the more sessions you plan on supporting concurrently, the bigger the overhead of a single-session server will be. However, it might be smaller than you think, so it might be a good idea to test this in a real environment before deciding.
  • Hardware availability - If you have a lot of hardware at your disposal for running your servers, it might not be worth the cost of developing a multiple-session server, even for a simple game with small sessions.
  • Game complexity - A game that is very light-weight on the server (low frequency of player actions, simple graphics, little or no physics, etc.) can benefit from having a multiple-session architecture. As with the player number, the heavier the game gets, memory and CPU wise, the smaller the overhead is for having a separate process per session.
  • Stability requirements - It is more stable to have a single session per server, since a crash in the server will only bring down that session. Having multiple sessions per server means a crash will bring down several sessions.
  • Speed of development - It is easier to implement a single-session server, since this is the case uLink was originally designed for. Implementing a multiple-session server requires dividing the game scene into several independent parts, making sure RPCs are communicated only within a session, and so forth.
Since a single-session server is both more stable and easier to implement, and often gives satisfactory performance, it is the recommended architecture for most games. If you are unsure about which architecture to choose for your game, a sensible way to start is to first implement a single-session server and then perform tests to see how well it scales. We provide the load-testing tool uTsung for generating realistic player traffic, and you could use this for testing how many concurrent sessions you can support on a server computer.
We at MuchDifferent have done our own tests using our game Snowbox, which has a single-session server architecture. We ran 450 parallel Snowbox servers with 3 players connected to each server on an 8-core Windows server with two Intel 4-core Xeon processors, without any problems.
If you decide to implement a multiple-session server, the next section gives some tips on how you can proceed.

Implementing a Multiple-Session Server

We have plans to implement features that will simplify writing multiple-session servers with uLink in the future.
The first problem you must tackle when writing a multiple-session server is how to separate the game objects of different sessions from each other in the server scene. If you have game objects that can collide with each other it is essential that you separate them somehow. A good way to structure this is to put all game objects belonging to a certain session in its own, isolated portion of the world space. For example, you could let all the objects of the first session be placed at y coordinate 0, those of the second session at y coordinate 100, and so on. Doing this means you don't have to disable collisions or go through any other troubles of trying to handle the case where objects of different sessions intersect, and instead you can let the physics engine work in the usual way.
Since each session needs a copy of all the objects that make up the level, it might be best to dynamically create new levels in runtime as the need arises, instead of statically placing several copies of it in the scene. By creating them when needed you get a flexible architecture that is easy to scale.
Keep in mind that now that you have several sessions in the same scene, you must make sure that the different sessions don't inadvertently affect each other. If you for example perform raycasts server-side you must filter the results to remove hits from objects in other, unrelated sessions.
Having separated the objects from each other, another issue that needs to be handled is keeping apart the network communication between sessions. When sending RPCs you cannot use uLink's standard RPC modes in the same way as for single-session servers, since they would operate on all connected peers in all sessions. What you can use instead is the uLink.Network.RPC() version that takes a list of uLink.NetworkPlayer as argument:
uLink.NetworkView.RPC(string rpcName, IEnumerable<NetworkPlayer> targets, params object[] args)

uLink.NetworkView.UnreliableRPC(string rpcName, IEnumerable<NetworkPlayer> targets, params object[] args)
Use this to send RPCs to all players belonging to a certain session.
State syncs should not be used for servers with multiple sessions, since they are always sent from the server to all players. Since each player only has a small subset of the objects on the server, this would result in a lot of unnecessary sending. You can instead implement your own state syncs using unreliable RPCs, which you send only to the players in a session.
The same holds true for uLink.Network.Instantiate(), which always instantiates objects on all players. You can implement your own session-private version of this method using a reliable RPC and Unity's Object.Instantiate method.