I’ve worked on this for the past few hours - but here it is! A module that you can use to fire or connect remotes with one function (instead of using Invoke, Fire, etc.) as well as a method for creating and deleting them.
This is based on a module created by @Eqicness but written entirely from scratch by me.
To set this up:
Put the module wherever, make a folder inside of it called “Remotes”, and put all your remotes inside of that folder. Then you can just require the module and run the functions accordingly. This isn’t exactly for beginners, but is very easy to learn and manage!
If you guys have any suggestions be sure to leave them below.
The Script
local m = {}
local Remotes = script.Remotes
local RunService = game:GetService("RunService")
m.DebugMode = true;
function m:Create(RemoteString, Type)
if not RemoteString then warn("Cannot create remote: Name not provided.") return end
if not Type then warn("Cannot create remote: Type not provided.") return end
if type(RemoteString) ~= "string" then warn("Cannot create remote: Name must be a string.") return end
local Types = {["RemoteEvent"] = true; ["RemoteFunction"] = true; ["BindableEvent"] = true; ["BindableFunction"] = true;}
if not Types[Type] then warn("Cannot create remote: Invalid type.") return end
local Remote = Instance.new(Type)
if Remotes:FindFirstChild(RemoteString) then
if Remotes[RemoteString]:IsA(Type) then
Remote.Name = RemoteString.."-Copy"
warn("A " ..Type.. "with the name" ..RemoteString.. " already exists. A clone has been made titled "..RemoteString.."-Copy.")
end
else
Remote.Name = RemoteString
end
Remote.Parent = Remotes
if m.DebugMode then print(Type.." "..RemoteString.." created.") end
return Remote
end
function m:Delete(RemoteString)
local Remote = Remotes:FindFirstChild(RemoteString)
if not Remote then
warn("Remote " ..RemoteString.. "not found. Did you put it in the Remotes directory?")
return
end
if m.DebugMode then print(Remote.ClassName.." "..RemoteString.." deleted.") end
Remote:Destroy()
end
function m:Fire(RemoteString, ...)
local Remote = Remotes:FindFirstChild(RemoteString)
if not Remote then
warn("Remote " ..RemoteString.. "not found. Did you put it in the Remotes directory?")
return
end
if Remote:IsA("BindableEvent") then
local succ, err = pcall(function(...) Remote:Fire(...) end)
if not succ then warn(err) elseif m.DebugMode then print("BindableEvent Fired:"..RemoteString) end
elseif Remote:IsA("BindableFunction") then
local succ, err = pcall(function(...) Remote:Invoke(...) end)
if not succ then warn(err) elseif m.DebugMode then print("BindableFunction Fired:"..RemoteString) end
end
if RunService:IsServer() then
if Remote:IsA("RemoteEvent") then
local succ, err = pcall(function(...) Remote:FireClient(...) end)
if not succ then warn(err) else if m.DebugMode then print("RemoteEvent Fired On Server:"..RemoteString) end return succ end
elseif Remote:IsA("RemoteFunction") then
local succ, err = pcall(function(...)
return Remote:InvokeClient(...)
end)
if not succ then warn(err) else if m.DebugMode then print("RemoteFunction Fired On Server:"..RemoteString) end return succ end
end
else
if Remote:IsA("RemoteEvent") then
local succ, err = pcall(function(...) Remote:FireServer(...) end)
if not succ then warn(err) else if m.DebugMode then print("RemoteFunction Fired On Client:"..RemoteString) end return succ end
elseif Remote:IsA("RemoteFunction") then
local succ, err = pcall(function(...)
return Remote:InvokeServer(...)
end)
if not succ then warn(err) else if m.DebugMode then print("RemoteFunction Fired On Client:"..RemoteString) end return succ end
end
end
end
function m:FireAll(RemoteString, ...)
local Remote = Remotes:FindFirstChild(RemoteString)
if not Remote then
warn("Remote " ..RemoteString.. "not found. Did you put it in the Remotes directory?")
return
end
if RunService:IsClient() then
local succ,err = pcall(function(...) Remote:FireAllClients(...) end)
if not succ then warn(err) elseif m.DebugMode then print("RemoteEvent Fired All:"..RemoteString) end
end
end
function m:Connect(RemoteString, CallbackFunction)
local Remote = Remotes:FindFirstChild(RemoteString)
if not Remote then
warn("Remote " ..RemoteString.. "not found. Did you put it in the Remotes directory?")
return
end
if not CallbackFunction then
warn("No callback function provided for remote " ..RemoteString.. ".")
end
if Remote:IsA("BindableFunction") then
Remote.OnInvoke = CallbackFunction
if m.DebugMode then print("BindableFunction Connected:"..RemoteString) end
elseif Remote:IsA("BindableEvent") then
if m.DebugMode then print("BindableEvent Connected:"..RemoteString) end
return Remote.Event:Connect(CallbackFunction)
end
if RunService:IsServer() then
if Remote:IsA("RemoteEvent") then
if m.DebugMode then print("RemoteEvent Connected On Server:"..RemoteString) end
return Remote.OnServerEvent:Connect(CallbackFunction)
elseif Remote:IsA("RemoteFunction") then
Remote.OnServerInvoke = CallbackFunction
if m.DebugMode then print("RemoteFunction Connected On Server:"..RemoteString) end
end
else
if Remote:IsA("RemoteEvent") then
if m.DebugMode then print("RemoteEvent Connected On Client:"..RemoteString) end
return Remote.OnClientEvent:Connect(CallbackFunction)
elseif Remote:IsA("RemoteFunction") then
Remote.OnClientInvoke = CallbackFunction
if m.DebugMode then print("RemoteFunction Connected On Client:"..RemoteString) end
end
end
end
return m
This seems like a very unnecessary abstraction - why is this really better than directly firing remotes? Are there any other benefits that you can actually get out of this module?
I would be more interested if this did more than just provide a single way of firing and connecting to remotes; for example, the sockets resource.
This is better used in a complete framework rather than standalone. I, for example, can use it from any script in my framework to set up remotes rather than having to actually use a pointer to find a remote and fire it. It saves a lot of time when actually writing my scripts and turns 3-4 lines into 1 line of code. Not only that, this is protected and allows for debugging. To put it simply, it’s just less code for the user to have to write.
Except that there are an abundance of unnecessary lines to run, like doing extra if statements, etc. Theoretically, this slows down code. And it really isn’t necessarily 1 line, I mean you can just make 3 ines into 1 line:
if true then
return
end
--// Becomes //
if true then return end
Honestly, I don’t see any problem in doing the first one. It’s just unnecessary abstraction to be honest, also you don’t have to do findfirstchild if you know remotes exists.
Frameworks direct flow in a game. Typically, a good framework will already come equipped with networking methods as part of its set up process and so you can fit in with the API that it sets. They too remain clear on exactly what you’re doing by using differently named methods based on if you’re firing/invoking to the server or to the client.
It’s better to write more code and to be clear about what your code is doing, rather than focus on wanting less code and obscuring away that clarity. Even libraries that deal with remotes don’t consolidate all remote capabilities into single functions.
The example you gave Crundee is also somewhat biased? Not every game is going to create remotes in the script and they’ll be available in the DataModel. A more realistic comparison is finding a remote in a remotes folder and using the Create function of your module which are identical behaviours. Less lines of code does not == better necessarily.
The create function is an extra addition; the main purpose of the module are the :Fire(), :Connect(), and :FireAll() methods provided. if you could provide some constructive criticism (or examples as to how others may handle this sort of module), please do so.
The question I’m having is how this improves development workflow beyond being an unnecessary abstraction that affects readability. The constructive criticism is that this module should be doing more than just making an abstraction on a simple process that doesn’t need abstraction.
You can check out the following resources which handle networking if you want to get a better understanding of what I’m trying to say here: that the provided resources do more than just supply alternatives for remote calling.
Alternative for remotes with features for improving networking:
Framework that controls networking between source code: *
Libraries that do exactly what this resource does and more: **
* See the following framework-level methods: RegisterEvent, RegisterClientEvent, Fire, FireClient, FireAllClients, FireOtherClients, ConnectEvent, ConnectClientEvent and Service Client Table (accessible via documentation website).
** Regarding the Nevermore repository: while these do exactly what your resource does, they also do more. They also provide clarity as needed when you’re requiring them in your code.
First is to acknowledge that RemoteEvent and RemoteFunction items are separated, as they should be, because there’s no real reason to be consolidating the API. They aren’t the same and shouldn’t be treated the same way either, which is the implication of your resource. Instance: the Connect method (why is this even a method when you’re not using the self parameter?). You connect to events, but you don’t connect to RemoteFunctions - you define their invocation function.
The second thing is the extensibility they provide on top of that. See the modules prefixed by Promise. You’ll need to delve into Promises to get a better understanding of what they are and how they can help in the process of fetching remotes here, if that kind of a circumstance arises in your game (script creates a nonexistent remote that other scripts/LocalScripts require).
I made this module around a year ago now. Looks like you just added a bunch of debug lines, pcalls, compatibility for bindable events (even though it’s meant for replication?), and a method to create remotes via script instead of just putting them in a folder beforehand. I originally made this module simply because I was really tired of referencing remotes and typing :FireServer or :FireClient all the time. I just wanted something simple that went with my framework and was fast to use. The only way this module improves workflow is by getting rid of the need to reference remotes and having shorter, more unified methods. It’s in no way an elegant solution or something I’d release to the public.
Here's my original code:
local rs = game:GetService("RunService")
local m = {}
local function GetRemote(name)
local remote = m.Remotes[name]
if not remote then
warn("Remote", name, "not found. Did you add it to the Remotes folder?")
end
return remote
end
function m:Fire(name, ...)
local remote = GetRemote(name)
if remote then
if rs:IsServer() then
if remote:IsA("RemoteEvent") then
remote:FireClient(...)
elseif remote:IsA("RemoteFunction") then
return remote:InvokeClient(...)
end
else
if remote:IsA("RemoteEvent") then
remote:FireServer(...)
elseif remote:IsA("RemoteFunction") then
return remote:InvokeServer(...)
end
end
end
end
function m:FireAll(name, ...)
local remote = GetRemote(name)
if remote then
if rs:IsServer() then
if remote:IsA("RemoteEvent") then
remote:FireAllClients(...)
end
end
end
end
function m:Connect(name, callback)
local remote = GetRemote(name)
if remote then
local signal
if rs:IsServer() then
if remote:IsA("RemoteEvent") then
signal = remote.OnServerEvent
elseif remote:IsA("RemoteFunction") then
remote.OnServerInvoke = callback
end
else
if remote:IsA("RemoteEvent") then
signal = remote.OnClientEvent
elseif remote:IsA("RemoteFunction") then
remote.OnClientInvoke = callback
end
end
if signal then
return signal:Connect(callback)
end
end
end
return m
I will (most likely) make a more open method for firing remotes with different methods, most likely using a class system. I’ll definitely be redoing this.