Event:Wait() doesn't cancel if loop value is set to false

So i am trying to make a deathmatchsystem (the script is server side) and the script below should check if a player dies it removes the player from the table until 1 player is remaining.
The Table “Drawplayers” can contain max. 4 players so i cant just use Event:Connect()

The loop ends if #Drawplayers value is 1.

The problem is there is a limited time in this deathmatch and the DMV.Value is the timer-active value located in ReplicatedStorage that is a loop too.

The Value changes through another timer script from the server.

But the while loop continues and nothing below the while loop doesn’t executes…

		while DMV.Value == true do
			while #DrawPlayers ~= 1 do
				
				local player = Hdied.Event:Wait() --Player Died
				
				print(player)
				local e = table.find(DrawPlayers, player) --Checks if player is one of the DrawPlayers
				if e then
					table.remove(DrawPlayers, e)	
				end
			end
			DMV.Value = false
		end

The reason this doesn’t work is because the while loop will never reach the false value, since you’re infinitely looping

code below the while #DrawsPlayers ~= 1 loop will never run.

Also your code is not very optimized as you’re using new while loops constantly in one while loop. Which would cause the server to experience tons of lag.

Instead change your code to this (if it’s a number value):

DMV:GetPropertyChangedSignal("Value"):Connect(function()
if DMV.Value == true then
local player = Hdied.Event:Wait()
local e = table.find(DrawPlayers, player) --Checks if player is one of the DrawPlayers
				if e then
					table.remove(DrawPlayers, e)	
				end
DMV.Value = false
end
end)

Unless, you need your code to be in a different way please provide more context to it. But the definite problem is that you’re changing DMV’s value to false below the while loops. Which will never happen. And your current code is very unoptimized.

By the way did you forget to add a wait inside your while loops? This would just cause the players to crash immediately.

A while loop will constantly loop as long as the condition required is met and will never run code below it until the condition is either not met or the player uses a “break” statement.

yea i forgot to explain that the value changes in another script, the dmv.value is a failsafe if a player won before the timer run out

Have you heard of for loops before? they allow you to loop a certain number of times instead of constantly. They’re usually used in timers like this, especially for round systems.

-- "1" represents the starting point, "500" represents the end point in this
for i = 1, 500, do
if #Players == 1 then -- Change this to the condition required for your player to win
break -- This means that the code "breaks out" of the loop and now code below can run.
end
task.wait(1)
end

I won’t get into for loops too much, but they are quite simple to understand so I will let you research about it.

By the way your original code doesn’t check if #Drawsplayers equals 1 since it checks if the #Drawsplayers is NOT equal to 1:

ok let me explain this in detail:

So i have a round system already and a timer too

there is a part of the timer script: (its server side and its seperate from that script before)

while DeathMV.Value == true do -- changed from the server script (scroll down to the server script)
		local DeathMatchPrep = true
		DeathTimer:FireAllClients(DeathMatchPrep, DeathMatchPrepTime)
		DeathMatchPrepTime = SDeathMatchPrepTime
		while DeathMatchPrepTime > 0 do
			wait(1)
			DeathMatchPrepTime -= 1
			DeathTimer:FireAllClients(DeathMatchPrep, DeathMatchPrepTime)

		end
		DeathMatchPrepTime = SDeathMatchPrepTime
		CountDown = SCountDown
		DeathMatch:Fire()
		CD:FireAllClients(CountDown)
		while CountDown > 0 do
			wait(1)
			CountDown -= 1
			CD:FireAllClients(CountDown)

		end
		CountDown = SCountDown
		DeathMatchPrep = false
		DeathTimer:FireAllClients(DeathMatchPrep, DeathMatchTime)
		DeathMatchTime = SDeathMatchTime
		while DeathMatchTime > 0 do
			wait(1)
			DeathMatchTime -= 1
			DeathTimer:FireAllClients(DeathMatchPrep,DeathMatchTime)

		end
		DeathMatchTime = SDeathMatchTime

		DeathMV.Value = false
		
	end


The reason i didnt use for loops bcs i thought there is no reason to.

here is the whole tie/Deathmatch function:

