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.