Datastore not saving during server shutdown

What’s up developers?

I was working on updating my save systems in a game I’m making, but I encountered a weird issue: I got this function that saves a table when the player leaves, so also when the server shuts down.

Note that I left the table building out for simplicity’s sake since I don’t think it’s relevant, so if that’s a problem feel free to tell me and I’ll add it back in.

local function SaveOnLeave(player)
	local scope = "Player_"..player.UserId
	local DSSaves = DSService:GetDataStore("Saves", scope)
	
	local Summary = {}
	--table building usually happens here
	print(Summary)
	
	repeat
		print("looping")
		local success, err = pcall(function()
			print("inside call")
			DSSaves:SetAsync("summary", Summary)
			print("end of call")
		end)
		print("after call")
		if not success then
			print("failed to save summary:", err)
		end
	until success
	print("saved summary")
end

game.Players.PlayerRemoving:Connect(SaveOnLeave)

The issue: For some reason, the script just ends when we get to the datastore request. No errors, nothing, it just doesn’t happen. Here’s the output when I end a test after playing enough to have a table to save then shutting the test server down:

image

I looked around the forums for a bit, but couldn’t find any similar issues. I heard of DataStores2 while searching but I would rather not have to remake all my save systems considering I save other tables successfully with this method during gameplay.

There we go, thanks for your attention. If any of you got any ideas I’m open to trying stuff out!

1 Like

Shutdowns do not fire the PlayerRemoving and usually you have to use game:BindToClose() functionality to catch that issue.

I thought so as well at first, but I tested it out and it does get called. I used to have it connected to BindToClose as well, but all it did is call the save function twice.

Plus as you can see, it does get called here, I’ve got an output. Unless BindToClose has a special condition that allows datastores upon closure, our issue isn’t there.

I am certain that the BindToClose should catch the unsaved instances. To properly handle the saving, please use a cache, that is a table holding saves with keys associated to the player’s ID or whatever saving keys they have.

Studio tests may be inaccurate though.

1 Like

BindToClose is the event you need to go out from, use that to your advantage. Here is a script example;

game:BindToClose(function() – The same event that @Operatik talked about
for index, player in pairs(Players:GetPlayers())do – get all the players in the server
– save data here
end
end)

Well, I’d be stupid not to give it a shot at least. So far I tested a bit more, and it’s not just about having the function called or not, since it works properly:

  • when I leave a server that doesn’t shut down
  • when I leave a server last and cause a shutdown
  • when I shut the server down manually before leaving

In all those cases, PlayerRemoving is being called properly, even on the manual shutdown, and the save happens properly. But when I end a test in Studios, it does not. So starting to think that it might be Studios being inaccurate.

Studio testing is “accurate” and is not exactly the same as a standard game. There may be very minimal differences between. Instead, why don’t you try the Start function in the test tab which simulates a virtual server and virtual clients?

Because in that case, I don’t think I can rejoin with the dummy clients after to check how their saves are doing. I had to publish in order to test with real players.

So one thing I didn’t take into account which you brought to my attention: when shutting down, the server is waiting up to 30 seconds for functions called by BindToClose to finish. So I’m pretty sure what’s happening is PlayerRemoving is being called successfully, but because I did not use BindToClose, it is not waiting for the save to happen.

So it would interrupt the script during the save because it’s the first operation that takes time. Therefore, it wasn’t in the way I was thinking, but BindToClose should indeed be the solution.

I will test this out then mark it as solution if it succeeds.

1 Like

Alright, after some testing I can confirm that this was the solution. To avoid double calls with both PlayerRemoving and BindToClose triggering, I made it so instead of saving again and having problems with queued datastore requests, a function bound to BindToClose waits until all the players are gone and there are no ongoing save requests. Works like a charm now!

2 Likes