Best way to handle server-side cooldowns for multiple skills?

Title is pretty self-explanatory, I’m looking for a way to handle multiple cooldowns for multiple skills at the same time, possibly on the server to make it safer.
I know I should use a dictionary, but I’m not sure on how to proceed.

I think the dictionary should look like this while in action:

local Cooldowns = {
	Player1 = {
		skill1 = lastTick,
		skill3 = lastTick,
	},
	Player2 = {
		skill4 = lastTick,
		skill1 = lastTick,
		skill3 = lastTick,
	}
},

2 Likes

Bump? No one has any idea to share? :sweat_smile:

1 Like

Yeah when you use the skill, store the tick() of when it was used. Then when the client tries to fire the server check if the new tick() is greater than the stored tick() plus the skill cooldown. Then set the last fired to the new tick()

--client fired server event to fire their weapon
local t = tick()
if t >= self.lastFired + 1/self.rateOfFire -1/30 then --valid fire

This is an obscure application from a shooter I am working on.
The 1/self.rateOfFire is the cooldown and -1/30 is the error allowance for ping because I was having problems with getting the magazine of my whole gun to register on the server.

I don’t see any reason to run a loop when you can check the time passed from one use of the skill to the next.

1 Like

Here is one method you could take. Though I am sure there are probably other more efficient methods. In this quick demo, “Fired” should only print three times.

local mt = {
	__index = {
		fire = (function(self)
			local t = tick()
			-- Less than coolDownTime indicates
			-- coolDownTime  has NOT elapsed
			if t - self.lastFired >= self.coolDownTime then
				print("Fire")
				self.lastFired = tick()
			end
		end)
	}
}

local function newSkill(coolDownTime)
	local self = {
		coolDownTime = coolDownTime,
		lastFired = 0
		
	}

	setmetatable(self, mt) 
	return self
end

local fireBall =  newSkill(2)


fireBall:fire()
fireBall:fire()
wait(2)
fireBall:fire()
wait(2)
fireBall:fire()
2 Likes

You can use attributes for cooldowns; it’s what I use for my medium-large scale combat system

All of my skills have a UID, so all I have to do is something like:

Status:Add(Character, Skill_UID.."-cooldown", CooldownDuration)

And to check if the player has the skill on cooldown:

local OnCooldown = Status:Check(Character, Skill_UID.."-cooldown")

Here are some functions from my Status module that I use for this exact purpose:

local Status = {}

function Status:Add(Unit : Character, StatusName : String, Duration : Number, Stacks : number | any)
	local CallTime = tick()
	local StackString = StatusName.."_Stacks"
	if Duration then
		Unit:SetAttribute(StatusName,CallTime+Duration)
		coroutine.wrap(function()
			task.wait(Duration)
			local CurrentTime = tick()
			local ExpireTime = Unit:GetAttribute(StatusName)
			if ExpireTime and ExpireTime < CurrentTime then
				Unit:SetAttribute(StatusName,nil)
				Unit:SetAttribute(StackString,nil)
			end
		end)()
	else
		Unit:SetAttribute(StatusName,-1)
	end
	if Stacks then
		local CurrentStacks = Unit:GetAttribute(StackString)
		CurrentStacks += Stacks
		Unit:SetAttribute(StackString,CurrentStacks)
	end
end

function Status:Remove(Unit : Character, StatusName : String): (boolean, number)
	local StackString = StatusName.."_Stacks"
	local StacksRemoved = Unit:GetAttribute(StackString) or 0
	Unit:SetAttribute(StatusName,nil)
	Unit:SetAttribute(StackString,nil)

	return StacksRemoved > 0, StacksRemoved
end

function Status:Check(Unit : Character, StatusName : String): (boolean, number)
	local StackString = StatusName.."_Stacks"
	local HasStatus = Unit:GetAttribute(StatusName) ~= nil
	local CurrentStacks = Unit:GetAttribute(StackString)

	return HasStatus, CurrentStacks
end

return Status

Cheers

3 Likes

@ everyone: Sorry for the late reply - been really busy these past 2 days. Thank you for your answers!

@overflowed Do you mind explaining how it works? I’m confused about the Status and Stacks system you use.
@kboy100 your system seems pretty damn good, but I’m struggling to apply it properly. For some reason it gives me this error once the :Fire event is used:
image

If you decide to implement your system in this manner, I would personally use module scripts. Unless I’m being pedantic of your reply, :fire() can only be called in lowercase (as the function is declared). If you wish to use a capital F, just change the code like so:

Fire = (function(self) -- Function is now called Fire and not fire
			local t = tick()
			-- Less than coolDownTime indicates
			-- coolDownTime  has NOT elapsed
			if t - self.lastFired >= self.coolDownTime then
				print("Fire")
				self.lastFired = tick()
			end
		end)

You can disregard the stacks if you want, it’s kinda implied in the syntax highlighting

My status system is basically a glorified SetAttribute/GetAttribute wrapper

Unit can literally be anything that supports Attributes, it’s specified as Character object since I use my Status module on Characters & NPC’s.

Add() adds an attribute to Unit for the given Duration; it’s automatically removed Duration seconds after Add() is initially called

Remove() and Check() are pretty self-explanatory