Data not always saving on PlayerLeaving

My script for saving data (below) is only saving part of the time. I have a feeling I know why, but I’m not totally sure how I can go about fixing this issue.

And for anyone wondering, the global variable “keys” is located in the same script, so it can’t be nil, right???

function savePlayerData(player)
	local leaderstats = player.leaderstats
	dataStoreService:GetDataStore(_G.keys.GeneralData):SetAsync(player.UserId, {
		["Wins"] = leaderstats.Wins.Value, 
		["Losses"] = leaderstats.Losses.Value,
		["Level"] = leaderstats.Level.Value, 
		["Experience"] = leaderstats.Experience.Value, 
		["Coins"] = leaderstats.Coins.Value, 
		["Gems"] = leaderstats.Gems.Value,
		["Equipped"] = {
			["Skin"] = tostring(player.EquippedData:GetAttribute("Skin")), ["Effect"] = tostring(player.EquippedData:GetAttribute("Effect")), ["Emote"] = tostring(player.EquippedData:GetAttribute("Emote"))
		}}
	);
end

game.Players.PlayerRemoving:Connect(function(player)
	local success,err
	repeat wait() success,err = pcall(function() savePlayerData(player) end) until success
end)

game:BindToClose(function()
	for _,player in pairs(game.Players:GetPlayers()) do
		local s = coroutine.wrap(function()
			savePlayerData(player)
		end)
		s(); 
	end
end)
4 Likes

Did you add debug prints?
See if leaderstats exists, see if _G.keys isn’t nil, see if the loop succeeded or failed, see whos data has been saved. Its all things you can debug and possibly figure out the answer yourself.
But add some debug prints and show us the output so we also know more information :+1:

EDIT:
Also update your BindToClose code to this and see if it works:

local RunService = game:GetService("RunService")
game:BindToClose(function()
	for _, player in pairs(game.Players:GetPlayers()) do
		local s = coroutine.wrap(function()
			savePlayerData(player)
		end)
		s()
	end

	if RunService:IsStudio() then
		wait(3) 
	end
end)

I say this every time someone encounters a problem with data-saving.


The reason why it’s not saving on player removal, is because the server shuts down before that code can even run, let alone let the data save. You can make the server wait a few seconds before leaving, by running additional code into one specific event. Take a look:

game:BindToClose(function()
    print("about to close")
end)

How this actually works is that the game waits for the code to run, then closes the server. If we add a delay, like task.wait(), then the game will still wait for that code to run, and then, again, close the server. We can use this to make the game delay a bit before shutting down the server, to let data save.

game:BindToClose(function()
    task.wait(5)
end)

So what’s happening now, is that the game waits 5 seconds before shutting down the server. Now, the player removal event can fire, and the data can actually be saved, with no issues whatsoever.

You can put this code in one script and it will function as intended. You can put this in the same script where the data-saving occurs too.


Edit:

I just realised that you did use game:BindToClose() , but you still have to do it my way. You don’t have to save data on the event, you can just make the game delay before shutting down the server. You can do the actual data-saving in a Players.PlayerRemoving event, like usual, whilst using the game:BindToClose() event to delay the server shutdown.

6 Likes

Because coroutines are in their own separate thread, The for loop runs almost instantly and NOT waiting for the coroutines to finish. letting the server close. also the s() is unnecessary.

You can solve this by removing the coroutines

Another problem is in the savePlayerData script, it does not repeatedly try to save the data.

2 Likes

As I mentioned before, you don’t need to save data on server shutdown. Save data normally using the Players.PlayerRemoving event, and on that bind to close event, you can just put a task.wait(5) to delay the shutdown process, letting that player removal event actually fire, and letting the data save.

1 Like

You could use task.wait() or something else to add a delay before they leave, so the code can run.

game:BindToClose(function()
task.wait(5)

Have you even checked if this has been mentioned before?

1 Like

If you’re having this issue in Studio, but not on live server, then it’s just the way it is. Sometimes Roblox Studio test server shuts down too fast and doesn’t give your script enough time to save player data.

Been there, spent way too much time on debugging that.

I’m also not sure if task.wait() does delay anything in the BindToClose function. Live server is supposed to give you some time to save data before fully shutting down.

The game:BindToClose() function literally binds code to the server shutdown process. It will let run code before it shuts down, even delays.

1 Like

Id recommend using profileservice API, I had data loss issues with my game before I had imp lamented this, then after 99% of the issues went away. Simple module to use too.

He’s not here because of data loss…

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