Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

DataStore isn't loading the stat?

local DSService = game:GetService('DataStoreService'):GetDataStore('Stats')
game.Players.PlayerAdded:connect(function(plr)
repeat wait() until plr.Character
local char = plr.Character
local uniquekey = 'id-'..plr.userId

    if DSService:GetAsync(uniquekey) then
		plr.Leaderstats.Skillpoints.Value = DSService:GetAsync(uniquekey)[1]
	end
	
	
plr.PlayerGui.Options.Frame.Save.MouseButton1Click:connect(function()
	DSService:SetAsync(uniquekey, plr.Leaderstats.Skillpoints.Value)
	plr.PlayerGui.Options.Frame.Save.Text = 'Saved'
	wait(2)
	plr.PlayerGui.Options.Frame.Save.Text = 'Save'
end)
	



end) -- end for playeradd function

This is a normal script so no it isn't localscript, Skillpoints is another value already made in inside the player in another script, and the GUI is just there to notify the players the data's been saved. the problem is it isn't loading when I rejoin?

In output, it also says ServerScriptService.Data:8: attempt to index a number value

2 comments

0

Because you're not saving it as a table. Just do DSService:GetAsync(uniquekey) and see if that works.

0

Thanks! it works now, however the script loads too fast to the point where the making of the values in the other script are slow, and when it says "Leaderstats is not apart of Player" but I just added a wait(10) above the GetAsync thing and now it works fine. Is there a better way for it to wait?

1 answer

There are a number of problems with your script.

  • You do not use the player's character, but wait for it anyway
  • You do not wait for the leaderboard script to insert the "Leaderstats" object into the player. You should use WaitForChild in that case (with a timeout in-case something goes wrong). Since you're accessing multiple children, it may be worth using the function I wrote below.
  • You call GetAsync twice unnecessarily. You should assign the result to a variable so you only need to call GetAsync once.
  • You do not implement debounce on the save feature. A person could rapidly click to save repeatedly, wasting your server's requests.
  • You must put datastore commands in pcall as they may error
local DSService = game:GetService('DataStoreService'):GetDataStore('Stats')
function WaitForChildren(obj, timeout, ...)
	local t = {...}
	for i = 1, #t do
		obj = obj:WaitForChild(t[i], timeout)
		if not obj then return nil end
	end
	return obj
end
game.Players.PlayerAdded:connect(function(plr)
	local skillpoints = WaitForChildren(plr, 5, "Leaderstats", "Skillpoints")
	--or could do "skillPoints = plr:WaitForChild("Leaderstats"):WaitForChild("Skillpoints")", but it wouldn't have a timeout
	if not skillpoints then print(plr, "did not receive Leaderstats.Skillpoints") return end
	local uniquekey = 'id-'..plr.userId
	--Uncomment the following line and its corresponding '--end))' if you want the player to be able to save immediately upon loading your game, even if the GetAsync errors. If it errors, this might mean that the datastores are down, so if you let the player save over it without first loading, they might lose the information stored there. Because of this, you might want to change what happens in the 'i == 5' code.
	--coroutine.resume(coroutine.create(function()
		local success, value
		for i = 1, 5 do --try 5x, once every 15 seconds
			success, value = pcall(function() return DSService:GetAsync(uniquekey) end)
			if success then break end
			if i == 5 then
				print("Could not retrieve data for", plr,", error message:", value)
				value = nil
				break
			end
			wait(15)
		end		
		if value then
			skillpoints.Value = value
		end
	--end))

	local save = WaitForChildren(plr, 5, "PlayerGui", "Options", "Frame", "Save")
	if save then
		local saveDB = false
		save.MouseButton1Click:connect(function()
			if saveDB then return end
			local success, errorMsg = pcall(function()
				DSService:SetAsync(uniquekey, skillpoints.Value)
			end)
			if success then
				save.Text = 'Saved'
			else
				save.Text = "Error: " .. errorMsg
			end
			saveDB = true
			wait(2)
			saveDB = false
			save.Text = 'Save'
		end)
	end
end) -- end for playeradd function

I also recommend against allowing players to save every 2 seconds. Per player, you get a request every 6 seconds (and a few more for the server), meaning that it's only safe to save every 6 seconds on average (per player). Though, if you don't expect players to abuse this, it's probably okay.

This discussion is preserved as it was. New answers, comments, and votes are closed.