Debounce Problem

So I have a Gui that contain a button to trigger an ability, and i want it everytime you activate them it will do the ability function and will have a delay/cooldown. But once you activate it the debounce are also implemented in another selected target ability. Is there a way i could fix this?.

local db = true
script.Parent.BillboardGui.Frame.Abilities.Ability.Activated:Connect(function()
	local TowerStats = require(selectedTarget.Stats)
	local AbilityButton = script.Parent.BillboardGui.Frame.Abilities.Ability
	if selectedTarget then
		if db == true then
			db = false
			script.Purchase:Play()
			local abilityFunc = AbilityTowerFunc:InvokeServer(selectedTarget)
			AbilityButton.Cooldown.Visible = true
			for i = TowerStats.AbilityCooldown, 0, -1 do
				AbilityButton.Cooldown.TextLabel.Text = i.."s"
				task.wait(1)
				if i == 0 then
					db = true
					AbilityButton.Cooldown.Visible = false
				end
			end
		end
	end
end)

Looks like you didn’t set db back to true at the end. Also you start doing stuff at the beginning of the code because your db should be checked immediately.

local db = true

script.Parent.BillboardGui.Frame.Abilities.Ability.Activated:Connect(function()
	if db then
		db = false
    	local TowerStats = require(selectedTarget.Stats)
    	local AbilityButton = script.Parent.BillboardGui.Frame.Abilities.Ability
    	if selectedTarget then
			script.Purchase:Play()
			local abilityFunc = AbilityTowerFunc:InvokeServer(selectedTarget)
			AbilityButton.Cooldown.Visible = true
			for i = TowerStats.AbilityCooldown, 0, -1 do
				AbilityButton.Cooldown.TextLabel.Text = i.."s"
				task.wait(1)
				if i == 0 then
					AbilityButton.Cooldown.Visible = false
				end
			end
        db = true
		end
	end
end)

that didnt fix it, the issue was if A ability is used then the B ability cant be used because of the debounce is still there

Not sure how this will work for you, but you could try using a coroutine for this. Anytime a button is pressed just refer back to db to see if it is true or not. With that said, I’m pretty certain you should be able to get away with the disable of the debounce as it is currently; coroutine need not apply.

local db = true

script.Parent.BillboardGui.Frame.Abilities.Ability.Activated:Connect(function()
	local TowerStats = require(selectedTarget.Stats)
	local AbilityButton = script.Parent.BillboardGui.Frame.Abilities.Ability
	if selectedTarget then
		if db == true then
			db = false
			script.Purchase:Play()
			local abilityFunc = AbilityTowerFunc:InvokeServer(selectedTarget)
			
			coroutine.resume(coroutine.create(function()
				AbilityButton.Cooldown.Visible = true
				
				for i = TowerStats.AbilityCooldown, 0, -1 do
					AbilityButton.Cooldown.TextLabel.Text = i.."s"
					task.wait(1)
				end
				
				db = true
				AbilityButton.Cooldown.Visible = false
			end))
		end
	end
end)


Also, what does the server implementation of this look like? Please tell me you are keeping track of active abilities on the server; a client could easily bypass this if you aren’t implementing a debounce on the server as well. Basically, on the server side I would have a table for example named “activeuserabilities”.

Each player gets their own table inside activeuserabilities, from there you could check that player’s table to see if they have any abilities running (for example if #activeuserabilities[plr.UserId] == 0 then, or more complex method if you eventually allow for certain abilities to be ran together.) and if they do then don’t allow the requested ability to run.

Hopefully I explained that right, if you need more clarification on the idea let me know.

For the server side I’m using a module script and no there’s no keeping track for active abilities.
But your explanation would surely help, thanks

1 Like

Yeah, so I worked on some abilities awhile back for someone and admittedly I didn’t end up committing to this project, looking back I wish I had, but anyways, you can see an example here.

On the client I am using a script that simply sends a message to the server via a remote event (I see you are using a remote function)

Once received by the server I immediately call a function to check if they are on an ability cooldown.

-- ON SERVER
local abilitycooldown = {}

function checkCooldown(plr)
 if abilitycooldown [plr.UserId] then
  return true
 else
  return false
 end
end

if the function returns true (user is on cooldown) I disregard what the user asked for, if the function returns false I continue. (if you wish to make it so it returns true if they aren’t on cool down this is fully within your power.

Assuming they aren’t on cooldown then adding them to the cooldown list via

-- ON SERVER
abilitycooldown [plr.UserId]  = true

afterwards start the ability and beginning a coroutine that uses a for loop to count down the cooldown after it reaches zero setting the player in the cooldown table to nil so the player can use their next ability.

-- ON SERVER
    coroutine.resume(coroutine.create(function()
       for i=AbilityCooldown,0,-1 do
         task.wait(1)
       end
       abilitycooldown[plr.UserId] = nil
    end))

That being said, you can still keep the gui countdown for when they can use the next ability on the client that way you don’t have to constantly update a IntValue or send messages to the client.

If you are looking to allow multiple abilities at once you can create a table with tables in them for each ability that exists in your game and then checking that with a function, it could look something like this.

-- ON SERVER
local abilities = {} -- Stores users who are on cooldown for certain abilities
abilities["A"] = {}
abilities["B"] = {}

local AbilityCooldownsTime = {} -- A table that stores the times (in seconds) for the cooldown of certain abilities
AbilityCooldownsTime["A"] = 5
AbilityCooldownsTime["B"] = 10 

function isAbilityOnCooldown(plr,abilityname)
 if abilities[abilityname][plr.UserId] then
  return true
else
 return false
end

afterwards a slight modification to the coroutine that keeps track of the cooldown.
it would look something like this:

 coroutine.resume(coroutine.create(function()
       for i=AbilityCooldownsTime[abilityname], 0,-1 do
         task.wait(1)
       end
       abilities[abilityname][plr.UserId] = nil
    end))

Now as to the reason you should be keeping track of the cooldown on the server that is because a player could take advantage of this client side limitation by simply just sending another message to the server and if the check wasn’t done on the server and instead only client, the server would just say, Okay, playing that ability. Bad actors will take advantage of any hole you give them and having a client sideded ability cooldown instead of a server sideded one is something that they will take advantage of. Also if a bug happens on the client and the cooldown ends early on the client they’ll be able to play the ability again when you don’t want them to.

This practice of not trusting the client with Remote Events and Functions is referred to by many here as securing the remote event/function, AKA a sanity check. (in other words not trusting that the client is able to make the call and instead preforming a server sided check to ensure they can make such a call).

Hope that makes some sense.

1 Like

So are you saying that another thing is using the debounce as well? Well in that case, you can make another debounce.

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