RequestService | Cooldowns made easy (aka RatelimitService)

RequestService

Ever wanted to implement cooldowns, the easy way?
Then this module will definitely help you, it is simple yet powerful, and can also be taken as a tutorial. :blush:

Why should I use it?

Not only does RequestService have support for different executors (aka players, “ROBLOX”, or even your dog!). But it also handles multiple topics!

How to install:

Example of use:

(plr.UserId should be wrapped inside tostring()!)

How does it work?

RequestService has a dictionary that contains each topic you have set, and each topic has a table of executors. As seen in this example below:

local requests = {
      ["DataStoreCooldowns"] = {
          ["JohnCena"] = 1234567890,
          ["TheRock"] = 0987654321,
      }
}

Each executor stores the last time it was allowed to proceed from a request in the given topic. We’ll refer to “tableName” later as “topic”, because it’s easier to understand.

A topic is automatically created if it doesn’t exists when you call the function:

requestService:RequestToProceed("CooldownTopicExample")

However, we are not done yet, we cannot call the function yet since we are missing some arguments…

We also need the desired “cooldown”, or in nerd explanation: the time that must have passed in seconds since the last successful request. Let’s use as an example, 5.

We also need an executor, which in this example case, would be our player. We will use its userId.

requestService:RequestToProceed("CooldownTopicExample", 5, tostring(plr.UserId))

You can also set “global” cooldowns for every topic by just setting the executor to the same one and just using that executor in particular.

requestService:RequestToProceed("CooldownTopicExample", 5, "ROBLOX")

If the given topic did not exist, it will be created as specified before.

If the given executor did not exist in the given topic, it will be created and be allowed to proceed, because there was no such “last request” in that topic.

Proper usage:

This module can be used server-sided and client-sided, however, it is recommended for important things like datastore cooldowns to be checked in the server.

Keep in mind that requests will not replicate client <-> server!

Other utility:

RequestService also gives you the ability to do things when a request is successful, just do:

requestService:OnRequest(function(requestInformation)
    print("A request was successful! Info: ", requestInformation)
end)

When a request is successful, this event will run the function you define here and pass a table containing the information about the request.

If you wish to disconnect the event, just do:

local connection = requestService:OnRequest(function(requestInformation)
    print("A request was successful! Info: ", requestInformation)
end)
--
connection:Disconnect()

What if I want to read ALL requests?

Well, there’s a function exactly made for that:

local allRequests = requestService:GetAllRequests(copy)

But what is copy? Copy is an optional parameter, if set to true, it will return you an identical copy of the requestsTable instead of the referenciation of it.

The parameter is useful when you want to prevent changes in the original requestsTable by your code.

Usage and API:

All documentation is inside the ROBLOX module, and it is Open Source.

If you have any suggestions, please let me know in the comments

13 Likes

I don’t believe this module is really necessary for anything at the moment, unless you were to make a very long cooldown for [insert thing here] and specific to a certain player.

Again, I don’t feel like many users actually need some utility like that. Can you provide some use cases?

1 Like

I think it’s very clear the pastebin i placed in the original post. If you still have any doubts read the source or ask here. But before asking make sure you’ve read the post correctly, please

By the way, yes, this can be achieved in some more lines, but it would be repetitive, this module can make your code cleaner by simplifying everything to 1 line and using it in different cases.

why can’t I just use a debounce :no_mouth:

This is not a false/true debounce, it uses time and can be used within 1 line in all your code, instead of being repetitive, which is what a MODULE helps you with

3 Likes

Why would I want to use a module for all of my cool downs though

wouldn’t that get repetitive for every cooldown I want in different games

and yes, I know what a module is

No offense, I don’t need to use a new Service for a cooldown when I could just make a debounce?
This isn’t very useful.

1 Like

I think you all are simply repetitive, this is not a debounce but rather a cooldown, and can be used within all your code, please read the documentation before making assumptions

Basically, simple explanation
RequestToProceed(time)
Will check if the time given in the parameter has passed in seconds since the last SUCCESSFUL request, to recreate a “cooldown”

