Code for voting GUI doesn't work

I’m trying to make a round system for my game, but I’m currently having issues with programming it. After I use table.sort to sort out the items in the table, for some reason after I try to get the map that has the most votes, it prints out as nil and throws an error.

  18:04:24.590  Roblox HQ  -  Server - Game:77
  18:04:45.047   ▶ {...}  -  Server - Game:109
  18:04:45.047  nil  -  Server - Game:110
  18:04:45.047  31  -  Server - Game:111
  18:04:45.048  ServerScriptService.Game:123: attempt to index nil with 'Parent'

Here’s the code

while task.wait() do
	if #Players:GetPlayers() >= 1 then
		for i = 1, 4, 1 do
			statusValue.Value = "◀ ".."Preparing Game.".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game..".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game...".." ▶"
			task.wait(.5)
		end

		local option1 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		print(option1)

		local option2
		repeat
			option2 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option2 ~= option1
		print(option2)

		local option3 
		repeat
			option3 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option3 ~= option1 and option3 ~= option2
		print(option3)

		local votes = {option1Votes = 0, option2Votes = 0, option3Votes = 0}


		mapVotingRemote:FireAllClients(option1, option2, option3)

		for i = 1, 20, 1 do
			timeValue.Value = convertToHMS(20 - i)

			task.wait(1)
		end

		task.spawn(function()
			mapVotingRemote.OnServerEvent:Connect(function(player, voteOption)
				if voteOption == 1 then
					votes.option1Votes += 1
				elseif voteOption == 2 then
					votes.option2Votes += 1
				elseif voteOption == 3 then
					votes.option3Votes += 1
				end
			end)
		end)


		table.sort(votes, function(a, b)
			return a > b
		end)
		
		local map
		
		print(votes)
		print(votes[1])
		print(votes.option1Votes)
		if votes.option1Votes == votes[1] then
			map = option1:Clone()
			print(map)
		elseif votes.option2Votes == votes[1] then
			map = option2:Clone()
			print(map)
		elseif votes.option3Votes == votes[1] then
			map = option3:Clone()
			print(map)
		end

		map.Parent = workspace

		local gamemode = gamemodes[math.random(1, #gamemodes)]
		local playerMode

		if gamemode == gamemodes.Demolition then
			playerMode = gamemodes.Demolition[math.random(1, #gamemodes.Demolition)]

			if playerMode == "FFA" then
				map:FindFirstChild("Spawns").BlueSpawns:Destroy()
				map:FindFirstChild("Spawns").RedSpawns:Destroy()
			end
		end

		statusValue.Value = "Map Selected, Beginning Game!"

		task.wait(3)

		screenTransitionRemote:FireAllClients(5)
	end

end
2 Likes

I believe your issue is that you are treating a dictionary (votes) like a regular array.

You’re defining votes as a dictionary, because you’re providing keys.

table.sort doesn’t work with dictionaries because, according to its documentation, the method “sorts elements of array t in a given order, from t[1] to t[#t].” And you can’t index values of dictionaries using numbers, like regular arrays, unless the numbers are keys of that dictionary.

If you attempt to get the length of a dictionary using #dictionary or you try to index an element as if the dictionary is a regular array, the returned value will be nil.

What’s happening is the conditions for your if and elseif statements are not being met, so map is never getting defined.

-- You're basically typing this:
if votes.option1Votes == nil then
    -- ...
end

Also, you should not be setting up the same event listener every loop; you’re creating unnecessary listeners which can affect perfomance in the long run. Instead, set the event listener outside of the loop. This would cause you to re-structure your code a little.

1 Like

It’s like Alexan said.
votes was not a sequential array.

Summary
while task.wait() do
	if #Players:GetPlayers() >= 1 then
		for i = 1, 4, 1 do
			statusValue.Value = "◀ ".."Preparing Game.".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game..".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game...".." ▶"
			task.wait(.5)
		end

		local option1 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		print(option1)

		local option2
		repeat
			option2 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option2 ~= option1
		print(option2)

		local option3 
		repeat
			option3 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option3 ~= option1 and option3 ~= option2
		print(option3)

		local votes = {option1Votes = 0, option2Votes = 0, option3Votes = 0}

		mapVotingRemote:FireAllClients(option1, option2, option3)

		for i = 1, 20, 1 do
			timeValue.Value = convertToHMS(20 - i)
			task.wait(1)
		end

		task.spawn(function()
			mapVotingRemote.OnServerEvent:Connect(function(player, voteOption)
				if voteOption == 1 then
					votes.option1Votes += 1
				elseif voteOption == 2 then
					votes.option2Votes += 1
				elseif voteOption == 3 then
					votes.option3Votes += 1
				end
			end)
		end)

		local sortedVotes = {}
		for _, count in pairs(votes) do
			table.insert(sortedVotes, count)
		end

		table.sort(sortedVotes, function(a, b)
			return a > b
		end)

		local map

		if votes.option1Votes == sortedVotes[1] then
			map = option1:Clone()
		elseif votes.option2Votes == sortedVotes[1] then
			map = option2:Clone()
		elseif votes.option3Votes == sortedVotes[1] then
			map = option3:Clone()
		end

		if map then
			map.Parent = workspace

			local gamemode = gamemodes[math.random(1, #gamemodes)]
			local playerMode

			if gamemode == gamemodes.Demolition then
				playerMode = gamemodes.Demolition[math.random(1, #gamemodes.Demolition)]

				if playerMode == "FFA" then
					map:FindFirstChild("Spawns").BlueSpawns:Destroy()
					map:FindFirstChild("Spawns").RedSpawns:Destroy()
				end
			end

			statusValue.Value = "Map Selected, Beginning Game!"
			task.wait(3)
			screenTransitionRemote:FireAllClients(5)
		else

		end
	end
end

Can also be done this way.

Summary
-- Set up the event listener outside the loop
mapVotingRemote.OnServerEvent:Connect(function(player, voteOption)
	if voteOption == 1 then
		votes.option1Votes += 1
	elseif voteOption == 2 then
		votes.option2Votes += 1
	elseif voteOption == 3 then
		votes.option3Votes += 1
	end
end)

while task.wait() do
	if #Players:GetPlayers() >= 1 then
		for i = 1, 4, 1 do
			statusValue.Value = "◀ ".."Preparing Game.".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game..".." ▶"
			task.wait(.5)

			statusValue.Value = "◀ ".."Preparing Game...".." ▶"
			task.wait(.5)
		end

		local option1 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		print(option1)

		local option2
		repeat
			option2 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option2 ~= option1
		print(option2)

		local option3
		repeat
			option3 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
		until option3 ~= option1 and option3 ~= option2
		print(option3)

		local votes = {option1Votes = 0, option2Votes = 0, option3Votes = 0}

		mapVotingRemote:FireAllClients(option1, option2, option3)

		for i = 1, 20, 1 do
			timeValue.Value = convertToHMS(20 - i)
			task.wait(1)
		end

		local sortedVotes = {}
		for _, count in pairs(votes) do
			table.insert(sortedVotes, count)
		end
		table.sort(sortedVotes, function(a, b)
			return a > b
		end)

		local map

		if votes.option1Votes == sortedVotes[1] then
			map = option1:Clone()
		elseif votes.option2Votes == sortedVotes[1] then
			map = option2:Clone()
		elseif votes.option3Votes == sortedVotes[1] then
			map = option3:Clone()
		end

		if map then
			map.Parent = workspace

			local gamemode = gamemodes[math.random(1, #gamemodes)]
			local playerMode

			if gamemode == gamemodes.Demolition then
				playerMode = gamemodes.Demolition[math.random(1, #gamemodes.Demolition)]

				if playerMode == "FFA" then
					map:FindFirstChild("Spawns").BlueSpawns:Destroy()
					map:FindFirstChild("Spawns").RedSpawns:Destroy()
				end
			end

			statusValue.Value = "Map Selected, Beginning Game!"
			task.wait(3)
			screenTransitionRemote:FireAllClients(5)
		else

		end
	end
end
1 Like

The error message you shared, “attempt to index nil with ‘Parent’”, means that you’re trying to access the ‘Parent’ property of a value that is currently nil (which means it doesn’t have a valid object assigned to it). This error typically occurs when you’re trying to perform an operation on something that doesn’t exist or hasn’t been properly initialized.

In your script, the error occurs on line 123, specifically when you’re trying to set the parent of the ‘map’ variable to the workspace. The reason this error is happening is that none of the conditions in your ‘if’ and ‘elseif’ blocks are being met, which means none of the options have received enough votes.

To resolve this error, we can add an ‘else’ block at the end of your conditions to handle the case where no option is selected. In that block, we can set a default value for the ‘map’ variable. This way, even if no option receives enough votes, the ‘map’ will have a valid object assigned to it, avoiding the ‘nil’ value.

I have provided a modified version of your script with the necessary changes made to handle this error gracefully. You can use this updated script to ensure that a default option is set when no votes are received:

while task.wait() do
    if #Players:GetPlayers() >= 1 then
        for i = 1, 4, 1 do
            statusValue.Value = "◀ ".."Preparing Game.".." ▶"
            task.wait(.5)

            statusValue.Value = "◀ ".."Preparing Game..".." ▶"
            task.wait(.5)

            statusValue.Value = "◀ ".."Preparing Game...".." ▶"
            task.wait(.5)
        end

        local option1 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
        print(option1)

        local option2
        repeat
            option2 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
        until option2 ~= option1
        print(option2)

        local option3 
        repeat
            option3 = maps:GetChildren()[math.random(1, #maps:GetChildren())]
        until option3 ~= option1 and option3 ~= option2
        print(option3)

        local votes = {option1Votes = 0, option2Votes = 0, option3Votes = 0}

        mapVotingRemote:FireAllClients(option1, option2, option3)

        for i = 1, 20, 1 do
            timeValue.Value = convertToHMS(20 - i)
            task.wait(1)
        end

        task.spawn(function()
            mapVotingRemote.OnServerEvent:Connect(function(player, voteOption)
                if voteOption == 1 then
                    votes.option1Votes += 1
                elseif voteOption == 2 then
                    votes.option2Votes += 1
                elseif voteOption == 3 then
                    votes.option3Votes += 1
                end
            end)
        end)

        table.sort(votes, function(a, b)
            return a > b
        end)
        
        local map
        
        print(votes)
        print(votes[1])
        print(votes.option1Votes)
        if votes.option1Votes == votes[1] then
            map = option1:Clone()
            print(map)
        elseif votes.option2Votes == votes[1] then
            map = option2:Clone()
            print(map)
        elseif votes.option3Votes == votes[1] then
            map = option3:Clone()
            print(map)
        else
            map = option1:Clone() -- Default option when no votes are received
            print("No option selected. Using default option.")
        end

        map.Parent = workspace

        local gamemode = gamemodes[math.random(1, #gamemodes)]
        local playerMode

        if gamemode == gamemodes.Demolition then
            playerMode = gamemodes.Demolition[math.random(1, #gamemodes.Demolition)]

            if playerMode == "FFA" then
                map:FindFirstChild("Spawns").BlueSpawns:Destroy()
                map:FindFirstChild("Spawns").RedSpawns:Destroy()
            end
        end

        statusValue.Value = "Map Selected, Beginning Game!"

        task.wait(3)

        screenTransitionRemote:FireAllClients(5)
    end
end
1 Like