How does my Auto Saving script look like? Will it ever error?

Currently, it loops through all players every 3 minutes. Checks to see if it is safe to save their data, making sure it was loaded correctly. I added a FindFirstChild and checking if its nil to prevent errors and breaking the code. Is this as safe as I can make it, to prevent it from breaking? So far its worked.


function saveData(player)
	if player ~= nil then
		local pfolder = game.ServerStorage.PlayerStats:FindFirstChild(player.UserId) 
		local SafeToSave = game.ServerStorage.PlayerSaveData:WaitForChild(player.UserId.."-SafeToSave")
		if pfolder ~= nil then
			local DataToStore = {}
			for i,v in pairs(pfolder:GetChildren()) do
				if v ~= nil then
					DataToStore[v.Name] = v.Value
				end
			end
			local tries = 0	
			local success
			repeat
				tries = tries + 1
				print("saving data")
				success = pcall(function()
					if pfolder ~= nil then
						if pfolder:FindFirstChild("Cash") ~= nil and pfolder:FindFirstChild("xp") ~= nil then
							if pfolder.Cash.Value > 0 and pfolder.xp.Value > 0 then
								if SafeToSave ~= nil then
									if SafeToSave.Value == true then
										playerData:SetAsync(prefix .. tostring(player.UserId),DataToStore)
									end
								end
							end
						end
					end
				end)
				if not success then wait(1) end
			until tries == 3 or success 
			if not success then
				warn("Cannot save data for player!: "..player.Name.."-"..player.UserId)
				--pfolder:Destroy()
			else
				print("Data has been saved successfully!: "..player.Name.."-"..player.UserId)
				--pfolder:Destroy()
			end
		end
	end
end


while true do
	wait(180)
	print("Starting auto saving")
	for i,v in pairs(game.Players:GetPlayers()) do
		saveData(v)
	end
	print("Finished auto saving")
	print("--------------------")
end
1 Like

There are a lot of small points I can see:-

  • There is a lot in this save so I would break it down a bit and use local functions.
  • Reduce the number of if nested if statements
  • There is a lot of comparisons e.g. pfolder:FindFirstChild("Cash") ~= nil and SafeToSave.Value == true which are not needed. Only nil and false are treated as false everything else is treated as true.
  • Generally avoid the use of repeat loops. A for loop is far more suited and just break / return when the data saves
  • Pcall should not be used for code that you can use logic to catch errors e.g if statements
  • The save call can be simplified local result, success = pcall(playerData.SetAsync, playerData, prefix .. tostring(player.UserId), DataToStore)
  • SetAsync is a yield function so for the loop for i,v in pairs(game.Players:GetPlayers()) do the wait mght mean players are no longer in your game. If the save errors you have even more time between getting the players and saving the data…

As a general point I never recommend that developers store player data using value objects. I am unsure of why this became the norm but it leads to a slower and possibly more complex save / load system.

2 Likes

Make sure you check out DataStore2.

Persistence always suffers from CAP theorem related issues and you need to consider techniques to protect against those.

1 Like

On the portion where you have repeat [save code] until tries == 3 or success, make it for i=1,3 and put if success then break end somewhere in the for loop, it would equal your repeat.

On top of auto-saving, also make sure to save the player data when the player leaves the game.
With just the auto-save implementation, if player data is changed before the auto-save and they leave the server, then the server will only loop through the current players in the server.

Also make sure to destroy player data folders in server-storage when players leave because it could turn into a massive memory leak.

I’m assuming that you already have these ideas implemented but I just wanted to put it out there for newer developers that stumble upon this topic for advice :slightly_smiling_face:

It’s not easy making sure that the data storing is 100% reliable because developers usually test their logic under near-perfect circumstances, such as testing it with a single player in studio that doesn’t experience much lag and unexpected server shutdowns… Once your game is almost ready to be released, you could turn to the QA testers and possibly have a group of testers try out the game which could give you a general idea of how reliable the data storing is.

The last thing any developer wants is to have a data store issue that causes thousands of cases of data loss, a spammed inbox full of complaints, and a ruined game reputation.

2 Likes