elseif #DrawPlayers <= 4 then -- 2 or more people have same kills somehow
		
		local DeathMatch = game.ReplicatedStorage.Events.RoundEvents:WaitForChild("DeathMatch")
		local DMV = game.ReplicatedStorage.Round:WaitForChild("DeathMatch")
		local Arena = game.ReplicatedStorage.Maps:WaitForChild("DeathMatchArena")
		local Event = false
		DMV.Value = true
		DeathMatch.Event:Wait() --waits until the timer starts
		
		local Duplicate = Arena:Clone()
		local Spawns = Arena.Spawns:GetChildren()
		Duplicate.Parent = game.Workspace

		for i = 1, #DrawPlayers do --teleports the tied players
			if DrawPlayers[i]:WaitForChild("playing").Value == false then
				DrawPlayers[i]:WaitForChild("playing").Value = true
				
			end
			
			
			DeathMUI:FireClient(DrawPlayers[i]) --disables some UI
			DrawPlayers[i].Character:FindFirstChild("HumanoidRootPart").CFrame = Spawns[i].CFrame + Vector3.new(0,10,0)

		end
				
		
		while DMV.Value == true do
			while #DrawPlayers ~= 1 do
								
				local player = Hdied.Event:Wait() 
				
				print(player)
				local e = table.find(DrawPlayers, player) --Checks if player is one of the DrawPlayers
				if e then
					table.remove(DrawPlayers, e)	
				end
			end
			DMV.Value = false --if all opponents died the timer stops with this before it has run out
		end
	
		if #DrawPlayers >= 2 then
			DMMSG:FireClient()
		else
			print(tostring(DrawPlayers[1].Name).. " Won the Deathmatch!!")
			Win:Fire(DrawPlayers[1])
			WinMsg:FireAllClients(DrawPlayers[1],Draw, AbsouluteDraw , nobody, DrawPlayers[1], #DrawPlayers)
		end
		Duplicate:Destroy()
		for i = 1, #DrawPlayers do
			local SpawnsE = game.Workspace.All.Map.Spawns:GetChildren()
			DrawPlayers[i].Character:FindFirstChild("HumanoidRootPart").CFrame = SpawnsE[i].CFrame + Vector3.new(0,10,0)
		end

The DVM.Value loop is in a function in a seperate script as the timer one, the whole script actually worked with the timer script but the actuall problem is why it doesnt stop

i mean maybe for loops was a better option but it worked until now

And the code

actually works because if all the players are removed until 1 is left, it cancels the loop
“~=” equals “is not”

sorry if i am not that good at explaining things

You could try running both threads asynchronously and using DMV.Value to control them.

Hm, this round system is very strange in my opinion. I’m not sure why you can’t just check if #DrawsPlayers == 0 because this means that in a real game if there were for example more than 1 player it would just end the loops instantly. But obviously you want to end the game when there’s 0 players.

You could try using task.spawn or something like that, although you probably don’t need to.

uh the death match should end until 1 player is left cuz he won

wdym by that, the value is already controlling like the whole part

the fact is the script worked until i used the dmv.value loop

What is the point of the outer while-loop? It doesn’t need to be there.

What if you change it to #DrawPlayers > 1?

1 Like

i mean it worked without the DMV.Value

But i will use it since its saver

You mentioned before that you have an iteration to control the round timer and an iteration to control the player count. I also noticed that you never check DMV.Value during the round timer code, which also might be the issue. Try that before trying any of my below ideas.

I’m also thinking in terms of optimising here. If you run one thread controlling the remaining players asynchronous (at the same time as) the thread that controls the round timer, you could control both with a basic Boolean and break out as intended. It’d be easier to combine both scripts into one single script, but if you don’t want to use a BoolValue instead of a basic Boolean.

When I talk about this Boolean, it isn’t really a Boolean, but is used like one. It’s the status of the asynchronous thread.

A system like @Amritss said using a count controlled iteration and breaking out appropriately would be better, but if you don’t want to use a for loop, this system would work with a while loop as well.

This is my example using a while loop (more complicated and untested)

local controlElimsAsync = coroutine.create(function()
	while #DrawPlayers ~= 1 do
		local player: string|Player|number = Hdied.Event:Wait() --don't know how you chose to identify the player (see type annotation)

		print(player)
		local index = table.find(DrawPlayers, player) --Checks if player is one of the DrawPlayers
		if index then
			table.remove(DrawPlayers, index)	
		end
	end
end)

--then, during round time...
coroutine.resume(controlElimsAsync)
while DeathMatchTime > 0 and coroutine.status(controlElimsAsync) ~= "dead" do
	task.wait(1)
end

This is also alright, but I think a for loop would probably be better. I recommend that OP does either this or the for loop idea.

so i should combine the timer and the roundevents script?**
image

ye i didnt know how to do corountine i will use that

Yes, try and combine both scripts. As I previously mentioned, @Amritss 's count controlled iteration suggestion is better, I only made a coroutine example because you said you didn’t want to use a count controlled iteration. Try and use the count controlled iteration if you can.