Round doesn't terminate when a player wins (3+)

Hello, I have made a round system that detects if a player dies, or leaves. It all works as well, and removes them from the table, etc. When one person is the last alive, it doesn’t end the round… Weird thing is, when there is only 2 people and one wins, it all goes well and ends, then starts over. Seems to fail at 3+ players.

Code:

local RS = game.ReplicatedStorage
local RINP = RS:WaitForChild("RoundInProgress", 5)
local Winner = RINP:WaitForChild("Winner", 5)
local PlayersLeft = RINP:WaitForChild("PlayersLeft", 5)
local Intermission = RS:WaitForChild("Intermission", 5)
local Time = Intermission:WaitForChild("Time", 5)
local Text = RS:WaitForChild("Text", 5)


RINP.Changed:Connect(function()
	if RINP.Value == false then
		Text.Value = "ROUND OVER! [" .. Winner.Value .. " WINS]"
	elseif RINP.Value == true then
		local update = coroutine.wrap(function()
		repeat
			Text.Value = "Round in progress [ALIVE: " .. PlayersLeft.Value .. "]"
			task.wait(.02)
			until tonumber(PlayersLeft.Value) <= 1
		end)
		update()
	end
end)

while task.wait() do
if #game.Players:GetPlayers() > 1 then
	local playing = {}
	Intermission:WaitForChild("Time", 5).Value = Intermission:WaitForChild("Time", 5):WaitForChild("UsualIntermission", 5).Value
	Intermission.Value = true
	if Intermission.Value == true and Time ~= "0" then
		for i = Time.Value, 1, -1 do
			Text.Value = "INTERMISSION: [" .. tostring(string.format("%d:%.02d", i/60, i%60, i*1000%1000)) .. "]"
			task.wait(1)
			end
		end
	Intermission.Value = false
	for i,v in pairs(game.Players:GetPlayers()) do
		table.insert(playing, v)
	end
	Text.Value = "SETTING UP ROUND"
	task.wait(2)
	local spawns = game.ServerStorage:WaitForChild("MapSpawns"):Clone()
	spawns.Parent = game.Workspace
	local points = {}
	for i,v in pairs(spawns:GetDescendants()) do
		table.insert(points, v)
	end
	for i,v in pairs(points) do
		local val = Instance.new("BoolValue")
		val.Name = "Taken"
		val.Value = false
		val.Parent = v
		end
		for i,v in pairs(playing) do
	local add = coroutine.wrap(function()
		local char = v.Character
		if char then
			pcall(function()
				local function entries()
					local chosen = points[math.random(#points)]
					if chosen.Taken.Value == false then
						chosen.Taken.Value = true
						char.HumanoidRootPart.Position = chosen.Position
						char.HumanoidRootPart.Anchored = true
					else
						return false
					end
				end
				local MAX_ENTRIES = 0
				local s, r = pcall(function()
					entries()
				end)
				if r == false then
					repeat
						local s, r = pcall(function()
							entries()
						end)
							MAX_ENTRIES += 1
							if MAX_ENTRIES >= 6 then
								table.remove(playing, i)
								PlayersLeft.Value = tostring(#playing)
								break
							end
					until r ~= false or MAX_ENTRIES >= 6
						end
					end)
				end
			end)
		add()
	end
		PlayersLeft.Value = tostring(#playing)
		for i = 3, 1, -1 do
			Text.Value = "STARTING IN: " .. tostring(i)
			task.wait(1)
		end
		for i,v in pairs(playing) do
			pcall(function()
				v.Character.HumanoidRootPart.Anchored = false
				local connector = coroutine.wrap(function()
					v.Character.Humanoid.Died:Connect(function()
					table.remove(playing, i)
					PlayersLeft.Value = tostring(#playing)
					end)
				end)
				connector()
				game.ServerStorage:WaitForChild("LinkedSword", 5):Clone().Parent = v.Backpack
			end)
		end
		RINP.Value = true
		game.Players.PlayerRemoving:Connect(function(plr)
			for i,v in pairs(playing) do
				if plr == v then
					table.remove(playing, i)
					PlayersLeft.Value = tostring(#playing)
				end
			end
		end)
		local callbacks = false
		repeat
			if #playing <= 1 or tonumber(PlayersLeft.Value) <= 1 then
				callbacks = true
				for i,v in pairs(playing) do
					pcall(function()
						RINP.Winner.Value = v.Name
						RINP.Value = false
					end)
						table.remove(playing, i)
						pcall(function()
							v.leaderstats.Wins.Value += 1
						end)
					pcall(function()
						v.Character.Humanoid:TakeDamage(math.huge)
						end)
					end
				task.wait(4)
				RINP.Winner.Value = ""
				PlayersLeft.Value = "0"
				task.wait(1)
				pcall(function()
					game.Workspace.MapSpawns:Destroy()
				end)
			end
			task.wait(.04)
	until callbacks == true
		repeat task.wait(.04) until RINP.Value == false
	elseif #game.Players:GetPlayers() < 2 then
		Text.Value = "2 or more players are needed to start!"
	end
	task.wait(1)
end

No errors in console

you should log to see if the pcalls are masking errors and to make sure the code goes to different blocks when it should

I was going through it to find any issues I can see which may or may not be related:


if a player resets or leaves during this code block, the Died event of the already dead character is connected to rather than the new one, and the .PlayerRemoving connection doesnt catch that the player has left




Removing values from a table while looping through the table that way is buggy because you aren’t keeping track of the changes in index
You do this multiple times


Don’t loop to check if the game is done, use the events you’ve setup
I’d handle it like this

list of people ingame = {}
round ID = -1

start new round()
   if there is not enough people
      display the message
      return
   end
   wait the intermission
   if there is not enough people
      display the message
      return
   end
   clear/reset the list of people ingame
   add everyone to list of people ingame
   set the round id
   set some temp value to the round id's value
   setup the field/tools/teleporting/etc()
   wait the length of the game
   if the id hasnt changed
      start a new round
   otherwise
      another round has started already, so do nothing
   end
end

check if ended()
   if there is at most 1 player left and the round is running
      display winner
      start a new round()
   end
end

on player added(player)
   on player's character added(character)
      on character died()
         remove from list of people ingame
         check if round should end()
      end
   end
   if there is no current round
      start a new round()
   end
end

on player removed()
   remove from list of people ingame
   check if round should end()
end

I think the issue when there are 3+ users is caused by how your remove values from the table
it should be normal after you fix that, but you still have a lot of hopes and redundancy

I did some tweaking with the code, I connected events and logged certain actions, then I just disconnected the events on end of the round, and it works!