Creating online games using Nakama and Defold

It feels like a pretty complicated way of testing local multiplayer, but yes, using Nakama should work!

Ahahah, It is just that I am alone at home (thank you corona virus!) and my art director and friend is in her houseā€¦

1 Like

To conclude on this, I can confirm that the latest update to the Websocket extension fixes this! :partying_face:

4 Likes

I also have trouble getting this example to work with my own server:

So far, nobody was able to help.

You have received a reply on the HeroicLabs forum a few hours ago: Ways to debug server code (Nakama Defold tictactoe XOXO example) - #8 by flavio - Heroic Labs

Thanks Britzl! Now it kinda works, but the bug with the default (Defold?) port for Nakama still persists. That is not fixed for me. I have the newest Defold and Nakama.

I added a PR for this change to the server. Please merge it for everyone else. Thanks!

Thanks. Iā€™ll take a look soon.

Which bug is that?

When you run locally with the default nakama port both players win when one player wins.

I started my playtest with Nakama once again, now following this tutorial and found some issues. Here is my experience:

Nakama local server setup

Running Nakama server from Docker was flawless on Ubuntu following just this few commands (I already had docker installed, old, but enough), but the link in tutorial is not pointing directly to the page mentioned (correct link is now: Heroic Labs Documentation | Docker Compose):

$ docker --version
Docker version 20.10.24, build 297e128
$ git clone https://github.com/heroiclabs/nakama-project-template.git
Cloning into 'nakama-project-template'...
...
$ cd nakama-project-template/
$ docker compose up

You might want to need to run docker with sudo.

Then the server is booting up and a lot is printed in console and the last message is:

backend           | {"level":"info","ts":"2023-06-18T08:59:38.242Z","caller":"main.go:175","msg":"Startup done"}

If everything is ok (like it was for me) you can open server website/admin panel on any web browser at http://127.0.0.1:7351 and login with admin and password default credentials. It is a server you run locally, so perfect for testing.

Defold integration

Defold integration have a picture of used libraries - it would be great to put there links as well. After quick search you can find it of course, but it would be good to have them in tutorial. Newest versions are 3.1.0 and thatā€™s what I used rn (version from tutorial is not compiling websocket native extension on this day).

image

Links:
https://github.com/heroiclabs/nakama-defold/archive/3.1.0.zip
https://github.com/defold/extension-websocket/archive/3.1.0.zip

Following the first few steps I have an initial code:

local defold = require "nakama.engine.defold"

-- The Nakama server configuration
local config = {}
config.host = "127.0.0.1" -- localhost, ie your own machine
config.port = 7351
config.use_ssl = (config.port == 443)
config.username = "admin" -- your Nakama server key, default is "defaultkey"
config.password = "password"
config.engine = defold -- Tell Nakama to use Defold (it can theoretically also work with other Lua based engines)


-- authentication using device id
local function device_login(client)
	-- login using the token and create an account if the user
	-- doesn't already exist
	local result = nakama.authenticate_device(client, defold.uuid(), nil, true)
	if result.token then
		-- store the token and use it when communicating with the server
		nakama.set_bearer_token(client, result.token)
		return true
	end	
	print("Unable to login")
	return false
end


function init(self)
	local client = nakama.create_client(config)
	device_login(client)
end

A small issue is that 127.0.0.1 is malformed number:
image

So I put it as a string. I donā€™t know yet, if this is wrong or not, but will probably find out! :smiley:

And here I am stopped at the moment, because:

image

Even though the extensions are fetched:

image

Canā€™t find any similar problem on forum, so I would love a help! :wink:


UPDATE 1:

Duck-debugging is always helpful, so after writing it down, I noticed in the code there is no require and nakama is not included in the namespace like e.g. defos is, but there is a Lua module named nakama, so I guess this is fixing the issue (but this is the issue after following tutorial, so I have to note it down, for improvement):

local nakama = require "nakama.nakama"

Next is that Nakama must be run from within a coroutine:

image


UPDATE 2:

So I put it in coroutine like this:

function init(self)
	local co = coroutine.create(function()
		local client = nakama.create_client(config)
		device_login(client)
	end)

	local ok, err = coroutine.resume(co)
end

And for now I am not able to log in, so will try to debug it:

image

But one user account is at least found in my local server admin dashboard:

But itā€™s a system user :sweat_smile:

By printing result I get:

image

So, I guess the user is not found, so canā€™t log in. Strange, because the call is to create the user
( @param create_bool () Register the account if the user does not already exist.)

2 Likes

Not entirely true. You can either run from within a coroutine to get nice looking synchronous code as in your example, but you could also use a callback:

