PlayerData not saving

I have gotten a script I made in a previous game and I’m trying to make it save boolean values, I’m not quite sure if that’s the main problem but here’s my current script, please tell me if you find anything wrong:

local dataStoreService = game:GetService("DataStoreService")
local PassivesDataStore = dataStoreService:GetGlobalDataStore("PassiveValues")

local loaded = {}

game.Players.PlayerAdded:Connect(function(player)
	local Passives = player:WaitForChild("Passives")
	if player.UserId > 0 and player.Parent then
		local PassivesData = PassivesDataStore:GetAsync(player.UserId)
		if PassivesData ~= "Request Rejected" then
			if PassivesData then
				for i, stat in ipairs(Passives:GetChildren()) do
					local value = PassivesData[stat.Name]
					if value then
						stat.Value = value
					end
				end
			end
			loaded[player] = true
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Passives = player:FindFirstChild("Passives")
	if Passives then
		print("foundpassives")
		if loaded[player] then
			local PassivesData = {}
			for i, stat in ipairs(Passives:GetChildren()) do
				PassivesData[stat.Name] = stat.Value
			end
			PassivesDataStore:SetAsync(player.UserId, PassivesData)
		end
	end
	loaded[player] = nil
end)
1 Like

Wrap the GetAsync and SetAsync into pcalls. You are checking if returned data is not equal to a string (“Request Rejected”), because is not equal to that the values probably are not being updated to be saved on playerRemove:

local dataStoreService = game:GetService("DataStoreService")
local PassivesDataStore = dataStoreService:GetGlobalDataStore("PassiveValues")

local loaded = {}

game.Players.PlayerAdded:Connect(function(player)
	local Passives = player:WaitForChild("Passives")
	if player.UserId > 0 and player.Parent then
		local success, PassivesData = pcall(function()
			return PassivesDataStore:GetAsync(player.UserId)
		end)
		
		if success then
			if PassivesData then
				for i, stat in ipairs(Passives:GetChildren()) do
					local value = PassivesData[stat.Name]
					if value then
						stat.Value = value
					end
				end
				loaded[player] = true
			else
				warn("give starter values")
				loaded[player] = true
			end
			
		else
			warn("Datastore GetAsync failed, do retries")
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Passives = player:FindFirstChild("Passives")
	if Passives then
		print("foundpassives")
		if loaded[player] then
			local PassivesData = {}
			for i, stat in ipairs(Passives:GetChildren()) do
				PassivesData[stat.Name] = stat.Value
			end
			
			-- wrap into pcall save too:
			PassivesDataStore:SetAsync(player.UserId, PassivesData)
		end
	end
	loaded[player] = nil
end)
2 Likes

:SetAsync()'s first argument takes a string value, that might be the issue.
You can use a key system like "Player_"..player.UserId
Also, do not forget to wrap :SetAsync() and :GetAsync() in a pcall as those API calls might fail.

1 Like

Nope, you can use the player.UserId for SetAsync and GetAsync without issues. Thats not the problem

1 Like

I don’t really know how to do pcalls, I don’t use datastores that often, I’m pretty sure this isn’t correct but here’s what I have made:

local dataStoreService = game:GetService("DataStoreService")
local PassivesDataStore = dataStoreService:GetGlobalDataStore("PassiveValues")

local loaded = {}

game.Players.PlayerAdded:Connect(function(player)
	local Passives = player:WaitForChild("Passives")
	if player.UserId > 0 and player.Parent then
		local success, PassivesData = pcall(function()
			return PassivesDataStore:GetAsync(player.UserId)
		end)
		
		if success then
			if PassivesData then
				for i, stat in ipairs(Passives:GetChildren()) do
					local value = PassivesData[stat.Name]
					if value then
						stat.Value = value
					end
				end
				loaded[player] = true
			else
				warn("give starter values")
				loaded[player] = true
			end
			
		else
			warn("Datastore GetAsync failed, do retries")
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Passives = player:FindFirstChild("Passives")
	if Passives then
		print("foundpassives")
		if loaded[player] then
			local PassivesData = {}
			for i, stat in ipairs(Passives:GetChildren()) do
				PassivesData[stat.Name] = stat.Value
			end
			
			local success, PassivesData = pcall(function()
				return PassivesDataStore:SetAsync(player.UserId, PassivesData)
			end)
		end
	end
	loaded[player] = nil
end)
1 Like

When using DatastoreService and other APIs that could fail due to things that are not under your control, like connection issues, servers down etc, its highly recomended you use pcall (protected call). So you can know when that happens.

pcalls are easy to use, it will call a function and if the function errors the script wont crash, pcall will return information about the function that you tried to run, as first argument it returns if it was successful, and the second argument could be the result of the function or an error message:

local success, theError = pcall(function()
	return PassivesDataStore:SetAsync(player.UserId, PassivesData)
end)

So the variable success will be true or false depending if the SetAsync did run correctly. If it didnt, then the second variable theError is the error message.

After the pcall you can check those variables to decide what to do:

if success then
	warn("DSS saved correctly")
	loaded[player] = nil			
