DataStore Causes Studio to Crash

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.

3 Likes

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.

2 Likes

Fixed it, sorry about that.

Another little thing that you did, which I see people do all the time, is using setAsync to update data.

It’s best practice to use updateAsync instead, because setAsync wipes any data which was in the data store before hand.

Therefore, if the call fails, your players may use data. So, I recommend changing to updateAsync for saving.

The problem here is your BindToClose function. You don’t need to make a bindableEvent. Try this:

game:BindToClose(function()
	while wait() do
          --keep the server running for as long as possible
	end
end)

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.