Hi, I’m developing my first datastore system, and I noticed it’s been crashing studio when I stop playing and saving data incorrectly. Basically, when I publish the game and run it, it crashes the client on stop. When it’s not published (meaning datastore doesn’t function), it doesn’t crash. I think it has to do with my BindToClose function. Thanks
playerCount = 0
game.Players.PlayerAdded:Connect(function(player)
playerCount = playerCount + 1
local leaderstats = Instance.new("IntValue")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local coins = Instance.new("IntValue")
coins.Parent = leaderstats
coins.Name = "Coins"
local diamonds = Instance.new("IntValue")
diamonds.Parent = leaderstats
diamonds.Name = "Diamonds"
local level = Instance.new("IntValue")
level.Parent = leaderstats
level.Name = "Level"
local xp = game.ReplicatedStorage:WaitForChild("XP"):Clone()
xp.Parent = level
xp.Changed:Connect(function(value)
local xpFormula = 500 * (level.Value ^ 2) - (500 * level.Value)
if value >= xpFormula then
level.Value = level.Value + 1
xp.Value = xp.Value - xpFormula
diamonds.Value = diamonds.Value + 10
end
end)
local wins = Instance.new("NumberValue")
wins.Parent = leaderstats
wins.Name = "Wins"
player.CharacterAdded:Connect(function(character)
character.Humanoid.WalkSpeed = 16
character.Humanoid.Died:Connect(function()
if character:FindFirstChild("GameTag") then
character.GameTag:Destroy()
end
player:LoadCharacter()
end)
end)
local player_data
local player_data2
local player_data3
local player_data4
local player_data5
pcall(function()
player_data = dataStores:GetAsync(player.UserId.."-Coins")
player_data2 = dataStores:GetAsync(player.UserId.."-Diamonds")
player_data3 = dataStores:GetAsync(player.UserId.."-Levels")
player_data4 = dataStores:GetAsync(player.UserId.."-XP")
player_data5 = dataStores:GetAsync(player.UserId.."-Wins")
end)
if player_data ~= nil then
coins.Value = player_data
else
coins.Value = 0
end
if player_data2 ~= nil then
diamonds.Value = player_data
else
diamonds.Value = 0
end
if player_data3 ~= nil then
level.Value = player_data
else
level.Value = 1
end
if player_data4 ~= nil then
xp.Value = player_data4
else
xp.Value = 0
end
if player_data5 ~= nil then
wins.Value = player_data5
else
wins.Value = 0
end
end)
local bindableEvent = Instance.new("BindableEvent")
game.Players.PlayerRemoving:Connect(function(player)
pcall(function()
dataStores:SetAsync(player.UserId.."-Coins", player.leaderstats.Coins.Value)
dataStores:SetAsync(player.UserId.."-Diamonds", player.leaderstats.Diamonds.Value)
dataStores:SetAsync(player.UserId.."-Levels", player.leaderstats.Level.Value)
dataStores:SetAsync(player.UserId.."-XP", player.leaderstats.Level.XP.Value)
dataStores:SetAsync(player.UserId.."-Wins", player.leaderstats.Wins.Value)
print("Saved")
playerCount = playerCount - 1
bindableEvent:Fire()
end)
end)
game:BindToClose(function()
while playerCount >= 0 do
bindableEvent.Event:Wait()
end
end)
Could you make sure your pasted code here has the proper symbols and proper indenting? This is hard to read like this and it makes it difficult to attempt helping you out.
The issue is the wait() in the BindToClose. It’s never going to close in the current way you have it. I wouldn’t use that Wait in the bind to close because it should never even need to be waited for.
You aren’t meant to keep instances pointlessly alive by use of this method. BindToClose simply offers you a window of time to a maximum of 30 seconds to perform any functionality post-instance closure. If you don’t need any post-instance closure functionality to happen, you don’t really have a reason to use BindToClose.
In regards to the crash issue, I can almost guarantee that the BindToClose function is causing the problem. In terms of the code overall, I would suggest that you give it an entire makeover. Here are some things I’d like to point out:
Always try to localise your variables. The playerCount variable doesn’t really need to be global. Just add “local” to the beginning.
The playerCount upvalue, in this case, is not necessary.
Set properties first before the parent. It’s generally good practice to have your instances already set up before you drop them into a container.
Perhaps you’d like to create a function to streamline the creation of stats? Something akin to CreateStat("Level", DefaultValue) or something. Not that necessary, but I personally like to do this if I need to perform certain functions multiple times.
Instead of creating a new key for every item of data (which is inefficient in itself), create a key that uses the player’s UserId and instead save a table of data to that key. Your current method has quite a few flaws, including the fact that you use 5 Get/Set requests every time a save/load is needed. This is exhaustive to the budget and will result in throttling.
You aren’t making use of pcalls correctly. For such calls, you will want to utilise the success boolean returned from a pcall as well as any returned arguments. You can create your own error and no-data handlers from this. The current implementation may see data loss in the future.
The existence of the BindableEvent is pointless.
UpdateAsync should typically be used in place of SetAsync, as UpdateAsync respects previous values as well as any data being queued from other instances and places. SetAsync should only be used if you need to force data to a key, otherwise use UpdateAsync.
I’d be happy to provide you some code samples based on the above points, though I will not provide an entire DataStore refactor. It’s up to you how to interpret this advice and use or leave it.