else
	warn("DSS saving failed")
	warn("Error:", theError)
end

For GetAsync, its the same, the first variable holds if the function ran correctly or not, and the second variable is the data gotten from dataStore, or nil if theres no data yet:

local success, theData = pcall(function()
	return PassivesDataStore:GetAsync(player.UserId)
end)

if success then
    warn("Reading DSS went fine")
    if theData then
        warn("And player has data:")
        warn(theData)
    else
        warn("Player is new, has no data")
    end
else
    warn("Datastore check did fail")
end

If you still have doubts, better check some tutorials or documentation on how to use pcalls.

1 Like

Thanks for explaining it! Also, should I add the last code you sent to the end? Just making sure.

1 Like

After I did that, the first time it printed the table correctly, with the abilities, however it said it was false instead of true, then, it started printing other data, like this:
image
I might know a fix to it but it wouldn’t be as practical, it would include getting rid of the entire passives folder, and instead placing the values inside of another folder inside the player, where I store all the other values.

1 Like

I did this and it still didn’t save, I believe it has something to with it being a bool value

Paste here the code you are currently using

local dataStoreService = game:GetService("DataStoreService")
local PlayerDataDataStore = dataStoreService:GetGlobalDataStore("PlayerData")

local loaded = {}

game.Players.PlayerAdded:Connect(function(player)
	local PlayerData = player:WaitForChild("StatFolder")
	if player.UserId > 0 and player.Parent then
		local PlayerDataData = PlayerDataDataStore:GetAsync(player.UserId)
		if PlayerDataData ~= "Request Rejected" then
			if PlayerDataData then
				for i, stat in ipairs(PlayerData:GetChildren()) do
					local value = PlayerDataData[stat.Name]
					if value then
						stat.Value = value
					end
				end
			end
			loaded[player] = true
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local PlayerData = player:FindFirstChild("StatFolder")
	if PlayerData then
		if loaded[player] then
			local PlayerDataData = {}
			for i, stat in ipairs(PlayerData:GetChildren()) do
				PlayerDataData[stat.Name] = stat.Value
			end
			PlayerDataDataStore:SetAsync(player.UserId, PlayerDataData)
		end
	end
	loaded[player] = nil
end)

I also just thought of a possibly better method, if it is a boolean value problem, instead I could save 3 stats inside the folder to indicate the 3 passives the player has.

Also the older script if you meant that:

local dataStoreService = game:GetService("DataStoreService")
local PassivesDataStore = dataStoreService:GetGlobalDataStore("PassiveValues")

local loaded = {}

game.Players.PlayerAdded:Connect(function(player)
	local Passives = player:WaitForChild("Passives")
	if player.UserId > 0 and player.Parent then
		local success, PassivesData = pcall(function()
			return PassivesDataStore:GetAsync(player.UserId)
		end)
		
		if success then
			if PassivesData then
				for i, stat in ipairs(Passives:GetChildren()) do
					local value = PassivesData[stat.Name]
					if value then
						stat.Value = value
					end
				end
				loaded[player] = true
			else
				warn("give starter values")
				loaded[player] = true
			end
			
		else
			warn("Datastore GetAsync failed, do retries")
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local Passives = player:FindFirstChild("Passives")
	if Passives then
		print("foundpassives")
		if loaded[player] then
			local PassivesData = {}
			for i, stat in ipairs(Passives:GetChildren()) do
				PassivesData[stat.Name] = stat.Value
			end
			
			local success, theData = pcall(function()
				return PassivesDataStore:GetAsync(player.UserId)
			end)

			if success then
				warn("Reading DSS went fine")
				if theData then
					warn("And player has data:")
					warn(theData)
				else
					warn("Player is new, has no data")
				end
			else
				warn("Datastore check did fail")
			end
		end
	end
	loaded[player] = nil
end)

This code is the old one, that one will not work

That is the script I use to save the player’s data that isn’t passives, and it’s been working fine currently.

This is the right code, but its wrong, you are Reading the DSS instead of Saving when Player leaves

Oh I see that now, I’ll fix it and see if it works.

Im gonna test the code, to see if its working good or not. Please send me the function that is creating the Passives folder and the values in it, so I dont have to create my own values for quicker test

Ok, here:

game.Players.PlayerAdded:Connect(function(plr)
	
	local passives = Instance.new("Folder", plr)
	passives.Name = "Passives"
	
	local cursedSpeech = Instance.new("BoolValue", passives)
	cursedSpeech.Name = "CursedSpeech"
	cursedSpeech.Value = false
	
	local celestialEyes = Instance.new("BoolValue", passives)
	celestialEyes.Name = "CelestialEyes"
	celestialEyes.Value = false
	
end)

It finally works, thank you for all your help, I’ll keep it like this for the time being, however later one I will probably switch to a more efficient method, aka, making passive slots instead.

1 Like

Good to hear! yeah, I tested and works as expected, just one thing. Sometimes Studio closes faster than the function that saves on PlayerRemove, so make sure you use game:BindToClose, to cause a task.wait(some seconds) to make sure studio is always saving when you close test