Note: This article was written using the GameLift Server SDK version 3.4.1.

Code examples in this article will be in C++. GameLift offers their SDK for C++ and C#.

brainCloud Lobbies and Hosting

This article will not detail Lobby, Matchmaking and brainCloud typical server configurations. It is recommended to read this article first:

Design | Multiplayer | Lobbies

For an example of a brainCloud custom server, view this code example:

C++ Custom Room Server Example

If you wish instead to host your servers directly with brainCloud, please refer to those articles instead:

How to run Room Servers from brainCloud

C++ Custom Room Server

Custom Multiplayer Server with Unreal

Custom Multiplayer Server with Unity

GameLift

In certain situations, GameLift might be a better choice for you. brainCloud uses Docker images to run its game servers. It is simpler to setup, but can add run-time overhead.

For more in-depth documentation about GameLift, see this link: https://docs.aws.amazon.com/gamelift/index.html

brainCloud S2S

To be able to communicate with brainCloud from your game server, you will need to add brainCloud S2S (Server to Server) dependency to your code. Depending on the language/engine you are using, here are the different libraries:

Server Flow

brainCloud will notify GameLift that a match should start, and GameLift will callback into your server instance to let you know it's ready.

brainCloud will pass information about the Lobby and how to connect to the S2S through GameLift's Game Properties. Properties are:

Property Name

Meaning

APP_ID

your brainCloud game Id

SERVER_HOST

URL for brainCloud S2S

SERVER_PORT

Port for brainCloud S2S

SERVER_NAME

As configured in the brainCloud Portal, My Servers section.

SERVER_SECRET

Found in the brainCloud Portal, My Servers section.

LOBBY_ID

Unique ID for the Lobby

Using this information, your server is now able to fetch the entire Lobby from brainCloud using S2S. This will allow you to know who is in the game, who is the owner of the Game, and more. It will also contains temporary passcodes that you can use to validate players coming in. A typical Lobby JSON looks like this:

{
"cRegions" : [ "ca-central-1" ],
"connectData" : {},
"id" : "23649:CursorPartyGameLift:150",
"isRoomReady" : false,
"legacyLobbyOwnerEnabled" : false,
"lobbyType" : "CursorPartyGameLift",
"lobbyTypeDef" : {
"desc" : "Uses gamelift for server hosting",
"lobbyTypeId" : "CursorPartyGameLift",
"rules" : {
"allowEarlyStartWithoutMax" : true,
"allowJoinInProgress" : false,
"disbandOnStart" : true,
"earliestStartSecs" : 1,
"everyReadyMinNum" : 1,
"everyReadyMinPercent" : 0,
"forceOnTimeStartWithoutReady" : true,
"onTimeStartSecs" : 600,
"tooLateSecs" : 600
},
"teams" : {
"all" : {
"autoAssign" : true,
"code" : "all",
"maxUsers" : 8,
"minUsers" : 1
}
}
},
"members" : [
{
"cxId" : "23649:76a7b033-def2-4115-8dc4-183dbf33de6b:6c2dcmojq3r8dlnc7nash2mkke",
"extra" : {
"colorIndex" : 3
},
"isReady" : true,
"name" : "david",
"passcode" : "807826",
"pic" : "",
"profileId" : "76a7b033-def2-4115-8dc4-183dbf33de6b",
"rating" : 0,
"team" : "all"
}
],
"numMembers" : 1,
"ownerCxId" : "23649:76a7b033-def2-4115-8dc4-183dbf33de6b:6c2dcmojq3r8dlnc7nash2mkke",
"rating" : 0,
"round" : 1,
"settings" : {},
"state" : "starting",
"timetable" : {
"createdAt" : 1632405469055,
"early" : 1632405470055,
"onTime" : 1632406069055,
"tooLate" : 1632406069055
},
"version" : 1
}

With this, you can now move on and Activate the Game Session using the GameLift SDK, and then also notify brainCloud that the server is ready using SYS_ROOM_READY.

Then do your normal gameplay from there, and don't forget to properly shutdown and exit 0 from your server once finished.

Code Example

In this example, we show how to initialize GameLift and brainCloud S2S, and how to shutdown properly. We omitted any gameplay and networking code with players.

#include <condition_variable>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>

#include <aws/gamelift/server/GameLiftServerAPI.h>
#include <aws/gamelift/server/LogParameters.h>
#include <aws/gamelift/server/ProcessParameters.h>

#include <brainclouds2s.h>
#include <json/json.h>

using namespace std;
using namespace Aws::GameLift;
using namespace Aws::GameLift::Server;
using namespace Aws::GameLift::Server::Model;

// Prototypes
void onStartGameSession(GameSession gameSession);
void onUpdateGameSession(UpdateGameSession updateGameSession);
void onProcessTerminate();
bool onHealthCheck();

Json::Value sendLobbyRequest(const string &sysCall);

// Game Properties passed to us from brainCloud, through GameLift.
static string APP_ID;
static string SERVER_HOST;
static string SERVER_PORT;
static string SERVER_NAME;
static string SERVER_SECRET;
static string LOBBY_ID;

// The port used for players to connect to your server. We should pass
// this as an argument to the server launch.
static int port = 0;

// Synchronisation objects used to wait on Game Session Activation
static bool gameReady = false;
static mutex gameReadyMutex;
static condition_variable gameReadyCV;

// brainCloud S2S
static S2SContextRef s2s;
static Json::Value lobbyJson;

