Playfab authentication issue when implementing highscore system (SOLVED)

Hi,

I’m trying to add a highscore system to my game based on the examples in https://github.com/britzl/playfabexamples and am stuck on having the player logged in so their scores can be added to the highscore list.

I see that some things have changed since the examples were written so I’ve updated lines in in authentication.lua like

local auth = savetable.open("auth").load()

to

local auth = savetable.load("auth", savetable.FORMAT_SYS)

For the line

local response, error = playfab.LoginWithPlayFab.flow(request)

I get
ERROR:SCRIPT: /playfab/authentication.lua:94: attempt to index field ‘LoginWithPlayFab’ (a function value)

And don’t understand how to fix it. If I remove the “.flow” part the line executes but I get ‘nil’ as the response and error.

Thanks for any hints!

The PlayFab API is callback based by default, meaning that you call functions like this:

playfab.LoginWithPlayFab({}, function(response)
	  playfab.GetUserData({}, function(response)
		-- handle user data
		playfab.GetLeaderboardAroundPlayer({}, function(response)
			-- handle leaderboard
		end,
		function(error)
			print(error)
		end)
	  end,
	  function(error)
		print(error)
	end)
end,
function(error)
	  print(error)
end)

It is quite messy with the nested callbacks.

The example you looked at provides a coroutine based sequential way of calling the functions as well:

local response, error = playfab.LoginWithPlayFab.flow({})
if error then
	  print(error)
	  return
end
local response, error = playfab.GetUserData.flow({})
if error then
	print(error)
	  return
end
handle user data
local response, error = playfab.GetLeaderboardAroundPlayer.flow({})
if error then
	  print(error)
	  return
end

This is only possible if the PlayFab API is modified to support coroutines as well. The setup for this happens here:

2 Likes

Thanks! Then I added

local util = require "playfab.util"
util.flow_apis()

to my authentication.lua and now when I try

local response, error = playfab.LoginWithPlayFab.flow(request)

or

local response, error = playfab.LoginWithPlayFab.flow({request})

I get this error:
ERROR:SCRIPT: /ludobits/m/flow.lua:113: You must provide a coroutine

How should I use flow and give the request argument to the function?

Yes, so you need call the playfab.LoginWithPlayFab function from within a coroutine. Like this (you can also use the flow module but it is not strictly needed):

-- create and immediately start coroutine
coroutine.wrap(function()
    local response, error = playfab.LoginWithPlayFab.flow(request)
end)()

Ok thanks again! I do like the flow look of the code compared to the nested callbacks.
I’m unsure of how much of my code should be wrapped in the coroutine. Does this look ok?

function M.login(username, password)
	local auth = savetable.load("auth", savetable.FORMAT_SYS) or {}
	auth.username = username or auth.username
	auth.password = password or auth.password
	if auth.username and auth.password then
		local request = {
			TitleId = playfab.settings.titleId,
			Username = auth.username,
			Password = auth.password,
		}
		coroutine.wrap(function()
			local response, error = playfab.LoginWithPlayFab.flow(request)
			if error then
				auth.username = nil
				auth.password = nil
				M.listeners.trigger(M.LOGIN_FAILED, error)
			else
				auth.response = response
				pprint(response)
				M.listeners.trigger(M.LOGIN_SUCCESS, response)
			end
		end)()
		savetable.save(auth)
		return response, error
	else
               -- code continues....

Your suggestion will not work. It is only the code within the coroutine that will run in sequence and as soon as the code in the coroutine hits something async (such as playfab.LoginWithPlayFab) the code will stop while waiting for the response and the code outside will continue to run. Consider this:

print("one")
coroutine.wrap(function()
	print("two")
	local response, error = playfab.LoginWithPlayFab.flow(request)
	print("three")
end)()
print("four")

This will print:

one
two
four
three
1 Like

Ok now I understand better, thanks! I used flow in the end and wrapped the call to the login function with a flow.start instead of like

	flow.start(function()
		local response, error = authentication.login()
		if error then
			pprint(error)
		end
	end)

and it’s working great :slight_smile:

1 Like