DataStore Problem/Data Loss

Hi!

I’m working on a DataStore. I have two games and I coded their DataStores in the same way. But the problem is here; I always get data loss reports in one game (about 10-15 people per month). The only difference is that there is more data in that game.

I leave the necessary parts of the code down below. I would be very happy if you could tell me where I made a mistake, or what I need to optimize. Thank you! (Please do not recommend the DataStore2 method, because the DataStore I’ve already written works good in my other game)

(“PlayerSave” is a folder with all the values)

local Data = game:GetService("DataStoreService"):GetDataStore("Data")
--PUBLISH PROJECT TO ACCESS DATASTORE!

function loadSave(plr)
	local XP = game.ReplicatedStorage[plr.Name].XP
	local Number = game.ReplicatedStorage[plr.Name].Number
	local Inventory = game.ReplicatedStorage[plr.Name].Inventory
	
	--Stats
	local Goals = game.ReplicatedStorage[plr.Name].Goals
	local Assists = game.ReplicatedStorage[plr.Name].Assists
	local AccuratePasses = game.ReplicatedStorage[plr.Name].AccuratePasses
	
	local SavedItems = Data:GetAsync(plr.UserId)
	if SavedItems then
		XP.Value = SavedItems.XP
		Number.Value = SavedItems.Number
		Inventory.Value = SavedItems.Inventory
		
		--Stats
		Goals.Value = SavedItems.Goals
		Assists.Value = SavedItems.Assists
		AccuratePasses.Value = SavedItems.AccuratePasses
	end
end

game.Players.PlayerAdded:Connect(function(plr)
	local success, message = pcall(function()
		game:WaitForChild("ReplicatedStorage")
		game.ReplicatedStorage:WaitForChild("PlayerSave")
		
		if game.ReplicatedStorage:FindFirstChild(plr.Name) ~= nil then
			game.ReplicatedStorage:FindFirstChild(plr.Name):Destroy()
		end

		local Folder = game.ReplicatedStorage.PlayerSave:Clone()
		Folder.Parent = game.ReplicatedStorage
		Folder.Name = plr.Name
		game.ReplicatedStorage:WaitForChild(plr.Name)
		
		loadSave(plr)
	end)
	
	if success then
		warn("DataStore Loaded!")
	else
		warn("DataStore Error While Loading!")
		wait(7)
		warn("DataStore: Retrying...")
		local success, message = pcall(function()
			loadSave(plr)
		end)
		if not success then
			warn("DataStore Error While Loading!")
			wait(7)
			warn("DataStore: Retrying...")
			loadSave(plr)
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	game:BindToClose(function()
		if game:GetService("RunService"):IsStudio() then
	        return
		end

		local Saving
		local PGoals
		local NewData = false
	
		local success, message = pcall(function()
			Saving = {
				["XP"] = game.ReplicatedStorage:FindFirstChild(plr.Name).XP.Value;
				["Number"] = game.ReplicatedStorage:FindFirstChild(plr.Name).Number.Value;
				["Inventory"] = game.ReplicatedStorage:FindFirstChild(plr.Name).Inventory.Value;
				
				--Stats
				["Goals"] = game.ReplicatedStorage:FindFirstChild(plr.Name).Goals.Value;
				["Assists"] = game.ReplicatedStorage:FindFirstChild(plr.Name).Assists.Value;
				["AccuratePasses"] = game.ReplicatedStorage:FindFirstChild(plr.Name).AccuratePasses.Value;
			}
			
			local SavedItems = Data:GetAsync(plr.UserId)
			
			if SavedItems == nil then
				NewData = true
			end
			
			if SavedItems then
				if SavedItems.Goals ~= nil then
					PGoals = SavedItems.Goals
				else
					NewData = true
				end
			end
		end)
			
		if success then
			if game.ReplicatedStorage:FindFirstChild(plr.Name) ~= nil then
				if NewData == true then
					Data:SetAsync(plr.UserId,Saving)
					warn("DataStore Saved!")
				else
					if PGoals ~= nil then
						if PGoals ~= "" then
							if game.ReplicatedStorage:FindFirstChild(plr.Name).Goals.Value >= PGoals then
								Data:SetAsync(plr.UserId,Saving)
								warn("DataStore Saved!")
							end
						end
					end
				end
			end
		else
			warn("DataStore Error While Saving!")
		end
	end)
end)
3 Likes

