EasyNetwork - Creates remote functions/events for you so you don't have to!

Intro

Network.rbxm (10.6 KB)

Hey everyone, we built a decent shared network module for The Wild West, and I wanted to release it as a free resource for you guys.

This is a single module that you slap into ReplicatedStorage, and it just works. It mimics functionality of Remote Events/Functions, without you having to create them manually.

No more having to instance a new remote event or function, then hooking them up to scripts every time you want to use them in your game!

(It still creates them, but behind the scenes)

Small peak at what it looks like in a game:

API/Use

-- Server API
Network:BindFunctions(functions) 
Network:BindEvents(events)
Network:FireClient(client,name,params)
Network:FireAllClients(name,params)
Network:FireOtherClients(ignoreclient,name,params)
Network:FireOtherClientsWithinDistance(ignoreclient,name,distance,params)
Network:FireAllClientsWithinDistance(name,distance,position,params)
Network:LogTraffic(duration)
-- Client API
Network:BindEvents(events) 
Network:FireServer(name,params)
Network:InvokeServer(name,params)
Server API Examples
Network:BindFunctions(functions) 
--[[
Takes a table of function names and binds the function to it
--]]
Network:BindFunctions({
  MyFunction1 = function(client,...) 
    return true
  end,
  MyFunction2 = function(client,...) 
  return false 
  end
})
Network:BindEvents(events) 
--[[
Takes a table of event names and binds the event to it
--]]
Network:BindEvents({
  MyEvent = function(client,...) 
    -- Do thing here
  end,
  MyEvent2 = function(client,...) 
   -- Do thing here
  end
})
Network:FireClient(client,name,params) 
--[[
 Fires the event on the given client
--]]
Network:FireClient(game.Players.Player1,"MyEvent","Hello")
Network:FireAllClients(name,params) 
--[[
 Fires the event on all clients
--]]
Network:FireAllClients("MyEvent","Hello")
Network:FireOtherClients(ignoreclient,name,params) 
--[[
 Fires the event on all clients EXCEPT for ignoreclient. 
 Useful if you want the client to do an effect instantly, and then have the effect replicate to others
--]]
Network:FireOtherClients(game.Players.Player1,"MyEvent","Hello")
Network:FireOtherClientsWithinDistance(ignoreclient,name,distance,params) 
--[[
 Similar to FireOtherClients, 
 but only fires if their distance to ignoreclient <= distance
--]]
Network:FireOtherClientsWithinDistance(game.Players.Player1,"MyEvent",100,"Hello")
Network:FireAllClientsWithinDistance(name,distance,position,params) 
--[[
 Fires to all clients <= distance away from position
--]]
Network:FireAllClientsWithinDistance("MyEvent",100,Vector3.new(0,0,0),"Hello")
Network:LogTraffic(duration)
--[[
 Logs network traffic over duration, 
 then prints out debug info so you can see how much traffic is being sent
--]]
Network:LogTraffic(10)
Client API Examples
Network:BindEvents(events)
--[[
 Takes a table of event names and binds the event to it
--]]
Network:BindEvents({
  MyEvent = function(...) 
    -- Do thing here
  end,
  MyEvent2 = function(...) 
   -- Do thing here
  end
})
Network:FireServer(name,params)
--[[
 Fires the RemoteEvent attached to name, with the given parameters
--]]
Network:FireServer("MyEvent","Hello")
Network:InvokeServer(name,params)
--[[
 Invokes the RemoteFunction attached to name, with the given parameters
--]]
local returned = Network:InvokeServer("MyFunction1","Hello")
print("MyFunction1 returned: ", returned)

Why? (Example Included)

Remotes can be pretty tedious to set up, without any wrappers to do the work for you.

Here’s an example of a before/after for creating some simple remotes to interact between server and client

Example: Client will return a value from the server
then it will send a message to the server, which fires a message back to the client

Before:

Vid :nauseated_face:

-- Client

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

local ReturnThing = Remotes.ReturnThing
local DoThing = Remotes.DoThing
local DoThing2 = Remotes.DoThing2

local value = ReturnThing:InvokeServer()
print("Returned value: ",value)

DoThing:FireServer()

DoThing2.OnClientEvent:connect(function(message)
  print(message)
end)
-- Server

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

local ReturnThing = Remotes.ReturnThing
local DoThing = Remotes.DoThing
local DoThing2 = Remotes.DoThing2

ReturnThing.OnServerInvoke = function(client,...)
	return true
end

DoThing.OnServerEvent:connect(function(client,...)
	print("Do things")
    DoThing2:FireClient(client,"Hello") -- Makes client print "Hello"
end)

This doesn’t look too hard at first, but it can get tedious when you have a lot of remotes

This is all the work required with this module:

-- Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Network = ReplicatedStorage.Network

Network:BindEvents({
  DoThing2 = function(message) 
    print(message)
  end
})

local value = Network:InvokeServer("ReturnThing")
print("Returned value: ",value)

Network:FireServer("DoThing",...)
-- Server

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Network = require(ReplicatedStorage.Network)

Network:BindFunctions({
  ReturnThing = function(client,...)
    return true
  end
})
Network:BindEvents({
 DoThing = function(client,...)
   print("Do things")
   Network:FireClient(client,"DoThing2","Hello")
 end
})

