Is it normal for datastores to mess up in Studio playtests or am I doing something horribly wrong?

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)

This is the hierarchy used for info
image

1 Like

Yes, it’s normal. Even if my datastore are super simple and all they still mess up because of how studio closes the game and etc.

By error do you mean you’re getting either a specific error or multiple errors in your output, or an occasional failure to save data?

Sometimes the data saving/loading just doesn’t happen. No specific error.

1 Like

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)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.