Heroic Labs Documentation

Defold Client Guide #

The client is built in Lua 5.1 on top of the Defold game engine. It is available at GitHub. It can also be used by other engines running lua, see how.

Full API documentation #

For the full API documentation please visit the API docs.

Setup #

Add the client to your project. #

Add the URL of a client release .zip as a library dependency to game.project. The client will now show up as a nakama folder in your project.

Add Defold plugins. #

Defold projects additionally require the following modules:

Authenticate #

There are many options when it comes to authentication. In this example we’ll do it with email and password but it’s just as easy to use a social profile from Google Play Games, Facebook, Game Center, etc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function init(self)
    local defold = require "nakama.engine.defold"
    local nakama = require "nakama.nakama"
    local config = {
        host = "127.0.0.1",
        port = 7350,
        username = "defaultkey",
        password = "",
        engine = defold,
    }
    local client = nakama.create_client(config)

    local email = "batman@heroes.com"
    local password = "password"

    nakama.sync(function()
        local session = client.authenticate_email(email, password)
        client.set_bearer_token(session.token)

        local account = client.get_account()
        print("user id is " .. account.user_id .. " and username is " .. account.username)
    end)
end

Sessions #

When client authenticates, the server responds with an auth token (JWT) which should be used when making API requests. The token contains useful properties and gets deserialized into a session table.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local client = nakama.create_client(config)

print(session.token) -- raw JWT token
print(session.user_id)
print(session.username)
print(session.expires)
print(session.created)

-- Configure client to use the token with API requests
client.set_bearer_token(session.token)

It is recommended to store the auth token from the session and check at startup if it has expired. If the token has expired you must reauthenticate. The expiry time of the token can be changed as a setting in the server.

1
2
3
4
5
6
7
8
9
-- Assume we've stored the auth token
local nakama_session = require "nakama.session"
local token_string = sys.load(token_path)
local session = nakama_session.create({ token = token_string })
if nakama_session.expired(session) then
    print("Session has expired. Must reauthenticate.")
else
    client.set_bearer_token(session.token)
end

Send requests #

The client includes many APIs for Nakama game server features. These can be accessed with the methods which either use a callback function to return a result or block until a response is available. You absolutely cannot use the blocking variety unless running in a coroutine.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- using a callback
client.get_account(function(account)
    print(account.user.id);
    print(account.user.username);
    print(account.wallet);
end)

-- if run from within a coroutine
local account = client.get_account()
print(account.user.id);
print(account.user.username);
print(account.wallet);

The Nakama client provides a convenience function for creating and starting a coroutine to run multiple requests synchronously:

1
2
3
4
nakama.sync(function()
    local account = client.get_account()
    local result = client.update_account(avatarUrl, displayName, langTag, location, timezone, username)
end)

Socket messages #

The client can create one or more sockets with the server. Each socket can have it’s own event listeners registered for responses received from the server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- create socket
local socket = client.create_socket()

nakama.sync(function()
    -- connect
    local ok, err = socket.connect()

    -- add socket listeners
    socket.on_disconnect(function(message)
        print("Disconnected!")
    end)

    socket.on_channelpresence(function(message)
        pprint(message)
    end)

    -- send channel join message
    local channel_type = 1 -- 1 = room, 2 = Direct Message, 3 = Group
    local channel_id = "pineapple-pizza-lovers-room"
    local persistant = false
    local hidden = false
    local result = socket.channel_join(1, channel_id, persistent, hidden)
end)

You can connect to the server over a realtime socket connection to send and receive chat messages, get notifications, and matchmake into a multiplayer match. You can also execute remote code on the server via RPC.

Handle events #

A socket object has event handlers which are called on various messages received from the server.

Event handlers only need to be implemented for the features you want to use.

CallbacksDescription
on_disconnectReceived when the client is disconnected from the server.
on_errorReceives events about server errors.
on_notificationReceives live in-app notifications sent from the server.
on_channelmessageReceives realtime chat messages sent by other users.
on_channelpresenceReceives join and leave events within chat.
on_matchdataReceives realtime multiplayer match data.
on_matchpresenceReceives join and leave events within realtime multiplayer.
on_matchmakermatchedReceived when the matchmaker has found a suitable match.
on_statuspresenceReceives status updates when subscribed to a user status feed.
on_streampresenceReceives stream join and leave event.
on_streamdataReceives stream data sent by the server.

Logs and errors #

The server and the client can generate logs which are helpful for debugging.

To enable verbose logging when you work on a client:

1
2
log = require "nakama.util.log"
log.print() -- enable trace logging in nakama client

For production use

1
log.silent() -- disable trace logging in nakama client

Full example #

A small example on how to manage a session object with Defold engine and the Lua client.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
local nakama = require "nakama.nakama"
local log = require "nakama.util.log"
local defold = require "nakama.engine.defold"

local function email_login(client, email, password, username)
    local result = client.authenticate_email(email, password, nil, true, username)
    if result.token then
        client.set_bearer_token(result.token)
        return true
    end
    log("Unable to login")
    return false
end

function init(self)
    log.print()

    local config = {
        host = "127.0.0.1",
        port = 7350,
        username = "defaultkey",
        password = "",
        engine = defold,
    }
    local client = nakama.create_client(config)

    nakama.sync(function()
        local ok = email_login(client, "batman@heroes.com", "password", "batman")
        if not ok then
            return
        end
        local account = client.get_account()
        pprint(account)

        local socket = client.create_socket()
        socket.on_channelmessage(function(message)
            pprint(message)
        end)

        socket.on_channelpresence(function(message)
            pprint(message)
        end)

        local ok, err = socket.connect()
        print("connect", ok, err)

        local channel_type = 1 -- 1 = room, 2 = Direct Message, 3 = Group
        local channel_id = "pineapple-pizza-lovers-room"
        local persistant = false
        local hidden = false
        local result = socket.channel_join(1, channel_id, persistent, hidden)
        pprint(result)
    end)
end

Adapting to other engines #

Adapting the Nakama Defold client to other Lua-based engines should be as easy as providing a different engine module when configuring the Nakama client:

1
2
3
4
5
6
local myengine = require "nakama.engine.myengine"
local nakama = require "nakama.nakama"
local config = {
    engine = myengine,
}
local client = nakama.create_client(config)

The engine module must provide the following functions:

  • http(config, url_path, query_params, method, post_data, callback) - Make HTTP request.

    • config - Config table passed to nakama.create()
    • url_path - Path to append to the base uri
    • query_params - Key-value pairs to use as URL query parameters
    • method - “GET”, “POST”
    • post_data - Data to post
    • callback - Function to call with result (response)
  • socket_create(config, on_message) - Create socket. Must return socket instance (table with engine specific socket state).

    • config - Config table passed to nakama.create()
    • on_message - Function to call when a message is sent from the server
  • socket_connect(socket, callback) - Connect socket.

    • socket - Socket instance returned from socket_create()
    • callback - Function to call with result (ok, err)
  • socket_send(socket, message, callback) - Send message on socket.

    • socket - Socket instance returned from socket_create()
    • message - Message to send
    • callback - Function to call with message returned as a response (message)