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
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.
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
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.