Game Loop Resets when a Player Joins

My game loop resets when a player joins. I know the problem, although I don’t know how to fix it.
I asked other people, although the solution was disputed, so I chose to move it here.

local gameStatus = game.ReplicatedStorage:WaitForChild("GameStatus")
local gameTimer = game.ReplicatedStorage:WaitForChild("TimerStatus")
local lobbyCFrame = workspace:WaitForChild("Lobby"):WaitForChild("SpawnLocation").CFrame + Vector3.yAxis*5
local maps = game.ReplicatedStorage.Maps:GetChildren()
local mapsLength = #maps
local random = math.random(1, mapsLength)
local chosenMap = maps[random]
local mapName = chosenMap.Name
local INTERMISSION = 0
local ROUNDLENGTH = 300

game.Players.PlayerAdded:Connect(function()
	if #game:GetService("Players"):GetPlayers() <= 1 then
		gameStatus.Value = "Waiting for Players"
		gameTimer.Value = 0
		game.Players.PlayerAdded:Wait()
	else
		while true do
			for countDown = INTERMISSION, 0, -1 do
				gameStatus.Value = "Intermission"
				gameTimer.Value = countDown
				task.wait(1)
			end
			for countDown = 5, 0, -1 do
				gameStatus.Value = "Map Name: "..mapName
				gameTimer.Value = countDown
				task.wait(1)
			end
			local map = chosenMap:Clone()
			map.Parent = workspace
			local players = game.Players:GetChildren()
			for i = 1, #players do
				if players[i].Character ~= nil then
					local spawnLocation = math.random(1, #map.Teleports:GetChildren())
					players[i].Character:MoveTo(map.Teleports:GetChildren()[spawnLocation].Position)
					players[i].Character.Parent = workspace.InGame
				end
			end
			local children = workspace.InGame:GetChildren()
			for i = 1, #children do
				local swordClone = map.ClassicSword:Clone()
				local rocketLauncherClone = map.RocketLauncher:Clone()
				local timebombClone = map.ClassicTimebomb:Clone()

				swordClone.Parent = children[i]
				rocketLauncherClone.Parent = children[i]
				timebombClone.Parent = children[i]
			end
			map.ClassicSword:Destroy()
			map.RocketLauncher:Destroy()
			map.ClassicTimebomb:Destroy()
			repeat
				ROUNDLENGTH = ROUNDLENGTH - 1
				gameStatus.Value = "Game in Progress"
				gameTimer.Value = ROUNDLENGTH
				task.wait(1)
			until ROUNDLENGTH == 0 or #workspace.InGame:GetChildren() == 0 or #workspace.InGame:GetChildren() == 1
			if #workspace.InGame:GetChildren() == 1 then
				gameStatus.Value = workspace.InGame:FindFirstChildWhichIsA("Model").Name.. " has won."
				task.wait(5)
				map:Destroy()
				for i = 1, #players do
					if players[i].Character ~= nil then
						players[i].Character.Parent = workspace
					end
				end
			end
			for _, player in ipairs(game.Players:GetPlayers()) do
				player.Character:BreakJoints()
			end
			ROUNDLENGTH = 300
		end
	end
end)
1 Like

well… you’ve built the entire loop system into the function for when a player joins. Its a no-brainer that it’d reset because its coded to do that.

You need to have the loop work externally, because this system is just poorly written in many ways.

local INTERMISSION = 15

... --// Other variables

while task.wait(INTERMISSION) do --// We use intermission here as to not overflow the loop.
    if #game.Players:GetPlayers() <= 1 then
        gameStatus.Value = "Waiting for Players"
        gameTimer.Value = 0
        continue --// Restart the loop instead of starting a new round.
    end

    --// rewrite round logic here, do NOT use playerAdded in this.
    --// More importantly, don't create any connections here without disconnecting them.
end
2 Likes

I knew what the problem was, I didn’t know how to approach it though. You were very rude about it yet you chose to avoid the problem completely.

I’ve provided code that would fix the problem, it strips away the PlayerAdded connection and gives you general advice in comments.

I’m not going to rewrite your entire system as I have my own things to work on. The rest of the code you have in your loop might work, it just needs to be ported into the new loop I’ve provided, however may require some rewrites here and there.

This should fix your problem, it’ll wait for at least one player before running the game loop.

local Players = game:GetService("Players")

local gameStatus = game.ReplicatedStorage:WaitForChild("GameStatus")
local gameTimer = game.ReplicatedStorage:WaitForChild("TimerStatus")
local lobbyCFrame = workspace:WaitForChild("Lobby"):WaitForChild("SpawnLocation").CFrame + Vector3.yAxis*5
local maps = game.ReplicatedStorage.Maps:GetChildren()
local mapsLength = #maps
local random = math.random(1, mapsLength)
local chosenMap = maps[random]
local mapName = chosenMap.Name
local INTERMISSION = 0
local ROUNDLENGTH = 300

local function getPlayerCount() : number
	return #Players:GetPlayers()
end

while true do
	if getPlayerCount() == 0 then
		gameStatus.Value = "Waiting for Players"
		gameTimer.Value = 0
		Players.PlayerAdded:Wait()
	end
	
	for countDown = INTERMISSION, 0, -1 do
		gameStatus.Value = "Intermission"
		gameTimer.Value = countDown
		task.wait(1)
	end
	for countDown = 5, 0, -1 do
		gameStatus.Value = "Map Name: "..mapName
		gameTimer.Value = countDown
		task.wait(1)
	end
	local map = chosenMap:Clone()
	map.Parent = workspace
	local players = game.Players:GetChildren()
	for i = 1, #players do
		if players[i].Character ~= nil then
			local spawnLocation = math.random(1, #map.Teleports:GetChildren())
			players[i].Character:MoveTo(map.Teleports:GetChildren()[spawnLocation].Position)
			players[i].Character.Parent = workspace.InGame
		end
	end
	local children = workspace.InGame:GetChildren()
	for i = 1, #children do
		local swordClone = map.ClassicSword:Clone()
		local rocketLauncherClone = map.RocketLauncher:Clone()
		local timebombClone = map.ClassicTimebomb:Clone()

		swordClone.Parent = children[i]
		rocketLauncherClone.Parent = children[i]
		timebombClone.Parent = children[i]
	end
	map.ClassicSword:Destroy()
	map.RocketLauncher:Destroy()
	map.ClassicTimebomb:Destroy()
	repeat
		ROUNDLENGTH = ROUNDLENGTH - 1
		gameStatus.Value = "Game in Progress"
		gameTimer.Value = ROUNDLENGTH
		task.wait(1)
	until ROUNDLENGTH == 0 or #workspace.InGame:GetChildren() == 0 or #workspace.InGame:GetChildren() == 1
	if #workspace.InGame:GetChildren() == 1 then
		gameStatus.Value = workspace.InGame:FindFirstChildWhichIsA("Model").Name.. " has won."
		task.wait(5)
		map:Destroy()
		for i = 1, #players do
			if players[i].Character ~= nil then
				players[i].Character.Parent = workspace
			end
		end
	end
	for _, player in ipairs(game.Players:GetPlayers()) do
		player.Character:BreakJoints()
	end
	ROUNDLENGTH = 300
end

The reason your game loop resets when a player joins is because all of your round code is running inside the PlayerAdded event. Every time a new player joins the PlayerAdded function fires (duh) and since your loop is inside it it effectively restarts the round logic for everyone!


A better approach is to separate your game loop from the PlayerAdded event. You can still use PlayerAdded to handle individual player setup (like moving them to the lobby) but the main game loop should run independently for example in a while true do loop in a server script that checks the number of players and progresses through intermissions, rounds and map spawning.

-- player setup
game.Players.PlayerAdded:Connect(function(player)
    -- move player to lobby set up character, and whatevr
end)

-- game loop
task.spawn(function()
    while true do
        if #game.Players:GetPlayers() >= 2 then
            -- intermission, map selection, spawn players, round logic, etc.
        else
            gameStatus.Value = "Waiting for Players"
            gameTimer.Value = 0
            task.wait(1)
        end
    end
end)

This way new players can join without resetting the round!


By separating player handling and game loop code you avoid resetting the round when someone joins.

You might also consider tracking the round state with a variable so joining players can be placed correctly depending on whether a round is in progress!

Hope my yapping helped you!

You just said “rewrite round logic here, do not use playerAdded in this”.

Use a debounce:

local gameRunning = false
game.Players.PlayerAdded:Connect(function()
	--your unrelated player code
	if gameRunning then return end
	gameRunning = true
	--your game start code
	--on round end call:
	gameRunning = false
end)

Additionally, as other mentioned, I think that it is better if you rely on a while loop for this. To wait for players you can call game.Players.PlayerAdded:Wait() which yields the code until the event fires, and when it does it runs the lines below it(so when a player is added).

No don’t do this, simply take out your game login from the player added event, this is not where your game logic should be. Put it in a server script where it runs once the game starts, then run a function or add something when a player joins.

1 Like

I provided a template, not a full script. Please for the love of god, don’t expect people here to fully rewrite everything all the time.

What I provided will work given you can re-implement round logic. I read your problem, I gave you a template to fix your problem. I’d argue its better practice for you to re-implement logic (even if just a copy-paste) as its more effort than relying on people the same way someone would rely on ChatGPT to program.

My problem was not the code inside the round, instead it was the PlayerAdded function. I knew that was the problem.

The first comment was well written including an example he spent time writing. That is the backbone of the average round system with 5 descriptive comments providing the skeleton of how to make it. You didn’t have to respond like that someone being nice. Nobody is paid to take time to help you.

1 Like

I didn’t need a backbone for a round system, I wanted to know how I should approach the problem.

My brother in christ does a backbone for a system not explain to you how to approach the problem?

  1. Remove PlayerAdded.
  2. Move the loop elsewhere and make it repeatedly loop until the required amount of players is met

That’s literally what I wrote out for you. That is how you should go about approaching it.

Please for the love of god get off the forums if you’re going to be this incompetent and ignore the solutions to your problems because they weren’t spoonfed to you the same way ChatGPT would.

My brother in christ

nitpicking here but i’m not christian

does a backbone for a system not explain to you how to approach the problem?

no, its quite literally the opposite of what i wanted, you told me “do NOT use playerAdded in this”, despite the fact that I knew the problem was there.

Please for the love of god get off the forums

no

if you’re going to be this incomptent

> someone asks a question
> you answer the wrong thing
> they tell you it’s the wrong thing
> you get angry

My solution tells you what you apparently already knew and how to fix it. You’ve had 2 other replies saying damn near the exact same thing I did. I have given you the solution, you have received the solution 3 TIMES.

We have told you “how to go about fixing it” 3 TIMES. We have supplied code 3 TIMES.

And you’re just ignoring it.

My code fixes the problem. I take the loop outside of the player added connection and rewrite the round start logic to be contained within the loop. The other 2 solutions do the exact same thing in slightly different methods.

MY BROTHER IN CHRIST.’

THAT IS WHAT YOU SHOULD USE. THE CODE IS SENT IN MY FIRST REPLY.
NO MORE PLAYER ADDED
LOOP INDEPENDENT
NO MORE RESTARTING EACH TIME A PLAYER JOINS

Is it THAT hard to read code?

it isn’t hard to read lua code, although you’re completely ignoring my points. i asked what i should use instead of player added

dude. Its as simple as you don’t use player added.

Move the loop outside and make the player count check happen within the loop. You literally just don’t use player added. The code I wrote is literally what you should do to fix the problem.

Stop rage baiting bro. You’re not funny.

2 Likes