EzRemote - Remote programming simplified

I made a basic module a while back for handling remotes without needing tons of different RemoteEvents/RemoteFunctions and maybe you can find a use for it or even incorporate it into your workflow.

You can download it: here


How it works:

-- Client
local Remotes = require(path.to.remotes)
Remotes.Fire("SayHello")

print(Remotes.Invoke("SayMyName"))

-- Server
local Remotes = require(path.to.remotes)
Remotes.Connect("SayHello", function(player, ...) -- arguments are also passed the same as FireServer
    print("Hello", player.Name)
end)

Remotes.SetCallback("SayMyName", function(player, ...)
    return player.Name
end)

and vise versa for the client


Downsides:
According to a few devforum posts, single remote structures are actually a bad idea. As a result, I only suggest using this if your a beginner or just lazy in general.
No InvokeClient support (this is probably an upside if you think about it)
You cannot disconnect events (not sure when you’d need this, just be aware of it)

Upsides:
Very easy to use
No creating a new remote when programming and having to move your hands and press a few buttons
Works practically the same as normal remotes
Depending on your implementation, can even be cleaner or more readable


Thanks for reading

5 Likes

Can’t you just create a RemoteEvent in your server script? I don’t think it’s a good trade-off to sacrifice organization and clean code in favor of saving a few seconds when you’re first setting up your game.

1 Like

isn’t this is just dumping functions to a table then call them when it’s fired

That is essentially what events are: a table whose keys are event names and values are callbacks.

I entirely agree, I’m just releasing this in case someone wants to use it

kind of ironic that we both made a similar module however mine is different in some ways:

  • instead of using the one pair of Remotes, users have the ability to create any amount of Remotes you want
  • has error handling / logging
  • uses Type Luau
Source Code
export type Remote = {
	Name: string,

	RemoteEvent: RemoteEvent,
	OnClientEvent: RBXScriptSignal,
	OnServerEvent: RBXScriptSignal,
	Events: Dictionary,
	FireAllClients: () -> (),
	FireClient: (Player) -> (),
	FireServer: () -> (),

	Wait: (Player?, string) -> (),

	RemoteFunction: RemoteFunction,
	-- OnClientInvoke = RemoteFunction.OnClientInvoke,
	-- OnServerInvoke = RemoteFunction.OnServerInvoke,
	Functions: Dictionary,
	InvokeClient: (Player) -> (),
	InvokeServer: () -> (),

	Connection: RBXScriptConnection?,
}

local RunService = game:GetService("RunService")

local RemoteAdded = Instance.new("BindableEvent")

local Remotes = {}

local function makeRemoteEvent(name: string): RemoteEvent
	local RemoteEvent = Instance.new("RemoteEvent")
	RemoteEvent.Name = name .. ".RE"
	return RemoteEvent
end

local function makeRemoteFunction(name: string): RemoteFunction
	local RemoteFunction = Instance.new("RemoteFunction")
	RemoteFunction.Name = name .. ".RF"
	return RemoteFunction
end

function Remotes.create(name: string): Remote
	if RunService:IsServer() then
		if not Remotes[name] then
			local RemoteEvent = makeRemoteEvent(name)

			local RemoteFunction = makeRemoteFunction(name)

			local Remote = {
				Name = name,

				RemoteEvent = RemoteEvent,
				OnServerEvent = RemoteEvent.OnServerEvent,
				Events = {},
				FireAllClients = function(...)
					RemoteEvent:FireAllClients(...)
				end,
				FireClient = function(player: Player, ...)
					RemoteEvent:FireClient(player, ...)
				end,

				Wait = function(player: Player?, event: string): string
					local result = table.pack(RemoteEvent.OnServerEvent:Wait())
					if player then
						if result[1] == player and result[2] == event then
							return unpack(result, 3)
						end
					else
						if result[2] == event then
							return unpack(result, 3)
						end
					end
				end,

				RemoteFunction = RemoteFunction,
				Functions = {},
				InvokeClient = function(player: Player, ...): any
					return RemoteFunction:InvokeClient(player, ...)
				end,
			}

			local functions = Remote.Functions
			function RemoteFunction.OnServerInvoke(player: Player, instruction: string, ...): any
				local callback = functions[instruction]
				if callback then
					local errorMessage, stacktrace
					local results = table.pack(xpcall(callback, function(err)
						errorMessage = err
						stacktrace = debug.traceback()
					end, player, ...))
					if not results[1] then
						error(string.format(
							"Error Message: %s.\nFrom Instruction %q with RemoteFunction %q from Player %q\nStack trace:%s",
							tostring(errorMessage),
							tostring(instruction),
							tostring(name),
							tostring(player),
							tostring(stacktrace)
						))
					end
					return unpack(results, 2)
				else
					warn(instruction, "is an Invalid Function for", name, "RemoteFunction")
				end
			end

			Remotes[name] = Remote

			RemoteEvent.Parent = script
			RemoteFunction.Parent = script

			script:SetAttribute(name, true)

			RemoteAdded:Fire()
		else
			warn("RemoteEvent", name, "already exists use Remotes.get instead")
		end
		return Remotes[name]
	else
		error("Can NOT call Remotes.create from Client")
	end
