Game unable to load DataStore player data

First of all, yes, API services are enabled and the game is published.
I am making a simple obby game that has a checkpoint system, and I’d like it so that the checkpoints save between sessions.
After following several tutorials, here’s what I’ve got:

local Players = game:GetService("Players")

local Checkpoints = workspace.Checkpoints
local DefaultSpawn = Checkpoints:WaitForChild("1")

local DatastoreServive = game:GetService("DataStoreService")
local Database = DatastoreServive:GetDataStore("data")
local SessionData = {}



game.Players.PlayerAdded:Connect(function(player)
	player.RespawnLocation = DefaultSpawn
	
	local leaderstats = Instance.new("Folder",player)
	leaderstats.Name = "leaderstats"
	
	local stage = Instance.new("IntValue",leaderstats)
	stage.Name = "Stage"
	stage.Value = 1
end)



function PlayerAdded(player)
	local success = nil
	local playerdatavalue = nil
	local attempt = 1
	
	repeat
		success, playerdatavalue = pcall(function()
			return Database:GetAsync(player.UserId, SessionData[player.UserId])
		end)
		attempt += 1
		if not success then
			warn(playerdatavalue)
			task.wait(3)
		end
	until success or attempt == 5
	
	if success then
		print("Connected to database")
		if not playerdatavalue then
			print("Assigning default data")
			playerdatavalue = {
				["Stage"] = 1
			}
		else
			warn("Unable to get data for", player.UserId)
			player:Kick("Unable to load your data, please try again later.")
		end
		SessionData[player.UserId] = playerdatavalue
	end
end
Players.PlayerAdded:Connect(PlayerAdded)



function PlayerLeaving(player)
	if SessionData[player.UserId] then
		local success = nil
		local errormsg = nil
		local attempt = 1
		
		repeat
			success, errormsg = pcall(function()
			Database:SetAsync(player.UserId, SessionData[player.UserId])
			end)
			attempt += 1
			if not success then
				warn(errormsg)
				task.wait(3)
			end
		until success or attempt == 5
		
		if success then
			print("Data saved for", player.Name)
		else
			warn("Unable to save for", player.Name)
		end
	end
end
Players.PlayerRemoving:Connect(PlayerLeaving)

First try, it worked and assigned me the default data for the first time join. But now when joining the game, it immediately gives me a load error and kicks me. This is part of the script but it doesn’t even do the attempts. It’s just an immediate kick the moment I load in.

I’m unsure as to why this is happening; I’m quite the coding noob so if this is an obvious solution then I apologise lol.

1 Like

Idk where the 49th line at, just because it’s not written like in studio, but your problem should to be on the 49th line.

Did this line warn? If not, can you add a line in your else statement to warn the error? Just here:

else
	warn("Unable to get data for", player.UserId)
    warn("Error thrown: ", playerdatavalue)
	player:Kick("Unable to load your data, please try again later.")
end

What is the warning?

1 Like

You’ll have to forgive me as I have zero clue when it comes to coding and am still trying to learn.
I don’t fully understand what you’re saying here, but I can provide line 49:

warn("Unable to get data for", player.UserId)

It is part of this section:

function PlayerAdded(player)
	local success = nil
	local playerdatavalue = nil
	local attempt = 1
	
	repeat
		success, playerdatavalue = pcall(function()
			return Database:GetAsync(player.UserId, SessionData[player.UserId])
		end)
		attempt += 1
		if not success then
			warn(playerdatavalue)
			task.wait(3)
		end
	until success or attempt == 5
	
	if success then
		print("Connected to database")
		if not playerdatavalue then
			print("Assigning default data")
			playerdatavalue = {
				["Stage"] = 1
			}
		else
			warn("Unable to get data for", player.UserId)
			player:Kick("Unable to load your data, please try again later.")
		end
		SessionData[player.UserId] = playerdatavalue
	end
end
Players.PlayerAdded:Connect(PlayerAdded)

From what I can tell, it doesn’t give me any warning. It just says “Unable to load data for [userid]” and then gives me the kick message. It does this instantly instead of waiting and doing the loading attempts.

I just thought - maybe I need a line that tells the script to attempt to load the data? Reading over it again, it seems like it just goes straight to the warn without even attempting. If this is the case, what would I add?

It keeps on failing because you tell the programm to kick you once it successfully retrieves the data.

if success then
	-- Data does not exist yet
	if not playerdatavalue then
		print("Assigning default data")
		playerdatavalue = {
			["Stage"] = 1
		}

    -- Since you already checked if the data does not exist,
    -- this 'else' will only fire if you actually have data.
    -- So basically you can remove this from here...
	else
		warn("Unable to get data for", player.UserId)
		player:Kick("Unable to load your data, please try again later.")
    -- ...until here
	end

	SessionData[player.UserId] = playerdatavalue
end

I think that the :GetAsync() is causing the problem as you are providing not only the key, which is fine, but also user-data, which does not work with :GetAsync(). The second argument of :GetAsync() can be either an Instance of DataStoreGetOptions or nil.

Line 49 is NOT the problem. It is a result of the wrongly used :GetAsync(). As the method is wrapped in a pcall it’s not going to output the error of the argument-missmatch, meaning it results in nil as being returned as data.

Remove the SessionData[player.UserId] from your :GetAsync()-Call and try again.

References:

Let me know whether this helped you out or not.
Feel free to ask questions.

  • DosenSuppe2_0

