C++ Client Library Release for Nakama

Alim Jaffer
Heroic Labs Blog
Published in
5 min readMar 12, 2019

To ring in Q1 of 2019, we’re releasing a new C++ client library for Nakama. This new library allows you to fully take advantage of the flexibility of Nakama, and implements the full API and socket options.

With Nakama you have unparalleled flexibility to build the game of your dreams, features include:
- Client-authoritative and server-authoritative gameplay.
- Complex matchmaking.
- Tournaments / leaderboards.
- Friends and clans.
- Social login.
- Customise and override server behaviour using Lua or Go Plugins.
- User accounts and storage.

And more which allows you to build anything from a Clash Royale, to a Fortnite, Hearthstone, Poker, etc.

As with all of our client libraries, it is fully open-source and we welcome contributions from the community. The library has been designed to be a generic C++ client to allow all engines to be integrated with it and is compiled with C++11. You can build from source, but we also make releases available for those wanting to integrate immediately.

The releases are available on GitHub to download for the following platforms:
- Windows x86, x86_64 (32bit and 64bit)
- Android 4.1 and later.
- Linux (Ubuntu 14.04)
- MacOS High Sierra or later.
- iOS 5.0 or later.

We’ve built this library to enable developers to integrate Nakama into any C++ project as the base design uses the most generic C++ libraries to ensure compatibility with new and existing build systems.

Similarly to our .NET client (which is used in our Unity Client), this library will be the basis for our upcoming client libraries for Unreal 4 Engine, as well as Cocos2d-x.

You can use the library with Godot engine via an interaction with GDScripts. Or alternatively use the .NET Client with the C# support built into Godot.

Below we’ve included some code samples on how to get started with the new library including authentication, RPC functions, using the real-time components, and storage APIs. You can find the full client guide here.

At Heroic Labs our goal is to democratise game development on a scalable level, with the core server of Nakama being open-source, as well as all of our related client libraries, this gives you as the developer the power to build your dream games without lock-in.

Members of our team will be attending GDC in San Francisco, Quo Vadis in Berlin, and Reboot Develop in Dubrovnik over the next few months. If you’d like to arrange a meeting, please feel free to reach out!

Authenticate

There’s a variety of ways to authenticate with the server. Authentication can create a user if they don’t already exist with those credentials. It’s also easy to authenticate with an account from Google, Facebook, Game Center, Steam, etc.

string email = "super@heroes.com";
string password = "batsignal";
auto successCallback = [](NSessionPtr session)
{
std::cout << "session token: "<< session->getAuthToken() << std::endl;
};
auto errorCallback = [](const NError& error)
{
};
client->authenticateEmail(email, password, "", false, successCallback, errorCallback);

Sessions

When authenticated the server responds with an auth token (JWT) which contains useful properties and gets deserialized into a NSession object.

std::cout << session->getAuthToken() << std::endl; // raw JWT token
std::cout << session->getUserId() << std::endl;
std::cout << session->getUsername() << std::endl;
std::cout << "Session has expired: "<< session->isExpired() << std::endl;
std::cout << "Session expires at: "<< session->getExpireTime() << std::endl;

Requests

The client includes many built-in APIs for various features of the game server. These can be accessed with the async methods. It can also call custom logic as RPC functions on the server. These can also be executed with a socket object.

All requests are sent with a session object which authorizes the client.

auto successCallback = [](const NAccount& account)
{
std::cout << "user id : "<< account.user.id << std::endl;
std::cout << "username: "<< account.user.username << std::endl;
std::cout << "wallet : "<< account.wallet << std::endl;
};
client->getAccount(session, successCallback, errorCallback);

Realtime client

The client can create one or more realtime clients with the server. Each realtime client can have it’s own events listener registered for responses received from the server.

int port = 7350; // different port to the main API port
bool createStatus = true; // if the socket should show the user as online to others.
NRtClientPtr rtClient = client->createRtClient(port);
// create listener in your class as NRtDefaultClientListener listener;
listener.setConnectCallback([]()
{
std::cout << "Socket connected" << std::endl;
});
rtClient->setListener(&listener);
rtClient->connect(session, createStatus);

To send messages to other users a user must join the chat channel they want to communicate on.

auto successCallback = [](NChannelPtr channel)
{
std::cout << "You can now send messages to channel id: "<< channel->id << std::endl;
};
string roomname = "MarvelMovieFans";
rtClient->joinChat(
roomname,
NChannelType::ROOM,
true, // persistence
false, // hidden
successCallback
);

A user joins a chat channel to start receiving messages in realtime. Each new message is received by an event handler and can be added to your UI. Messages are delivered in the order they are handled by the server.

rtListener->setChannelMessageCallback([](const NChannelMessage& msg)
{
// msg.content is JSON string
std::cout << "OnChannelMessage "<< msg.content << std::cout;
});

Matchmaker

Nakama’s matchmaker allows users to find opponents and teammates for matches, groups, and other activities. The matchmaker maintains a pool of users that are currently looking for opponents and places them together whenever a good match is possible.

auto successCallback = [](const NMatchmakerTicket& ticket)
{
std::cout << "Matchmaker ticket: "<< ticket.ticket << std::endl;
};
int32_t minCount = 2;
int32_t maxCount = 4;
string query = "*";
NStringMap stringProperties;
NStringDoubleMap numericProperties;
stringProperties.emplace("region", "europe");
numericProperties.emplace("rank", 8.0);
rtClient->addMatchmaker(
minCount,
maxCount,
query,
stringProperties,
numericProperties,
successCallback);

Join a match

Each matchmaker result event carries a token that can be used to join a match together with the matched opponents. The token enables the server to know that these users wanted to play together and will create a match dynamically for them.

rtListener->setMatchmakerMatchedCallback([this](NMatchmakerMatchedPtr matched)
{
std::cout << "Matched! matchId: "<< matched->matchId << std::endl;
rtClient->joinMatch(matched->matchId, [](const NMatch& match)
{
std::cout << "Joined Match!" << std::endl;
});
});

Send data messages

A user in a match can send data messages which will be received by all other opponents. These messages are streamed in realtime to other users and can contain any binary content. To identify each message as a specific “command” it contains an Op code as well as the payload.

string id = "<matchid>";
int64_t opCode = 1;
NBytes data = “{ \"move\": {\"dir\": \"left\", \"steps\" : 4} }";
rtClient->sendMatchData(id, opCode, data);

Join our Gitter community channel if you have further questions about using the C++ Client.

--

--