end

function Remotes.get(name: string): Remote
	script:WaitForChild(name .. ".RE")
	script:WaitForChild(name .. ".RF")
	while not Remotes[name] do
		RemoteAdded.Event:Wait()
	end
	return Remotes[name]
end

local function setEvents(Remote: Remote, newInstructions: Dictionary)
	local events = Remote.Events

	for instruction: string, callback in pairs(newInstructions) do
		events[instruction] = callback
	end
end

function Remotes.handleEvents(Remote: Remote, newInstructions: Dictionary)
	setEvents(Remote, newInstructions)
	if not Remote.Connection then
		local events = Remote.Events
		if RunService:IsClient() then
			Remote.Connection = Remote.OnClientEvent:Connect(function(instruction: string, ...)
				local callback = events[instruction]
				if callback then
					local errorMessage, stacktrace
					local success = xpcall(callback, function(err)
						errorMessage = err
						stacktrace = debug.traceback()
					end, ...)
					if not success then
						error(string.format(
							"Error Message: %s.\nFrom Instruction %q with RemoteEvent %q\nStack trace:%s",
							tostring(errorMessage),
							tostring(instruction),
							tostring(Remote.Name),
							tostring(stacktrace)
						))
					end
				else
					warn(instruction, "is an Invalid Event for", Remote.Name, " a Client RemoteEvent")
				end
			end)
		elseif RunService:IsServer() then
			Remote.Connection = Remote.OnServerEvent:Connect(function(player: Player, instruction: string, ...)
				local callback = events[instruction]
				if callback then
					local errorMessage, stacktrace
					local success = xpcall(callback, function(err)
						errorMessage = err
						stacktrace = debug.traceback()
					end, player, ...)
					if not success then
						error(string.format(
							"Error Message: %s.\nFrom Instruction %q with RemoteEvent %q from Player %q\nStack trace:%s",
							tostring(errorMessage),
							tostring(instruction),
							tostring(Remote.Name),
							tostring(player),
							tostring(stacktrace)
						))
					end
				else
					warn(instruction, "is an Invalid Event for", Remote.Name, "a Server RemoteEvent from Player", player)
				end
			end)
		end
	end
end

local function setFunctions(Remote: Remote, newInstructions: Dictionary)
	local functions = Remote.Functions

	for instruction: string, callback in pairs(newInstructions) do
		functions[instruction] = callback
	end
end

function Remotes.handleFunctions(Remote: Remote, newInstructions: Dictionary)
	setFunctions(Remote, newInstructions)
end

if RunService:IsClient() then
	local function _Create(name: string)
		local RemoteEvent = script:WaitForChild(name .. ".RE")

		local RemoteFunction = script:WaitForChild(name .. ".RF")

		local Remote = {
			Name = name,

			RemoteEvent = RemoteEvent,
			OnClientEvent = RemoteEvent.OnClientEvent,
			Events = {},
			FireServer = function(...)
				RemoteEvent:FireServer(...)
			end,

			Wait = function(event: string): string
				local result = table.pack(RemoteEvent.OnClientEvent:Wait())
				if result[1] == event then
					return unpack(result, 2)
				end
			end,

			RemoteFunction = RemoteFunction,
			Functions = {},
			InvokeServer = function(...): any
				return RemoteFunction:InvokeServer(...)
			end,
		}

		local functions = Remote.Functions
		function RemoteFunction.OnClientInvoke(instruction: string, ...): any
			local callback = functions[instruction]
			if callback then
				local errorMessage, stacktrace
				local results = table.pack(xpcall(callback, function(err)
					errorMessage = err
					stacktrace = debug.traceback()
				end, ...))
				if not results[1] then
					error(string.format(
						"Error Message: %s.\nFrom Instruction %q with RemoteFunction %q\nStack trace:%s",
						tostring(errorMessage),
						tostring(instruction),
						tostring(name),
						tostring(stacktrace)
					))
				end
				return unpack(results, 2)
			else
				warn(instruction, "is an Invalid Function for", name, "RemoteFunction")
			end
		end

		Remotes[name] = Remote

		RemoteAdded:Fire()
	end
	script.AttributeChanged:Connect(_Create)

	for name: string, _ in pairs(script:GetAttributes()) do
		_Create(name)
	end
end

return Remotes

compatible with Deferred Events

3 Likes