Testing game:BindToClose is not same as on Client and "game is shutting down" method for PlayerRemoving

As a Roblox developer, it is currently too hard to …

To test game:BindToClose in Roblox Studio. If I wanted to test that, I’d have to unironically use webhooks that send to Discord to print out what RCC would have been printing out, for debugging.

The Local Server Playtest method, won’t solve this logging problem either.

Additionally, you can’t properly check if a game is shutting down. If a game is shutting down, PlayerRemoving is also triggered.

If Roblox is able to address this issue, it would improve my development experience because …

Testing and BindToClose experience would be improved.


Issue 1

There is a difference on how game:BindToClose seems to work when ran in Studio compared to the Client. Either that, OR it shuts down in a different way.

There’s CloseReason.DeveloperShutdown. When you click the “Stop” button, the game shuts down but there’s something.

game:BindToClose(function()
  -- Let's check the player count
  print( #game.Players:GetPlayers() )
end)

On a real Roblox Server, you’d see a value higher than 0, if there were players. But in Studio, this seems to just be 0 at all times, and PlayerRemoving is invoked first in Studio.

My development experience would be improved if…

If there would be a way to simulate the actual behavior from a Serverwithin Roblox Studio, it would be very useful for testing.

Without having to run Local Servers, and these output windows close anyways, you can’t even read. I rather just go on a Roblox Server and print with webhooks instead of waiting for Local Servers to start up in Roblox Studio.


Issue 2

Let’s have Data Stores in mind. The game server is shutting down and you need to keep some tasks up-alive, so you call game:BindToClose for help, to keep its own thread alive to delay the shutdown.

So, the somewhat ideal concept would be.

game.Players.PlayerRemoving:Connect(savePlayerData)

game:BindToClose(function()
 -- Something that yields this thread
 -- loop
 savePlayerData -- and well you get the idea, right?
end)

This should have no problems, right?

But there is a problem. Your data is now saving twice, or at least it tries to.

When you shut down a game, BindToClose is called but PlayerRemoving is also triggered.

If you have saving logic bound to PlayerRemoving but also BindToClose then it is actually going to invoke things twice in theory.

So ideally you want this?

local IS_SHUTDOWN = false

game.Players.PlayerRemoving:Connect(function()
  if (IS_SHUTDOWN) then
    return
  end
end)

game:BindToClose(function()
  IS_SHUTDOWN = true

end)

Now, the problem is that this here doesn’t work.

Even though BindToClose seems to fire first before PlayerRemoving

Eitherways, at that time IS_SHUTDOWN is still false, even if the game is actually shutting down.

Only workarounds is to task.wait() long enough (literally), but that doesn’t sound ideal.

And because of that, any concept that follows this combo of BindToClose and PlayerRemoving would actually be running the DataStore update function.

 

Maybe this is a bug?

My development experience would be improved if…

You could cancel things if the game is shutting down in a proper way, maybe even with the reason included as well.

I think in an ideal world you’d just wait for all pending datastore calls to finish in :BindToClose, because in an ideal world all of the .PlayerRemoving events would have fired before reaching that point. Then, you wouldn’t have any duplicated calls at all.

Is this something you’ve experienced? I would have assumed that :BindToClose only fires after all players have left.

1 Like

It doesn’t. It seems to fire and all Players are still present. Note that I tested this with one total Player.

The documentation reflects the same.

But in Studio this acts different, which makes testing more dumb.

 

That sounds good as well. As long there’s no race condition between these both events.

At that point task.wait(30) is probably the easiest and more lazy way out of this.