Managing Roblox Remote Events and Remote Functions

Recently, I realized I could mass spam my remote from my client to claim my daily reward multiple times in fraction of seconds before the server updates, that’s when I learned about remote spamming. That’s why today I’ll be showing you how to handle this without having to copy paste a code that checks if the player fired a remote over and over for each remotes.

What We’ll Cover

  • Handling the RemoteFunctions/ RemoteEvents requests of player
  • Set call backs limiter per player preventing Abuse.
    Returning multiple values from the server to the client.
    Using table.unpack to handle variable arguments.

Why Is This Useful?

In multiplayer games, it’s super important to prevent players from spamming remote calls. These can overload the server or cause unexpected behavior. By limiting how often each player can fire a callback, we can ensure the server stays performant and responsive.

The Code

Here is the completed script for handling multiple remote callbacks in a module script:

local Remotes = game:GetService("ReplicatedStorage").Remotes

local Calls = {}

function ListenToRemote(RemoteName:string,Callback)
	local Remote = Remotes:FindFirstChild(RemoteName)
	
	if not Remote then
		warn("Could not find remote:",RemoteName)
		return
	end

	if Remote:IsA("RemoteFunction") then
		Remote.OnServerInvoke = function(plr,...)
			local Id = plr.UserId


			if not Calls[Id] then
				Calls[Id] = true
			else
				print("Returning, callback limit exceeded")
				return
			end

			local ReturnedValues = {Callback(plr,...)}

			Calls[Id] = nil

			return table.unpack(ReturnedValues)
		end
	elseif Remote:IsA("RemoteEvent") then
		Remote.OnServerEvent:Connect(function(plr,...)
			local Id = plr.UserId
			
			if not Calls[Id] then
				Calls[Id] = true
			else
				print("Returning, callback limit exceeded")
				return
			end

			Callback(plr,...)

			Calls[Id] = nil
		end)
	end
end

return ListenToRemote

How It Works

  1. Getting the Remote: We first retrieve the remote object using game:GetService("ReplicatedStorage").Remotes. This is assuming the remotes are stored under ReplicatedStorage in a folder called Remotes.
  2. Handling RemoteFunction:
    • RemoteFunction enables synchronous calls, which mean the server waits for the response from the client.
  • The OnServerInvoke event is used to handle the player’s requests. In such a way, when a player calls this remote, the server will execute the callback and return the results back to the client.
    • In order for one player to not be able to trigger multiple callbacks at once, use a Calls table, which keeps track of player requests by UserId.
  1. Handling RemoteEvent:
  • RemoteEvent is asynchronous, meaning the server does not wait for a response from the client. The event handler is fired whenever the client calls the remote.
  • We use the OnServerEvent event to connect a function that executes the callback logic. Like with RemoteFunction, we block multiple requests from the same player at the same time.
  1. Returning Values:
  • For RemoteFunction, we allow the callback to return multiple values, using the table.unpack function to return the values correctly from the table returned by the callback.
  • For RemoteEvent, we don’t need to return anything, but we still execute the callback logic.
  1. Callback Limiting:
  • The Calls table keeps track of whether the player has already called a remote. If he then makes another call before processing has finished, we block it by checking up on Calls[Id] and output a message.

How to Use It

You can use this system with any remote function or event you may have in your game. Here’s how it is set up:

  1. Create a RemoteFunction or RemoteEvent: In ReplicatedStorage, create a RemoteFunction or a RemoteEvent, then parent it to the Remotes folder.
  2. Listen for the Remote: The ListenToRemote function should be called from within a server script. Here’s an example:
local ListenToRemote = require(game.ServerScriptService:WaitForChild("ListenToRemote"))

-- Configure a RemoteFunction or RemoteEvent
ListenToRemote("MyRemoteFunction", function(plr, arg1, arg2)
    -- Logic handling the callback
    print(plr.Name, "called with", arg1, arg2)

-- Return multiple values
    return "Success", arg1 + arg2
end)

ListenToRemote("MyRemoteEvent", function(plr, arg1)
    -- Handle the callback logic
    print(plr.Name, "triggered the event with", arg1)
end)

Testing the Code

To test the system:

  1. Create a RemoteFunction or RemoteEvent in ReplicatedStorage under Remotes.
  2. Call the remote from a LocalScript in the client and observe the server behavior.

For instance, invoking a RemoteFunction using a LocalScript:

local RemoteFunction = game.ReplicatedStorage.Remotes:WaitForChild("MyRemoteFunction")

local result1, result2 = RemoteFunction:InvokeServer(5, 10)
print(result1, result2)  -- Should print: "Success", 15

And for RemoteEvent:

local RemoteEvent = game.ReplicatedStorage.Remotes:WaitForChild("MyRemoteEvent")

RemoteEvent:FireServer(42)

Server Checks

Make sure to verify the sent data by the client, because exploiters can send whatever they want.
So let’s say we’re making a remote to buy an item in the shop.

  1. Verify the argument with typeof().
  2. Verify if the ItemName exists in our possible items to buy.
local ListenToRemote = require(game.ServerScriptService:WaitForChild("ListenToRemote"))

local Items = {
    Banana = 5,
    Apple = 3,
    Juice = 4
}

ListenToRemote("BuyItem", function(plr, ItemName:string)
    if typeof(ItemName) ~= "string" then return end -- checks if value is a string
    if not Items[ItemName] then return end -- checks of item is in the shop
    plr.leaderstats.Money -= Items[ItemName] -- removes the player money
end)

For more information on this you can check this page from the Roblox API

Conclusion

This system allows you to handle remote events and functions effectively while limiting how often players can trigger them. By using RemoteFunction and RemoteEvent properly.
If I’m doing anything wrong, please tell me I’ll update my code.

8 Likes

I challenge you to set

RemoteFunction.OnServerInvoke = function() 
RemoteFunction:Destroy() RemoteFunction = nil 
end

Placing a RemoteFunction in the PlayerGui folder with this behavior, intendedly removes the possibility for multiple packets to be spammed at the same time removes the possibility for players to share RemoteFunctions, and awesomely, we could have players request something, where they absolutely need to wait for a connection, but the code automatically gets the stuff done without checking for signals all the time. It seems like there’s always a chance for the RemoteFunction to get 2 callbacks at the same time, i.e running a coroutine and using something like:

local spam1,spam2 = 0,0
coroutine.wrap(function()     
    while true do
        spam2 += 1
        RemoteFunction:InvokeServer()
        if spam2 == 512 then
            spam2 = 0
            task.wait()
        end
    end
end)()
while true do
    spam1 += 1
    RemoteFunction:InvokeServer()
    if spam1 == 512 then
        spam1 = 0
        task.wait()
    end
end

if it was a hacker using injected stuff kinda like using the command bar, it could fire twice without being removed from the server, but then the client hangs forever waiting on a response or something else, idk honestly.

2 Likes

I tried my own system by firing multiple times with different coroutines and it fired on time on the server, the other calls were ignored printing: returning, callback limit exceeded

1 Like

If you spawn a task, it can keep client from yielding, there’s a way to cut the task using replication checking, surely!