Each effect its own Remote

Is it bad practice to assign each effect/client-based module its own remote for easy server-to-client communication? I tried various network modules but they seem to rack up my ping UP TO 4000MS.
I thought of giving each effect its own remote like this.

_G._connectedNetworks = {} :: {RemoteEvent}

for _,v in game:GetService("ReplicatedFirst")._Modules:GetChildren() do
	if v:IsA("ModuleScript") then
		local p = Instance.new("UnreliableRemoteEvent")
		p.Name = v.Name
		p.Parent = ReplicatedStorage
		_G._connectedNetworks[v.Name] = p
	end
end

-- e.g

_G._connectedNetworks.a4a_doMessage:FireAllClients("Hi", "", false)
for _,v in ReplicatedStorage:GetChildren() do
	if v:IsA("UnreliableRemoteEvent") then
		print(v:GetFullName())
		v.OnClientEvent:Connect(function(...)
			print(v:GetFullName())
			if type(_G._clientEffects[v.Name]) == "table" then
				_G._clientEffects[v.Name][...]()
				return
			end
			_G._clientEffects[v.Name](...)
		end)
	end
end

Is it any better bandwith-wise?

This is actually worse. It’s better to funnel calls through one remote that way Roblox batches the calls together. This is what most networking libraries do alongside other optimizations. Additionally, don’t use _G in your scripts as it is very slow.

If you need help finding a networking library I personally recommend Red 2.0, it’s what I use and I find it super efficient yet intuitive and easy to use.

Also I’m not sure if bandwidth usage is directly correlated to your ping rather than your connection to the server, make sure you’re looking at the correct network sending/receiving bars when considering your bandwidth usage. That aside, if you are experiencing high bandwidth usage it could be due to unnecessary calls you’re making in your code or an oversight.

3 Likes

Actually grouping is better network wise.

local net = Instance.new("UnreliableRemoteEvent")
net.Name = "EffectSignal"
net.Parent = game.ReplicatedStorage

_G._clientEffects = {}

net.OnClientEvent:Connect(function(effect, ...)
	local fx = _G._clientEffects[effect]
	if type(fx) == "table" then
		fx[1](...)
	else
		fx(...)
	end
end)

function sendEffect(effect, ...)
	net:FireAllClients(effect, ...)
end

sendEffect("a4a_doMessage", "Hi", "", false)

4000ms ping sounds like you’re flooding the network; UnreliableRemoteEvent uses bandwidth and firing many per second can overload Roblox’s system causing drops and backlog.

I did both and yet I still suffer from high bandwidths especially from activating a gear which sends two different remotes to clients.

I was just following your script…
You’ll need to remove things to test where it’s coming from. Possible could be the use of the global.

It’s very bad practice, the more remote calls the more overhead you need, and the more overhead, the more data you need to send over to client through network

What you should do is use only numbers to communicate between server and client, and compress them into the buffers, also you should send only neccesary data and don’t use _G as it’s unoptimized, modules are better solution to this

2 Likes

I’ve been thinking into getting into Buffer lately but it requires types and you can’t pass nil arguments and sometimes events would just “cancel”.

Though on ping issue, only I seem to be in issue, other testers don’t seem to run into any lag.

How is this?

local a = {}

local g = Instance.new("UnreliableRemoteEvent", game:GetService("ReplicatedStorage"))

for _,v in {"a4a_d", "a4a_c", "a4a_p"} do
	a[v] = {}
	
	local n = a[v]
	
	function n:FireAllClients(...)
		g:FireAllClients(v, ...)
	end
	
	function n:FireClient(Player:Player ,...)
		g:FireClient(Player, ...)
	end
end

_G._connectNetworks = a

a.a4a_d:FireAllClients("test")
game:WaitForChild("ReplicatedStorage"):WaitForChild("UnreliableRemoteEvent").OnClientEvent:Connect(function(...)
	print(...)
end)

You shouldn’t use _G for this set up. Check out this old post for a little more context - Is it ever ok to use _G? - #2 by RuizuKun_Dev.

One of the key takeaways is that _G is less performant than other alternatives, measured to be about 2.4x slower (source is icy_oasis, 2023). But beyond performance, _G introduces tighter coupling and hidden dependencies between scripts, which can make your code harder to debug and scale over time.

Modules are now the industry standard because they offer better structure, encapsulation, and tools support like autocomplete and IntelliSense.

In your case, you could simply turn this into a ModuleScript that returns the a table, then require it wherever it’s needed instead of relying on _G.

2 Likes

If you intend for all events to have the first argument as the event name (v), both methods must follow that pattern.

local a = {}
local g = Instance.new("UnreliableRemoteEvent", game:GetService("ReplicatedStorage"))

for _,v in {"a4a_d", "a4a_c", "a4a_p"} do
	a[v] = {}
	local n = a[v]

	function n:FireAllClients(...)
		g:FireAllClients(v, ...)
	end

	function n:FireClient(p, ...)
		g:FireClient(p, v, ...)
	end
end

_G._connectNetworks = a

a.a4a_d:FireAllClients("test")
game:GetService("ReplicatedStorage"):WaitForChild("UnreliableRemoteEvent").OnClientEvent:Connect(function(...)
	print(...)
end)

Now both .FireAllClients() and .FireClient() send v as the first parameter, keeping the event parsing logic consistent on the client.

I showed you a way to make your _G global a module. Considering your original issue and the fact it persists, have you tried or at least tested that method?

The last script was following your script. This isn’t how I like to do things myself. I use a more top down method when programing like this…

local rs = game:GetService("ReplicatedStorage")
local g = Instance.new("UnreliableRemoteEvent", rs)
local a = {}

for _, v in {"a4a_d", "a4a_c", "a4a_p"} do
	a[v] = {}
	local n = a[v]

	function n:FireAllClients(...)
		g:FireAllClients(v, ...)
	end

	function n:FireClient(p, ...)
		g:FireClient(p, v, ...)
	end
end

_G._connectNetworks = a
a.a4a_d:FireAllClients("test")

g.OnClientEvent:Connect(function(...)
	print(...)
end)

Things are defined once and used accordingly. I feel it’s more efficient, less error-prone, and less wordy for a cleaner-looking script overall.

1 Like

_G is unavoidable when working on a group project with spaghetti code and the owner refuses to fix their code or even undos every fix you do

but generally, in my own projects i just create a module script in replicatedstorage to hold global functions, it does the trick

The reason I intentionally avoided modules is because I thought it would create a new remote everytime it is called, but that wasn’t the case, so I’m just gonna use them.


image

2 Likes

FYI, modulescripts only run once in order to get that return value, and then other scripts just fetch that already-calculated return value.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.