// Main server entrance
int main(int argc, char **argv)
{
// Grab the players port from the arguments
if (argc > 1) port = atoi(argv[argc - 1]);

// Initialize GameLift SDK.
// Some GameLift calls seem to return an error with
// ALREADY_INITIALIZED. So make sure to check for that too.
auto initOutcome = InitSDK();
if (!initOutcome.IsSuccess() &&
initOutcome.GetError().GetErrorType() !=
GAMELIFT_ERROR_TYPE::ALREADY_INITIALIZED)
{
return 1;
}

// Create a list of log files that GameLift will copy after the
// server Shutdown.
vector<string> logPaths;
logPaths.push_back("log_file.txt");

// Game process parameters and callbacks
ProcessParameters processParameters(onStartGameSession,
onUpdateGameSession,
onProcessTerminate,
onHealthCheck,
port,
LogParameters(logPaths));

// Let GameLift know that the process is ready.
// Some GameLift calls seem to return an error with
// ALREADY_INITIALIZED. So make sure to check for that too.
auto readyOutcome = ProcessReady(processParameters);
if (!readyOutcome.IsSuccess() &&
readyOutcome.GetError().GetErrorType() !=
GAMELIFT_ERROR_TYPE::ALREADY_INITIALIZED)
{
return 1;
}

// Wait for game ready
{
unique_lock<mutex> lock(gameReadyMutex);
gameReadyCV.wait(lock, []() -> bool { return gameReady; });
}

// Initialize brainCloud S2S
{
auto url = "https://" + SERVER_HOST +
":" + SERVER_PORT + "/s2sdispatcher";
s2s = S2SContext::create(APP_ID,
SERVER_NAME,
SERVER_SECRET,
url,
true);
s2s->setLogEnabled(true);
}

// Get Lobby from S2S
lobbyJson = sendLobbyRequest("GET_LOBBY_DATA")["data"];

// Initialize your socket listeners here, load level, etc.
// ... TCP/UDP/Whatever

// Now we are fully ready to accept players' connections,
// let GameLift know.
ActivateGameSession();

// Now let brainCloud know through S2S. This will disband the
// lobby and send players our way.
sendLobbyRequest("SYS_ROOM_READY");

// Game main loop.
bool done = false;
while (!done)
{
// ...
}

// Once gameplay is done, send stats to brainCloud through S2S
// ... collect and send player stats, leaderboard, etc.

// If the Lobby is backfille (Join in progress), we need to let
// brainCloud know we're stopping the server. As a simple rule,
// always do this.
sendLobbyRequest("SYS_ROOM_STOPPED");

// Let GameLift know we're about to shutdown. After this,
// GameLift will give us about 30sec to cleanup properly and
// then exit with code 0.
ProcessEnding();

return 0;
}

// Called by GameLift, after brainCloud created a new game session.
// Here we should grab the Game Properties.
void onStartGameSession(GameSession gameSession)
{
// Get passed properties
const auto &gameProperties = gameSession.GetGameProperties();
for (const auto &game_property : gameProperties)
{
const auto &key = game_property.GetKey();
const auto &value = game_property.GetValue();

if (key == "APP_ID") APP_ID = value;
if (key == "SERVER_HOST") SERVER_HOST = value;
if (key == "SERVER_PORT") SERVER_PORT = value;
if (key == "SERVER_NAME") SERVER_NAME = value;
if (key == "SERVER_SECRET") SERVER_SECRET = value;
if (key == "LOBBY_ID") LOBBY_ID = value;
}

// Notify our gameloop that we are ready
unique_lock<mutex> lock(gameReadyMutex);
gameReady = true;
gameReadyCV.notify_one();
}

void onUpdateGameSession(UpdateGameSession updateGameSession) { }

// If GameLift decides to terminate your server
void onProcessTerminate()
{
// .. do any shuting down steps required
exit(0); // Terminate cleanly
}

bool onHealthCheck()
{
return true; // Make sure we let GameLift know we're ok
}

// Helper function to serialize a lobby request and return its result
Json::Value sendLobbyRequest(const string &sysCall)
{
Json::Value messageJson(Json::ValueType::objectValue);
Json::Value dataJson(Json::ValueType::objectValue);

// Construct our request JSON
dataJson["lobbyId"] = LOBBY_ID;
messageJson["service"] = "lobby";
messageJson["operation"] = sysCall;
messageJson["data"] = dataJson;

// Convert JSON to string
stringstream requestStream;
requestStream << messageJson;
string request = requestStream.str();

// Send request sync. This is for simplicity of this example.
// It is recommended to use async requests and rely on callbacks.
string result = s2s->requestSync(request);

// Convert string result to JSON
Json::Value resultJson;
stringstream resultStream(result);
resultStream >> resultJson;

return resultJson;
}

GameLift Configuration

The Best Practice

Without going into too much detail on how GameLift works, we would like to provide a bit of insight on the best practices that can save you money and downtime.

The two types of Fleet available are Spots and On-Demand. Spots are the cheapest, but not always guaranteed to be available. On-demands are guaranteed to always be available but have a higher cost. The best is to use both. With spots having higher priority, and an on-demand fleet as a backup plan if no spots are available.

Here is an example of the priority setup in a Queue:

#1 is the Spot fleet, and #2 is the on-demand fleet.

We also recommend having two multi-region queues set up in different home regions. The idea is if a region goes down at AWS, then the other one can get the relay. If both are active, brainCloud will randomly pick one.

AWS Integration

Under Design -> Integrations -> Manage Integrations, enter your IAM Access Key and the IAM Secret Key:

This will allow brainCloud to communicate with your GameLift Queues.

Lobby config

Lobby configuration in the brainCloud Portal doesn't change from a normal brainCloud hosted server.

Server Config

When configuring your server, make sure to select "GameLift Room Server" from the Server Type dropdown. Then select the regions where you have Queues setup.

Overview

Did this answer your question?