View as Markdown

Session-based multiplayer

Along with relayed and authoritative multiplayer models, Nakama additionally supports session-based multiplayer. Session-based multiplayer is a gaming model that structures gameplay into distinct sessions (e.g. matches), each with a clear beginning and end, and each running on dedicated servers such as headless Unity or Unreal instances.

These server instances are created on-demand for each new gameplay session, enabling players join a match and participate in the game for its duration, before then being shutdown at the conclusion of each match. This model is typically only required by complex physics/graphic-laden games, and is particularly popular in competitive games.

Nakama and session-based multiplayer #

Session-based multiplayer games require additional considerations from developers. As each gameplay session is isolated, there is a need for a centralized way to track player progression, stats, achievements, and the like. Matchmaking is crucial for any successful multiplayer game but even more so when it is session-based. Players must not only be quickly connected to a server to join a match, but that server must be one that is location-appropriate to provide the best possible performance and must match players with the correct criteria to provide for balanced, fair, and fun gameplay. Otherwise, players can face unreasonably long wait times to play, risking their abandoning the game, or be matched in non-competitive sessions where the gameplay doesn’t keep them engaged.

Nakama handles both of these necessary factors for managing successful session-based multiplayer games. Nakama can be used to manage the headless instances for your session-based multiplayer game, as well as other aspects of multiplayer gameplay. You can use Nakama’s powerful Matchmaker to find opponents for your players and create matches and, when a match ends, Nakama can report the results, saving all relevant stats to the player accounts, and then be used to move players as appropriate.

Nakama’s powerful APIs and extensible server runtime mean in can be used with any service providing game server fleet and management, providing you the freedom to work with any preferred platforms.

The FleetManager interface #

In order to provide developers with a way of interacting with a set of headless game servers, also known as a fleet, Nakama provides a FleetManager interface. This interface provides a mechanism with which Nakama can coordinate the creation, deletion, and updating of data within headless game server instances.

Supported Fleet Services

Heroic Labs maintains an implementation of the interface for Amazon GameLift. Edgegap and i3D.net each maintain their own implementation. The interface is designed to be extensible.

Contact us if you would like to see your favorite fleet management service supported by Nakama.

The FleetManager object can be accessed in server runtime code as follows:

1
fm := nk.GetFleetManager()

Register more than one fleet manager #

Register several fleet managers under different names, for example to place sessions with more than one hosting provider. Use RegisterFleetManagerByName in InitModule, and retrieve each one with GetFleetManagerByName:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// In InitModule.
if err := initializer.RegisterFleetManagerByName("edgegap", edgegapFm); err != nil {
    return err
}
if err := initializer.RegisterFleetManagerByName("gamelift", gameliftFm); err != nil {
    return err
}

// Anywhere in runtime code.
fm := nk.GetFleetManagerByName("gamelift")
if fm == nil {
    // No fleet manager registered under that name.
}

The Fleet Manager is designed to internally manage the state of the fleet of headless game server instances and provide a means of allowing these active instances to be created, deleted, listed, and queried similar to the way Nakama’s Match Listing feature works.

Interacting with the FleetManager #

A typical workflow for placing a user into a game session is to search for an active game session using the List function, providing a search query using the Query Syntax to filter for particular parameters. Active headless game sessions are searched for based on their current properties in near real-time and their connection details can be sent to clients allowing them to join in-progress session-based multiplayer matches.

Finding and joining game sessions #

An example of this would be searching for a game session with a game mode of "freeforall" and a current playerCount of less than 3. e.g.

1
2
3
4
query := "+values.GameProperties.gamemode:freeforall +values.player_count:<3"
limit := 1
cursor := ""
instances, _, err := fm.List(ctx, query, limit, cursor)

Once a game session has been found, players can be be placed into it by using the Join function:

1
2
3
4
5
6
id := "<game session ID>"
userIds := []string{userId}
metadata := map[string]string{
  userId: "<player data>",
} // metadata is optional. It can be used to set an arbitrary string that can be retrieved from the Game Session. Each key needs to match an userId present in userIds, otherwise it is ignored.
joinInfo, err := fm.Join(ctx, id string, userIds []string, metadata map[string]string)

Creating game sessions #

