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!
Create an empty project
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)
Set a breakpoint at the line print("breakpoint") (line 9):
Publish (because DataStore)
Run
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.
The same error also happens when there is some script error:
Issue Area: Studio Issue Type: Freezing Impact: High Frequency: Constantly
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.
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)
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