Problems with Data Loss

I’ve been problems with players losing their data for a while now and I still don’t really know / understand if it’s a saving or a loading problem, I’ve never changed my code and now that I’m revising it, I can’t really find anything blatantly wrong.

I tried but was unsuccessful at trying to replicate the data loss errors myself, so I still don’t know if it’s a loading problem or a saving problem.
Suspecting it was a loading problem, I tried wrapping the loading in a pcall() and not letting the player save if the Loading PCall returns an error so that its data cannot be overwritten, but nothing much changed.

Any ideas or overall help is greatly appreciated, thanks!

------CODE------
There’s a ModuleScript in the serverStorage that handles saving and loading.

Module Script Functions:

local Data = {}

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DataStoreName")

function Data.Load(plr: Player)
	local plrKey = plr.UserId.."-levelData"
	local plrData;
	
	local Success, Error = pcall(function()
		plrData = ds:GetAsync(plrKey)
	end)

	if Success then
		if Data then
			print("Level Data Loaded "..plr.UserId)

			return plrData
			
		else
			warn("Level Data not found, the player is new")
			
			return nil
			-- No data was found, so it will return nil and the loading data Player added function will load an empty table (new player).
		end
	else
		print(Error)
		plr:SetAttribute("ShouldSaveData", false)
		plr:Kick("Your data could not be loaded correctly. To prevent data loss you're going to get kicked out of the game, please try again by joining the game again!")
		-- Tag this player that it shouldn't save data and kick them
	end

end

function Data.Save(plr: Player)

	local plrStats = plr.playerFolder
	local plrLVL = plrStats.PLR_Level
	local plrEXP = plrStats.PLR_XP

	local plrKey = plr.UserId.."-levelData"

	local plrData = {
		LEVEL = plrLVL.Value;
		EXP = plrEXP.Value;
	}


	--// Check if the player is eligible for data saving
	if plr:GetAttribute("ShouldSaveData") == false then
		warn("To prevent data overwrite, data CANNOT be saved as there were problems while loading it")
	else
		--// Main saving function
		local success, errormessage = pcall(function()
			ds:SetAsync(plrKey, plrData)
		end)

		if not success then
			warn("Could not save Level Data! "..errormessage)
		else
			print("Level Data Saved!")
		end
	end

end

return Data

Loading Data:

game.Players.PlayerAdded:Connect(function(plr: Player)
	
	PlayerStats.New(plr) --It creates a folder with the actual Value instances
	
	--// Data Loading
	local plrData = Data.Load(plr) --Function in the module
	
	--// values check
	local savedLvl;
	local savedExp;
	
	--// Player Level
	if plrData ~= nil then 
		print("plrData is not an empty table ===> Player is not new")
		savedLvl = plrData.LEVEL
	else
		print("plrData is an empty table ===> Player is new")
		savedLvl = '0'
	end
	
	--// Player EXP
	if plrData ~= nil then 
		print("plrData is not an empty table ===> Player is not new")
		savedExp = plrData.EXP
	else
		print("plrData is an empty table ===> Player is new")
		savedExp = 0
	end

	--// new chec
	if plrData == nil then
		plrData = {}
	end
	
	local plrStats = plr:WaitForChild("playerFolder")
	local plrLVL = plrStats.PLR_Level
	local plrEXP = plrStats.PLR_XP
	
	plrLVL.Value = savedLvl
	plrEXP.Value = savedExp

Saving Data:

game.Players.PlayerRemoving:Connect(function(plr: Player)
	Data.Save(plr)
end)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
		Data.Save(plr)
	end
end)
1 Like

I have two ideas on fixing your data losing problem;

first one could be typo;

second is this code block for saving I saw same in another forum post u might want to check it out;

1 Like

Whilst I agree with what @ROXAR0 has posted, his solution really does not solve your underlying issue. What is important however is you do that BindToClose function as it does prevent the loss of data overall.

For data to be lost, data must first exist, this is an implicit fact. Two key things I have noticed in your code:

  • If theirs an error, it is assumed their is no data, however if their is an error the data is overwritten. As such you should try and account for errors when data cannot load. A single miswritten like could mean data is wiped in the future.
  • When saving, if their data is wrongly saved or not saved at all, the data is automatically removed. As such check that the data exists when saving.

It’s also important to note your in structure should reflect your out structure, as such ensure that data is not corrupted during the read/write exchange.

1 Like

The code looks fine to me; it avoids a common issue of data resetting upon DataStore errors.

Without more information it is hard to say what the cause is. Are players reporting a complete loss of their entire data or is it just them losing a small amount (for example only one play session) of progress?

1 Like

Yes, every report about data loss is about the loss of all of the data

This is really helpful; the code is pretty old so I’ll have to check into that Data / plrData.
Also the BindToClose thing, looks like it might be the case. I will test out things and give an update.
Thanks!

This means that somehow either the data is failing to be loaded yet not marked as unsavable, or the Save function is failing to retrieve the data. I’m more inclined to think the first one. Is there any possibility that the data can fail to be placed into the Value instances that store it? Perhaps some edge case in another part of code?

(By the way, the advice about BindToClose is good and should be implemented, but it seems unlikely to be related to this specific issue.)

Just looked into it and it doesn’t seem like it. The only time that the value instances are referred to in other scripts are always in local scripts only to retrieve their stored value.