Here I am publishing my ModuleScript called “SafeFunctions”, showing what its do and more!
SafeFunctions is a ModuleScript created to offer a safe communication between Clients and Server, simulating a “new version” of a RemoteFunction. Generally we always avoid use the “InvokeClient” method, but when working with this module, it is not more a possible game-breaker!
Method:
This ModuleScript uses a RemoteEvent (internally called of “DataTransfer”) to send data between clients and the server. For example,if the server invokes a client, the server-side part of module will send a signal to client-side part of module with the ‘name’ of a “pseudo-RemoteFunction” and some parameters (it works as a “InvokeClient”); The client must bind a function in client-side part of module, and when client receive the signal from server, its will search for a binded function with the ‘name’ parameter, if it exists then its call the function and send a signal to server-side of module with parameters (which will be interpreted as a “return” from client-side); At end, the “return” from client is returned to the caller of the “InvokeClient” script.
Cool module. If you plan on updating it in the future, it might be worth it to optimize the function names. From what I’ve heard from others, sending an entire function name string through a single RemoteEvent is less efficient than just having multiple RemoteEvents for each function, since Roblox will optimize each RemoteEvent for you. Maybe have it use some sort of enum system instead?
I won’t update as often, but at some days ago I was thinking about the performance of RemoteEvent, and I decided to maintain one RemoteEvent to entire module for while, because I always think that with less objects is better. But someday I may test this into a benchmark and change the module to improve his effectiveness
This module only simulates a common RemoteFunction with RemoteEvents, preventing infinite-yielding problems. Out of this, is your work to filter this RemoteFunction just like any other common RemoteEvent, because its impossible to the module do this automatically for all systems.
you are just doing more work when you can do the same thing with my method (sorry for self advertising) of securing remotefunctions which dosent require any remoteevents
Your method only calls InvokeClient into a different thread and returning if not reached the timeout, what is potentially dangerous yet because this InvokeClient’s call can yield forever the thread of task.spawn. This module does it by another way, which you can open it to see how.
InvokeClient stops an execution of a function until the client invoked return something to server. If it doesn’t return (like when the player leaves the game during invoke) the server will await forever causing a potentially performance issue in your game. I’ve create this module to try resolve this problem
The thread created with task.spawn() doesn’t depend of main function to be cancelled, which means sub-thread can continue executing (in this case yielding) even if main function returned or no.
what do you mean? the spawned function dosent return no or anything. the main thread has a timeout but will be waiting for the variable to be changed by the subthread.
local InvokerSignal = Instance.new("BindableEvent") -- Tested PureSignal vs. Bindable, same results.
local function TimedInvoke(Timeout, Remote, Target, ...)
assert(Remote and Remote:IsA("RemoteFunction"))
assert(Target and Target:IsA("Player"))
local Timeout = Timeout or 10
local Wrapper
Wrapper = task.spawn(function(...)
local Yielded = 0
local Results
local Invoked = task.spawn(function(...)
_, Results = pcall(Remote.InvokeClient, Remote, Target, ...)
end, ...)
while (not Results) do
if Yielded >= Timeout then
coroutine.close(Invoked)
break
end
Yielded += task.wait()
end
InvokerSignal:Fire(Results)
task.defer(coroutine.close, Wrapper);
end, ...)
return InvokerSignal.Event:Wait()
end
That’s all. That’s all it takes.
Use:
local Remote = ...; -- RemoteFunction here.
-- Assumes Player is a Player.
-- 10 Seconds is default, but you can customize it.
local Result = TimedInvoke(10, Remote, Player, Args)
-- Will either timeout and return nil or return the result of InvokeClient.
This will kill the threads if they exceed the yield time limit.
This kills the Wrapper thread after execution as well.
Also
@commitblue, your implementation will leave suspended threads running, taking up server memory over time, as well as performance. It’s a memory leak and it’s not ideal.
This is what OP was trying to tell you.