“Not running script because past shutdown deadline" when using DATASTORE

Reproduction Steps

I had already created a similar bug report OVER A YEAR AGO, and nothing has been resolved so far!
EVERY TIME the script is interrupted, either by an error or by a breakpoint, after pressing the STOP button, I always have to wait 30 seconds!
Now, multiply 30 seconds by HUNDREDS OF TIMES A DAY and then multiply that by ONE YEAR and count how many hours of work were lost!

  1. Create an empty project
  2. Paste this code inside ServerScriptService:
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local Jogo -- player's data
local CanClose = false

game.Players.PlayerAdded:Connect(function(Player)
	local JogoStore = DataStoreService:GetDataStore("Jogo", Player)
	Jogo = JogoStore:GetAsync("Jogo") or {}
	print("breakpoint")
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local JogoStore = DataStoreService:GetDataStore("Jogo", Player)
	JogoStore:SetAsync("Jogo", Jogo)
	CanClose = true
end)

game:BindToClose(function()
	while not CanClose do
		wait()
	end
	print ('here I make some things before closing the server') 
end)
  1. Set a breakpoint at the line print("breakpoint") (line 9):
  2. Publish (because DataStore)
  3. Run
  4. When the debug stops on the breakpoint, press STOP.

Expected Behavior

No errors

------------------------------

Actual Behavior

When BindToClose is active and I stop debugging, the error mentioned happens.


Issue Area: Studio
Issue Type: Freezing
Impact: High
Frequency: Constantly

3 Likes

As an easy workaround you can add and RunService:IsRunning() to the loop in BindToClose to avoid the wait by detecting that you’re at a breakpoint and never going to satisfy the condition. A better code fix for this would be to not wait at all in BindToClose unless you confirm that you actually started off the teardown process that you’re waiting on (this is how people usually write it).

What is your expected behavior here?

Do you expect the debugger to resume from the breakpoint automatically when hitting stop? Because we can’t just cut off the BindToClose right away: We can’t tell what exactly it is that you’re doing in there and it might be something important.

2 Likes

This will stop PlayerRemoving without completing the actions pending there (in this case a SetAsync) that needs to be done before the player exits. The lack of this last SetAsync will cause a serious failure within the game logic.

Test this:

local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local Jogo -- player's data
local CanClose = false

game.Players.PlayerAdded:Connect(function(Player)
	local JogoStore = DataStoreService:GetDataStore("Jogo", Player)
	Jogo = JogoStore:GetAsync("Jogo") or {}
	local a={}
	print(a.b.c)
	print("breakpoint")
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local JogoStore = DataStoreService:GetDataStore("Jogo", Player)
	JogoStore:SetAsync("Jogo", Jogo)
	CanClose = true
	print('SetAsync done, CanClose:', CanClose) -- << Will never get here if pressed STOP after breakpoint or script error
end)

game:BindToClose(function()
	while RunService:IsRunning() and not CanClose do -- << RunService:IsRunning() will abort PlayerRemoving 
		wait()
	end
	print ('here I make some things before closing the server') 
end)

Okay, there’s a few things to unpack here.

The lack of this last SetAsync will cause a serious failure within the game logic.

Unrelated to the main topic, but I have to mention that this is a separate issue in your game if that’s the case. Servers will fail sometimes: You can’t rely on clean shutdown every time, you have to build your game in a way where it’s at least somewhat tolerant to unexpected shutdowns.

– << Will never get here if pressed STOP after breakpoint

Yes… because execution is suspended, and no event handlers, yield resumptions, or per frame events will happen until execution is resumed.

or script error

You mean an error in the handler? If you hit an error on the prior lines then execution naturally can’t proceed after the error, I’m not sure what the issue is there.

Like I said, I see a few potential solutions here to avoid getting stuck for 30 seconds:

  • If you press Stop at a breakpoint, the OnClose handler doesn’t get run at all (this seems undesirable)

  • If you press Stop at a breakpoint, execution automatically resumes as though you had pressed play first (allowing your game to attempt nominal shutdown process)

  • Show a popup allowing you to force-kill the BindToClose handler if it’s taking a long time on shutdown

Which of those do you think is most preferable?

As an aside, the code should really be written more like this, so that if the game code fails in some random place, the shutdown won’t be blocked (it will only be blocked if things fail in the middle of the leave handling code, and you can’t really avoid that without some much more complex approach involving promises / error handling):

HandlingClose = 0
...
function handlePlayerLeaving()
    HandlingClose += 1
    stuff()
    HandlingClose -= 1
end
...
while HandlingClose > 0 do task.wait() end
2 Likes

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