Self-explanatory title. It isn’t so common but every once in a while my DataStores error when I’m playtesting the game in Studio. Is this normal or am I doing something wrong?
Below is the script I use to handle my data.
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("UserData")
local function saveData(player) -- The functions that saves data
local tableToSave = {
["XP"] = player.info.XP.Value;
["Level"] = player.info.Level.Value;
["Tokens"] = player.info.Tokens.Value;
["Cash"] = player.info.Cash.Value;
["Towers"] = {
};
["Loadout"] = {
};
}
for i,v in pairs(player.info.Towers:GetChildren()) do
tableToSave["Towers"][v.Name] = v.Value
end
for i,v in pairs(player.info.Loadout:GetChildren()) do
tableToSave["Loadout"][tonumber(v.Name)] = v.Value
end
local success, err = pcall(function()
dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
end)
if success then -- If the data has been saved
print("Data has been saved!")
else -- Else if the save failed
print("Data hasn't been saved!")
warn(err)
end
end
game.Players.PlayerAdded:Connect(function(player)
local data
local success, err = pcall(function()
data = dataStore:GetAsync(player.UserId) -- Get the data from the datastore
end)
local info = player:WaitForChild("info")
if success and data then -- If there were no errors and player loaded the data
info.XP.Value = data["XP"]
info.Level.Value = data["Level"]
for i,v in pairs(data["Loadout"]) do
info.Loadout[tostring(i)].Value = v
end
for i,v in pairs(data["Towers"]) do
info.Towers[tostring(i)].Value = v
end
elseif success and not data then
-- newbie
info.Towers:WaitForChild("Freezer").Value = true
info.Towers:WaitForChild("Soldier").Value = true
info.Loadout["1"].Value = "Soldier"
info.Loadout["2"].Value = "Freezer"
end
end)
game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
local success, err = pcall(function()
saveData(player) -- Save the data
end)
end)
game:BindToClose(function() -- When the server shuts down
for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
local success, err = pcall(function()
saveData(player) -- Save the data
end)
if success then
print("Data has been saved")
else
print("Data has not been saved!")
end
end
end)
It’s due to how BindToClose works. I’ll attempt to fix the problem for you and explain what I mean (I’ll edit the reply when done btw)
Edit: @lunari_s This should work to solve your problem:
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("UserData")
local function saveData(player) -- The functions that saves data
local tableToSave = {
["XP"] = player.info.XP.Value;
["Level"] = player.info.Level.Value;
["Tokens"] = player.info.Tokens.Value;
["Cash"] = player.info.Cash.Value;
["Towers"] = {
};
["Loadout"] = {
};
}
for i,v in pairs(player.info.Towers:GetChildren()) do
tableToSave["Towers"][v.Name] = v.Value
end
for i,v in pairs(player.info.Loadout:GetChildren()) do
tableToSave["Loadout"][tonumber(v.Name)] = v.Value
end
local success, err = pcall(function()
dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
end)
if success then -- If the data has been saved
print("Data has been saved!")
else -- Else if the save failed
print("Data hasn't been saved!")
warn(err)
end
end
game.Players.PlayerAdded:Connect(function(player)
local data
local success, err = pcall(function()
data = dataStore:GetAsync(player.UserId) -- Get the data from the datastore
end)
local info = player:WaitForChild("info")
if success and data then -- If there were no errors and player loaded the data
info.XP.Value = data["XP"]
info.Level.Value = data["Level"]
for i,v in pairs(data["Loadout"]) do
info.Loadout[tostring(i)].Value = v
end
for i,v in pairs(data["Towers"]) do
info.Towers[tostring(i)].Value = v
end
elseif success and not data then
-- newbie
info.Towers:WaitForChild("Freezer").Value = true
info.Towers:WaitForChild("Soldier").Value = true
info.Loadout["1"].Value = "Soldier"
info.Loadout["2"].Value = "Freezer"
end
end)
game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
local success, err = pcall(function()
saveData(player) -- Save the data
end)
end)
if game:GetService("RunService"):IsStudio() then
game:BindToClose(function()
task.wait(1) -- If you still experience issues try increasing this value
end)
else
game:BindToClose(function() -- When the server shuts down
local x, y = 0, 0
for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
x += 1
task.spawn(function()
local success, err = pcall(function()
saveData(player) -- Save the data
end)
if success then
print("Data has been saved")
else
print("Data has not been saved!")
end
y += 1
end)
end
repeat task.wait() until y == x
end)
end
This section was added because sometimes Studio closes the server so fast that the code inside of PlayerRemoving doesn’t have time to run:
if game:GetService("RunService"):IsStudio() then
game:BindToClose(function()
task.wait(1) -- If you still experience issues try increasing this value
end)
else
I also modified the existing BindToClose function in order to guarantee that the function has enough time to attempt to save all player’s data, otherwise without the repeat task.wait() until y == x the function will close exactly after the for loop finishes running which won’t give enough time for the code inside of it to run
game:BindToClose(function() -- When the server shuts down
local x, y = 0, 0
for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
x += 1
task.spawn(function()
local success, err = pcall(function()
saveData(player) -- Save the data
end)
if success then
print("Data has been saved")
else
print("Data has not been saved!")
end
y += 1
end)
end
repeat task.wait() until y == x
end)