⌘K
↑↓ Navigate ↵ Open Esc Close
Tier 7: Server & Custom Infrastructure

A different audience: your own backend, not a game client

Everywhere else in this series, a player's client talks to brainCloud. Here, your own server does, either running arbitrary trusted logic or as a full custom room server brainCloud launches on demand.

Tier 7S2S28 / 29

Server-to-Server: your backend gets its own login, and a raw request format

Cloud Code, the pattern is the client-triggered equivalent of trusted server logic. This article is for when the trusted logic lives on a server you run yourself, outside brainCloud. Room Servers is the concrete case where you'll use this the most.

Everything in this series so far has been client code talking to brainCloud through a player's session. S2S is a different door: a backend authenticates as a server, not as a player, and gets full access to brainCloud's services through one generic request format. There's no per-service method wrapper the way the client SDKs have ScriptService.RunScript or LobbyService.findOrCreateLobby. A JSON object names the service and operation, and gets sent as-is. cpp-s2s's test suite and the csharp-examples/RSM room server both use exactly this pattern, so it's well proven across two languages.

How it works

  1. Create a context with server credentials, not a player login. S2SContext::create(appId, serverName, serverSecret, serverUrl, autoAuth) in C++, or BrainCloudS2S.Init(appId, serverName, serverSecret, useShared, s2sUrl) in C#. These are app-level secrets issued for a specific server identity, not a player's email or anonymous id.
  2. Decide up front whether auto-auth is wanted. That last boolean matters more than it looks. With auto-auth off, authenticateSync() (or Authenticate(callback)) must be called before sending any request, and re-authenticating after the session expires is the caller's responsibility. With auto-auth on, the library handles that. cpp-s2s runs its entire test suite twice, once with each setting, because the behavior differs enough to be worth testing separately.
  3. Every call has the same generic format: service, operation, data. There's no getLobbyService()->something(). A request like {"service": "lobby", "operation": "GET_LOBBY_DATA", "data": {"lobbyId": lobbyId}} gets sent as that whole object. The service and operation names come from brainCloud's API reference, the same names a client SDK method wraps internally, just exposed directly here instead of hidden behind a method call.
  4. Check status on the response directly. A raw S2S response comes back as JSON with a status field. There's no typed success or failure callback deciding that the way client SDK calls do. A status other than 200 should be treated as a failure and branched on accordingly.

Code

Create a context, no auto-authtestsS2S.cpp:9-19
auto pContext = S2SContext::create(
    BRAINCLOUD_APP_ID,
    BRAINCLOUD_SERVER_NAME,
    BRAINCLOUD_SERVER_SECRET,
    BRAINCLOUD_SERVER_URL,
    false
);
REQUIRE(pContext);
The same call, with auto-auth on insteadtestsS2SAutoAuth.cpp:10-19
auto pContext = S2SContext::create(
    BRAINCLOUD_APP_ID,
    BRAINCLOUD_SERVER_NAME,
    BRAINCLOUD_SERVER_SECRET,
    BRAINCLOUD_SERVER_URL,
    true
);
REQUIRE(pContext);
Authenticate manually, then check the response for a 200tests.cpp:80-93
bool runAuth(S2SContextRef pContext)
{
    std::string ret = pContext->authenticateSync();

    Json::Value data;
    Json::Reader reader;
    bool parsingSuccessful = reader.parse(ret.c_str(), data);
    if (!parsingSuccessful)
    {
        return false;
    }

    return data["status"].asInt() == 200;
}
The generic request format, in C# this timeGameServer.cs:80-92
void RequestLobbyData()
{
    var request = new Dictionary<string, object>
    {
        { "service", "lobby" },
        { "operation", "GET_LOBBY_DATA" },
        { "data", new Dictionary<string, object>
        {
            { "lobbyId", _lobbyId }
        }}
    };
    _s2s.Request(request, OnLobbyData);
}
Run a cloud-code script over S2S, same generic formattestsS2S.cpp:48-56
auto request = "{ \
    \"service\": \"script\", \
    \"operation\": \"RUN\", \
    \"data\": { \
        \"scriptName\": \"AddTwoNumbers\" \
    } \
}";

Gotchas worth knowing up front

  • Raw request JSON fails at runtime on a typo, not compile time. There's no IDE autocomplete to catch "service": "lobbby" the way a client SDK method call would. Check the exact service and operation strings against brainCloud's API reference before shipping a new call.
  • Auto-auth changes error-handling responsibilities, not just setup code. With it off, an expired session on a long-running server process is a bug the caller must catch and recover from. With it on, the library covers that case, but the case where auto-auth itself fails still needs handling.
  • This context is a server identity; treat it like any backend credential. S2SContext::create takes the app's server secret. That's not something to hand to a client build or check into a public repo.
  • A 200 status on the transport doesn't mean the requested operation succeeded. The same "transport succeeded, check the response body" rule from cloud code (see Article 5) applies here too. Read the actual response payload, not just whether the callback fired.