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

Datastore not working and leaderboards not saving?

My leaderstats aren't saving and i've tried everything so I need some help

local datastore = game:GetService("DataStoreService")
local ds1 = datastore:GetDataStore("SaveCashSystem")
local ds2 = datastore:GetDataStore("SaveCoffeeSystem")


game.Players.PlayerAdded:Connect(function(player)
	
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	
	local money = Instance.new("IntValue", leaderstats)
	money.Name = "Money"
	
	local totalcoffee = Instance.new("IntValue", leaderstats)
	totalcoffee.Name = "Total Coffee"
	
	money.Value = ds1:GetAsync(player.UserId) or 0
	ds1:SetAsync(player.UserId, money.Value)
	totalcoffee.Value = ds2:GetAsync(player.UserId) or 0
	ds2:SetAsync(player.UserId, totalcoffee.Value)
	
	totalcoffee.Changed:Connect(function(changed)
		ds2:SetAsync(player.UserId, totalcoffee.Value)
		print("Saved " .. totalcoffee.Value .. " coffees!")
	end)
	money.Changed:Connect(function(changed)
		ds1:SetAsync(player.UserId, money.Value)
	end)
end)


game.Players.PlayerRemoving:Connect(function(player)
	
end)

4 comments

0

Is there even a need for lines 20 and 23?

0

Also, keep in mind that using .Changed might overload the system, and having a button to save is a MUCH better way.

0

Thanks!

0

If my answer had helped you then click on the 'Approve Answer' button below my answer to mark this question as solved.

2 answers

There's much to go through and explain in this DataStore script that needs to be said since I've seen many individuals have been loading data this way. Thankfully my explanation follows a lot of Roblox's own article on the RobloxDev site.


I see a problem when needing more than one DataStore. DataStores give you many options like saving to different scopes but your case could be done an easier way.

DataStores become even easier to use when you understand that they can save tables. Even subtables within the table for some data organization. This is useful to know because you use Folders and IntValues to show the player the data they have. All we need to do is convert them to a table.


###We can start in the PlayerAdded event which is usually when you load player data:

--Defined services with reasonable names
local Players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")

--Our DataStore we save everything to
local LeaderboardStore = DSS:GetDataStore("Leaderboards")

Players.PlayerAdded:Connect(function(player)
	local playerData --Will be set to any data player has as a table
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player	
	
	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Parent = leaderstats
	
	local totalcoffee = Instance.new("IntValue")
	totalcoffee.Name = "Total Coffee"
	totalcoffee.Parent = leaderstats

	local key =  player.UserId.."_data"
	--We need a personal key for the joining player.
	--You should know what this is for already.
	
	--This part is explained some below
	local success, _error = pcall(function()
		LeaderboardStore:UpdateAsync(key, function(data)
			playerData = data 
		end)
	end)
	
	if success then
		if playerData then
			money.Value = --set to the values in 'playerData' table
			totalcoffee.Value = --set to the values in 'playerData' table
		else
			--If no data was found then we set default values.
			money.Value = 0
			totalcoffee.Value = 0
		end
	else
		--DataStores requests fail sometimes so we could warn the player here
		warn("Data for player could not be loaded.")
	end
end)

After creating the custom key for the player, UpdateAsync is used here to set our playerData variable to any previous data the player may have. This is also wrapped in a pcall() function to check if calling for the data fails for some reason.

Below that we use the first result of the pcall, success variable of type boolean, to check if the DataStore call had worked. If it didn't, then we could handle the player differently. If success was true then it means the DataStore did work but it doesn't really mean that the player had any data to start.


###Next we need to actually save data... but how?

Your original code does this by saving (By using :SetAsync()) everytime the IntValues change through the .Changed() event. This is NEVER the way you want to go about saving because you will exceed data store limits. You could read on that here.

Instead you should only save at important moments or use some interval. A point you should always save that I will touch on is when the player leaves through PlayerRemoving

First we need to convert our 'leaderstats' folder to a table that will serve as the data to be saved. We can do this with a new function with a generic for-loop:

--This function will be passed the player as an argument
local function requestPlayerData(player)
	local data = {}
	local folder = player.leaderstats
	
	for _, object in pairs(folder:GetChildren()) do
		if object:IsA("IntValue") then
			data[object.Name] = object.Value
		end
	end
	
	return data
end

We define a table that we will write our IntValues to using the Name as the key and Value as the value. We then return that table once it's done.

We then can continue with the PlayerRemoving:

Players.PlayerRemoving:Connect(function(player)
	local key = player.UserId.."_data"
	
	--We call our function and pass the player to get player stats in table form
	local dataToSave = requestPlayerData(player)
	
	--pcall again to catch errors.
	local success, _error = pcall(function()
		LeaderboardStore:SetAsync(key, dataToSave)
	end)
end)

When we call the requestPlayerData() function, we are returned with a proper table that the DataStore can use to save. Almost that simple.

We then bring all the code together to get a more secure DataStore system:

local Players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")

local LeaderboardStore = DSS:GetDataStore("Leaderboards")

local function requestPlayerData(player)
	local data = {}
	local folder = player.leaderstats
	
	for _, object in pairs(folder:GetChildren()) do
		if object:IsA("IntValue") then
			data[object.Name] = object.Value
		end
	end
	
	return data
end

Players.PlayerAdded:Connect(function(player)
	local playerData;
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player	
	
	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Parent = leaderstats
	
	local totalcoffee = Instance.new("IntValue", leaderstats)
	totalcoffee.Name = "Total Coffee"
	totalcoffee.Parent = leaderstats
	
	local key = player.UserId.."_Data"
	
	local success, _error = pcall(function()
		LeaderboardStore:UpdateAsync(key, function(data)
			playerData = data
		end)
	end)
	
	if success then
		if playerData then
			money.Value = playerData[money.Name]
			totalcoffee.Value = playerData[totalcoffee.Name]
		else
			money.Value = 0
			totalcoffee.Value = 0
		end
	else
		warn("Data could not be loaded for "..player.Name)
	end
end)


Players.PlayerRemoving:Connect(function(player)
	local key = player.UserId.."_data"
	
	local dataToSave = requestPlayerData(player)
	
	local success, _error = pcall(function()
		LeaderboardStore:SetAsync(key, dataToSave)
	end)
end)

The last thing I had added to the above code are lines 44 and 45. If any data retrieved is found for the player then the playerData table can be indexed using the Names of our IntValues just like we had it set up in our requestPlayerData() function!

That wraps up my explanation in using tables to save and when not to save for DataStores. This might be pretty lengthy so do make sure to read the articles I had linked.

#####If I missed something or got something wrong please let me know.

Thank you!

1 comment

0

If my answer had helped you then click on the "Accept Answer" button. Helps me and you.

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