Any better way to detect when a player has left the game?

To summarize for my game I have a table in which each joined player is in. This table is updated whenever someone joins or leaves the game. For detecting when someone has left the game I use the PlayerRemoving event, which calls a function to update the table. However it appears that by the time the function is done updating the table the player that is leaving the game is still considered to be in the game. Due to this a player that isn’t in the game is still in the table after it is updates. I was able to fix this by adding a short wait command before calling the function, this seems to have worked. However I want to know, is there a better way to achieve this, as I do not feel satisfied with my solution, I worry that it might not work all the time.

8 Likes

I recommend using the table.remove function after the player removing event is fired :slight_smile:

3 Likes

Why update a table when you can just use game.Players? game.Players:GetChildren() will be returned as a table itself.

3 Likes

The function i currently have accomplishes the same thing but differently, ill try out your suggestion see if its better then what i currently have.

3 Likes

Didnt know about that, i acces this table a few times throughout the script, so i dont know if i want to redo what i currently have.

2 Likes

I would recommend to keep using PlayerRemoving but adding this line

Player:GetPropertyChangedSignal("Parent"):Wait()

What this line does is wait until the player instance is removed, and once this happens, the player should no longer be considered as being in the game.
I wouldn’t be surprised if Players.PlayerRemoving() fires like the frame prior to the player instance being removed so maybe a simple RunService.Hearbeat:Wait() would work, though that wouldn’t be the most robust solution

4 Likes

Could you send me your script?

1 Like

How is this possible, could you show some code?

2 Likes
local stageTP = game.Workspace.Stage.StageTP
local floorTP = game.Workspace.FloorTP
local countEvent = game.ReplicatedStorage.Count
local chooseNewPLRevent = game.ReplicatedStorage.ChooseNewPLR
local left = game.Workspace.CountParts.Left
local inGame = {}
local db = false
local askPLR = game.ReplicatedStorage.AskPlr
local backtoserv = game.ReplicatedStorage.Backtoserv


local debounceTP = false
local remember = "thing"
local breakLoop = false


inGame = {}

local countTP = 30



function tpback(root)
	while true do
		if countTP ~= 0 then
			task.wait(1)
			countTP -= 1
		
			if breakLoop == true then
				break
			end
		else
			root.CFrame = floorTP.CFrame + Vector3.new(0, 0.5, 0)
			countTP = 30
		end
		
	end
	root.CFrame = floorTP.CFrame + Vector3.new(0, 0.5, 0)
	countTP = 30
end

local function update()
	inGame = {}
	for i, player in pairs(game.Players:GetChildren()) do
		table.insert(inGame, player)	
	end
end

local counter = 30

local function count()

	
	while true do
		if counter ~= 0 then
			
				task.wait(1)
				counter -= 1
				left.Value -= 1
			if breakLoop == true then
				print("breakloop triggered count")
				break
			end

				
			else
			break		
			end
	
	end
	local function pick()
		print("pick function begun")
		breakLoop = false
		local chosenPlr = inGame[math.random(1, #inGame)]
		print(chosenPlr.Name)
	
		if remember ~= chosenPlr.Name then 
			askPLR:FireClient(chosenPlr)
			print("ask plr event fired to client for:".. chosenPlr.Name)
			
			local root = workspace:FindFirstChild(chosenPlr.Name):FindFirstChild("HumanoidRootPart")
			root.CFrame = stageTP.CFrame + Vector3.new(0, 0.5, 0)
			
			remember = chosenPlr.Name
			counter = 30
			left.Value = 30


			spawn(count)
			local teleportBack = coroutine.wrap(tpback)(root)
		print("print function ended")	

		else
			print("cant pick same player twice")
			pick()
		end
	end	
pick()
	
end

backtoserv.OnServerEvent:Connect(function(player)
	breakLoop = true
	print("player name is: "..player.Name.. " event recieved by server and breakloop set to false")
	local root = workspace:FindFirstChild(player.Name):FindFirstChild("HumanoidRootPart")
	root.CFrame = floorTP.CFrame + Vector3.new(0, 0.5, 0)
	print("player teleported back")
end)

local onlyonce = 0

game.Players.PlayerAdded:Connect(function()
	update()
	if onlyonce == 0 then
		onlyonce += 1 
		spawn(count)
		
	else
		print("not running again")
	end
	
end)

game.Players.PlayerRemoving:Connect(function()
	task.wait(1)
	update()
end)


left.Changed:Connect(function()
	left.Parent.Count1.BillboardGui.TimeLeft.Text = "Time until next player is chosen: "..left.Value
end)
2 Likes

this seems to have done the trick, thanks!

You can just remove the player from the inGame table instead of what you’re doing rn

if you mean doing table.remove in the PlayerRemoving function I’m aware of that. I just don’t see how it’s and improvement over what I currently have.

It fixes your problem + it’s faster than looping over a table of players again and more memory efficient than resetting a table every time.

1 Like

Ill try that.
Totally not random text because roblox wants more characters

You want to actually use the player reference that comes with this event, e.g.:

game.Players.PlayerRemoving:Connect(function( leavingPlayer )
	update( leavingPlayer) -- inside update() remove the key for this player, or skip them if you rebuild the table
end)

The problem is that the leaving player hasn’t actually fully gone yet so Players:GetChildren() will still include them. This is why the PlayerRemoving event passes you the player reference.

1 Like

invalid argument #2 to ‘remove’ (number expected, got Instance)

probably an error on my behalf for doing something wrong but anyways. Your suggestion got me to change how I add players into the table; table.insert them when they join instead of calling the update() function.

You’re gonna need to use table.find() to get the index of the player instance in the table. Then remove that index with table.remove()

1 Like

Given what I showed above, regarding the argument to PlayerRemoving, your update function would become more like this:

local function update( leavingPlayer )
	inGame = {}
	for i, player in pairs(game.Players:GetChildren()) do
        if player ~= leavingPlayer then
            table.insert(inGame, player)
        end
    end
end

You could also just have inGame[leavingPlayer] = nil, or use table.remove, if you put players in on PlayerAdded. It really isn’t going to matter much how you add/remove them vs. rebuild the list, it’s knowing who to remove that’s your bug.

1 Like

Thanks, I no longer need the update() function at all with this

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