Avoid Click Spamming

local module = {}

local replicatedStorage = game:GetService("ReplicatedStorage")
local remotes = replicatedStorage:FindFirstChild("Remotes")
local CowButtonDownRemote = remotes:FindFirstChild("CowButtonDown")
local CowButtonUpRemote = remotes:FindFirstChild("CowButtonUp")

local down = {}

CowButtonDownRemote.OnServerEvent:Connect(function(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	local playerStats = player:FindFirstChild("PlayerStats")
	local cows = leaderstats:FindFirstChild("Cows")
	local cowsPerSecond = playerStats:FindFirstChild("CowHoldPerSecond")
	local cowsPerClick = playerStats:FindFirstChild("CowsPerClick")
	if not (down[player]) or down[player] - tick() > 1 / cowsPerSecond  then	
		down[player] = tick()
		coroutine.wrap(function()
			while down[player] do
				cows.Value += cowsPerClick.Value
				task.wait(1 / cowsPerSecond.Value)
			end
		end)()
	end
end)

CowButtonUpRemote.OnServerEvent:Connect(function(player)
	down[player] = nil
end)



return module

This code is intended to limit users to only be able to get CowsPerSecond cows per second whether they click a button or hold it down (this is only the server side). The issue with this code is that you can spam click since the buttonup event removes the player from the debounce table. How can I work around this?

local module = {}

local replicatedStorage = game:GetService("ReplicatedStorage")
local remotes = replicatedStorage:FindFirstChild("Remotes")
local CowButtonDownRemote = remotes:FindFirstChild("CowButtonDown")
local CowButtonUpRemote = remotes:FindFirstChild("CowButtonUp")

local down = {}

CowButtonDownRemote.OnServerEvent:Connect(function(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	local playerStats = player:FindFirstChild("PlayerStats")
	local cows = leaderstats:FindFirstChild("Cows")
	local cowsPerSecond = playerStats:FindFirstChild("CowHoldPerSecond")
	local cowsPerClick = playerStats:FindFirstChild("CowsPerClick")
	if not (down[player]) or down[player] - tick() > 1 / cowsPerSecond  then
	    coroutine.wrap(function()
            down[player] = tick()
            task.wait(0.25) --// delay time
            down[player] = nil
        end)()
		coroutine.wrap(function()
			while down[player] do
				cows.Value += cowsPerClick.Value
				task.wait(1 / cowsPerSecond.Value)
			end
		end)()
	end
end)




return module

This should work to achieve what you’re looking for:

local module = {}

local replicatedStorage = game:GetService("ReplicatedStorage")

local remotes = replicatedStorage.Remotes
local CowButtonDownRemote = remotes.CowButtonDown
local CowButtonUpRemote = remotes.CowButtonUp

local threads = {}
local time = {}

local function setCows(cows, cowsPerClick, cowsPerSecond)
	while true do
		cows.Value += cowsPerClick
		task.wait(cowsPerSecond)
	end
end

CowButtonDownRemote.OnServerEvent:Connect(function(player)
	if threads[player] then return end -- Prevents overwriting the existing thread, which will cause multiple loops to run

	local leaderstats = player:FindFirstChild("leaderstats") -- FindFirstChild will return nil if the Instance isn't found, and if so, will cause an error in the original code
	local playerStats = player:FindFirstChild("PlayerStats")

	if leaderstats and playerStats then
		local cows = leaderstats:FindFirstChild("Cows")
		local cowsPerSecond = playerStats:FindFirstChild("CowHoldPerSecond")
		local cowsPerClick = playerStats:FindFirstChild("CowsPerClick")

		if cows and cowsPerSecond and cowsPerClick then
			if time[player] and (tick() - time[player]) < (1 / cowsPerSecond.Value) then return end
			time[player] = tick()

			threads[player] = task.spawn(setCows, cows, cowsPerClick.Value, 1 / cowsPerSecond.Value)
		end
	end
end)

CowButtonUpRemote.OnServerEvent:Connect(function(player)
	if threads[player] then
		task.cancel(threads[player])
		threads[player] = nil
	end

	time[player] = nil
end)

return module

Do remember that FindFirstChild will return nil if the Instance isn’t found, and that you’ll need to use .Value to access a value that’s stored in a value instance:


@U_npluggedDev Added a guard statement to fix a bug that can cause the original thread to be overwritten, but still continue to run

This solution still does not fix click spamming since the mousebuttonup removed thread and time for the player. Its really confusing to me

The time[player] = nil may be interfering, and it’s safe and recommended to remove it with the code’s current state. It should work correctly without it

To clarify, the thread still needs to be cancelled when the mouse button is up, so that part has to stay, otherwise you’ll get an issue where two loops may run at the same time

Wouldnt this mean that as players leave that the list will keep increasing in size?

1 Like

Yes, but the best way to fix that problem is to use Players service’s PlayerRemoving event

Gotcha, and its a good thing its a module script since I can just make a function and call that function in my player removing script in a seperate file. Thank you so much for your help

1 Like

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