How do I create a queue system similar to Bathroom Line Simulator?

Hello there.
I am here because I encountered some trouble when it came to creating a queue system similar to that game.
I have tried several attempts at creating that myself, yet I faced a tremendous amount of problems when it comes to creating it, such as when you play it in multiplayer, the queue system just crashes itself, leaving players stuck in two different queue positions that are not even closer to the maximum position.

Here is my current progress so far:

local LevelSquareSlots = workspace.LevelSquareSlots -- Container holding each of the slots (1-50 in this case)

local queue = {} -- List of players in the queue, starting from the back (position 50)

-- function to move the player towards a specified position within the queue
-- step by step
local function moveTo(humanoid, j, targetPosition)
	local function moveFunction(i : number)
		humanoid:MoveTo(LevelSquareSlots:FindFirstChild(tostring(i)).Movement.Position)
		humanoid.MoveToFinished:Wait(2)
	end
	
	if j < targetPosition then
		for i = j, targetPosition do
			moveFunction(i)
		end
	else
		for i = j, targetPosition, -1 do
			moveFunction(i)
		end
	end
end

-- Function to move the player to a specific position in the queue
local function moveToPosition(character, targetPosition)
	local humanoid = character:FindFirstChildOfClass("Humanoid")
	if humanoid then
		local targetSquare = LevelSquareSlots:FindFirstChild(tostring(targetPosition))
		if targetSquare then
			moveTo(humanoid, character:GetAttribute("Position"), targetPosition)
			character:SetAttribute("Position", targetPosition)
			print(character.Name .. " is moving to square " .. targetPosition)
		end
	end
end

-- Function to update the queue
local function updateQueue()
	task.spawn(function()
		for i, player in ipairs(queue) do
			local character = player.Character
			if character then
				-- Move each player to their respective slot (51 - i)
				local position = 51 - i
				moveToPosition(character, position)
				print(player.Name .. " is now at position " .. position)
			end
		end
	end)
end

-- Function to find the next free position in the queue
local function findNextFreePosition()
	for i = 50, 1, -1 do
		local occupied = false
		for _, player in ipairs(queue) do
			if player.Character:GetAttribute("Position") == i then
				occupied = true
				break
			end
		end
		if not occupied then
			return i
		end
	end
	return nil -- Return nil if the queue is full
end

-- Function to join the queue
local function joinQueue(player)
	local character = player.Character
	if character then
		local nextPosition = findNextFreePosition()
		if nextPosition then
			table.insert(queue, player) -- Add player to the queue list
			updateQueue() -- Update the queue positions
			print(player.Name .. " joined the queue at position " .. nextPosition)
		else
			print(player.Name .. " could not join the queue: Queue is full.")
		end
	end
end

-- Function to leave the queue
local function leaveQueue(player)
	for i, p in ipairs(queue) do
		if p == player then
			table.remove(queue, i) -- Remove the player from the queue
			print(player.Name .. " left the queue.")
			break
		end
	end
	updateQueue() -- Update the queue after removal
end

-- Function to skip ahead in the queue
local function skipQueue(player)
	for i = 2, #queue do
		if queue[i] == player then
			-- Move the player ahead of the person directly in front of them
			local targetPlayer = queue[i - 1]
			table.remove(queue, i) -- Remove from current position
			table.insert(queue, i - 1, player) -- Insert ahead of the previous player
			print(player.Name .. " skipped ahead of " .. targetPlayer.Name)
			updateQueue()
			break
		end
	end
end

plr.CharacterAdded:Connect(function(character)
	character:SetAttribute("Position", 1) -- Players start at position 50
	joinQueue(plr) -- Join the queue when character is added
end)

Please bear in mind that playing alone wouldn’t cause such an issue, but when playing it in multiplayer, especially with let’s say 50 players in a server, that could be quite a different story.
This is when playing alone:

And this is when playing with two or more players:

Some output as well:
01:46:12.901 Player1 joined the queue at position 50 - Server - Queue:83
01:46:12.947 Player2 joined the queue at position 50 - Server - Queue:83

Thank you so much for taking the time to read this; any help is appreciated.

2 Likes

I want to make the same type of game as well; these games might be using the same scripts, as all of them are like the exact same but rebranded. As far as this script, I don’t actually have an answer, but at least this can boost the algorithm.

1 Like

