Round being fired more than once when not singleplayer

Hi,

I’m having a issue with my games round being fired more than once. I think this is happening because

is being run by how many people are in the game. if theres 2 people, it will run twice. How do I fix this?

Client:

	
	local function chooseEvent()
		if not ReplicatedStorage:GetAttribute(Variables.StatusAttribute) then
			return
		end

		local strings = string.split(ReplicatedStorage:GetAttribute(Variables.EventAttribute), " ")
		local event, difficulty = strings[1], strings[2]

		for i = 1,10 do
			if i ~= 10 then
				local randomEvent = math.random(1, #Events)
				local randomDifficulity = math.random(1, #Variables.Difficulties)

				local difficulty = Variables.Difficulties[randomDifficulity]
				local event = Events[randomEvent]

				Difficulty.Text = "DIFFICULTY: "..string.format(Variables[difficulty], difficulty)
				Event.Text = tostring(Events[randomEvent])
			else
				Difficulty.Text = "DIFFICULTY: "..string.format(Variables[difficulty], difficulty)
				Event.Text = event
			end

			Transparency:set(1, true)
			task.wait(0.1)
			Transparency:set(0, true)
			task.wait(0.1)

			if i == 10 then

				Audio.play(SoundService.Master.Interface.Chosen)
				task.wait(3)
				Transparency:set(1, true)

				if not ReplicatedStorage:GetAttribute(Variables.StatusAttribute) then
					return
				end

				BeginEvent:FireServer()
			end
		end
	end
	
	-- this fires when the game intro finishes (beginevent is not fired)
	ReplicatedStorage:GetAttributeChangedSignal(Variables.IntroAttribute):Connect(function(...: any)
		if not ReplicatedStorage:GetAttribute(Variables.IntroAttribute) then
			print("intro")
			chooseEvent()
		end
	end)
	
	-- this fires when a new event is needed to be chosen (after a round end)
	BeginEvent.OnClientEvent:Connect(function(...: any) 
		print("event fired")
		chooseEvent()
	end)

Server:

local function getRandomEvent()
	local randomEvent, randomDifficulty = math.random(1, #Events:GetChildren()), math.random(1, #Variables.Difficulties)
	local event, difficulty =  Events:GetChildren()[randomEvent]:: ModuleScript, Variables.Difficulties[randomDifficulty]

	ReplicatedStorage:SetAttribute(Variables.EventAttribute, tostring(event).. " " .. difficulty)

	return event, difficulty
end

local function isGameOver() -- ends game if everyone is dead
	if #Participants == 0 then
		ReplicatedStorage:SetAttribute(Variables.StatusAttribute, false)
		ReplicatedStorage:SetAttribute(Variables.TextAttribute, Variables.DeadText)

		beginEvent:Disconnect()
		
		for _, v in pairs(Variables.Cache:GetChildren()) do -- clear cache
			v:Destroy()
		end
		
		task.spawn(function()
			task.wait(Variables.GAME_END_DELAY)
			GameService.RoundAsync() -- redo intermission
		end)
		
		return true
	else
		return false
	end
end

function GameService.RoundAsync()
	-- intermission
	for seconds = Variables.Intermission, 0, -1 do
		ReplicatedStorage:SetAttribute(TextAttribute, string.format(Variables.IntermissionText, seconds))
		task.wait(1)
	end
	-- insert participants in table
	for _, participant in pairs(Players:GetPlayers()) do
		table.insert(Participants, participant)
		
		-- connecting participants death event and removing them upon death
		if participant.Character then
			local humanoid = participant.Character.Humanoid :: Humanoid
			local humanoidDied = nil :: RBXScriptConnection?

			humanoidDied = humanoid.Died:Connect(function()
				table.remove(Participants, table.find(Participants, participant))
				humanoidDied:Disconnect()
				
				if isGameOver() then return end
			end)
		end
		
		task.spawn(function() -- seperate thread to not block for loop
			task.wait(Variables.TELEPORT_DELAY)
			-- teleport players
			local root = participant.Character.HumanoidRootPart
			root.CFrame = Variables.Map.Spawn.CFrame + Vector3.new(0, 5, 0)
		end)
	end	
	
	-- change game status to began
	ReplicatedStorage:SetAttribute(Variables.StatusAttribute, true)
	
	local event, difficulty = getRandomEvent() -- get a random event
	
	beginEvent = BeginEvent.OnServerEvent:Connect(function(player: Player, ...: any) 
		print("event start")
        event, difficulty = getRandomEvent() -- chooses another random event
		require(event).StartAsync(difficulty)
		-- startasync yields code until event is done
		if #Participants == 0 then return end 

		print("firing")
		BeginEvent:FireAllClients() -- tell the client to start the next event
	end)
end

note: difficulty and event is chosen on server, the client side is just ui and tells the server when to start the next event

2 Likes

Maybe you could fix this with a debounce?

Make a variable called debounce or roundInProgress if you want and default it as false, and when the game starts:

function GameService.RoundAsync()
    if debounce then
        return -- Game is already ongoing
    end

    debounce = true
    -- ...
    -- debounce = false when you need to start the game again
end

The idea is that the debounce variable is only false when the round is not ongoing. And when the round is ongoing, the debounce variable should be true, and executing GameService.RoundAsync() again will not start one more round.

This should prevent the RoundAsync() being fired twice.

I assume you fire it once when a player joins? This fix should prevent that issue. If it doesn’t work let me know

2 Likes

No I call RoundAsync instantly when the server is loaded, regardless if the player is in the game or not. Since it’s server sided I don’t need to wait for the player to load I can just start the game once and the client will handle the ui.

I’m not sure about your solution since I’ve tested if it ran more than once and it only runs twice in the remote event. I’ll try it though

1 Like

Sorry I missed an important detail when reading your script. So the reason why RoundAsync is being fired twice is because the BeginEvent:FireServer() in your client script is actually being fired by both the first player and the second player when you are playing with two players.

You should apply the debounce method inside the BeginEvent.OnServerEvent connection instead, so the BeginEvent remote doesn’t get fired twice.

Like this:

beginEvent = BeginEvent.OnServerEvent:Connect(function(player: Player, ...: any) 
    if debounce then
        return
    end
    debounce = true
	print("event start")
        event, difficulty = getRandomEvent() -- chooses another random event
	require(event).StartAsync(difficulty)
	-- startasync yields code until event is done
	if #Participants == 0 then return end 

	print("firing")
    debounce = false
	BeginEvent:FireAllClients() -- tell the client to start the next event
end)

However though I want to mention that being able to do BeginEvent:FireServer() anytime from the client-side is a huge security risk, as exploiters can just fire the BeginEvent anytime and your server will start the event or round with no checking if it’s valid or not.

I’m not sure if this is a very severe issue after applying the debounce method, but you should definitely try to avoid doing this as much as possible, or exploiters can take advantage of this for other functions and potentially break the game.

1 Like

I can probably get past that by using a networking library.

I’m kind of skeptical if this would be good in terms of optimal. If theres 10 players playing my game then that means it fires 10 times.

Ignore the last edit lol I did a silly mistake

The performance is not an issue tho so you don’t have to worry about it. Firing an event 10 or 100 or even 1000 does not impact the performance heavily on the server (ok, maybe a bit if you are doing it excessively)

1 Like

Yeah but it still feels inefficient ykwim; I’ll solution your post if I don’t find another fix to this / or i’ll just solution yours if I find my own solution.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.