Closed SamsTheGreatest closed 2 years ago
https://heroiclabs.com/docs/nakama/concepts/multiplayer/matchmaker/ would probably have been helpful but docs are missing there. Would it be possible to restore them?
@tomglenn is working on updated Defold documentation. I do not know the status of this.
I am trying to make this example work https://defold.com/2021/03/02/Creating-online-games-using-Nakama-and-Defold/ but I was not able to use the same matchmaking function. It seems to have been renamed to
matchmaker_add
. Now I'm trying to make that work.
Yeah, that article (and accompanying example project) will have to be updated at some point to match the new Nakama release.
What you want to do now is to call the matchmaker_add() function on the client socket:
local client = nakama.create_client(config)
local socket = client.create_socket()
local result = socket.matchmaker_add(2, 2, "*")
Thanks @britzl. @tomglenn do you know how long is it going to take? Then I would syspect there is something wrong implementaiton of the function perhaps? Passing just 2 of those values will result in assertion error as nil values for other parameters are not allowed?
There is a parameter count_multiple which is not mentioned in any docs. And even if I run the function like solocal result = socket.matchmaker_add(2, 2,"*",{},{},10)
I get EVENT_DISCONNECTED: Server closing (1005). Reason: ''
. Could there be something wrong on the server side I did not set up properly?
Everything works if I replace those lines in socket.lua
:
assert((not string_properties) or _G.type(string_properties) == 'table')
assert((not numeric_properties) or _G.type(numeric_properties) == 'table')
assert((not count_multiple) or _G.type(count_multiple) == 'number')
It's basically allowing to not provide other parameters.
Also was meant to mention, log statements across lua
files don't do anything, were we supposed to call log.print() in each file to make debugging work?
Hi @SamsTheGreatest, we do not currently have an ETA on when the full Defold Client SDK docs will be updated to the latest 3.0 release, however it is something we are actively working on. I apologise I do not have a more concrete answer than this at this time.
Passing just 2 of those values will result in assertion error as nil values for other parameters are not allowed?
Ah, hmm, yes, maybe we should accept nil for any value. There is however nothing in the API spec which says if an argument is required or optional.
@tomglenn do the other engine APIs accept null values for function arguments?
Also was meant to mention, log statements across
lua
files don't do anything, were we supposed to call log.print() in each file to make debugging work?
The log is silent by default. You can call log.print()
once to switch to using print()
logging.
There is a parameter count_multiple which is not mentioned in any docs
https://github.com/heroiclabs/nakama-common/blob/master/rtapi/realtime.proto#L378
@britzl For this function specifically yes, the .NET client does allow null
values for example (https://github.com/heroiclabs/nakama-dotnet/blob/master/Nakama/Socket.cs#L202).
Hi @SamsTheGreatest, we do not currently have an ETA on when the full Defold Client SDK docs will be updated to the latest 3.0 release, however it is something we are actively working on. I apologise I do not have a more concrete answer than this at this time.
Thanks for clarifications. Would it be possible for me to refer to something apart from source code when waiting for docs from your side? I am trying to assemble a match-based game and have some questions regarding the development. Would be very cool to have updated docs and more examples ;)
Btw, when trying to test matchmaker API, what is the best practice? Do I have to clone the project manually and then run another client, simulating a new user trying to join the match? Or is there something more neat to test if I can create a match? I have some more devices at hand, could this be helpful? Maybe you could give some tips :)
@SamsTheGreatest For this function specifically yes, the .NET client does allow
null
values for example (https://github.com/heroiclabs/nakama-dotnet/blob/master/Nakama/Socket.cs#L202).
I am not sure if I can propose changes to your code but check the snippet above, worked for me:) I have added a pull request with the proposed change.
Btw, when trying to test matchmaker API, what is the best practice? Do I have to clone the project manually and then run another client, simulating a new user trying to join the match? Or is there something more neat to test if I can create a match? I have some more devices at hand, could this be helpful? Maybe you could give some tips :)
The problem is that you can't launch multiple instances of your project from the editor. What I usually do is to drop a Defold engine binary in the root of my project and use that to launch additional instances:
dmengine
from the project root (download from http://d.defold.com/stable/)@britzl so if I do open -n -a defold
and open the same project there its not the same? Could you clarify how to run a copy of dmengine
the way you describe? I have one defold app in my Applications
folder. dmengine
shall be placed in the folder of the project itself? is it different from defold.app
?
dmengine
shall be placed in the folder of the project itself? is it different fromdefold.app
?
dmengine is the game engine itself. Defold.app is the editor, build tools etc
@britzl what would be the right dmengine
file I need to download from there? And how do I use it afterwards?
what would be the right
dmengine
file I need to download from there
From your other comments it looks like you are using a Mac. You then need an x86_64-darwin executable. Here's the one for Defold 1.3.2:
You put it in the root of your project and run it from the terminal:
$> ./dmengine
You probably also need to set the executable flag on the file:
$> chmod +x dmengine
@britzl works like a charm! But then I need to make changes to the Nakama plugin (due to corrupted code). I can do it within the editor but changes will not be saved. I downloaded it manually and store it in the same fashion as the defold
does by "default" (hehe). That seems to have worked, but now websocket
extension is complaining (only! when running via ./dmengine
), specifically DEBUG:SCRIPT: nakama/engine/defold.lua:152: attempt to index global 'websocket' (a nil value)
- guess it refers to that it cant find connect
method in the table?. I did not download it in the same fashion, just provided as a dependency in the project
file. I still don't understand the mechanics of how websocket
is being loaded in the code without require("websocket")
statement tbh...
Ah, sorry about that! You are using extensions. Doh. My mistake. Then you need a version of the engine with the websocket extension included. This custom version with your extensions can be found in PROJECT_ROOT/build/x86_64-osx/dmengine
Thanks @britzl I am able to run it now! :)
Just trying to make the basics work..
So now I am able to create amultiple instances of clients. I do authetication by email, and i generate a random email and username for each client. Server dashboard shows 4 active sessions when I have 4 clients (happy times I guess?).. Aight so when I then do
local result = socket.matchmaker_add(2, 2,"*")
pprint(result)
output - > DEBUG:SCRIPT: { --[[0x11af6fae0]]
cid = "1",
matchmaker_ticket = { --[[0x11af6fbb0]]
ticket = "8314f4eb-902c-4b0f-bc1c-8b3465a58496"
}
}
Happy times again?
additionally, before calling matchmaker_add
I registered events:
socket = client.create_socket()
socket.on_matchmaker_matched(function(message)
log('We are matching')
local matched = message.matchmaker_matched -- make sure we got matched
if matched then
print(matched.match_id)
print(matched.token)
end
end)
socket.on_match_presence_event(function(message)
log("on_match_presence_event:",message)
end)
socket.on_match_data(function(message)
log("on_match_data:",message)
end)
local ok, err = socket.connect()
And on the server I have main.lua with:
function matchmaker_matched(context, matched_users)
log('hello world')
end
nk.register_matchmaker_matched(matchmaker_matched)
When I start the server it says Registered Lua runtime Matchmaker Matched function invocation
, but the function matchmaker_matched
never seems to be called, when clients are connected. Neither there is any print statement on the client..
Am I missing something?
And on the server I have main.lua with:
function matchmaker_matched(context, matched_users) log('hello world') end nk.register_matchmaker_matched(matchmaker_matched)
When I start the server it says
Registered Lua runtime Matchmaker Matched function invocation
, but the functionmatchmaker_matched
never seems to be called, when clients are connected. Neither there is any print statement on the client..
I can't remember the details of the server I'm afraid. The snippet of code you shared for main.lua, is that actually all you have in the file or did you extract only that bit? Make sure you have a structure similar to this:
https://github.com/defold/game-xoxo-nakama-server/blob/main/main.lua
@britzl I do imports, of course, tried also with run_once
just like in your example but nothing gets printed neither on the server nor on the client. I even copied all code from your server example and still get the same result.
Tried giving extra arguments to matchmaker_add
but all the same doesn't work for some reason...
Once client connects the server only outputs this:
nakama_1 | {"level":"info","ts":"2022-05-11T16:43:10.607Z","caller":"server/session_ws.go:80","msg":"New WebSocket session connected","uid":"e8541466-c7da-410d-bdd7-eb35b7ef739e","sid":"71584884-d149-11ec-888d-7106fdcb5b46","format":0}
nakama_1 | {"level":"debug","ts":"2022-05-11T16:43:10.649Z","caller":"server/pipeline.go:65","msg":"Received *rtapi.Envelope_MatchmakerAdd message","uid":"e8541466-c7da-410d-bdd7-eb35b7ef739e","sid":"71584884-d149-11ec-888d-7106fdcb5b46","cid":"1","message":{"MatchmakerAdd":{"min_count":2,"max_count":2}}}
nakama_1 | {"level":"debug","ts":"2022-05-11T16:43:10.651Z","caller":"server/session_ws.go:395","msg":"Sending *rtapi.Envelope_MatchmakerTicket message","uid":"e8541466-c7da-410d-bdd7-eb35b7ef739e","sid":"71584884-d149-11ec-888d-7106fdcb5b46","envelope":"cid:\"1\" matchmaker_ticket:{ticket:\"d8cd02f2-8982-4536-80b2-eb8ec4a61bf6\"}"}
Thanks for your help!
It worked after upgrading server to 3.11.0
, praise the lord
Happy to hear you got it working!
@britzl could I ask another thing. When authenticated does it make sense to store a session
as a variable? For example I have a file client_nakama.lua
that I then import into main.script
In client_nakama.lua
:
local client
local session
local socket
local account
M = {}
function M.create_client()
local config = {}
config.host = sys.get_config("nakama.host", "127.0.0.1")
config.port = tonumber(sys.get_config("nakama.port", "7350"))
config.use_ssl = (config.port == 443)
config.username = sys.get_config("nakama.server_key", "defaultkey")
config.password = ""
config.engine = defold
config.timeout = 10
client = nakama.create_client(config)
end
function M.authenticate_email()
local email = "super@heroes.com"
local password = "batsignal"
nakama.sync(function()
session = client.authenticate_email(email, password) --client table is accessible from here
pprint(session) -- prints session
nakama.set_bearer_token(client, session.token)
end)
pprint(session) -- no print!!!!
end
return M
in main.script
in init
(I guess we only must do it in init
?) I import client.lua
and then do
local nakama_client = require 'main/client_nakama'
nakama_client.create_client()
nakama_client.authenticate_email()
The first print statement of the session
is executed. The second one gives session->nil
as we set on top of the lua script. So I have to do everything in a single sync
call?
Imagine I have an app, launch it, do login via email, and get my session (which is only accessible in coroutine). Can I use this session somehow during other stages of the game? Like when a player tries to join a match, or do I have to get a new session (reauthenticate)? Do I save the session token and then restart the session when a player wants to join a match? What's the right workflow? Guess all via callbacks as in your xoxo example?
Can I use this session somehow during other stages of the game? Like when a player tries to join a match, or do I have to get a new session (reauthenticate)? Do I save the session token and then restart the session when a player wants to join a match? What's the right workflow? Guess all via callbacks as in your xoxo example?
The recommended way is to:
nakama.set_bearer_token()
To allow for relogin for a returning user you can also store the token and check if it has expired and only re-authenticate if the token has expired.
All of this is mentioned in the Sessions section of the readme:
https://github.com/heroiclabs/nakama-defold#sessions
Also a note on your example:
function M.authenticate_email()
local email = "super@heroes.com"
local password = "batsignal"
nakama.sync(function()
session = client.authenticate_email(email, password) --client table is accessible from here
pprint(session) -- prints session
nakama.set_bearer_token(client, session.token)
end)
pprint(session) -- no print!!!!
end
The reason why the pprint() at the end of the authenticate_email() function is nil is because the code inside the nakama.sync() block is asynchronous and involves server communication etc. The pprint() below the nakama.sync() will run immediately and before the code inside the sync has finished.
@britzl Looking at your example here https://github.com/defold/game-xoxo-nakama-client/blob/main/main/xoxo_nakama.lua I was trying to implement something similar. I have a game.script
where I do all drawing, spawning objects and such, as well as recording player inputs. I have added middle-layer client.lua
which handles all nakama-related things. I then import this module into all other defold scipts
such as game.script
.
Now, in game.script
I call client.lua
module, I am able to log in, and add event hooks (guess that's what you call them). I reached the stage when the server adds us to a match and sends back game states, I am able to receive them no problem.
Now I am trying to send inputs from game.script
to client.lua
somehow. You did it in reverse, you import game.script
into client.lua
and then hook an input event using game.on_player_input(callback)
.
Do you think I would be able to communicate with game.script
with my setup in any way? Specifically, I am trying to send match state to game.script
and send user inputs to client.lua
.
client.lua to game.script:
nakama.sync(function()
local socket = nakama_socket.create(client)
...
socket.on_matchmaker_matched(function(message)
local match_id = message.matchmaker_matched.match_id <-- match_id is only available within sync call right?
...
end)
socket.on_match_data(function(message)
local data = json.decode(message.match_data.data)
local op_code = tonumber(message.match_data.op_code)
if op_code == 1 then
msg.post('game#game', 'update_state', data) <-- will this send the match state to game.script?
end)
end)
game.script to client.lua:
function on_input(self, action_id, action)
if action_id == hash("jump") then
msg.post('client', 'jump') <-- will this send the input to client.lua?
end
I need match_id
to be able to send inputs to nakama, but its locked inside the nakama.sync
, so I need to define some function in the nakama.sync
scope just like you did in your code.
Is it possible to implement this with my setup? I don't think doing it in reverse like in your script is good for my case as I import client.lua
in a lot of script files over game lifecycle.
All those nested callbacks look like spaghetti to me for now 🤪. Wonder if its possible to bring some structure to it.
msg.post('client', 'jump') <-- will this send the input to client.lua?
You can't post messages to Lua modules. I think I'd create a client.lua module and let that handle communication with Nakama and let it receive and send user input/actions:
-- client.lua
local M = {}
-- these are values that you need access to from all functions in this module
local current_match_id = nil
local client = nil
local socket = nil
-- send a message to game.script on the game object name "game"
local function send_to_game(message_id, message)
msg.post("game#game", message_id, message or {})
end
local function on_matchmaker_matched(message)
current_match_id = message.matchmaker_matched.match_id
send_to_game("matchmaker_matched")
end
local function on_match_data(message)
local data = json.decode(message.match_data.data)
local op_code = tonumber(message.match_data.op_code)
if op_code == 1 then
send_to_game.post('update_state', data)
end
end
-- login
-- create client and socket
-- set up event handlers
function M.login(email, password)
nakama.sync(function()
-- create and authenticate client
client = nakama.create_client(config)
local session = client.authenticate_email(email, password)
-- handle client connect errors
...
nakama.set_bearer_token(client, session.token)
-- create and connect socket
socket = nakama_socket.create(client)
local ok, err = socket.connect()
-- handle socket connect errors
...
-- set up socket event handlers
socket.on_matchmaker_matched(on_matchmaker_matched)
socket.on_match_data(on_match_data)
end)
end
function M.jump()
-- buffer and or send here
end
return M
-- game.script
local client = require "client"
function on_input(self, action_id, action)
if action_id == hash("jump") then
client.jump()
end
end
@britzl thank you, I made it work your way! However when sending an encoded message to the server though, I get:
on server:
nakama_1 | {"level":"warn","ts":"2022-05-18T16:25:43.463Z","caller":"server/session_ws.go:239","msg":"Received malformed payload","uid":"8fe7bfbe-613e-4e28-8d8b-13e716e63776","sid":"21a1a0a4-d6c7-11ec-89a6-7106fdcb5b46","data":"eyJtYXRjaF9kYXRhX3NlbmQiOnsib3BfY29kZSI6MiwiZGF0YSI6IntcImxlZnRcIjpmYWxzZSxcImp1bXBcIjp0cnVlLFwicmlnaHRcIjpmYWxzZX0iLCJtYXRjaF9pZCI6ImFlZTMzMDdhLTUwZmQtNGJkOS1iZTI3LTM0M2ZlZDA0YmEwYi5uYWthbWExIn0sImNpZCI6IjMifQ=="}
decoding data gives:
{"match_data_send":{"op_code":2,"data":"{\"left\":false,\"jump\":true,\"right\":false}","match_id":"aee3307a-50fd-4bd9-be27-343fed04ba0b.nakama1"},"cid":"3"}
original message before encoding:
action = {jump = true,left = false,right = false}
The only error message in Defold logs is ERROR:WEBSOCKET: Failed to setup callback
Does it make sense or am I doing something wrong again? 😅
Something similar here but it seems like it's not the OP_CODE that's causing the trouble:
There seems to be a typo either in socket_send
or match_data_send
of socket.lua
, when composing a message there is match_data_send
but socket_send
wants just match_data
. As a result, b64 encoding is ommited. I am not sure which one was intended to be used here.
Tested, all stuff in socket_send
shall be renamed to match_data_send
.
Good catch!
Thanks for the client implementation! I am trying to make this example work https://defold.com/2021/03/02/Creating-online-games-using-Nakama-and-Defold/ but I was not able to use the same matchmaking function. It seems to have been renamed to
matchmaker_add
. Now I'm trying to make that work. https://heroiclabs.com/docs/nakama/concepts/multiplayer/matchmaker/ would probably have been helpful but docs are missing there. Would it be possible to restore them?