Why not just use one Remote Event/Function?

Good question! @Tomarty goes over it in some more detail here ORE (One Remote Event) - #33 by Tomarty

But the quick answer is: Sending identification strings can be dangerous, depending on how frequently you send data. An identifier string can result in significantly more data being sent over the network for no reason, which can harm your game’s network performance.

This module doesn’t actually send any extra data over the network for event identification.
Instead, it creates or retrieves the remote event or function to use based on the name you pass in.

So multiple remotes still exist(as many as you register), but you don’t have to create them yourself and hook them up individually

Hope you guys like it! As always, feel free to post feedback or problems here

242 Likes

Seems pretty nifty! I’ll be sure to check it out some time in the future.

3 Likes

Thanks for sharing this module, it will definitely facilitate my work!

3 Likes

Now here comes another ease in roblox game programming, thanks for this plug-in.

7 Likes

Cool module!

This is interesting.
I’ve also made something for quick client-to-server communication which uses a single RemoteFunction.

I was unaware of the true cost of sending strings through the internet.
I’m guessing that Roblox takes care of assigning IDs to functions to reduce this cost.
So by making a bunch of RemoteFunctions you improve network performance?

I have yet to see any serious performance issues with the way I’m doing it now (probably because it hasn’t been used in anything of a large enough scale) but I will definitely consider making an updated version that takes this into account, or using this module.

4 Likes

No harm in switching to this if it fits your game’s workflow, should have everything you need.

Using one remote can be done, but it’s ill-advised due to the bandwidth overhead of sending string-identifiers over the network. This is mostly an issue when you send data frequently of course.

4 Likes

I get that for convenience’s sake it is nicer having one module to require for both server and client however you are potentially exposing server-side security flaws to exploiters.

1 Like

How? The client still can’t read any of your server souce code. Them being able to read the network module doesn’t really matter

4 Likes

Can you elaborate on how using remote events in place of remote functions is faster? I noticed in the module you wait at least .5 seconds if the remote event you spawned doesn’t fire right away. I 'm guessing it fires the event right after the event code is executed instead of yielding a certain amount that a remote function would wait for? I do like this though I was previously using identification strings using a single remote event/function, but now I am going to switch over to this. Thank you for sharing this with everyone.

2 Likes

Of course! I’m not sure why RemoteFunctions are slower, I just know they are so we use RemoteEvents to implement InvokeServer

3 Likes

I’ve been implementing this module into my game but I came across an issue. There doesn’t seem to be a way to disconnect any OnClientEvent/OnServerEvent event connections when using the BindEvents method. Do you think you could update the module to add a way to do this?

1 Like

I noticed this module doesn’t have an API for BindableEvents. Does your game not utilize Server to Server communication? How would you go about this if needed? Would you just implement them the standard way?

1 Like

Typically I have server modules communicate with each other just by requiring each other and calling public methods, with a little setmetatable trick to eliminate the stack overflow problem of circular dependencies. BindableEvents also work though, but I typically just use that if I’m wanting to listen for a specific event.

I didn’t include that kind of stuff in this module since it’s easy enough to add on your own and isn’t really relevant to what this module is for

1 Like

little setmetatable trick? mind enlightening us?

2 Likes

Does it clean out event connections to prevent memory leaks?

1 Like

Hi there~ @tyridge77

Thank you for sharing your creation with us, this is very interesting stuff.

Would you kindly create a link to Source code (preferably Github and/or Pastebin) for people who want to read the source but don’t have any access to a PC or maybe want to contribute to the source

1 Like

Does this not support :BindFunctions on the client?

1 Like

Nothing against this, but how much performance would this take away if I was doing a bunch of different remotes?
Because if it is creating them, it is using a small amount of processing power to replicate them.

1 Like

Really cool module, was super simple to implement into my workflow!

Only thing I noticed though was that there’s no out-of-the box support for Events that are only ‘Server=>Client’ and don’t have a Server-Sided Callback.

I was able to add support super easily thanks to the cleanliness of your code, but it would still be cool to see methods like “CreateEvents” & “CreateFunctions” for those events/functions that don’t require OnServerEvent/OnServerInvoke connections and are only intended to be used with FireClient/InvokeClient.

3 Likes

Sorry if this has already been fixed and I’m on an outdated version.

I had this error occur to me on one of my clients in-game:
ReplicatedStorage.MyGame.Network:593: invalid argument #1 to 'char' (invalid value) ReplicatedStorage.MyGame.Network, line 593 - function ToByteString ReplicatedStorage.MyGame.Network, line 645 - function InvokeServer

I found the root cause (pic below)
It looks like ResponseCounter is used to basically created a pseudo-unique id for tracking responses from the client, to the server, and it is only allowed to increment up, with a rolling modulo on 2^32 to presumably keep it in check from having an integer overflow and going into negative numbers.

Somehow one of my clients got a negative integer going into ToByteString. I assume either 2 ^32 rolled over into a negative int or ResponseCounter did. I’ve re-written the logic to something a little simpler to hopefully prevent this, won’t know for a few weeks of 0 issues, though.

1 Like