Try to use UpdateASync() instead of SetASync()

1 Like

We are only seeing your loading code here. We’ll need to see your uploading code as well :slight_smile:

1 Like

It’s not working on this situation. So I have to use SetASync()

It’s already there, can you check it out all the code again?

This is by far one of the best resources for this kind of thing. Read up on it, it’s super useful. Just get the existing data and convert it to DataStore2. Check if a save doesn’t exist in order to convert, so you don’t get caught in an endless loop of updating to your old data. (You can just do :DeleteAsync() also.)

Thanks but please do not recommend the DataStore2 method, because the DataStore I’ve already written works actively, and working good on my other game. (As written above)

My bad. Just so you know, it’s the best way to prevent data loss.

It would be a good idea if you were to use BindToClose. This will save the PlayerData if the game was to shutdown.

game:BindToClose(function()
	-- Code to Save Data
end)
1 Like

Thanks, I added BindToClose() to the code but data loss problem persists. People still reports completely deleted data.

It would probably be helpful if you knew what error the pcall is giving. You could either have the pcall displayed on the clients screen when the data is auto saved or you can send the error to a discord webhook with the player name and the data if you want. Just be sure not to spam the discord webhook.

Why are you using GetAysnc when the player is leaving? i think it would be better to call that once, when the player joins. if you needed a way to check if a player has new data you should use UpdateASync() as it returns the previously stored data and allows you to update it at the same, furthermore adding a BindtoClose (as kind of mentioned above) and placing a wait inside should make the server wait (given amount, max is 30secs ) before fulling closing, thus allowing time for all players data to save( in theory) , here’s what i mean:

game:BindToClose(function()---Fired when the server is about to close
  wait(10) -----wait
print("ServerClosing")
end)
1 Like

I added BindtoClose() to the code but data loss problem persists. And UpdateASync() is not working on this situation. So I have to use SetASync() for now. (PlayerRemoving function)

I’m using GetAysnc to check data, it’s like; if one of player data is less than saved data, don’t save.

What do you mean UpdateAsync() is not working in this situation? From past experience, that would suggest an issue with values, not the actual saving/loading.

Some data is saving and some is not right( so its not that its not saving any data at all)?

What if there is all of that values are strings, which is you can’t use UpdateAsync()? I want that solution actually. There must be a solution about that.

You can use UpdateAsync for anything that set Async can save as well, so you can save strings using it For example: (using your set-up)

(this is just some quick pseudo code, you probably should modify it)

  local success, message = pcall(function()
DataStore:UpdateAsync(PlayerKey, function(oldData)
if oldData == nil then
 return Saving ----returns saving, (table)
     else
         --Save other data using return
                end
         end)

      end

end

Can you explain me more the else statement in that script? Because it’s not saving data if there are data.

If the game shuts down unexpectedly the data will not save. The reason is because the BindToClose function will not work if it is in the PlayerRemoving Function. PlayerRemoving does not fire if the server unexpectedly shuts down. If I were you I would create a save function(Be sure to put pcalls and if you could DataStore the pcall errors and check them that would probably give you the answer to Data Loss). Like so

function saveData(plr)
    --Code to save
end

Then you call this function to Save Data when player is leaving.

game.Players.PlayerRemoving:Connect(function(plr)
       saveData(plr)
end)

If the server shuts down BindToClose is going to save the data.

game:BindToClose(function()
	for _,v in pairs(game.Players:GetChildren()) do
		saveData(v.Name) -- v is the player name
	end
end)

There is a Example BindToClose script here. If this still doesn’t help could you take a picture of the PlayerSave folder.

2 Likes

game:BindToClose(function()
saveData(plr) --?
end)

How to detect plr (Player) on BindToClose()?