Where a game session is not available, the Fleet Manager interface can be used to Create a new game session for a particular individual or set of users. When a request is made to create a new game session, the Fleet Manager will reserve seats within the session for all specified users to ensure there is enough space for all players to join the session appropriately.

Note
When using the Create function, there is no need to call the Join function to place players into the game session, joining is implicit.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// The purpose of the callback is to facilitate asynchronous game session creation.
// Typically notifications can be used to let players know the instance is ready.
var callback runtime.FmCreateCallbackFn = func(status runtime.FmCreateStatus, instanceInfo *runtime.InstanceInfo, sessionInfo []*runtime.SessionInfo, metadata map[string]any, createErr error) {
  switch status {
  case runtime.CreateSuccess:
    logger.Info("Headless server instance created")
    return
  case runtime.CreateTimeout:
    logger.WithField("error", createErr.Error()).Error("Failed to create headless server instance, timed out")
  default:
    logger.WithField("error", createErr.Error()).Error("Failed to create headless server instance")
    return
  }
}

// User IDs passed to the Create function will be reserved in the game session for a period of time.
maxPlayers := 10
userIds := []string{"UserId1", "UserId2"}
metadata := map[string]interface{}{}
if err = fm.Create(ctx, maxPlayers, userIds, metadata, callback); err != nil {
  logger.WithField("error", err.Error()).Error("failed to create new fleet game session")
  return "", ErrInternalError
}

Get active game sessions #

Running game sessions can be retrieved with the Fleet Manager’s Get function:

1
2
id := "<Game Session ID>"
instance, err := fm.Get(ctx, id)

Delete game sessions #

Running game sessions can also be delete with the Fleet Manager’s Delete function:

1
2
id := "<game session ID>"
err := fm.Delete(ctx, id)

Nakama-GameLift integration #

Nakama Version
The Nakama-GameLift plugin is compatible with Nakama 3.21 and above.

While any games server management service can be used with Nakama, Heroic Labs provides an open-source Nakama-GameLift Plugin that enables easy implementation of session-based multiplayer gameplay with the full benefits and functionality provided by Nakama and Amazon GameLift.

Below is an example of a typical workflow when integrating with Amazon GameLift.

Sequence diagram of Nakama and Amazon GameLift integration: Nakama matchmaker finds players, calls the FleetManager API to request a session, GameLift creates a game server, and players connect to it directly
An example of Nakama and Amazon GameLift integration using the FleetManager API

Read the GameLift integration guide to get started.

Nakama-Edgegap integration #

Edgegap is a game server orchestration platform that deploys a dedicated game server on demand for each match, in the location closest to that match’s players. Edgegap picks the location from real-time data such as player IP addresses, and scales server capacity up and down with player demand across bare metal and cloud providers.

The open-source Nakama-Edgegap plugin implements the FleetManager interface, so Nakama’s matchmaker can trigger Edgegap deployments directly. The plugin works with both Heroic Cloud and self-hosted Nakama. Your game server reports instance and connection events back to Nakama over HTTP. If you’re using Unity, the Edgegap Server Nakama Plugin for Unity handles this reporting for you.

The following diagram shows a typical workflow when integrating with Edgegap.

Sequence diagram of Nakama and Edgegap integration: the Nakama matchmaker finds players and calls the FleetManager to create a deployment, Edgegap starts a game server near the players, the server reports READY, Nakama sends players the connection details, and players connect to the server directly
An example of Nakama and Edgegap integration using the FleetManager API

Read the Edgegap integration guide to get started.

Nakama-i3D.net integration #

i3D.net provides game server hosting on bare metal, bursting to cloud instances under load. i3D.net keeps a pre-warmed pool of game server processes that are deployed ahead of time and sit idle until needed, so allocation does not wait for a cold start. The open-source Nakama-i3D Plugin implements the FleetManager interface, allowing Nakama’s matchmaker to allocate servers from that pool and pass session configuration to them.

Read the i3D.net integration guide to get started.

Other integrations #

We are open to adding more Fleet Manager interfaces in the future, as well as open to contributions from the community. If you are interested in seeing your favorite fleet management service supported by Nakama, we welcome your contributions on GitHub.

Related Pages