Fully Optimized RemoteFUNCTIONS? (Ultra simplified)

RemoteFunctions have an epic behavior, the sender yeilds until callback.

Wouldn’t it be nice to secure the remote, and prevent Invoke() forcibly, without a memory leak? I think I found a method, that does several things:

Put the RemoteFunction in PlayerGui, giving each player a privately secured communication method, no heckers spam your remote.

Immediately delete the RemoteFunction, preventing InvokeServer() completely, with logic that respects garbage collection, unspammable???

stresstestpls.rbxl (62.7 KB)

debug.setmemorycategory("Test")

local remote = script.RemoteFunction

--LocalScript's old code (spammable if you want to attempt breaking the script) is:
--script.Parent:WaitForChild("RemoteFunction"):InvokeServer("Random")

local function runNetwork(Player,signal,invokeData)
	if Player then
		if signal then 
			signal.OnServerInvoke = nil
			Player:LoadCharacter()
			task.wait() -- this wait is necessary for garbage collection
			signal:Destroy() signal = nil
			runNetwork(Player)
		else
			signal = remote:Clone()
			local scrip = signal.LocalScript
			scrip.Parent = Player.PlayerGui
			signal.Parent = Player.PlayerGui
			local connection = Player.AncestryChanged:Once(function() if not Player or Player and not Player.Parent then warn("Player exit") signal.OnServerInvoke = nil task.wait() -- this wait is necessary for garbage collection
			signal:Destroy() signal = nil end end)
			signal.OnServerInvoke = function(Player,...) scrip:Destroy() connection:Disconnect() runNetwork(Player,signal,...) print("activated") end
		end
	end
end

game.Players.PlayerAdded:Connect(function(Player) 
	runNetwork(Player)	
end)

Intended to make stress free networking the easiest…

ServerScriptService contains the localscript, which you can modify to spam just about any comms to the remote.

If you manage to break it, please share.

How would this prevent that, and what memory leak does this fix?

Why? You can just check who’s the sending player, this doesn’t stop exploiters from spamming it.

I don’t think you quite understand how garbage collection and connections work, and please don’t inline a bunch of code (that makes it pretty hard to read).

The memory leak being fixed is part of how fast the remote is deleting, immediately setting OnServerInvoke to nil and customizing the iteration’s cooldown. The intended behavior is to prevent hooks from happening by hiding or deleting the instance, comparatively, it’s always vulnerable to :InvokeServer().

the leak is here:

			signal.OnServerInvoke = nil
			Player:LoadCharacter()
			task.wait() -- this wait is necessary for garbage collection,
-- removing it presents extreme memory usage perceptually leaking over a long period of time, even if it may not entirely be a leak
			signal:Destroy() signal = nil
			runNetwork(Player)

What hook are you preventing? That callback is on the server.
Hiding Instances also doesn’t prevent exploiters from tampering with them, they have several methods to get them.

Additionally, what do you mean by “this wait is necessary for garbage collection”? Values are marked for GC when they go out of scope.

when InvokeServer() calls, the RemoteFunction.OnServerInvoke is immediately set to nil, but it doesn’t always clean it all up in one heartbeat. A hacker can spam it fast enough to invoke within the same iteration it is set to nil, where it gets entirely deleted instead, that gets prevented.

The purpose of PlayerGui is to keep the remote private, only the server should be able to access a PlayerGui folder, but not other players.

remove the garbage collector task.wait() and check F9, server memory, search “test”, compare the bar graphs!

spamming it without the wait stacks memory in a strange pattern, 2 iterations up, 1 backwards… the footprint goes up by x2 or something idk

You haven’t answered the question: Why?

What is the point of keeping it hidden from other players and going through this overcomplicated process of setting callbacks, destroying, etc.?

If a RemoteFunction is in ReplicatedStorage, any player can Invoke it. Sanity checks are simple, but the function is always going to call without delay if we don’t actually remove the Instance. Without anything to Invoke, there’s 0 potential for programmatical spam and reassured, no callback runs 2x

MAINLY, the remote deletes after ANY invocation, any hacker can send 1 message and screw it up.
in reducing code, i opted to utilize how replication is handled client - server

What delay, and what’s the problem with the simple “return if something isn’t what we expect”?

You might think you’ve reduced “programmatical spam”, but really you’ve just complicated RemoteFunctions unnecessarily, increasing memory usage and making your code less efficient.

remoteFunction.OnServerInvoke = function() end

when a hacker duplicates the same localscript over and over, this invokes extremely fast.

adding the players to a cooldown table is avoided because we can just delete the thing

RemoteEvents don’t reliably work EVERY time the client fires, but here, im trying to emulate its on-demand behavior while also yielding automatically.

…Why?
There’s a reason why things are standardized, why not just add them to a cooldown table? It avoids needing to create remotes for every single player, setting callbacks over and over, and janky garbage collection.

Im intending to scale the code up into a network module that handles more things, mostly for crucial network interactions that should work with single inputs. (like wanting to press the spawn button 1 time and it just works, practically disregarding how laggy you are completely)
RemoteEvents don’t yield automatically, and im straight up trying to avoid them completely

That doesn’t explain why though?

I don’t know how to tell you this… but that’s because remote events are for events, not requests.

Your edit still hasn’t explained why you aren’t just using RemoteFunctions normally and using return when parameters are wrong :sob:

Recursive networking is the goal, idk. Only 1 replication happens on the client, 1 replication giving them an event listener that only they can communicate with, without tampering. This is especially good at a simple task like a spawn button that is practically unhackable, also easy to obtain

I dont understand why the “exploiter” can’t be kicked from the game or have the client temporarily hide the remotefunction on their end.

What is this approach specifically designed to prevent other than the harassment of Remotes?

I don’t get what you mean by this, what are you doing that can’t be achieved with a regular RemoteFunction?

No, this wouldn’t make it less exploitable.

Remote Functions are not the best idea for networking. It takes a round trip for you to get the data you are looking for which is suboptimal, this means you experience twice the latency that you would normally experience if you are trying to do game state syncing (call takes one trip to arrive at the server, response comes one trip later). You would do this with remote events or unreliable remote events instead (data is sent every tick, you recieve the latest data after one trip).

Remote functions are generally to be reserved for asynchronous behaviors, such as completing or verifying a purchase, or interacting with a server-side system in a scenario where minor latency is not particularly a point of a concern, so in any case where they have the opportunity to be “spammed” it shouldn’t happen unless someone is doing something such as auto-clicking the “Buy” button in a store.

1 Like

This does exactly what a RemoteEvent would normally do, but the behavior favors waiting for the client network to be ok, send the packet, and continue.

A situation where 1 click needs to register on server, without a miss

this skips having to check for packet loss

Roblox uses RakNet, which is already reliable networking.
Unless you explicitly use UnreliableRemoteEvents, all events are reliable, and packet loss isn’t an issue.

1 Like

if im not mistaken doesn’t RemoteEvent:FireServer() skip during a lag spike?
i remember pressing menu buttons and the event wasn’t firing every time.