Removing that part unfortunately made no difference.
My issue is coding is something that just goes straight over my head; whenever I try to learn it my brain just shuts off, so a lot of these terms are hard for me to understand. This is why I have to so closely follow tutorials. It’s super annoying cus I bet you’re giving me a pretty straight forward answer and yet I still can’t understand it XD
For reference, here’s the two tutorials I used to make my leaderstats and the datastore:

Ahh ok, I understand.
I think that maybe the problem lies with the if-statement.

You check if the playerdatavalue exists by using if not playerdatavalue then.
If the playerdatavalue is nil you give it a default value.

However, if not, you kick the player because you are unable to load the data?
That’s the part that make no sense, as you have data at that point.

I think you meant to do this:

if success then -- data has been loaded successfully

	print("Connected to database")
	if not playerdatavalue then
		print("Assigning default data")
		playerdatavalue = {
			["Stage"] = 1
		}
	end

		SessionData[player.UserId] = playerdatavalue


else -- an error interrupted the loading
	warn("Unable to get data for", player.UserId)
	player:Kick("Unable to load your data, please try again later.")
end

It’s within your PlayerAdded-function

1 Like

That worked, thank you so much! Just a couple more questions:

  1. What is it that you did to change the script? Would you mind explaining it to me so that I could learn and understand? :slight_smile:

  2. If I test the game, get a checkpoint, stop the test then load the test back in, it resets me back to default spawn (checkpoint 1). Is this because it just won’t do it in Studio and it’ll work on Roblox itself, or is something else wrong? (Also, when I give myself a further checkpoint to test part of the obby, it still doesn’t spawn me at that checkpoint upon death)

Thank you for the help so far!!

To your first question:
I moved the else-statement of the “not playerdatavalue”-if-statement to the “success”-if-statement, in other words, I changed the scope of the else-statement, as seen in the picture.

(I moved the else-statement from the pink scope to the blue scope.)

Now off to your second question:

There are some common errors that could cause the issue you are describing.

Are you using the same Datastore when saving/ loading data?
Are you using the same Key when saving/ loading data?
Are you using the Data correctly?

The first two are self explaining. The last one is when you missuse the data you saved.
An example would be incorrect indexing.

-- given Data:
-- imagine that the "data" variable is the data received from the DataStore after using :GetAsync()
local data = {
    ["checkpoint"] = 4
}

-- rather than using "checkpoint" as index, you accidentally use "Checkpoint" as index:
print(data["Checkpoint"]) -- this is going to print "nil" as there is no data connected to the key "Checkpoint".

-- this would be the correct key: "checkpoint"
print(data["checkpoint"]) -- this is going to print "4"

If it’s neither of those issues I would recommend you to send me the code which handles the :SetAsync() or :UpdateAsync() on your Datastore.

Additionally, make sure that you are receiving the data by printing it after it has been loaded:

if success then -- data has been loaded successfully

	print(playerdatavalue) -- is the value printed by this method expected?

	print("Connected to database")
	if not playerdatavalue then
		print("Assigning default data")
		playerdatavalue = {
			["Stage"] = 1
		}
	end

	SessionData[player.UserId] = playerdatavalue


else -- an error interrupted the loading
	warn("Unable to get data for", player.UserId)
	player:Kick("Unable to load your data, please try again later.")
end

Sometimes it can take a while for ROBLOX to catch up with loading/ saving to their datastores.

  • DosenSuppe2_0

Alright, so
I had a look through trying to figure out what’s up based on what you said, but just can’t wrap my head around it honestly.
I did add print(playerdatavalue) and yet it printed nothing of the sort, still just “connected to database”.
I’m going to provide the whole script, because maybe you can catch something in it that I can’t? You might be able to find an error I’m blind to lol.

local Players = game:GetService("Players")

local Checkpoints = workspace.Checkpoints
local DefaultSpawn = Checkpoints:WaitForChild("1")

local DatastoreServive = game:GetService("DataStoreService")
local Database = DatastoreServive:GetDataStore("data")
local SessionData = {}



game.Players.PlayerAdded:Connect(function(player)
	player.RespawnLocation = DefaultSpawn
	
	local leaderstats = Instance.new("Folder",player)
	leaderstats.Name = "leaderstats"
	
	local stage = Instance.new("IntValue",leaderstats)
	stage.Name = "Stage"
	stage.Value = 1
end)



function PlayerAdded(player)
	local success = nil
	local playerdatavalue = nil
	local attempt = 1
	
	repeat
		success, playerdatavalue = pcall(function()
			return Database:GetAsync(player.UserId, SessionData[player.UserId])
		end)
		attempt += 1
		if not success then
			warn(playerdatavalue)
			task.wait(3)
		end
	until success or attempt == 5
	
	if success then

		print(playerdatavalue)
		print("Connected to database")
		if not playerdatavalue then
			print("Assigning default data")
			playerdatavalue = {
				["Stage"] = 1
			}
		end

		SessionData[player.UserId] = playerdatavalue


	else
		warn("Unable to get data for", player.UserId)
		player:Kick("Unable to load your data, please try again later.")
	end
end
Players.PlayerAdded:Connect(PlayerAdded)



function PlayerLeaving(player)
	if SessionData[player.UserId] then
		local success = nil
		local errormsg = nil
		local attempt = 1
		
		repeat
			success, errormsg = pcall(function()
			Database:SetAsync(player.UserId, SessionData[player.UserId])
			end)
			attempt += 1
			if not success then
				warn(errormsg)
				task.wait(3)
			end
		until success or attempt == 5
		
		if success then
			print("Data saved for", player.Name)
		else
			warn("Unable to save for", player.Name)
		end
	end
end
Players.PlayerRemoving:Connect(PlayerLeaving)