Then you have topics and executors, that’s another story

Module Update: Fixed an issue, it should work now perfectly!

Please grab the new updated module

Module Update: RequestService now uses DateTime.now() instead of os.time()

This update allows users to add cooldowns that are not restricted by just seconds.
DateTime.now().UnixTimestamp and os.time() both use UTC time and are really similar, although os.time() does not utilize milliseconds

Grab the updated module!

Still don’t understand what the main function of my module does? Read this easy example of use which is self explanatory:

Yes, you can achieve what RequestService does in some more lines of code, but your code will get repetitive over time and unreadable. That’s why this is called a WRAPPER.
This is also my personal module which I use very frequently, so you can have my support on it.

1 Like

It’s not really useful If I can just do this rather than writing 20 lines of code

local DebounceTime = 3
local DC = false

script.Parent.MouseButton1Click:Connect(function()
if DC == false then
DC = true
task.wait(DebounceTime)
DC = false
end
end)

Also, For the Username thing you could do it easily to.

2 Likes

Please read the comments by @ UltraYummyChocolate before assuming this is just a module for simple static debounces. A much more effective use of this module would be to rate limit client’s requests to the server for performance sensitive tasks, and to prevent remote spam by exploiters. Your code, although simple and easy to use -would not be able to do this. Judging by the comments in this thread, a lot of people are assuming the term ‘cooldown’ is another way to talk about a local debounce, which isn’t always the case.

2 Likes

I have already explained like 3 times in a row. They don’t seem to understand yet :smiling_face_with_tear:

Roblox has already a warning feature telling when people are trying to spam remote events, but yes, this service can be used to
prevent remote event spam as well

In my case I use this module a lot, maybe that’s only my case? In my game you are able to create servers, they use datastores though, so it is important that people cannot spam create servers because datastore calls have limits

1 Like

Thanks! I’m going to use this for my upcoming game.

1 Like

I’ll go over everything you are saying
If you want a real cooldown, don’t use task.wait(), it will never be the real time you asked for, plus since it uses RunService.Heartbeat:Wait(), if the server lags the cooldown will yield even more

Stop making false assumptions about how my module works because it only uses DateTime.now() and compares it, thank you


local DebounceTime = 3
local DC = false

script.Parent.MouseButton1Click:Connect(function()
if DC == false then
DC = true
datastore:UpdateAsync() --Will yield until finished
task.wait(3)
DC = false --Your cooldown is not 3 because of yield calls
end
end)

Your code is fine, however, if we set debouncetime to 3, for example, and use yielding calls, it will not really be three but vary and be bigger.

You could do:

local DebounceTime = 3
local DC = false

script.Parent.MouseButton1Click:Connect(function()
if DC == false then
task.spawn(function()
 DC = true
 task.wait(DebounceTime)
 DC = false
end)
Datastore:UpdateAsync()
end
end)

Now this would be more similar, but still not realistic, as i explained how task.wait works.

And yes, my source code is easy to write, write it ten times in normal scripts now. You’ve got yourself some useless lines of code which, if you want to update, you’d need to go through all of the same code over and over to fix / change everything which you can convert to 1 line by using my service

Plus what are you saying about 20 lines of code?

“Why would I want to use ProfileService for all of my datastores?” This question has the same energy.

It’s called a wrapper for some reason, provide you with data globally through scripts and make your code cleaner in general because you don’t need to be repetitive

Why are you replying to this 12 days later ;_;

1 Like

Oh, I understand now. Sorry for the Misunderstanding. I’ll check out the Module.
Thanks again!

1 Like

We have brains, we know what a cooldown is. It’s not useful. It’s much easier to implement a denounce or cooldown. Stop forcing people to like your module.

@. lilmazen1234 doesn’t think so… He had doubts about the module, it’s ok if i reply to them

If you don’t want to use the module, you don’t have to announce it. Plus, my
1 line method is definitely way harder to use than all your cooldowns.

If my method is so hard then just use the brain you mentioned😪

1 Like