There was a report of data loss in one of my games. Since no one plays my games (fortunately), I was the one that lost all my data while testing the game. It hurt a little (ok maybe it hurt alot), so that is why I want to improve my datastore so it won’t happen again.
The thing is, I followed all the Datastore tutorials on the developer site, and applied all its good practices over to my datastore script. However, I haven’t considered using UpdateAsync, which people on this forum seem to recommend over SetAsync, because of its benefits. (like reviewing old value first)
How can I use UpdateAsync on a table value though? I don’t quite know how to approach saving all its values. Would I need a for loop or something? Any help and feedback is greatly appreciated.
Saving data function
local function getPlayerStats(player)
local leaderstats = player:WaitForChild("leaderstats")
local cash = leaderstats:WaitForChild("Cash")
local wins = leaderstats:WaitForChild("Wins")
local playerstats = player:WaitForChild("playerstats")
local roundsPlayed = playerstats:WaitForChild("RoundsPlayed")
local highestPoints = playerstats:WaitForChild("HighestPoints")
local highestPointsDuringRound = playerstats:WaitForChild("HighestPointsDuringRound")
local highestStreak = playerstats:WaitForChild("HighestStreak")
local totalButtonsClicked = playerstats:WaitForChild("TotalButtonsClicked")
local gameStats = {cash,wins,roundsPlayed,highestPoints,highestPointsDuringRound,highestStreak,totalButtonsClicked}
local playerStats = {}
for _,stat in ipairs(gameStats) do
playerStats[stat.Name] = stat.Value
end
return playerStats
end
local function saveData(player)
local playerStats = getPlayerStats(player)
local tries = 0
local success
repeat
tries += 1
success = pcall(function()
local playerUserId = "Player_" .. player.UserId
playerData:SetAsync(playerUserId, playerStats)
end)
if not success then wait(1) end
until tries == NUMBER_OF_RETRIES or success
if not success then
warn("There was an issue saving the player's data.")
end
end
Whole data script
-- [[ Services ]] --
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
-- [[ Datastores ]] --
local playerData = DataStoreService:GetDataStore("PlayerData")
-- [[ Configurable ]] --
local NUMBER_OF_RETRIES = 3
local AUTOSAVE_INTERVAL = 5 * 60
local function loadData(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local points = Instance.new("IntValue")
points.Name = "Points"
points.Parent = leaderstats
local streak = Instance.new("IntValue")
streak.Name = "Streak"
streak.Parent = leaderstats
-- Datastore stats
local cash = Instance.new("IntValue")
cash.Name = "Cash"
cash.Parent = leaderstats
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Parent = leaderstats
-- Other datastore stats
local playerstats = Instance.new("Folder")
playerstats.Name = "playerstats"
playerstats.Parent = player
local roundsPlayed = Instance.new("IntValue")
roundsPlayed.Name = "RoundsPlayed"
roundsPlayed.Parent = playerstats
local totalButtonsClicked = Instance.new("IntValue")
totalButtonsClicked.Name = "TotalButtonsClicked"
totalButtonsClicked.Parent = playerstats
local highestPoints = Instance.new("IntValue")
highestPoints.Name = "HighestPoints"
highestPoints.Parent = playerstats
local highestPointsDuringRound = Instance.new("IntValue")
highestPointsDuringRound.Name = "HighestPointsDuringRound"
highestPointsDuringRound.Parent = playerstats
local highestStreak = Instance.new("IntValue")
highestStreak.Name = "HighestStreak"
highestStreak.Parent = playerstats
-- Non-datastore stats during round
local roundstats = Instance.new("Folder")
roundstats.Name = "roundstats"
roundstats.Parent = player
local finalPoints = Instance.new("IntValue")
finalPoints.Name = "FinalPoints"
finalPoints.Parent = roundstats
local finalStreak = Instance.new("IntValue")
finalStreak.Name = "FinalStreak"
finalStreak.Parent = roundstats
local redButtonsClicked = Instance.new("IntValue")
redButtonsClicked.Name = "RedButtonsClicked"
redButtonsClicked.Parent = roundstats
local yellowButtonsClicked = Instance.new("IntValue")
yellowButtonsClicked.Name = "YellowButtonsClicked"
yellowButtonsClicked.Parent = roundstats
local greenButtonsClicked = Instance.new("IntValue")
greenButtonsClicked.Name = "GreenButtonsClicked"
greenButtonsClicked.Parent = roundstats
local buttonsClicked = Instance.new("IntValue")
buttonsClicked.Name = "ButtonsClicked"
buttonsClicked.Parent = roundstats
local playercosmetics = Instance.new("Folder")
playercosmetics.Name = "playercosmetics"
playercosmetics.Parent = player
local trail = Instance.new("IntValue")
trail.Name = "Trail"
trail.Parent = playercosmetics
local title = Instance.new("StringValue")
title.Name = "Title"
title.Parent = playercosmetics
local playerUserId = "Player_" .. player.UserId
local success, data = pcall(function()
return playerData:GetAsync(playerUserId)
end)
if success then
if data then
cash.Value = data["Cash"]
wins.Value = data["Wins"]
roundsPlayed.Value = data["RoundsPlayed"]
highestPoints.Value = data["HighestPoints"]
highestPointsDuringRound.Value = data["HighestPointsDuringRound"]
highestStreak.Value = data["HighestStreak"]
totalButtonsClicked.Value = data["TotalButtonsClicked"]
else
cash.Value = 0
wins.Value = 0
roundsPlayed.Value = 0
highestPoints.Value = 0
highestPointsDuringRound.Value = 0
highestStreak.Value = 0
totalButtonsClicked.Value = 0
end
else
warn("There was an issue loading the player's data.")
end
end
local function getPlayerStats(player)
local leaderstats = player:WaitForChild("leaderstats")
local cash = leaderstats:WaitForChild("Cash")
local wins = leaderstats:WaitForChild("Wins")
local playerstats = player:WaitForChild("playerstats")
local roundsPlayed = playerstats:WaitForChild("RoundsPlayed")
local highestPoints = playerstats:WaitForChild("HighestPoints")
local highestPointsDuringRound = playerstats:WaitForChild("HighestPointsDuringRound")
local highestStreak = playerstats:WaitForChild("HighestStreak")
local totalButtonsClicked = playerstats:WaitForChild("TotalButtonsClicked")
local gameStats = {cash,wins,roundsPlayed,highestPoints,highestPointsDuringRound,highestStreak,totalButtonsClicked}
local playerStats = {}
for _,stat in ipairs(gameStats) do
playerStats[stat.Name] = stat.Value
end
return playerStats
end
local function saveData(player)
local playerStats = getPlayerStats(player)
local tries = 0
local success
repeat
tries += 1
success = pcall(function()
local playerUserId = "Player_" .. player.UserId
playerData:SetAsync(playerUserId, playerStats)
end)
if not success then wait(1) end
until tries == NUMBER_OF_RETRIES or success
if not success then
warn("There was an issue saving the player's data.")
end
end
local autoSave = coroutine.wrap(function()
while true do
for _,player in ipairs(Players:GetPlayers()) do
saveData(player)
end
wait(AUTOSAVE_INTERVAL)
end
end)
autoSave()
Players.PlayerAdded:Connect(loadData)
Players.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
if RunService:IsStudio() then return end
if #Players:GetPlayers() > 1 then
for _,player in ipairs(Players:GetPlayers()) do
saveData(player)
end
end
end)