Datastore Issue

I’m currently working on a brand new project, however this is a major roadblock that I’ve been trying to fix for the past week. Honestly, I’m not the best at coding. This is actually from another project, but the datastores function correctly. When coping the code it wasn’t working, one of the issues that has been happening is the datastore isn’t saving the values. So, upon rejoining it believes that the data is ‘nil’ which brings me to my current output log. My plan for this script is to save leaderboard values, which I will also be using similar scripts for some of my other values that will not be displayed on the leaderboard. This script is to save three different leaderboard values, and unlike the developer hub shows, this script doesn’t autosave. Instead, it will load the data when a player joins (If there is any), then saves that data once the user leaves.

DataStoreError

The warnings that are saying ‘Cannot load your ___ count!’ are intentional, as the game believes that no data exists for the player, but this error only occurs when the player enters the game for the first time (Working on fixing that). I have tried fixing my code as much as possible, but even while testing it the values are not saving. I have looked in the developer hub, but it doesn’t relate to my script. Here is what I have currently:

local DSS = game:GetService("DataStoreService")
local EssenceSave = DSS:GetDataStore("EssenceValueSave")
local MagicSave = DSS:GetDataStore("MagicValueSave")
local SoulSave = DSS:GetDataStore("SoulValueSave")

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local Essence = Instance.new("IntValue")
	Essence.Name = "Essence"
	Essence.Parent = leaderstats

	local Magic = Instance.new("IntValue")
	Magic.Name = "Magic"
	Magic.Parent = leaderstats

	local Soul = Instance.new("IntValue")
	Soul.Name = "Soul"
	Soul.Parent = leaderstats

	local data = nil
	local data2 = nil
	local data3 = nil

	local success, errormessage = pcall(function()
		data = EssenceSave:GetAsync(player.UserId.."-Essence")
	end)
	local success2, errormessage = pcall(function()
		data2 = MagicSave:GetAsync(player.UserId.."-Magic")
	end)
	local success3, errormessage = pcall(function()
		data3 = SoulSave:GetAsync(player.UserId.."-Soul")
	end)

	if data ~= nil then
		Essence.Value = data
	else
		print("Cannot load your essence count!")
		warn(errormessage)
	end

	if data2 ~= nil then
		Magic.Value = data2
	else
		print("Cannot load your magic count!")
		warn(errormessage)
	end

	if data3 ~= nil then
		Soul.Value = data3
	else
		print("Cannot load your soul count!")
		warn(errormessage)
	end
end)

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

	local success, errormessage = pcall(function()
		EssenceSave:SetAsync(player.UserId.."-Essence",player.leaderstats.Essence.Value)
	end)
	local success2, errormessage = pcall(function()
		MagicSave:SetAsync(player.UserId.."-Magic",player.leaderstats.Magic.Value)
	end)
	local success3, errormessage = pcall(function()
		SoulSave:SetAsync(player.UserId.."-Soul",player.leaderstats.Soul.Value)
	end)

	if success then
	else
		print("Cannot save your essence count!")
		warn(errormessage)
	end
	if success2 then
	else
		print("Cannot save your magic count!")
		warn(errormessage)
	end
	if success3 then
	else
		print("Cannot save your soul count!")
		warn(errormessage)
	end
end)

If anyone could help me with this issue, please try. Also, instead of commenting a fixed script, can you please tell me what is wrong with it, so I can fix it in the future. Thank you!

1 Like

Often datastores don’t work well in Studio. Have you tested this in the real game? Also how are you changing he leaderstats, through a script or through the properties tab?

The stats are gained through another script, however it changes the value inside of the leaderstats folder.

UPDATE: I attempted to play in-game and it did successfully work inside. I believe something I should take into affect is joining the game first, as the datastore works in studio afterwards.

1 Like

If you’re testing in studio then whenever you change a value and close the game, you’re actually just shutting down a server and PlayerRemoving has no time to fire. I recommend using something like a kick or just save the game and test on roblox and not on roblox studio. Another problem is that you might be saving your values on the client and not on the server, remember that the client cannot edit server values so if you’re changing it on the client, go in the Test tab and save your data through the server. Also make sure that API access is enabled through game settings. (I don’t recommend spamming pcalls around your script, just save your data using a table)

I know that I cannot edit certain values instead the client. The script above is a script, not actually a local script. Thanks for the tips though.

Also, what are you saying about a table for saving player data? I haven’t really used tables much, neither know how to program it.

This is actually a very simple fix. When the server has one player and the player leaves, the server doesn’t have time to save the player’s data, because it instantly shuts down. There is a lil’ somethin’ called game:BindToClose(), which delays the server shutdown. Doing this will give your script enough time to save. (BTW, I recommend saving player data in a table, like in the example below because it might slow down datastore) Here is an example:

local dss = game:GetService("DataStoreService")
local playerData = dss:GetDataStore("PlayerData")

local function save(player)
	local key = "Data_"..player.UserId
	local s, f = pcall(function()
		local leaderstats = player:WaitForChild("leaderstats")
		playerData:UpdateAsync(key, function()
			return {
				Coins = leaderstats:WaitForChild("Coins").Value,
				Points = leaderstats:WaitForChild("Points").Value
			}
		end)
	end)
	if not f then
		print("Successfully saved "..player.Name.."'s Data ("..player.UserId.."). Key: "..key)
	elseif f then
		warn("Failed to save "..player.Name.."'s Data ("..player.UserId.."). Key: "..key..". Error: "..f)
	end
end

game.Players.PlayerAdded:Connect(function(player)
	local savedData = playerData:GetAsync("Data_"..player.UserId) or {}
	
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	
	local coins = Instance.new("NumberValue", leaderstats)
	coins.Name = "Coins"
	coins.Value = savedData.Coins
	
	local points = Instance.new("NumberValue", leaderstats)
	points.Name = "Points"
	points.Value = savedData.Points
end)

game.Players.PlayerRemoving:Connect(save)

game:BindToClose(function()
	for i, v in pairs(game.Players:GetPlayers()) do
		wait()
		save(v)
	end
end)

So what you are saying is game:BindToClose is another function that fires when a server is going to shutdown?

Yeah, instead of having 3 datastores you can use 1 single table.


--saving data
local statsTable = {}
statsTable.Essence = player.leaderstats.Essence.Value
statsTable.Magic = player.leaderstats.Magic.Value
statsTable.Soul = player.leaderstats.Soul.Value

mainDatastore:SetAsync(player.UserId, statsTable)


--calling data
local data
data = mainDatastore:GetAsync(player.UserId)

print(data)
print(data.Magic, data.Soul, data.Essence)

You can use only 1 pcall function and that is when you’re loading the data and saving the data, It’s not necessary however but people use it so that if the script errors, the pcall should print the error. Here is an example of how to save data using a table :

local MyData = {
	IntValue = player.leaderstats["IntValue"].Value;
	StringValue = player.leaderstats["StringValue"].Value;
}
	
local saved, error_ = pcall(function()
	MyDataStore:SetAsync(**KEY**, MyData)
end)

and an example of how to load data using tables :

local loaded, error_ = pcall(function()
	MyData = MyDataStore:GetAsync(**KEY**)
end)
	
if loaded then
	IntValue.Value = MyData.IntValue
	StringValue.Value = MyData.StringValue
	print("Loaded data")
elseif error_ then
	warn(error_)
end

yes, it is. I use it in all my games that have DataStore, so nobody’s data is lost.