Better way to make serversided cooldown system?

What I am currently thinking about doing is having it like this, since I do not think there is a way to have a dictionary added to in roblox.

local Players = {}
local PlayerUsage = {}

for i,v in pairs(game.Players:GetPlayers()) do
	table.insert(Players, v)
	table.insert(PlayerUsage, 0)
end

-- Later in the script.
function AirPalm:DoMove(Target, Mouse, Mobile)
	local now = tick()
    local GetPosition = 0
	for i,v in pairs(Players) do
		if Target == v then
			GetPosition = i
		end
	end
    local LastUsed = PlayerUsage[i]
	if now - LastUsed > self.Cooldown then 
         PlayerUsage[i] = now
-- Do stuff
2 Likes

I think you’re on the right track, but I would change a couple things:

  1. Don’t just collect the players once, because the player list can change. Collect the list every time you need to use it.
  2. You can use the player object itself as a key to point to the usage, which will allow you to avoid needing to scan through to find the player.

Here’s how I would modify the code:

local PlayerUsage = {}

-- Later in the script.
function AirPalm:DoMove(Target, Mouse, Mobile)
	local now = tick()
	local LastUsed = PlayerUsage[Target] or 0
	if now - LastUsed > self.Cooldown then
		PlayerUsage[Target] = now
		-- Do stuff
	end
end

-- Cleanup:
game.Players.PlayerRemoving:Connect(function(Player)
	PlayerUsage[Player] = nil
end)

Some notes about the code:

  • The line PlayerUsage[Target] or 0 includes the “or 0” part to default the value to “0” if no value has been set yet. This will protect it from erroring on the next line where it tries to perform arithmetic on the variable.
  • Since we are collecting info per player into the table, we need to make sure to remove that data when the player leaves. If we don’t do that, we will introduce a somewhat high-level data leak (i.e. data continues to exist even after it is never used again). Therefore, we have the last few lines where we use PlayerRemoving to set the table index of the player to nil.
6 Likes

Thank you for clearing it up, but, even though this is a table, we can still treat it like a dictionary?

So basically we’re creating something like this:

local PlayerUsage = {
["ThousandDegreeKnife"] = 0,
["Crazyman32"] = 2,
}

Instead of this?

local PlayerUsage = {0,1,5,7}

I had no idea you could do that.

3 Likes

Yeah, so Lua is weird. Tables are a hybrid of both arrays and dictionaries. Unfortunately, you can use the same table as both a table and a dictionary, but I highly advise against ever doing this. That’s known as a “mixed” table and causes all sorts of problems.

But yeah, we’re using the actual player object as the key, not just the name. But you could also use string keys such as your example above. Lua lets you use just about anything as a key. Strings, numbers (including floats), userdata, other tables, functions, etc.

The Lua PIL describes them as “associative” arrays.

3 Likes

Yeah I knew that, I just assumed LUA treated dictionaries like Java treats tables, where you cannot just add entries other than the ones that are predefined since I only saw table.add(). But thanks for clearing that one up.

3 Likes

Fair. Good question all-around though. Getting a hang of Lua tables is an oddly difficult learning curve at first. I’d more-so describe Lua tables as a mix of both Java’s ListArray and HashMap (or alternatively C#'s List and Dictionary).

2 Likes

Funny enough, HashMaps were actually the first thing I learned in my CS class, since we were tasked with making a bank account system.

1 Like

Is there a way to minimize (or even remove) latency when using the Server to Set a Cooldown?

The problem is that the Server and Client is out of sync because the Server will always receive the RemoteEvent a little later from when it’s fired therefore the Client won’t be able to use the Skill after the CoolDown but a little bit later depending on Ping, so if the Ping isn’t stable the delay would be inconsistent and cause terrible UX and even more if you have a ClientSide CoolDown to prevent RemoteEvent spamming,

This is probably not something you can’t avoid from happening but I want to ask if you have a better method to deal with this.

1 Like

What I am doing is having cooldown stored on the client and the server, and then doing a simple check to see if the cooldown is in check with the server. Don’t know if that eliminates the delay you’re talking about, but it’s working fine for me.

Yeah that’s kinda the way to do it. Always do the check on the client first for the sake of UX. But also do a server-side check to validate the action. Basically, make the client believe he/she is performing the action, but don’t actually perform the action on any data until the server can verify it. Regarding the ping issue…this is tricky, but usually isn’t a huge issue.

Ping can be inconsistent, so two actions caused by the player 10 milliseconds apart could appear to be 90 milliseconds, if the request for the second action is sent in the next network step. It would be nice if Roblox gave us a “time sent” parameter for remote events & functions. You could also send a timestamp yourself. Regardless, it could be spoofed by an exploiter. However, you could verify the timestamp by making sure that it’s not in the future & that it’s not in the past too much (e.g. not in the past more than a large latency would cause … 1 second maybe?)

1 Like

What you are doing would obviously work perfectly fine in Studio, if you have a live game this problem will present itself to players with a low end device and poor internet connection.

Testing with poor connections I’m experiencing ping of over 1 thousand very often and it could go way beyond that.

Not to mention players from all over the world outside of USA would obviously experience High - Medium Ping.


@Crazyman32, sometimes the Client is also out-sync with the server as well, not sure if tick or os.time is recommended but do you have any other solution that doesn’t allow spoofing?

Anyone looking at this you can just set the server cooldowns always .1 seconds less and do the actual check on the client so even if it’s exploited then they can get .1 seconds less cooldown at most or not at all if they have latency.

1 Like

Yeah after alot more testing for anyone who see’s this just use Remote Functions and do all the checking and cooldowns server-side then fire back to the server to do whatever else you have to do. So if it’s a skill then fire the remote function and have it return if the cooldown is off and if the action is allowed and then do animations based on if it returns true or false then fire back to the server to do whatever else you have to do if you have to and check the same cooldowns. There’s more sanity checks that go into this but that’s the base of it and the latency of going back and forth isn’t a issue.