local id = defold.uuid()
local vars = nil
local create = true
local username_str = nil
nakama.authenticate_device(client, id, vars, create_bool, username_str, function(result)
    if result.token then
		-- store the token and use it when communicating with the server
		nakama.set_bearer_token(client, result.token)
	end	
end)

Thatā€™s strange. No idea whatā€™s going on there!

I think you need to set the username_str when creating the user.

1 Like

Yes, this is also working, good to know! (but I just noted what is an error that is given to user when following the tutorial).

From the code I understand the config.host actually should be string, so the tutorial should be corrected imho.

It didnā€™t help :confused: All the time I got response:

DEBUG:SCRIPT: Logging in,
{ --[[0x7f62429e2940]]
  message = "Not Found",
  error = true,
  code = 5
}

Socket creation

This also is problematic following the code from the tutorial, because there are simply no such functions in nakama Lua module:

local socket = nakama.create_socket(client)
local ok, err = nakama.socket_connect(socket)

Instead, I found those:

local nakama_socket = require "nakama.socket"
local socket = nakama_socket.create(client)
local ok, err = nakama_socket.connect(socket)

But it for now is not working for me, because of the previous problem, without any user, resulting in not heaving a correct bearer_token :confused:

True. Iā€™ll see if I can update the tutorial to work with the latest version of Nakama!

1 Like

I would be very thankful! I canā€™t dig into the issues atm, but I will try to dive in asap. For now the XOXO example is not working for me out of the box. I printed the result from xoxo_nakama.lua:L24 under ā€œUnable to loginā€:

I can reach the dashboard from XOXO example via browser.

2 Likes

Hey @Pawel, Iā€™m also messing around in Nakama and recognise some issues, so I might be able to help!

Fƶr The couroutine stuff I use the nakama.sync() helper function which works great.

Iā€™m using authenticate_custom() to log the player in, getting the id from Google or Appleā€™s authentication. See comments in the code.

nakama.sync(function()

		print("Authenticating Nakama.")

		local custom_id = social_user_id -- Used every time when logging in.
		local vars = nil
		local create_bool = true
		local username_str = social_user_id -- Used once when creating an account. Optional, if nil Nakama will create a unique username.
		local callback = nil
		local retry_policy = nil
		local cancellation_token = nil
		local authenticate_result = nakama.authenticate_custom(client, custom_id, vars, create_bool, username_str, callback, retry_policy, cancellation_token)

		if authenticate_result.error then
			print("Authentication error")
			pprint(authenticate_result)
			return
		end

		print("Authenticated.")
		pprint(authenticate_result)

		nakama.set_bearer_token(client, authenticate_result.token)

end)
4 Likes

Thank you @totebo! This works also well!

So the issue I have is that I forcefully changed the port to the same port, where I can see the Dashboard (7351) instead of the one shown in examples (7350). I can authenticate and receive a correct session when using 7350!

So for future me:

{ --[[0x7f62429e2940]]
  message = "Not Found",
  error = true,
  code = 5
}

Might just mean you are not connecting to your server and should check the port.

2 Likes

Ah, glad you managed to figure it out! It would be nice with a reference to the error codes, to avoid this type of confusion.

1 Like

Going on, I have this issue when trying to write anything to Storage:

local result = nakama.write_storage_objects(client, {[test] = 1}) -- even with 2nd argument being nil
pprint(Result, result)

Gives me:

DEBUG:SCRIPT: Result,
{ --[[0x7f6f2c1839f0]]
  message = "proto: syntax error (line 1:12): unexpected token {",
  code = 3,
  error = true
}

I think youā€™re missing a few parameters. Check out this page where there is an example:

local objects = {
  {
    collection = "Favorites",
    key = "Hats",
    value = json.encode({ "cowboy", "alien" }),
    permissionRead = 1,
    permissionWrite = 1,
    version = ""
  }
}
local result = nakama.write_storage_objects(client, objects)
3 Likes

Thank you again @totebo!

Result is though:

DEBUG:SCRIPT: Result,
{ --[[0x7f2b6ac3c4b0]]
  code = 3,
  error = true,
  message = "Value must be a JSON object."
}

Strange, but what I had to change is value to be a table of keys and pairs:

...
value = json.encode({ cowboy = 1, alien = 2}),
...

And then it works! I can store and read!

3 Likes

Another thing to point out in tutorial is socket connection:

Socket connect function was removed from nakama and moved to nakama.socket, so we should require it:

local realtime = require "nakama.socket"

And use function connect on created socket (create_socket is still part of nakama module):

local socket = nakama.create_socket(client)
local ok, err = realtime.connect(socket)

Buut, it still doesnā€™t work for me :smiley:

AL lib: (EE) ALCpulsePlayback_streamStateCallback: Received stream failure!