Cooldown issue (UI)

I’m facing challenges within a codebase while trying to integrate a cooldown period while also executing the rest of the code simultaneously.

Here’s the intended objective:
When a command is entered, such as ‘/tryout 1md 6’, a UI should appear and repeat itself three times. However, I want to prevent a new UI activation for 30 seconds after the initial command, even if the user attempts to re-enter ‘/tryout 1md 6’.

Moreover, if the player does re-enter ‘/tryout 1md 6’, I want the remaining time to be displayed in the console (this feature is already functional). However, I’m currently struggling to implement the cooldown while ensuring the rest of the code continues to execute.

If I omit the cooldown, the code works perfectly. At the moment, I’m unsure of the best way to implement that cooldown.

-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

-- Visual configurations for notifications
local notificationStyle = {
	BackgroundTransparency = 0.3,
	BackgroundColor = Color3.fromRGB(0, 0, 0),
	ContentTransparency = 0,
	ContentColor = Color3.fromRGB(255, 255, 255)
}

-- Module and Remotes
local GameSettings = require(script.Parent)
local NotificationModule = require(ReplicatedStorage.BannerNotification_Storage.BannerNotificationModule)
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local Announcement = Remotes.Announcement

local cooldownActive = false
local cooldownDuration = 30 -- Cooldown time in seconds
local lastCommandTime = tick()

local function startCooldown()
	cooldownActive = true
	lastCommandTime = tick()
	task.wait(cooldownDuration)
	cooldownActive = false
end

local function PlayerRank(player)
	local Rank = player:GetRoleInGroup(GameSettings.new().mainGroupID)
	return Rank and (Rank:match("%b[]") or not Rank:find("%[.-%]") and "[" .. Rank .. "]") or (warn("Player rank is nil for player", player.Name) and nil)
end

local function getDivisionDetails(player, divisionKey, padValue)
	local settings = GameSettings.new()

	for groupId, groupData in pairs(settings.divisions) do
		local requiredGroupRank = groupData.RequiredGroupRank

		for key, divisionData in pairs(groupData) do
			if type(divisionData) == "table" and key:lower() == divisionKey:lower() then
				if player:IsInGroup(groupId) and player:GetRankInGroup(groupId) and player:GetRankInGroup(groupId) >= requiredGroupRank then
					local success, formattedText = pcall(string.format, divisionData.description, player.Name, padValue, "[" .. settings.announcementCounter .. "/" .. settings.maxAnnouncements .. "]")
					if success then
						local playerAbbreviation = PlayerRank(player)
						return { divisionKey, formattedText:gsub(player.Name, string.format("<b>%s %s:</b>", playerAbbreviation, player.Name), 1), divisionData.Icon, divisionData.Header }
					else
						warn("Error formatting text for division " .. divisionKey)
					end
				else
					warn("User is not in the group or does not have the required group rank")
				end
			end
		end
	end

	warn("No division with the name " .. divisionKey)
end

local function handleCommand(playerEvent, args)
	local currentTime = tick()
	local timeSinceLastCommand = currentTime - lastCommandTime
	local remainingCooldown = math.max(0, cooldownDuration - timeSinceLastCommand)

	if cooldownActive and timeSinceLastCommand < cooldownDuration then
		print("Command is on cooldown. Remaining time:", math.floor(remainingCooldown), "seconds")
		return
	end

	if not playerEvent or not args or type(args) ~= "table" or #args < 3 then
		warn("Invalid parameters for the onServer function.")
		return
	end

	local divisionKey = tostring(args[2]):lower()
	local tryoutPads = workspace.Pads.Tryout_Pads:GetDescendants()
	local dynamicPadAmount = math.min(#tryoutPads, GameSettings.new().maxPadAmount)

	local modelName = args[3]:lower()
	local isValidModelName = false

	for _, descendant in ipairs(tryoutPads) do
		if descendant:IsA("Model") and descendant.Name:lower() == modelName then
			isValidModelName = true
			break
		end
	end

	if not isValidModelName then
		warn("Provided name does not match any model name in Tryout_Pads.")
		return
	end

	local numericValue = tonumber(args[3])
	local padValue = numericValue and math.min(numericValue, dynamicPadAmount) or args[3]

	if not divisionKey then
		warn("Invalid divisionKey.")
		return
	end

	local result = getDivisionDetails(playerEvent, divisionKey, padValue)

	if result then
		local _, formattedText, iconUrl, headerText = unpack(result)
		local settings = GameSettings.new()
		local newAnnouncements = settings.announcementCounter
		local maxAnnouncements = settings.maxAnnouncements

		while newAnnouncements < settings.maxAnnouncements do
			newAnnouncements = newAnnouncements + 1
			formattedText = formattedText:gsub("%[%d/3%]", "[" .. newAnnouncements .. "/3]")

			local success, errorOrPlayerList = pcall(function()
				local Players = game:GetService("Players")
				for _, player in pairs(Players:GetPlayers()) do
					local playerRank = player:GetRoleInGroup(settings.mainGroupID)
					local richTextPlayerName = string.format("<b>%s %s:</b>", playerRank, player.Name)

					local escapedPlayerName = player.Name:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
					local pattern = escapedPlayerName .. "%]%]"

					local fullText = formattedText:gsub(pattern, richTextPlayerName, 1)

					NotificationModule:Notify(headerText, fullText, iconUrl, settings.waitTimeInSeconds, notificationStyle, player)
					startCooldown() -- THIS PART
				end
			end)

			if not success then
				warn("Error during notification: " .. tostring(errorOrPlayerList))
				return
			end

			task.wait(settings.waitTimeInSeconds)
		end
	end
end

Announcement.OnServerEvent:Connect(handleCommand)
1 Like

Instead of doing task.wait(cooldown), you can use an if statement to see if the time elapsed has surpassed the cooldown variable.

An implementation could look like this:

local cooldownTime = 10
local lastCheck = os.clock()

-- Example in use:
local function doSomething()
    if lastCheck - os.clock() < cooldownTime then
        return
    end
    -- Reset the variable
    lastCheck = os.clock()
    
    -- rest of your code...
end

Now if you do:

-- This one will run
doSomething()
task.wait(5)
-- This one will end prematurely because it has not been 10 seconds
doSomething()
1 Like

I am already checking that on this line

local currentTime = tick()
	local timeSinceLastCommand = currentTime - lastCommandTime
	local remainingCooldown = math.max(0, cooldownDuration - timeSinceLastCommand)

	if cooldownActive and timeSinceLastCommand < cooldownDuration then
		print("Command is on cooldown. Remaining time:", math.floor(remainingCooldown), "seconds")
		return
	end

I am just thinking, should i use coroutines to let the cooldown run other another thread, Cuz i don’t see a way of implementing this, without breaking the main code

1 Like

Coroutines did the trick for now

For anyone wondering

local function cooldownCoroutine()
	cooldownActive = true
	lastCommandTime = tick()
	task.wait(cooldownDuration)
	cooldownActive = false
end

local function startCooldown()
	if not cooldownActive then
		coroutine.wrap(cooldownCoroutine)() -- Start de coroutine
	end
end
3 Likes

You do not need the cooldownActive boolean nor task.wait() to handle the cooldown properly. That is why we check using lastCheck - os.clock() < cooldownDuration, to see if the time has elapsed to warrant another use of the command.

Using coroutines will solve the problem the way your code presented it, but it is unnecessarily creating a new thread to handle something that could be done without it.

You are right, This is a temporary solution, i will play around with your suggestions and will come back to it if i have any questions about it

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