Eventio | Enhance your Events


Roblox | GitHub


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

  • Client → Server

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]
  • Server → Client → Server

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)
  • Custom event for a Class

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!

Thanks for reading!

23 Likes

This will be very useful for me! Thanks a lot :smiley:

1 Like

This is amazing! I’d definitely be using this in the near future! Great work!

1 Like

This is so useful, thanks!! :smiley:

2 Likes