Introduction
When was the last time you used a RemoteEvent? Probably not too long ago, since they play a huge role in Roblox game development. However, script communications tend to be overcomplicated.
In Roblox, there are four instances which allow for scripts to communicate:
- RemoteEvent
- BindableEvent
- BindableFunction
- RemoteFunction
To use any of these, you first need to create an instance you need in some way or another, find the instance from both scripts and only then establish the connection using repetitive phrases.
That’s quite a lot of unneeded complexity, and in big projects you may only wish it could have been simpler.
Eventio – Enhance your Events
Eventio is a Networking Library which was made to enhance your experience with Events & overall script communications! It introduces four new classes: Signal, RemoteSignal, Invoke and RemoteInvoke which act as wrappers for the original instances.
So why should you use Eventio?
-
Simple gathering – you can get any object you want with only its name!
-
Easier usage – forget about
:Connect()
ing to an event with any kind of.Event
signal or thinking whenever to use:FireServer()
or:FireClient()
– only:Fire()
exists now! -
Smart solutions – Bindable & RemoteFunctions Yield the code and may Error if something happens on the other side. That is especially bad for situations like
Server => Client => Server
, since that way Server is under a threat of erroring or infinite yield.
To prevent both of the problems, Invokes & RemoteInvokes return Promises! -
Extended functionality – Added many new methods such as
:Once()
and:ConnectCall()
which were made to simplify your life! -
Object-orientated structure – Eventio creates classes which are intuitive as working with them is just like working with instances. Additionaly, this allows you to, for example, create custom events for your own classes!
API
Signal
-
.new(Data: string | nil | BindableEvent) -> Signal
-
.Is(Anything: any) -> boolean
-
.Name: string | nil
-
.ClassName: string = "Signal"
-
:Fire(...) -> void
-
:Wait() -> (...) [yielding]
-
:Connect(Callback: (...) -> void) -> RBXScriptConnection
-
:ConnectCall(Callback: (...Args) -> void, ...Args) -> RBXScriptConnection
– Passes arguments followed by the callback rather than the ones that came with the event -
:Once(Callback: (...) -> void) -> RBXScriptConnection
– One time:Connect()
-
:OnceCall(Callback: (...Args) -> void, ...Args) -> RBXScriptConnection
-
:DisconnectAll() -> void
– Disconnect all connections gathered on the current Signal, doesn’t affect Signals made on other scripts which lead to the same BindableEvent -
:Destroy() -> void
– Destroys the instance associated with the Signal, makes the Signal unusable
RemoteSignal
-
.new(Data: string | RemoteEvent) -> RemoteSignal
-
.Is(Anything: any) -> boolean
-
.Name: string | nil
-
.ClassName: string = "RemoteSignal"
-
:Fire(Player: Player, ...) -> void [server-side]
-
:Fire(...) -> void [client-side]
-
:FireAll(...) -> void [server-side]
-
:FireExcept(Player: Player, ...) -> void [server-side]
-
:FireChosen({Player}, ...) -> void [server-side]
-
:Wait() -> (...) [yielding]
-
:Connect(Callback: (...) -> void) -> RBXScriptConnection
-
:ConnectCall(Callback: (...Args) -> void, ...Args) -> RBXScriptConnection
– Passes arguments followed by the callback rather than the ones that came with the event -
:Once(Callback: (...) -> void) -> RBXScriptConnection
– One time:Connect()
-
:OnceCall(Callback: (...Args) -> void, ...Args) -> RBXScriptConnection
-
:DisconnectAll() -> void
– Disconnect all connections gathered on the current RemoteSignal, doesn’t affect RemoteSignals made on other scripts which lead to the same RemoteEvent -
:Destroy() -> void
– Destroys the instance associated with the RemoteSignal, makes the Signal unusable
Invoke
-
.new(Data: string | nil | BindableFunction) -> Invoke
-
.Is(Anything: any) -> boolean
-
.Name: string | nil
-
.ClassName: string = "Invoke"
-
:Invoke(...) -> Promise
-
:InvokeAsync(...) -> (...) [yielding]
– If you prefer yielding -
:Connect(Callback: (...) -> (...)) -> void
– Direct replacement for .OnInvoke -
:Destroy() -> void
RemoteInvoke
-
.new(Data: string | RemoteFunction) -> RemoteInvoke
-
.Is(Anything: any) -> boolean
-
.Name: string | nil
-
.ClassName: string = "RemoteInvoke"
-
:Invoke(Player: Player, ...) -> Promise [server-side]
-
:Invoke(...) -> Promise [client-side]
-
:InvokeAsync(Player: Player, ...) -> (...) [server-side] [yielding] [not recommended!]
-
:InvokeAsync(...) -> (...) [client-side] [yielding]
-
:InvokeAll(...) -> {Player: Promise} [server-side]
-
:InvokeExcept(ExceptionPlayer: Player, ...) -> {Player: Promise} [server-side]
-
:InvokeChosen({Player}, ...) -> {Player: Promise} [server-side]
-
:Connect(Callback: (...) -> (...)) -> void
– Direct replacement for .OnServerInvoke & .OnClientInvoke -
:Destroy() -> void
Code Examples
Let’s create a simple RemoteSignal which will receive text from Client and print it on Server:
Server:
local Eventio = require(game.ReplicatedStorage.Eventio) --// Get the library
local myRemote = Eventio.RemoteSignal.new("myRemote") --// Get "myRemote"
myRemote:Connect(print) --// Just connect the callback without indexing .OnServerEvent!
Client:
local Eventio = require(game.ReplicatedStorage.Eventio)
local myRemote = Eventio.RemoteSignal.new("myRemote") --// Eventio makes sure to WaitForChild for you
myRemote:Fire("Hi from client!") --> PlayerName Hi from client! [server-side]
How about something risky? Let’s make Server ask Client about something and wait for the response: (Don’t try this at home without Promises!)
Client:
local Eventio = require(game.ReplicatedStorage.Eventio)
local askForOpinion = Eventio.RemoteInvoke.new("askForOpinion")
askForOpinion:Connect(function(subject) --// Invokes use the same :Connect() style
local mood = math.random(1, 3) --// What mood are we in today?
if mood == 1 then --// Great mood!
return "I think that " .. subject .. " are nice!"
elseif mood == 2 then --// Meh mood
return "I don't like ".. subject .. "."
elseif mood == 3 then --// Really bad mood
error("No surveys today! I am in a bad mood!!")
end
end)
Server:
local Eventio = require(game.ReplicatedStorage.Eventio)
local askForOpinion = Eventio.RemoteInvoke.new("askForOpinion")
game.Players.PlayerAdded:Connect(function(player) --// A player joined!
player.CharacterAdded:Wait() --// A small delay to make sure they loaded!
askForOpinion:Invoke(player, "Apples") --// What do they think about apples?
:Then(function(opinion) --// Player said their opinion successfully!
print(player.Name .. " said: " .. opinion)
end)
:Catch(function(err) --// Catching errors!
warn("Oh no! Glad we're using promises so that server is safe!")
end)
print("We're now waiting for player's response..") --// Promises don't yield code!
end)
Object-Orientated Programming is something many scripters use on Roblox! Let’s create a simple cat class and assign it a custom event:
ModuleScript
local Cat = {} --// Create a Cat class!
Cat.__index = Cat
local Eventio = require(game.ReplicatedStorage.Eventio)
function Cat.new(Name) --// Cat constructor
local self = setmetatable({}, Cat) --// Make an empty table and tell it that it's a cat now
self.Name = Name --// Name our cat
self.Meowed = Eventio.Signal.new() --// Create a Meowed event!
--// Don't pass the name of the signal since we will get it from our cat instead!
return self
end
function Cat:Meow()
print("MEEEOOOOOOW") --// Translates to "FOOOOOOOD"
self.Meowed:Fire() --// Notify everyone that we meowed successfully
end
return Cat
Script
local Cat = require(game.ReplicatedStorage.Cat) --// Get the Cat ModuleScript we made
local Luna = Cat.new("Luna") --// Create a Cat named Luna!
Luna.Meowed:ConnectCall(print, "Luna, please, I gave you food 2 minutes ago!..")
Luna:Meow() --> Luna, please, I gave you food 2 minutes ago!..
Credits
Eventio relies on Promise implementation by evaera:
Check it out to get more information on Promises and why they are so cool!
Note: Promises in Eventio were changed a bit to support Eventio’s PascalCase style.
Send Feedback
Eventio is still in development so feedback is highly appreciated! Feel free to report any bugs you may encounter and suggest new features!