Datastore sometimes not saving for certain players

PLEASEEE help i cannot figure this out for the life of me… this datastore doesnt save some players data and I have no idea why, what could be wrong with it? I’ve tried so many things and nothing fixes it, please help if you have any idea whats going on with it.

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("PlayerData_1.2")
local yenLeaderboard = dss:GetOrderedDataStore("YenLeaderboard")
local PlayerLoaded = {}

local function GetData(Key, MaxRetries)
	local Retries = 0
	while true do 

		local Success, Result = pcall(function()
			return ds:GetAsync(Key)
		end)

		if Success then 
			return Result
		else 
			warn("[GetData]: "..Result)

			Retries += 1
			if Retries > MaxRetries then -- Give up :(
				return nil
			end

			-- Exponential backoff (increases the wait, to alleviate the load on roblox's servers if they go down)
			task.wait(2^Retries)

			continue
		end

	end
end

local function LoadData(plr,plot,items,timeleft)
	local Success, Result = pcall(function()
		return GetData(plr.UserId,5)
	end)

	if not Success then
		warn("Datastore Error: "..Result) -- In case of an error, Result is the error message
		return
	end

	PlayerLoaded[plr.UserId] = true

	if Result then -- If there are no errors, Result is the data
		print(Result)
		print("got savedata")
		local savedValues = Result[1]
		local savedItems = Result[2]

		-- Cache Data descendants by name for fast lookup
		local dataDescendants = {}
		for _, valObj in pairs(plr.Data:GetDescendants()) do
			dataDescendants[valObj.Name] = valObj
		end

		-- Apply saved values using the cache
		for _, savedVal in pairs(savedValues) do
			local name = savedVal[1]
			local value = savedVal[2]

			local valObj = dataDescendants[name]

			if valObj then
				valObj.Value = value
			end
		end

		-- Recreate saved items
		for _, itemName in pairs(savedItems) do
			local item = Instance.new("StringValue")
			item.Name = itemName
			item.Parent = items
		end

		wait(1)
		for _,v in pairs(plot.Spots:GetChildren()) do
			if v:FindFirstChildWhichIsA("Model") then
				local income = v:FindFirstChildWhichIsA("Model"):GetAttribute("Income")
				local secondsgone = os.time() - timeleft.Value
				local profit = math.round((income*secondsgone)/2)

				plr.Data.Characters[v.Name]["CashGenerated"..v.Name].Value+=profit
			end
		end
	end
end

game.Players.PlayerAdded:Connect(function(plr)
	local plot = game.Workspace.Plots:FindFirstChild("Unclaimed")
	plot.Name = plr.Name
	plot.NameTag.BillboardGui.TextLabel.Text = plr.Name.."'s Base"

	plr.CharacterAdded:Connect(function()
		coroutine.wrap(function()
			print("pivoted character")
			plr.Character:PivotTo(plot.Lasers.Block.CFrame*CFrame.new(0,0,-10))

			for _,v in pairs(plr.Data:WaitForChild("ToolPasses"):GetChildren()) do
				if v.Value == true then
					local tool = game.ReplicatedStorage.ToolGamepasses[v.Name]:Clone()
					tool.Parent = plr.Backpack
				end

				v.Changed:Connect(function()
					if v.Value == true then
						local tool = game.ReplicatedStorage.ToolGamepasses[v.Name]:Clone()
						tool.Parent = plr.Backpack
					end
				end)
			end
		end)()
	end)

	local data = Instance.new("Folder",plr)
	data.Name = "Data"

	local bannedbool = Instance.new("BoolValue")
	bannedbool.Parent = data
	bannedbool.Name = "Banned"

	local codes = Instance.new("Folder",data)
	codes.Name = "Codes"

	for _,v in pairs(game.ReplicatedStorage.Codes:GetChildren()) do
		local codebool = Instance.new("BoolValue",codes)
		codebool.Parent = codes
		codebool.Name = v.Name
	end

	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = plr
	leaderstats.Name = "leaderstats"
	local yenleaderstat = Instance.new("NumberValue")
	yenleaderstat.Parent = leaderstats
	yenleaderstat.Name = "Yen"
	local rebirthsleaderstat = Instance.new("NumberValue")
	rebirthsleaderstat.Parent = leaderstats
	rebirthsleaderstat.Name = "Rebirths"

	local yen = Instance.new("NumberValue",data)
	yen.Name = "Yen"
	yen.Value = 50
	yenleaderstat.Value = 50

	local timeleft = Instance.new("NumberValue",data)
	timeleft.Name = "TimeLeft"

	local rebirths = Instance.new("NumberValue",data)
	rebirths.Name = "Rebirths"

	local ownedcharacters = Instance.new("Folder",data)
	ownedcharacters.Name = "Characters"

	local items = Instance.new("Folder",data)
	items.Name = "Items"

	local yenmulti = Instance.new("NumberValue",data)
	yenmulti.Name = "YenMultiplier"
	yenmulti.Value = 1

	local quests = Instance.new("Folder",data)
	quests.Name = "Quests"
	
	local ownsfloor2 = Instance.new("BoolValue",data)
	ownsfloor2.Name = "OwnsFloor2"

	local toolgamepasses = Instance.new("Folder",data)
	toolgamepasses.Name = "ToolPasses"
	for _,v in pairs(game.ReplicatedStorage.ToolGamepasses:GetChildren()) do
		local toolbool = Instance.new("BoolValue",toolgamepasses)
		toolbool.Name = v.Name
	end

	for _,v in pairs(game.ReplicatedStorage.Quests:GetChildren()) do
		local questbool = Instance.new("BoolValue",quests)
		questbool.Name = v.Name
		questbool.Value = false

		local claimed = Instance.new("BoolValue",questbool)
		claimed.Name = "Claimed"..v.Name
		claimed.Value = false
	end

	local statsfolder = Instance.new("Folder",data)
	statsfolder.Name = "Stats"
	local CharactersSold = Instance.new("NumberValue",statsfolder) CharactersSold.Name = "CharactersSold"
	local Steals = Instance.new("NumberValue",statsfolder) Steals.Name = "Steals"

	local index = Instance.new("Folder",data)
	index.Name = "Index"
	for _,v in pairs(game.ReplicatedStorage.Characters:GetDescendants()) do
		if v:IsA("Model") then
			local indexed = Instance.new("BoolValue",index)
			indexed.Name = v.Name
		end
	end

	local function setupcharacterval(i)
		local character = Instance.new("StringValue",ownedcharacters)
		character.Name = "Character"..i

		local amounttoclaim = Instance.new("NumberValue",character)
		amounttoclaim.Name = "CashGenerated".."Character"..i

		local mutation = Instance.new("StringValue",character)
		mutation.Name = "Mutation".."Character"..i

		--ignore this its just to load the characters when they're changed
		character.Changed:Connect(function(val)
			if val == "" then
				if workspace.Plots:FindFirstChild(plr.Name).Spots:FindFirstChild(character.Name):FindFirstChildWhichIsA("Model") then
					workspace.Plots:FindFirstChild(plr.Name).Spots:FindFirstChild(character.Name):FindFirstChildWhichIsA("Model"):Destroy()
				end
				mutation.Value = ""
				amounttoclaim.Value = 0
			else
				index:FindFirstChild(val).Value = true
				local characterbody
				local rarity
				local raritycolor
				local mutationtype

				for _,v in pairs(game.ReplicatedStorage.Characters:GetDescendants()) do
					if v.Name == val then
						characterbody = v:Clone()
						rarity = v.Parent.Name
					end
				end

				if rarity == "Secret" then
					raritycolor = Color3.new(0, 1, 0.584314)
				elseif rarity == "Godly" then
					raritycolor = Color3.new(0.533333, 0, 1)
				elseif rarity == "Mythic" then
					raritycolor = Color3.new(1, 0.0980392, 0.85098)
				elseif rarity == "Legendary" then
					raritycolor = Color3.new(1, 0.745098, 0.0941176)
				elseif rarity == "Rare" then
					raritycolor = Color3.new(0.0901961, 0.486275, 1)
				elseif rarity == "Common" then
					raritycolor = Color3.new(0.6, 0.6, 0.6)
				end

				characterbody.HumanoidRootPart.Anchored = true
				characterbody.Parent = game.Workspace.Plots:FindFirstChild(plr.Name).Spots:FindFirstChild(character.Name)
				characterbody.PrimaryPart.CFrame = game.Workspace.Plots:FindFirstChild(plr.Name).Spots:FindFirstChild(character.Name).CFrame*CFrame.new(0,3,0)

				local idle = characterbody.Humanoid.Animator:LoadAnimation(characterbody.Idle)
				idle:Play()

				local label = game.ReplicatedStorage.CharacterLabel:Clone()
				label.Parent = characterbody
				label.Position = characterbody.Head.Position

				label.BillboardGui.CharName.Text = characterbody.Name
				label.BillboardGui.Rarity.Text = rarity
				if raritycolor then
					label.BillboardGui.Rarity.TextColor3 = raritycolor
				else
					label.BillboardGui.Rarity.TextColor3 = Color3.new(1,1,1)
					local gradient = game.ReplicatedStorage.CelestialGradient:Clone()
					gradient.Parent = label.BillboardGui.Rarity
				end

				label.BillboardGui.Income.Text = characterbody:GetAttribute("Income").."/s"
				label.BillboardGui.Price.Text = "¥"..characterbody:GetAttribute("Price")

				if mutation.Value ~= "" then
					mutationtype = mutation.Value
					label.BillboardGui.Mutations.Text = mutationtype
					characterbody:FindFirstChildWhichIsA("Shirt"):Destroy()
					characterbody:FindFirstChildWhichIsA("Pants"):Destroy()
					local mutationinstance = Instance.new("StringValue",characterbody)
					mutationinstance.Value = mutationtype
					mutationinstance.Name = "Mutation"
				end

				if mutationtype == "Golden" then
					local oldincome
					for _,v in pairs(game.ReplicatedStorage.Characters:GetDescendants()) do
						if v.Name == characterbody.Name then
							oldincome = v:GetAttribute("Income")
						end
					end

					characterbody:SetAttribute("Income",oldincome*3)
					label.BillboardGui.Income.Text = (characterbody:GetAttribute("Income")).."/s"

					local particles = game.ReplicatedStorage.GemMutation:Clone()
					particles.Parent = characterbody.Torso
					particles.Stars.Color = ColorSequence.new(Color3.new(1, 0.737255, 0.0745098))
					particles.Cross.Color = ColorSequence.new(Color3.new(1, 0.737255, 0.0745098))
					label.BillboardGui.Mutations.TextColor3 = Color3.new(1, 0.737255, 0.0745098)
					for _,v in pairs(characterbody:GetDescendants()) do
						if v:IsA("SpecialMesh") then
							v.TextureId = ""
						end

						if v:IsA("BasePart") then
							v.Color = Color3.new(1, 0.737255, 0.0745098)
						end
					end
				elseif mutationtype == "Diamond" then
					local oldincome
					for _,v in pairs(game.ReplicatedStorage.Characters:GetDescendants()) do
						if v.Name == characterbody.Name then
							oldincome = v:GetAttribute("Income")
						end
					end

					characterbody:SetAttribute("Income",oldincome*5)
					label.BillboardGui.Income.Text = (characterbody:GetAttribute("Income")).."/s"
					local particles = game.ReplicatedStorage.GemMutation:Clone()
					particles.Parent = characterbody.Torso
					particles.Stars.Color = ColorSequence.new(Color3.new(0.101961, 0.654902, 1))
					particles.Cross.Color = ColorSequence.new(Color3.new(0.101961, 0.654902, 1))
					label.BillboardGui.Mutations.TextColor3 = Color3.new(0.101961, 0.654902, 1)
					for _,v in pairs(characterbody:GetDescendants()) do
						if v:IsA("SpecialMesh") then
							v.TextureId = ""
						end

						if v:IsA("BasePart") then
							v.Color = Color3.new(0.101961, 0.654902, 1)
						end
					end
				elseif mutationtype == "Emerald" then
					local oldincome
					for _,v in pairs(game.ReplicatedStorage.Characters:GetDescendants()) do
						if v.Name == characterbody.Name then
							oldincome = v:GetAttribute("Income")
						end
					end

					characterbody:SetAttribute("Income",oldincome*7)
					label.BillboardGui.Income.Text = (characterbody:GetAttribute("Income")).."/s"
					local particles = game.ReplicatedStorage.GemMutation:Clone()
					particles.Parent = characterbody.Torso
					particles.Stars.Color = ColorSequence.new(Color3.new(0.0980392, 1, 0.231373))
					particles.Cross.Color = ColorSequence.new(Color3.new(0.0980392, 1, 0.231373))
					label.BillboardGui.Mutations.TextColor3 = Color3.new(0.0980392, 1, 0.231373)
					for _,v in pairs(characterbody:GetDescendants()) do
						if v:IsA("SpecialMesh") then
							v.TextureId = ""
						end

						if v:IsA("BasePart") then
							v.Color = Color3.new(0.0980392, 1, 0.231373)
						end
					end
				end

				local wc = Instance.new("WeldConstraint",label)
				wc.Part0 = characterbody.Head
				wc.Part1 = label

				local stealpromptpart = Instance.new("Part")
				stealpromptpart.CanCollide = false
				stealpromptpart.Size = Vector3.new(0.1, 0.1, 0.1)
				stealpromptpart.Anchored = true
				stealpromptpart.CFrame = characterbody.HumanoidRootPart.CFrame*CFrame.new(0,3.5,0)
				stealpromptpart.Transparency = 1
				stealpromptpart.Name = "StealPrompt"
				stealpromptpart.Parent = characterbody

				local stealprompt = game.ReplicatedStorage.StealPrompt:Clone()
				stealprompt.Parent = stealpromptpart

				stealprompt.Triggered:Connect(function(plr)
					if not plr.Character:FindFirstChild("Stealing") then
						game.ReplicatedStorage.Notify:FireClient(plr,"Someone is stealing your "..val)
						game.ReplicatedStorage.StealingFrom:FireClient(plr)
					end
				end)

				wait(1)
				game.ReplicatedStorage.ChangeToSell:FireClient(plr,characterbody)
				print("changed to sell")
			end	
		end)
	end

	for i=1,10 do
		setupcharacterval(i)
	end

	pcall(function()
		yenLeaderboard:SetAsync(plr.UserId, math.round(plr.Data.Yen.Value))
	end)

	yen.Changed:Connect(function(val)
		yenleaderstat.Value = val
	end)

	rebirths.Changed:Connect(function(val)
		rebirthsleaderstat.Value = val
	end)

	LoadData(plr,plot,items,timeleft)


	if bannedbool.Value == true then
		plr:Kick("You are banned from the game.")
	end

	bannedbool.Changed:Connect(function(val)
		if val == true then
			plr:Kick("You are banned from the game.")
		end
	end)
end)

local function savedata(plr)
	if not PlayerLoaded[plr.UserId] then return end
	PlayerLoaded[plr.UserId] = nil
	
	plr.Data.TimeLeft.Value = os.time()

	local savedata = {}
	local basevalues = {}
	for _,v in pairs(plr.Data:GetDescendants()) do
		if v:IsA("ValueBase") and v.Parent.Name ~= "Items" then
			table.insert(basevalues,{v.Name,v.Value})
		end
	end
	table.insert(savedata,basevalues)
	local items = {}
	for _,v in pairs(plr.Data.Items:GetChildren()) do
		table.insert(items,v.Name)
	end
	table.insert(savedata,items)

	print("set savedata")

	pcall(function()
		yenLeaderboard:SetAsync(plr.UserId, math.round(plr.Data.Yen.Value))
	end)

	local success, err = pcall(function()
		ds:SetAsync(plr.UserId, savedata)
	end)

	if not success then
		warn("Failed to save data for "..plr.Name..": "..err)
	end
end

game.Players.PlayerRemoving:Connect(function(plr)
	local plot = game.Workspace.Plots:FindFirstChild(plr.Name)
	plot.Name = "Unclaimed"
	plot.NameTag.BillboardGui.TextLabel.Text = "Empty Base"

	for _,v in pairs(plot.Spots:GetChildren()) do
		if v:FindFirstChildWhichIsA("Model") then
			v:FindFirstChildWhichIsA("Model"):Destroy()
			v.Claim.BillboardGui.TextLabel.Text = ""
		end
	end

	savedata(plr)
	wait(1)
end)

game:BindToClose(function()
	-- Wait for all PlayerRemoving connections to finish
	for _, plr in pairs(game.Players:GetPlayers()) do
		-- Save data for all players still in game
		local success, err = pcall(function()
			savedata(plr)
		end)

		if not success then
			warn("Failed to save data for "..plr.Name..": "..err)
		end
	end

	-- Give time for datastore requests to complete
	wait(4)
end)

Is there a pattern to the player that gets affected? Or is it seemingly random?

1 Like

Seemingly Random. I made an update to my script which I assumed to be the problem, it has been reflected in the original post.

to clarify: the update has not fixed the issue.

Why not update the original post?

Is player data getting erased, or is it failing to save the session?

Found the reason why it doesn’t work

	local Success, Result = pcall(function()
		return GetData(plr.UserId,5)
	end)

This pcall was for GetAsync(), not a retry function. The retry function should return two variables instead of 1 (to differentiate failure and a new player that doesn’t have data)

Use the GetData function from my previous reply, and change the GetData call to this

	local Success, Result = GetData(plr.UserId,5)

No pcalls needed because GetData has pcalls and cannot error

1 Like