Hey, I recreated your project and found many things to fix:

  • Whenever I test the game alone, my character does walk to square #1; however, he starts inching his way down to square #50.
    Line 44 of your code (local position = 51 - i) sends people to the back of the line and should be replaced with local position = i.

  • Functions joinQueue() and findNextFreePosition() are basically useless. You could just insert the player into the queue table whenever they join the game and use their position within the table as their position in line because whenever a value is added to a table, it is automatically placed at the back.

  • Whenever a player joins the queue, you should teleport their character to the square they need to go to instead of walking them to it.

  • Whenever I test the game multiplayer, all other characters aside from the one moving stand still and wait for the moving character to stop moving. You added a task.spawn(), but you placed it wrong. Before I tell you where it should be placed, I want to address another useless function: moveToPosition(). There is no point in having moveToPosition() when it only redirects you to moveTo(). So, in this case, replace the moveToPosition() with moveTo(). Anyways, you tried creating a task for the entire updateQueue() function, while you should’ve only placed it on the moveTo() function, now that we’ve replaced moveToPosition() with moveTo().

Here is the code that I made:

local LevelSquareSlots = workspace.LevelSquareSlots -- Container holding each of the slots (1-50 in this case)

local queue = {} -- List of players in the queue

-- function to move the player towards a specified position within the queue
-- step by step
local function moveTo(character, j, targetPosition)
	local humanoid = character.Humanoid
	
	local function moveFunction(i : number)
		humanoid:MoveTo(LevelSquareSlots:FindFirstChild(tostring(i)).Movement.Position)
		humanoid.MoveToFinished:Wait(2)
		character:SetAttribute("Position", i)
	end

	if j < targetPosition then
		for i = j, targetPosition do
			moveFunction(i)
		end
	else
		for i = j, targetPosition, -1 do
			moveFunction(i)
		end
	end
end

-- Function to update the queue
local function updateQueue()
	for i, player in pairs(queue) do
		local character = player.Character
		if character then
			-- Move each player to their respective slot (51 - i)
			local position = i
			task.spawn(function()
				moveTo(character, character:GetAttribute("Position"), position)
				print(player.Name .. " is now at position " .. position)
			end)
		end
	end
end

-- Function to leave the queue
local function leaveQueue(player)
	for i, p in ipairs(queue) do
		if p == player then
			table.remove(queue, i) -- Remove the player from the queue
			print(player.Name .. " left the queue.")
			break
		end
	end
	updateQueue() -- Update the queue after removal
end

-- Function to skip ahead in the queue
local function skipQueue(player)
	for i = 2, #queue do
		if queue[i] == player then
			-- Move the player ahead of the person directly in front of them
			local targetPlayer = queue[i - 1]
			table.remove(queue, i) -- Remove from current position
			table.insert(queue, i - 1, player) -- Insert ahead of the previous player
			print(player.Name .. " skipped ahead of " .. targetPlayer.Name)
			updateQueue()
			break
		end
	end
end

game.Players.PlayerAdded:Connect(function(player)
	table.insert(queue, player) -- Add player to the queue list
	player.CharacterAdded:Connect(function(character)
		character:SetAttribute("Position", 1) -- Players start at position 50
		updateQueue() -- Update the queue positions
		--character:MoveTo(LevelSquareSlots:FindFirstChild(table.find(queue, player)).Position)
		print(player.Name .. " joined the queue at position " .. table.find(queue, player))
	end)
end)

There is a lot more to making a line game than this, so this whole post only scratches the surface.
Please let me know if I made any mistakes or if you don’t understand something, and I hope this helps you out.

1 Like

Thank you very much for your incredible help

1 Like

Could you explain what the movement is under the name of the parts you move to? Also, instead of spawning on the front of the line and moving up when someone leaves, it just walks to the front of the line instead, and if someone in front of you leaves, you won’t go up.

The .Movement attached to the square positions isn’t from me. For some reason, the OP has blocks called “Movement” within each square in LevelSquareSlots. All I did was recreate his project in a simpler form.

Players do not spawn on their square when joining a game because I marked line 74 (character:MoveTo(LevelSquareSlots:FindFirstChild(table.find(queue, player)).Position)) as a comment in the script to show that players can walk to their designated squares. If you read through the script, you would have recognized this.

Also, as I stated before, this isn’t the whole thing to making a line game; I only fixed the OP’s issues with his project. Nobody moves up when somebody leaves because no function checks for when a player leaves the game. There is the leaveQueue() function, but nothing calls it. Just like how nobody can skip because there is no button to skip anyone yet.

These are very simple additions and changes, so, if you want, try fixing the script to your liking. I am also working on a line queue system and might release it to the public if nobody else has done it by then. If you need any help, let me know, and I hope these explanations are what you’re looking for.

1 Like