DataStore script reads wrong values from leaderstats

Hello, fellow scripters!

I am making my own game and I got issues with my datastore script. It saves and retrieves the values, but when the player leaves the game. It for some reason reads the wrong value(the value that was saved manually by me there previously) from the leaderstats. I already tried so many things it gets the right player, I use the right directory and just everything should work, but it doesn’t…

local players = game:GetService("Players")
local DataStore = game:GetService("DataStoreService")
local ds = DataStore:GetDataStore("Leaderstats")

players.PlayerAdded:Connect(function(plr)
	local success, response = pcall(function()
		data = ds:GetAsync(tostring(plr.UserId))
	end)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local coins= Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats
	coins.Value = 100
	
	local rank = Instance.new("StringValue")
	rank.Name = "Rank"
	rank.Parent = leaderstats
	rank.Value = "Begginer"
	if data then
		rank.Value = data[2]
		coins.Value = data[1]
	end
end)
players.PlayerRemoving:Connect(function(plr)
	local save = {plr.leaderstats.Coins.Value, plr.leaderstats.Rank.Value}
	print(save) --
	ds:SetAsync(tostring(plr.UserId), save) 
end)

Any help would be appreciated.

2 Likes

Can you please explain a bit more? What is exactly a “wrong value”?

1 Like

I manually setted a value to be stored in the datastore for testing purposes and when I tried printing what the script was reading the value I manually stored there before.
Here:

local save = {plr.leaderstats.Coins.Value, plr.leaderstats.Rank.Value}
print(save) --
1 Like

not sure if i understand but maybe its because you print the data before saving it?

Printing something doesn’t change anything’s value

Part 1: Fixes

Add a line to:

To:

	local success, response = pcall(function()
		local data = ds:GetAsync(tostring(plr.UserId))
		return data
	end)

Adding local in front of data to make it a local variable.
return data passes the value of data to response.

Replace:

To:

	if response then
		rank.Value = data[2]
		coins.Value = data[1]
	end

data is a variable inside of another function, Lua cannot read it. However, it can read the value of response.

EDIT:

Change:

	if response then
		rank.Value = data[2]
		coins.Value = data[1]
	end

To:

	if response then
		if typeof(response) == "table" then
			rank.Value = data[2]
			coins.Value = data[1]
		end
	else
		ds:SetAsync(tostring(plr.UserId), {coins.Value, rank.Value})
	end

SetAsync should only be used when it’s the player’s first time playing. It will not update the player’s data in other servers. Instead, you have to use UpdateAsync within the PlayerRemoving event to update it for other servers.

Change:

To:

players.PlayerRemoving:Connect(function(plr)
	local save = {plr.leaderstats.Coins.Value, plr.leaderstats.Rank.Value}
	print(save) --
	ds:UpdateAsync(tostring(plr.UserId), function(oldData)
		local newData = save or oldData
		return newData
	end) 
end)

Part 2: Efficiency

The code below does not include the edits from Part 1. Make sure you include the edits.

We know the PlayerAdded event fires when a player is added to the game. The drawback is sometimes it doesn’t fire, leaving the player with no leaderstats nor data at all.

To fix it so the player gets their initial data no matter what:

Change:

To:

local function onPlayerEntered(plr)
	local success, response = pcall(function()
		data = ds:GetAsync(tostring(plr.UserId))
	end)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local coins= Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats
	coins.Value = 100
	
	local rank = Instance.new("StringValue")
	rank.Name = "Rank"
	rank.Parent = leaderstats
	rank.Value = "Begginer"
	if data then
		rank.Value = data[2]
		coins.Value = data[1]
	end
end

for _, plr in pairs(players:GetPlayers()) do
	local thread = coroutine.wrap(function())
		onPlayerEntered(plr)
	end)

	thread()
end

players.PlayerAdded:Connect(onPlayerEntered)

coroutine.wrap basically performs a function in another CPU thread, which is useful for simultaneous tasks, to give the data to the player as soon as possible.

1 Like

Still doesn’t fix the problem.

It reads the wrong value because you are using dot notation, it shouldn’t be used for DataStores. Instead of writing leaderstats.Coins, use leaderstats:FindFirstChild("Coins").

The dot notation only works for serialized objects. If you don’t know, they are objects that were created by you in Studio, not the script.

1 Like

pcall already returns 2 variables - success and result (error message). In that case, you will have to make a third variable.

local success, response, data = pcall(function()
	local data = ds:GetAsync(tostring(plr.UserId))
	return data
end)
1 Like

I think your problem is that it reads the values before they are actually loaded. Try putting task.wait(10) to see if it outputs right values.

1 Like

I have no idea why, but the script started working after I changed the values through a script instead of changing it in the studio.

Yes I fixed it 2 months after asking this question…

I would like to thank you @SkyWarrior805 and @AlternativeOrNot for all of your help, I really appriciate it!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.