How can I detect when a player disconnects?

Hey there,

So I have an auto rejoin system in my game that works through a “marco, polo” system where a client sends a ping to the server and the server sends one back and if the client dosent receive one within 6 seconds or so itll assume that client is disconnected and initiates the recovery process, I’ve tested the grace period roblox gives to clients before fully kicking them out and disabling the engine and it seems to be around 20 seconds sometimes being as low as 10 so its not very stable, if the grace period ends before the recovery system starts the client will be completely lost so i was wondering if there was someway to detect when a client fully disconnects and fire an event before that happens instead of using a not 100% reliable system, I tried playeremoving but that only fires if the player closes roblox not disconnects from it and I tried running a connection and detecting if it is halted for too long but it seems the connection just gets terminated.

Thanks in advance

1 Like

Every once in a while send a message to the server as a client. If the server doesn’t respond with it’s own message. Make the client initiate a rejoin.
( if teleports are allowed on client, since im pretty sure you have to initiate a teleport through server )

If the player still has no connection while being teleported, the game will just instantly reject the join with the obvious reason of no internet connection.

that would work the problem is if you send the messsage too frequently you will wear out the server or you can falsely reconnect players over server lag spikes and if you send it not frequently enough the player might get fully disconnected before you can detect a faulty connection and by then youve aleady lost them what i need is some way to detect when exactly roblox fully discconnects the player and gives them the disconnection pop

Roblox doesn’t expose any “half-connected” signal

The server only considers a client gone after a built-in ~10-20 second grace window, and that moment is exactly when PlayerRemoving fires. That’s the earliest point you can react, everything prior must be done with your own heartbeat system

Your best bet is something like this:

  1. Ping every second from the client - use a RemoteEvent called Ping so you only ever send a tiny payload
  2. Record the last-seen timestamp on the server
  3. Heartbeat-check for stale entries - When the timestamp gap exceeds a set timeout (e.g. 6 seconds) treat the client as lost and start your recovery
  4. Let PlayerRemoving clean up later - you can safely save data here

FYI: A small ping for every player on the server every second wont affect the server much at all.

To get technical:

A RemoteEvent adds ~9 bytes` of protocol overhead. A simple “ping” that only passes the player object (which is implicit) or a tiny boolean/number ends up around 15-20 bytes total

Roblox’s soft cap is ~50 kB/s per cleint. One 20 byte ping every second uses 0.02 kB/s. Even with 100 players you’re still under 2 kB/s server-side, which is negligible

The call rate limit throttle only kicks in when a single remote exceeds ~20 calls/s, or you push the 50 kB/s budget, so you’re very much below both thresholds

Example client script:

local Ping = game.ReplicatedStorage:WaitForChild("Ping")

while task.wait(1) do
    Ping:FireServer()
end

Example server script:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local PingEvent = game.ReplicatedStorage:WaitForChild("Ping")

local TIMEOUT = 6      -- seconds
local lastSeen = {}

PingEvent.OnServerEvent:Connect(function(plr)
    lastSeen[plr] = os.clock()
end)

Players.PlayerAdded:Connect(function(plr)
    lastSeen[plr] = os.clock()
end)

Players.PlayerRemoving:Connect(function(plr)
    lastSeen[plr] = nil
end)

RunService.Heartbeat:Connect(function()
    local now = os.clock()
    for plr, t in pairs(lastSeen) do
        if now - t > TIMEOUT then
            -- client considered lost; trigger your recovery
            -- e.g. TeleportService:TeleportAsync/place re-join logic
            lastSeen[plr] = now      -- optional: stop repeats
        end
    end
end)

Hope this helps out!

1 Like

shame roblox is very restrictive with error message access but i guess it makes sense and i suppose if a client dosent send a signal for atleast 5 seconds its safe to assume they wont be reconnecting anytime soon this helped alot thanks

1 Like