When I join the game, update a value, then leave and rejoin, the value is at 0. My datastore isn’t saving. There are multiple values.
I tested in game.
Can someone help?
local DataStore = game:GetService("DataStoreService")
local GameData = DataStore:GetDataStore("GameData")
local AccessData = DataStore:GetDataStore("TP_Data")
local function addValues(player)
local folder = Instance.new("Folder")
folder.Name = "leaderstats"
folder.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Parent = folder
local diamonds = Instance.new("IntValue")
diamonds.Name = "Diamonds"
diamonds.Parent = folder
local rocks = Instance.new("IntValue")
rocks.Name = "Rocks"
rocks.Parent = folder
print("Values created")
---------- TP Access ------------
local accessFolder = Instance.new("Folder")
accessFolder.Name = "TP_Folder"
accessFolder.Parent = player
local beach = Instance.new("BoolValue")
beach.Name = "Beach"
beach.Parent = accessFolder
local magma = Instance.new("BoolValue")
magma.Name = "Magma"
magma.Parent = accessFolder
local winter = Instance.new("BoolValue")
winter.Name = "Winter"
winter.Parent = accessFolder
local candy = Instance.new("BoolValue")
candy.Name = "Candyland"
candy.Parent = accessFolder
print("Access folders created")
local playerUserId = "Player_" .. player.UserId
local data = GameData:GetAsync(playerUserId)
local TP_Data = AccessData:GetAsync(playerUserId)
if data then
coins.Value = data["Coins"]
diamonds.Value = data["Diamonds"]
rocks.Value = data["Rocks"]
print("Updated values")
else
coins.Value = 0
diamonds.Value = 0
rocks.Value = 0
print("Updated values")
end
if TP_Data then
beach.Value = TP_Data["Beach"]
magma.Value = TP_Data["Magma"]
winter.Value = TP_Data["Winter"]
candy.Value = TP_Data["Candyland"]
print("Access values updated")
else
beach.Value = false
magma.Value = false
winter.Value = false
candy.Value = false
print("Access values updated")
end
end
local function create_table_leaderstats(player)
local player_stats = {}
for _, stat in pairs(player.leaderstats:GetChildren()) do
player_stats[stat.Name] = stat.Value
end
print("leaderstats values table made")
return player_stats
end
local function create_table_access(player)
local player_stats = {}
for _, stat in pairs(player.TP_Folder:GetChildren()) do
player_stats[stat.Name] = stat.Value
end
print("access values table made")
return player_stats
end
local function on_Exit(player)
local leaderstatsData = create_table_leaderstats(player)
local accessData = create_table_access(player)
local success, err = pcall(function()
local playerUserId = "Player_" .. player.UserId
GameData:SetAsync(playerUserId, leaderstatsData)
AccessData:SetAsync(playerUserId, accessData)
print("saved")
end)
if success then
print("Worked")
else
warn(err.." - DATA NOT SAVED!")
end
end
game.Players.PlayerAdded:Connect(addValues)
print("D1")
game.Players.PlayerRemoving:Connect(on_Exit)
print("D2")
game:BindToClose(function()
task.wait(1)
for _, plr in pairs(game:GetService("Players"):GetPlayers()) do
on_Exit(plr)
end
print("saved values for server shutdown")
end)
Mostly likely the server is closing before your data is saved, call
game:BindToClose(function() — function called before server closes
task.wait(1)— waits one second. The server closing is delayed for one second
end
game.Players.PlayerAdded:Connect(addValues)
game.Players.PlayerRemoving:Connect(on_Exit)
game:BindToClose(function()
— wait code here
task.wait 1
end
Game bind to close does not fire when a player leaves but when the server closes. When the last player leaves, this will give them time to save their data before the server closes
last player leaves the game
game:BindToClose(function()— game sees there is a bind on close function so it fires it before the server ends
tast.wait(1) — waits one. It delays the server from closing so your saving code can run
end)—finishes the function
— server closes
Sorry if I make a spelling mistake, I am on tablet
Still not working. The output is printing all the print codes. I think it could do with saving, but I’m not quite sure because the code should be correct.
I just found out that “TP_Access” is the only thing saving. The leaderstats aren’t saving! How is that possible when the saving process is the exact same for both?!?
local Players = game:GetService("Players")
local DataStores = game:GetService("DataStoreService")
local DataStore = DataStores:GetDataStore("DataStore")
local XProtectedCall = xpcall
local TypeCheck = type
Players.PlayerAdded:Connect(function(Player)
local Leaderstats = Instance.new("Folder")
Leaderstats.Name = "leaderstats"
Leaderstats.Parent = Player
local Coins = Instance.new("IntValue")
Coins.Name = "Coins"
Coins.Parent = Leaderstats
local Diamonds = Instance.new("IntValue")
Diamonds.Name = "Diamonds"
Diamonds.Parent = Leaderstats
local Rocks = Instance.new("IntValue")
Rocks.Name = "Rocks"
Rocks.Parent = Leaderstats
local TPFolder = Instance.new("Folder")
TPFolder.Name = "TP_Folder"
TPFolder.Parent = Player
local Beach = Instance.new("BoolValue")
Beach.Name = "Beach"
Beach.Parent = TPFolder
local Magma = Instance.new("BoolValue")
Magma.Name = "Magma"
Magma.Parent = TPFolder
local Winter = Instance.new("BoolValue")
Winter.Name = "Winter"
Winter.Parent = TPFolder
local Candy = Instance.new("BoolValue")
Candy.Name = "Candyland"
Candy.Parent = TPFolder
local Success, Result = XProtectedCall(function()
return DataStore:GetAsync(Player.UserId)
end, function(Error)
return Error
end)
if Success then
if Result then
print(Result)
if TypeCheck(Result) == "table" then
local PlayerStats = Result[1]
Coins.Value = PlayerStats[Coins.Name]
Diamonds.Value = PlayerStats[Diamonds.Name]
Rocks.Value = PlayerStats[Rocks.Name]
local PlayerTPs = Result[2]
Beach.Value = PlayerTPs[Beach.Name]
Magma.Value = PlayerTPs[Magma.Name]
Winter.Value = PlayerTPs[Winter.Name]
Candy.Value = PlayerTPs[Candy.Name]
end
end
else
warn(Result)
end
end)
Players.PlayerRemoving:Connect(function(Player)
local PlayerStats = {}
for _, Stat in ipairs(Player.leaderstats:GetChildren()) do
PlayerStats[Stat.Name] = Stat.Value
end
local PlayerTPs = {}
for _, TP in ipairs(Player.TP_Folder:GetChildren()) do
PlayerTPs[TP.Name] = TP.Value
end
local Data = {PlayerStats, PlayerTPs}
local Success, Result = XProtectedCall(function()
return DataStore:SetAsync(Player.UserId, Data)
end, function(Error)
return Error
end)
if Success then
if Result then
print(Result)
end
else
warn(Result)
end
end)
game:BindToClose(function()
for _, Player in ipairs(Players:GetPlayers()) do
local PlayerStats = {}
for _, Stat in ipairs(Player.leaderstats:GetChildren()) do
PlayerStats[Stat.Name] = Stat.Value
end
local PlayerTPs = {}
for _, TP in ipairs(Player.TP_Folder:GetChildren()) do
PlayerTPs[TP.Name] = TP.Value
end
local Data = {PlayerStats, PlayerTPs}
local Success, Result = XProtectedCall(function()
return DataStore:SetAsync(Player.UserId, Data)
end, function(Error)
return Error
end)
if Success then
if Result then
print(Result)
end
else
warn(Result)
end
end
end)
I’d recommend using a function to contain the save data rather than copy/pasting it 2-3 times. Also, why are you using xpcall, when it doesn’t have any benefits over pcall in this case? Also, why did you assign it
and type to a much longer variable name?
(You also probably shouldn’t use SetAsync, UpdateAsync with a handler is safer)
Twice & I didn’t wrap it inside a function for the sake of clarity, xpcall was used to demonstrate its use and assigning the global function “type” to a local variable integrates it into the local environment (allowing for it to be fetched/indexed quicker) same applies for “xpcall”.
You should really use UpdateAsync so data doesn’t get lost, or use one of the open source modules. Using SetAsync is a data loss crisis waiting to happen.
Other things about the script
Using xpcall actually takes away from clarity, since xpcall is used for custom error handling (which you’re not doing any of). Making it not a function also takes away from clarity, since you don’t know if they’re supposed to have different uses.
Also, I decided to run a benchmark of using type and a variable containing type (using this module, script I benchmarked with below in the folding tab)
Benchmarking Script
local tbl = table.create(1e6,"t")
local last = false
local ServerScriptService = game:GetService('ServerScriptService')
local Benchmarker = require(ServerScriptService:WaitForChild("ModuleScript"))
local function globalType()
for i,v in ipairs(tbl) do
if type(i) == "string" then
-- last = true
else
-- last = false
end
end
end
local typeVariable = type
local function typeVar()
for i,v in ipairs(tbl) do
if typeVariable(v) == "string" then
--last = true
else
-- last = false
end
end
end
Benchmarker:compare(globalType,typeVar)
According to the results, using type() is much faster:
I stuck with SetAsync() as was done in the provided script, my comment about clarity was regarding the bit of code which was copied twice, use of xpcall was to demonstrate its use (to those who are unfamiliar) and finally your bench-marking results are incorrect, localizing any variable will allow for it to be fetched faster (as you circumvent the need to index the global environment table in order to find it). I’m going to assume your use of that module was improper somehow.