Hello, I’m new to the forums so sorry if this is the wrong category or something.
I had an issue with my game saving data, I had some reports by players getting their data loss. Below is my script to save player’s data. Now, I’m asking how can I improve the way I save my players data to ensure data loss will never happen again?
local DataStoreService = game:GetService(“DataStoreService”)
local DataStore = DataStoreService:GetDataStore(“Cash”)
local DataStore1 = DataStoreService:GetDataStore(“Wins”)
local DataStore2 = DataStoreService:GetDataStore(“Donated”)
local DataStore3 = DataStoreService:GetDataStore(“EasyWins”)
local DataStore666 = DataStoreService:GetDataStore(“ImpWins”)
local trail1 = DataStoreService:GetDataStore(“trailcyan”)
local trail2 = DataStoreService:GetDataStore(“trailpurple”)
local LeveLStore = DataStoreService:GetDataStore(“LeveL”)
local levelleaderboard = DataStoreService:GetDataStore(“LeveLboard”)
local secsStore = DataStoreService:GetDataStore(“Time Played”)
local earnedCoinsStore = DataStoreService:GetDataStore(“Earned”)
local spentCoinsStore = DataStoreService:GetDataStore(“Spent”)
game.Players.PlayerAdded:Connect(function(Player)
local Leaderstats = Instance.new("Folder")
Leaderstats.Name = "leaderstats"
Leaderstats.Parent = Player
local LeveL = Instance.new("NumberValue")
LeveL.Parent = Leaderstats
LeveL.Name = "LeveL"
LeveL.Value = 0
local Leaderstats2 = Instance.new("Folder")
Leaderstats2.Name = "leaderstats2"
Leaderstats2.Parent = Player
local Currency = Instance.new("IntValue")
Currency.Parent = Leaderstats2
Currency.Name = "Cash"
Currency.Value = 0
local Donated = Instance.new("IntValue")
Donated.Parent = Leaderstats2
Donated.Name = "Donated"
Donated.Value = 0
local wins = Instance.new("IntValue")
wins.Parent = Leaderstats2
wins.Name = "Wins"
wins.Value = 0
local easywins = Instance.new("IntValue")
easywins.Parent = Leaderstats2
easywins.Name = "EasyWins"
easywins.Value = 0
local ImpWins = Instance.new("IntValue")
ImpWins.Parent = Leaderstats2
ImpWins.Name = "ImpWins"
ImpWins.Value = 0
local trailcyan = Instance.new("IntValue")
trailcyan.Parent = Leaderstats2
trailcyan.Name = "trailcyan"
trailcyan.Value = 0
local trailpurple = Instance.new("IntValue")
trailpurple.Parent = Leaderstats2
trailpurple.Name = "trailpurple"
trailpurple.Value = 0
local check = Instance.new("IntValue")
check.Parent = Leaderstats2
check.Name = "check"
check.Value = 0
local LeveLboard = Instance.new("IntValue")
LeveLboard.Parent = Leaderstats2
LeveLboard.Name = "LeveLboard"
LeveLboard.Value = 0
local secs = Instance.new("IntValue")
secs.Parent = Leaderstats2
secs.Name = "Time Played"
secs.Value = 0
local Earned = Instance.new("IntValue")
Earned.Parent = Leaderstats2
Earned.Name = "Earned"
Earned.Value = 0
local Spent = Instance.new("IntValue")
Spent.Parent = Leaderstats2
Spent.Name = "Spent"
Spent.Value = 0
local Data = DataStore:GetAsync(Player.UserId)
local Data1 = DataStore1:GetAsync(Player.UserId)
local Data2 = DataStore2:GetAsync(Player.UserId)
local Data3 = DataStore3:GetAsync(Player.UserId)
local Data666 = DataStore666:GetAsync(Player.UserId)
local datatrail1 = trail1:GetAsync(Player.UserId)
local datatrail2 = trail2:GetAsync(Player.UserId)
local dataLeveLStore = LeveLStore:GetAsync(Player.UserId)
local dataLeveLboard = levelleaderboard:GetAsync(Player.UserId)
local datasecs = secsStore:GetAsync(Player.UserId)
local dataEarned = earnedCoinsStore:GetAsync(Player.UserId)
local dataSpent = spentCoinsStore:GetAsync(Player.UserId)
if Data then
Currency.Value = Data
end
if Data1 then
wins.Value = Data1
end
if Data2 then
Donated.Value = Data2
end
if Data3 then
easywins.Value = Data3
end
if Data666 then
ImpWins.Value = Data666
end
if datatrail1 then
trailcyan.Value = datatrail1
end
if datatrail2 then
trailpurple.Value = datatrail2
end
if dataLeveLStore then
LeveL.Value = dataLeveLStore
end
if dataLeveLboard then
LeveLboard.Value = dataLeveLboard
end
if datasecs then
secs.Value = datasecs
end
if dataEarned then
Earned.Value = dataEarned
end
if dataSpent then
Spent.Value = dataSpent
end
It is completely unnecessary to have so many datastores (one for each stat), you only need one datastore to save the player’s data. To do this, store all of the stats in a single table and save that table to the datastore.
For example:
local data = {}
for _, stat in pairs(player.leaderstats:GetChildren())do
data[stat.Name] = stat.Value
end
I want to make sure if my understanding to your suggestion is correct. Are you referring to my “if data then”? so instead of writing many if for different stat, ill use for loop and a table right?
I think you have so many datastores that it’s queuing and preventing you from saving and getting data properly hence why you’re experiencing data loss.
I should point out that if you now condense those 13 datastores into a single one, all of your players data would be wiped. Either you have to live with that or maybe run a transition period for week or two where if a player joins and they have data stored in the old datastores it grabs all the data from it, saves it in the new one and removes their data from the all the old datastores.
Note: Doing this transition period comes with the same risks of data loss, like as @RatiusRat said it would be querying the datastores to many times.
The game has been running for months now so the data must be move to the new datastore but I have no idea on how I will grab the data from old datastore to save to new datastore, do you have any example so I can try to understand?
The issue with my game is that some of player’s data resets so by using UpdateAsync, will it prevent from getting the player’s data back to 0 instead it will only go back to the last saved data? for example, the player has 5 coins, but during the game it gets to 10 coins then he leaves the game or crashed the game. When he go back he will get 5 coins instead having 0 again? sorry for my bad english
Okay, essentially keep those datastores declared so they can be accessed, when a player joins create an empty table and run GetAsync on each datastore, if there is data for the player in those datastores, put the retrieved value in the table. Once that is done save that table to the new datastore and run RemoveAsync on each of those 13 datastores so it does not repeat this process again.
I suggest using some wait statements when getting and removing the data during the above process so that you do not reach the datastore limits quickly. It will slow the process but reduce the likelihood of failure.
Hello I still don’t understand well how to do what you suggested but I try this one which I feel I had many mistakes.
On top of the codes below the declaration of old datastore, I wrote this one
local player = game:GetService(“Players”)
local NewData = DataStoreService:GetDataStore(“NewData”)
–created empty table, im not sure if this should be global or inside the player added function
local data = {}
Inside the game players added function, I didn’t touch anything instead I just added this at the end of the function
if dataSpent then
Spent.Value = dataSpent
end
--this one
for _, stat in pairs(player.leaderstats:GetChildren())do
data[stat.Name] = stat.Value
end end)
Then on game players removing, I still didn’t touch anything but added this which I don’t know if correct
earnedCoinsStore:SetAsync(Player.UserId, Player.leaderstats2.Earned.Value)
spentCoinsStore:SetAsync(Player.UserId, Player.leaderstats2.Spent.Value)
--this one
for _, stat in pairs(player.leaderstats:GetChildren())do
NewData:SetAsync(Player.UserId,data[stat.Name].Value)
end end)
On the RemoveAsync, I have no idea how I will write it on my codes.
No, at least I don’t think it can. " A OrderedDataStore is essentially a GlobalDataStore with the exception that stored values must be positive integers ."