Datastore script fails to get player stats, but only the second time it tries

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
  • The script is supposed to load leaderstats values for points and wins saved to a DataStore and make them show up on the leaderboard as the correct value for each player.
  1. What is the issue? Include screenshots / videos if possible!
  • The script only loads the first value it was asked to load, making the second value show up as 0 in the leaderboard, thus autosaving that value as 0 and wiping any data the player may have had there.
  • Update: The problem actually seems to be that the data isn’t successfully saved even when it says it is.
  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
  • Switching the values around only makes it so that the other value isn’t loaded. I could not find anyone with similar issues. Can there only be one playerstats value per player? Should I be using a table?
  • Currently trying to figure out what’s going on

Here’s the problem script. I’ve marked the bug with a comment. This is my first time working with DataStores, please help me figure out what I did wrong here.

--[[
Datastore names:

Points: UserId-points
Wins: UserId-wins

]]

local DataStoreService = game:GetService("DataStoreService")

local datastore = DataStoreService:GetDataStore("datastore")



local function savedata(player)
	local success, errormessage = pcall(function()
		datastore:SetAsync(player.UserId.."-points",player.leaderstats.points.Value)
		print(datastore:SetAsync(player.UserId.."-points",player.leaderstats.points.Value))
		datastore:SetAsync(player.UserId.."-wins",player.leaderstats.wins.Value)
		print(datastore:SetAsync(player.UserId.."-wins",player.leaderstats.wins.Value))
	end)
	
	if success then
		print("data successfully saved for " .. tostring(player))
	else
		print("error saving data for " .. tostring(player) .. ". They left, so their progress is lost.")
		warn(errormessage)
	end
end

local function loadstats(stat,player)
	local leaderstat = nil
	local success, errormessage = pcall(function()
		leaderstat = datastore:GetAsync(player.UserId.."-"..tostring(stat))
		print(tostring(stat))
		print(player.UserId.."-"..tostring(stat))
		print(leaderstat)
	end)
	
	if success then
		stat.Value = leaderstat
		print("successfully recieved leaderstats data for " .. tostring(player))
	else
		print("error recieving " .. tostring(player) .. "'s leaderstats data")
		warn(errormessage)
	end		
end



game.Players.PlayerAdded:Connect(function(player) --Fires when players are added, loads their playerstats
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent =  player
	
	local points = Instance.new("IntValue")
	points.Name = "points"
	points.Value = 10
	
	local wins = Instance.new("IntValue")
	wins.Name = "wins"
	wins.Value = 1
		

	
	--BUG: only the first stat called actually loads, the second stat will always show up as the default value (0)
	loadstats(points,player)	
	loadstats(wins,player)
	points.Parent = leaderstats
	wins.Parent = leaderstats
	
end)




-----------------------------VVV-- Saving Data --VVV------------------------------------




game.Players.PlayerRemoving:Connect(function(player) --Fires when players leave, saves their playerstats
	savedata(player)
end)

--[[
game:BindToClose(function() --Fires when the server is about to close
	
	for i, player in pairs(game.Players:GetPlayers()) do
		if player then
			player:Kick("Server shutting down, try again later")
		end
	end
	
	wait(5)
	
end)
]]

-- Uncomment ^^this^^ for the full release to protect data loss on server shutdown

while true do --Autosaves playerstats for all players every few seconds
	wait(45)
	for i, player in pairs(game.Players:GetPlayers()) do
		if player then
			savedata(player)
		end
	end

end
1 Like

Create a temporary value.

local Saved

local Ok, Result = pcall(function()
   Saved = DataStore:GetAsync(UserId)
end)

if Ok then
   IntValue.Value = Saved or o -- if nil, set it to 0, think of it as a default value if there is no data saved
end

I don’t why you are using tostring() after the userid, but it that is most likely the reason as it can only be the player’s userid / name.

2 Likes

I don’t see what using a temporary value will accomplish since the bugged script doesn’t throw any errors and prints “success” both times. Also, removing the tostring() causes it to error.

Update: It seems that the problem isn’t loading the data, that part works perfectly fine. For some reason, the datastore doesn’t save it’s data 100% of the time even when it says it does. I’m looking into it.

You should use UpdateAsync() for that and add “tries”.

Like this:

local success, err
local tries = 0
repeat
success, err = pcall(function()
       data:UpdateAsync(player.UserId.."-wins", function(oldValue)
                if player.leaderstats.points.Value == oldValue then
                      return nil
                      -- Don't update if the ammount of points is the same as the saved data
                end
               return player.leaderstats.wins.Value
       end)
       data:UpdateAsync(player.UserId.. "-points", function(oldValue)
               if player.leaderstats.points.Value == oldValue then
                     return nil
               end
               return player.leaderstats.points.Value
       end)
       
end)
if success then 
    tries = 3
    print("Success saving data!")
else
    tries = tries + 1
    print("Couldn't save data, Retrying...")
end
until tries == 3 

-- To get the data you can use the tries method again too.
1 Like

Are you waiting 45 seconds because that is when it would save, PlayerRemoving can be glitchy quite often… Also the BindToClose would be useful if uncommented. Does it print success saving?

PlayerRemoving has been printing a successful save when I kick myself from the server, which is a bit odd since it doesn’t seem to work. I’m gonna try AstralBlu_e’s method and see how it goes. I have the BindToClose function commented because I don’t wanna have Studio freeze for 5 seconds every time I stop playtesting, but I’ll try again with it on.

Save both variables in a table, that would be neater and I believe that your points are being overwritten with your wins, that may not be the case but I believe thats whats happening, but then it should error unless that is overwritten too.

In that case just save the wins and the points in a table, aka SetAsync({Points,Wins})

later you will load it by looping through the table,

for _, v in pairs(datastore:GetAsync()) etc…

1 Like

Your solution works! Although, it’s possible that the code has worked flawlessly this entire time and that I may or may not have been making client-side changes to the points value and expecting them to replicate. Aha…