Problems saving data

Basically I have a problem where it saves perfectly fine in studio and in game for me. However I am getting complaints from the players saying that there data isnt being saved properly. I have 2 datastore scripts (which I believe is the problem). One is the old datastore script which holds all the olds players values until it completely stopped working. Then the new one which transfers old data to the new datastore.

Im not sure what is causing the issue. But heres the sever side script:

local playerDataStore = DataStoreService:GetDataStore("Data_")
local ShopItems = require(game.ReplicatedStorage:WaitForChild("ShopItems"))
local BadgesMod = require(game.ReplicatedStorage:WaitForChild("Badges"))

game.Players.PlayerAdded:Connect(function(player)
	local Gears = Instance.new("Folder",player)
	Gears.Name = "Gears"
	local Effects = Instance.new("Folder",player)
	Effects.Name = "Effects"
	local Transport = Instance.new("Folder",player)
	Transport.Name = "Transport"
	local InventoryItems = Instance.new("Folder",player)
	InventoryItems.Name = "InventoryItems"
	local Badges = Instance.new("Folder",player)
	Badges.Name = "Badges"
	local Nametags = Instance.new("Folder",player)
	Nametags.Name = "Nametags"
	for i,v in pairs(ShopItems["Gears"]) do
		Instance.new("BoolValue", Gears).Name = tostring(i)
	end
	for i,v in pairs(ShopItems["Effects"]) do
		Instance.new("BoolValue", Effects).Name = tostring(i)
	end
	for i,v in pairs(ShopItems["Transport"]) do
		Instance.new("BoolValue", Transport).Name = tostring(i)
	end
	for i,v in pairs(ShopItems["Nametags"]) do
		Instance.new("BoolValue", Nametags).Name = tostring(i)
	end
	for i,v in pairs(BadgesMod["Badges"]) do
		Instance.new("BoolValue", Badges).Name = tostring(i)
	end
	local NewLeaderstats = Instance.new("Folder",player)
	NewLeaderstats.Name = "leaderstats"
	local Points = Instance.new("IntValue", NewLeaderstats)
	Points.Name = "Points"
	local Survivals = Instance.new("IntValue", NewLeaderstats)
	Survivals.Name = "Survivals"
	local Equip = Instance.new("Folder",player)
	Equip.Name = "Equipped"
	local GearEquip = Instance.new("Folder", Equip)
	GearEquip.Name = "Gears"
	local EffectEquip = Instance.new("StringValue", Equip)
	EffectEquip.Name = "Effects"
	local TransportEquip = Instance.new("Folder", Equip)
	TransportEquip.Name = "Transport"
	local Joined = Instance.new("BoolValue", player)
	Joined.Name = "Joined"
	
	-- NEW --
	local Nametag = Instance.new("StringValue", Equip)
	Nametag.Name = "Nametags"
	
	wait(1)
	local success, errormsg = pcall(function() -- a pcall will run even if the script has an error and you can check if it is an error with errormsg or success but the name doesn't matter
		local data, Success, ErrorMsg
		local Attempts = 4
		repeat
			Attempts -= 1
			Success, ErrorMsg = pcall(function()
				data = playerDataStore:GetAsync(player.UserId)
			end)
			if not Success then wait(1) end
		until
		Success or Attempts == 0 or not game.Players:GetPlayerByUserId(player.UserId)
		if data ~= nil then
			if data[1] ~= nil then
				for i, dataval in pairs(data[1]) do
					if dataval[4] == "Equip" or dataval[4] == "InventoryItems" then
						local stat
						if dataval[4] == "Equip" then
							stat = player:WaitForChild("Equipped"):WaitForChild(dataval[5])
						else
							stat = player:WaitForChild("InventoryItems")
						end
						local instance = Instance.new(dataval[3])
						if instance then
							instance.Name = dataval[1]
							instance.Parent = stat
							local Tool = game.ServerStorage.ShopItems:FindFirstChild(dataval[1]):Clone()
							Tool.Parent = player.Backpack
						end
					else
						local stat = player:WaitForChild(dataval[4])
						if stat:FindFirstChild(dataval[1]) then
							print(dataval[4], dataval[1])
							stat:FindFirstChild(dataval[1]).Value = dataval[2]
						else
							local instance = Instance.new(dataval[3])
							if instance then
								instance.Name = dataval[1]
								instance.Value = dataval[2]
								instance.Parent = stat
							end
						end
					end
				end
			end
			if data[2] ~= nil then
				Points.Value = data[2]
			end
			if data[3] ~= nil then
				Survivals.Value = data[3]
			end
			if data[4] ~= nil then
				EffectEquip.Value = data[4]
			end
			if data[5] ~= nil then
				Joined.Value = data[5]
			end
			if data[6] ~= nil then
				Nametag.Value = data[6]
			end
		else
			playerDataStore:SetAsync(player.UserId, {})
			wait(6)
			Points.Value = player.oldleaderstats.Points.Value
			Survivals.Value = player.oldleaderstats.Survivals.Value
		end
		print("Loaded ["..player.Name.."] Data")
	end)
	
	if errormsg then
		print(errormsg)
	end
	
	if EffectEquip.Value ~= "" then
		local Tool = game.ServerStorage.ShopItems:FindFirstChild(EffectEquip.Value):Clone()
		if Tool:IsA("ParticleEmitter") then
			Tool.Parent = player.Character:FindFirstChild("HumanoidRootPart")
		elseif Tool:IsA("Trail") then
			local Attachment0
			local Attachment1
			if player.Character:FindFirstChild("Head"):FindFirstChild("TrailAttachment0") then
				Attachment0 = player.Character:FindFirstChild("Head"):FindFirstChild("TrailAttachment0")
			else
				Attachment0 = Instance.new("Attachment", player.Character:FindFirstChild("Head"))
				Attachment0.Name = "TrailAttachment0"
			end
			if player.Character:FindFirstChild("HumanoidRootPart"):FindFirstChild("TrailAttachment1") then
				Attachment1 = player.Character:FindFirstChild("HumanoidRootPart"):FindFirstChild("TrailAttachment1")
			else
				Attachment1 = Instance.new("Attachment", player.Character:FindFirstChild("HumanoidRootPart"))
				Attachment1.Name = "TrailAttachment1"
			end
			Tool.Attachment0 = Attachment0
			Tool.Attachment1 = Attachment1
			Tool.Parent = player.Character:FindFirstChild("Head")
		end
	end
	
	if Nametag.Value ~= "" then
		local nt = game.ServerStorage.ShopItems:FindFirstChild(Nametag.Value):Clone()
		if nt then
			nt.Parent = player.Character:FindFirstChild("Head")
			nt.Adornee = player.Character:FindFirstChild("Head")
		end
	end

	game.Players.PlayerRemoving:Connect(function(leavingPlayer)
		if leavingPlayer.userId == player.UserId then -- Check if the leaving player is the same player that joined so we can save their data correctly
			local Data = playerDataStore:GetAsync(player.UserId)
			local objData = {}
			for i, obj in pairs(Gears:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Gears"})
			end
			for i, obj in pairs(Effects:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Effects"})
			end
			for i, obj in pairs(Transport:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Transport"})
			end
			for i, obj in pairs(GearEquip:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Equip", "Gears"})
			end
			for i, obj in pairs(TransportEquip:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Equip", "Transport"})
			end
			for i, obj in pairs(InventoryItems:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "InventoryItems"})
			end
			for i, obj in pairs(Nametags:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Nametags"})
			end
			for i, obj in pairs(Badges:GetChildren()) do
				table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Badges"})
			end
			if Data == nil then
				playerDataStore:SetAsync(player.UserId, {
					objData,
					Points.Value,
					Survivals.Value,
					EffectEquip.Value,
					Joined.Value,
					Nametag.Value,
				})
			else
				playerDataStore:UpdateAsync(player.UserId, function(oldValue)
					oldValue = {objData,
						Points.Value,
						Survivals.Value,
						EffectEquip.Value,
						Joined.Value,
						Nametag.Value,
					}
					return {objData,
						Points.Value,
						Survivals.Value,
						EffectEquip.Value,
						Joined.Value,
						Nametag.Value,
					}
				end)
			end
			print("Saved ["..leavingPlayer.Name.."] Data")
		end
	end)
end)

game:BindToClose(function()
	for i,plr in pairs(game.Players:GetPlayers()) do
		local Data = playerDataStore:GetAsync(plr.UserId)
		local objData = {}
		for i, obj in pairs(plr.Gears:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Gears"})
		end
		for i, obj in pairs(plr.Effects:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Effects"})
		end
		for i, obj in pairs(plr.Transport:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Transport"})
		end
		for i, obj in pairs(plr.Equipped.Gears:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Equip", "Gears"})
		end
		for i, obj in pairs(plr.Equipped.Transport:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Equip", "Transport"})
		end
		for i, obj in pairs(plr.InventoryItems:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "InventoryItems"})
		end
		for i, obj in pairs(plr.Nametags:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Nametags"})
		end
		for i, obj in pairs(plr.Badges:GetChildren()) do
			table.insert(objData, {obj.Name, obj.Value, obj.ClassName, "Badges"})
		end
		if Data == nil then
			playerDataStore:SetAsync(plr.UserId, {
				objData,
				plr.leaderstats.Points.Value,
				plr.leaderstats.Survivals.Value,
				plr.Equipped.Effects.Value,
				plr.Joined.Value,
				plr.Equipped.Nametags.Value,
			})
		else
			playerDataStore:UpdateAsync(plr.UserId, function(oldValue)
				oldValue = {objData,
					plr.leaderstats.Points.Value,
					plr.leaderstats.Survivals.Value,
					plr.Equipped.Effects.Value,
					plr.Joined.Value,
					plr.Equipped.Nametags.Value,
				}
				return {objData,
					plr.leaderstats.Points.Value,
					plr.leaderstats.Survivals.Value,
					plr.Equipped.Effects.Value,
					plr.Joined.Value,
					plr.Equipped.Nametags.Value,
				}
			end)
		end
	end
end) 

Ive researched as much as I can and made some tweaks to the script. Nothing much has changed though since it still inconsistently doesn’t save.

I need a solution or any help urgently since the game is public and has around 500 active players. Any help is appreciated :slight_smile:

Dont use UpdateAsync, just use SetAsync

1 Like

Okay I will try that and let you know if it works or not!

I got this error:

104: Cannot store Function in data store. Data stores can only accept valid UTF-8 characters. - Server
08:10:35.749 Stack Begin - Studio
08:10:35.749 Script ‘ServerScriptService.Leaderstats/Data’, Line 199 - Studio
08:10:35.749 Stack End

I don’t know if this will help, but here’s a datastore template I made:

local DSS = game:GetService("DataStoreService")
local DataStore = DSS:GetDataStore("DronebotTestingData2")

game.Players.PlayerAdded:Connect(function(player)
	-- Create basic leaderstats values.
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local ValueName = Instance.new("IntValue")
	ValueName.Parent = player
	ValueName.Name = "ValueName"
	ValueName.Value = 1
	
	-- Create a variable for the player's user id.
	local userId = "Player_" .. player.UserId
	
	-- Create a pcall function to sync the data
	local data
	local success, errormessage = pcall(function()
		data = DataStore:GetAsync(userId)
	end)
	
	-- If the data is found, load the player's data.
	if success then
		print(userId .. "\'s data loaded successfully.")
		
		ValueName.Value = data.ValueName
		
		ValueName.Value = data.ValueName
	else
		print("There was an error saving " .. userId .. "\'s data.")
		warn(errormessage)
	end
	
end)


-- Save data when the player leaves the game.
game.Players.PlayerRemoving:Connect(function(player)
	local userId = "Player_" .. player.UserId
	
	local data = {
		ValueName = player.ValueName.Value;
	}
	
	local success, errormessage = pcall(function()
		DataStore:SetAsync(userId, data)
	end)
	
	if success then
		print(userId .. "\'s data successfully saved.")
	else
		print("There was an error saving " .. userId .. "\'s data.")
		warn(errormessage)
	end
end)

Use can use the ‘Replace’ tool to replace ValueName with the name of the value you’re trying to save.

Okay, so I have a question.

Why are you setting the value of an existing parameter? What’s the use of it?

1 Like

I think you are giving pressure to datastores I don’t know.

Ill try this method since its pretty understandable and let you know if it works or not

I followed a tutorial (forgot which one) and made a couple tweaks to it. Im not so good with datastores.

Update Async is A Much more safer and preferable choice.
UpdateAsync Updates the Data, Hence if theres an error while saving, your data will not be nil, it will instead be the old data saved.

In Simple words, UpdateAsync prevents DataLoss.

Looking at the devhub page, (GlobalDataStore:UpdateAsync), UpdateAsync appears to only work if there is data in the first place. So maybe an if statement could be included to split new players and players who have already played and need to load their data?

im pretty sure the data for a new player is nil.
So, when you Use UpdateAsync on it, it just updates the data from nil to the data you set it to have.

In the retrieving data area. There’s an if statement where if the data is not nil then retrieve data else it SetAsync to a empty table ({}).

Im also getting this error if that helps

I think that you are overloading the Datastore with too much values. In my game, i would get the same problem if i had done it like you. So if i was you, i would create a table, containing all data, and saving 1 value (table) instead of 100 values. (i just said a random number, i do not know the exact number also, i am not 100% sure if that would work, but it would make your script way shorter.)

Edit: and if you want to make leaderstats, you can just get all values from the table, and than put them in intvalues, that changes when the table (where the data is) is changed.

Wouldnt this already be a table though? Since I am saving the values inside of {}

playerDataStore:SetAsync(player.UserId, {
					objData,
					Points.Value,
					Survivals.Value,
					EffectEquip.Value,
					Joined.Value,
					Nametag.Value,
				})

now that you tell me that, i have absolutely no idea. But i found something tyhat might help you!

User-defined metadata has the following limits: Data Stores says:

Key length: up to 50 characters.
Value length: up to 250 characters.
No limit for the total number of key-value pairs but the total size cannot exceed 300 characters.

Maby you could decrease the server size just as posted here? (look at the ansver)

(extra screenshot)

And as last is it this link that might help you (also look at the ansver).

I hope this works!