From bd6467b0d2abbd30ea40f003525e7d9f7a941cf4 Mon Sep 17 00:00:00 2001 From: jackdotink Date: Wed, 20 Dec 2023 14:44:11 -0600 Subject: [PATCH] unreliable events and version bump --- docs/2.0/Red.md | 6 +- docs/guide/events/declaring.md | 17 ++- lib/Event/Client.luau | 12 +- lib/Event/Server.luau | 36 +++++- lib/Event/init.luau | 25 +++- lib/SharedEvent.luau | 217 --------------------------------- wally.toml | 2 +- 7 files changed, 83 insertions(+), 232 deletions(-) delete mode 100644 lib/SharedEvent.luau diff --git a/docs/2.0/Red.md b/docs/2.0/Red.md index c6dd2ca..46efcc9 100644 --- a/docs/2.0/Red.md +++ b/docs/2.0/Red.md @@ -8,12 +8,14 @@ Creates a new [event](./Event/index) object. ```lua ( - Name: string, -- The name of the event + Name: string | { Name: string, Unreliable: boolean? }, -- Event config Validate: (...unknown) -> T..., -- Validates event payloads ) -> Event ``` -This will create an event with the passed name. +This will create an event with the passed config. + +If you pass a string, it will be used as the event name and the event will be reliable. If you pass a table you have the option to make the event unreliable. ::: danger The name of the event must be unique, using the same name twice will result in an error. diff --git a/docs/guide/events/declaring.md b/docs/guide/events/declaring.md index 4be9b7c..697cc60 100644 --- a/docs/guide/events/declaring.md +++ b/docs/guide/events/declaring.md @@ -12,7 +12,7 @@ return Red.Event("MostBasicEvent", function() end) ``` -This is the most basic event in Red. Here we've declared an event which has no payload. The event name is gathered from the module name. +This is the most basic event in Red. Here we've declared an event which has no payload. The event name is passed as the first argument to `Red.Event`. ::: danger Make sure the event name is unique - using the same name multiple times will result in an error. @@ -79,3 +79,18 @@ end) ::: warning Ensure you follow rule 1 when using multiple payloads. The callback must return the arguments in the same order they were passed in. ::: + +## Unreliable Events + +Events can be unreliable. This means that the event will not be guaranteed to fire. This is useful for events that are not important, or are fired very frequently. + +To make an event unreliable, pass a table as the first argument to `Red.Event`. + +```lua +return Red.Event({ + Name = "UnreliableEvent", + Unreliable = true, +}, function() + return +end) +``` diff --git a/lib/Event/Client.luau b/lib/Event/Client.luau index 2b6b90c..8f18f8a 100644 --- a/lib/Event/Client.luau +++ b/lib/Event/Client.luau @@ -5,12 +5,18 @@ local Net = require(script.Parent.Parent.Net) export type Client = { Id: string, + Unreliable: boolean, + Fire: (self: Client, T...) -> (), On: (self: Client, Callback: (T...) -> ()) -> (), } local function Fire(self: Client, ...: T...) - Net.Client.SendReliableEvent(self.Id, table.pack(...)) + if self.Unreliable then + Net.Client.SendUnreliableEvent(self.Id, table.pack(...)) + else + Net.Client.SendReliableEvent(self.Id, table.pack(...)) + end end local function On(self: Client, Callback: (T...) -> ()) @@ -19,10 +25,12 @@ local function On(self: Client, Callback: (T...) -> ()) end) end -local function Client(Id: string): Client +local function Client(Id: string, Unreliable: boolean): Client return { Id = Id, + Unreliable = Unreliable, + Fire = Fire, On = On, } :: any diff --git a/lib/Event/Server.luau b/lib/Event/Server.luau index f7cce57..180e362 100644 --- a/lib/Event/Server.luau +++ b/lib/Event/Server.luau @@ -8,6 +8,8 @@ export type Server = { Id: string, Validate: (...unknown) -> T..., + Unreliable: boolean, + Fire: (self: Server, Player: Player, T...) -> (), FireAll: (self: Server, T...) -> (), FireAllExcept: (self: Server, Except: Player, T...) -> (), @@ -18,14 +20,22 @@ export type Server = { } local function Fire(self: Server, Player: Player, ...: T...) - Net.Server.SendReliableEvent(Player, self.Id, table.pack(...)) + if self.Unreliable then + Net.Server.SendUnreliableEvent(Player, self.Id, table.pack(...)) + else + Net.Server.SendReliableEvent(Player, self.Id, table.pack(...)) + end end local function FireAll(self: Server, ...: T...) local Args = table.pack(...) for _, Player in Players:GetPlayers() do - Net.Server.SendReliableEvent(Player, self.Id, Args) + if self.Unreliable then + Net.Server.SendUnreliableEvent(Player, self.Id, Args) + else + Net.Server.SendReliableEvent(Player, self.Id, Args) + end end end @@ -34,7 +44,11 @@ local function FireAllExcept(self: Server, Except: Player, ...: T... for _, Player in Players:GetPlayers() do if Player ~= Except then - Net.Server.SendReliableEvent(Player, self.Id, Args) + if self.Unreliable then + Net.Server.SendUnreliableEvent(Player, self.Id, Args) + else + Net.Server.SendReliableEvent(Player, self.Id, Args) + end end end end @@ -43,7 +57,11 @@ local function FireList(self: Server, List: { Player }, ...: T...) local Args = table.pack(...) for _, Player in List do - Net.Server.SendReliableEvent(Player, self.Id, Args) + if self.Unreliable then + Net.Server.SendUnreliableEvent(Player, self.Id, Args) + else + Net.Server.SendReliableEvent(Player, self.Id, Args) + end end end @@ -52,7 +70,11 @@ local function FireWithFilter(self: Server, Filter: (Player) -> bool for _, Player in Players:GetPlayers() do if Filter(Player) then - Net.Server.SendReliableEvent(Player, self.Id, Args) + if self.Unreliable then + Net.Server.SendUnreliableEvent(Player, self.Id, Args) + else + Net.Server.SendReliableEvent(Player, self.Id, Args) + end end end end @@ -67,11 +89,13 @@ local function On(self: Server, Callback: (Player, T...) -> ()) end) end -local function Server(Id: string, Validate: (...unknown) -> T...): Server +local function Server(Id: string, Validate: (...unknown) -> T..., Unreliable: boolean): Server return { Id = Id, Validate = Validate, + Unreliable = Unreliable, + Fire = Fire, FireAll = FireAll, FireAllExcept = FireAllExcept, diff --git a/lib/Event/init.luau b/lib/Event/init.luau index f443577..0a83de4 100644 --- a/lib/Event/init.luau +++ b/lib/Event/init.luau @@ -9,6 +9,8 @@ export type Event = { Id: string, Validate: (...unknown) -> T..., + Unreliable: boolean, + ServerEvent: ServerEvent.Server?, ClientEvent: ClientEvent.Client?, @@ -20,7 +22,7 @@ local function Server(self: Event): ServerEvent.Server assert(RunService:IsServer(), "Server events can only be accessed from the server") if not self.ServerEvent then - self.ServerEvent = ServerEvent(self.Id, self.Validate) + self.ServerEvent = ServerEvent(self.Id, self.Validate, self.Unreliable) end return self.ServerEvent :: any @@ -30,19 +32,36 @@ local function Client(self: Event): ClientEvent.Client assert(RunService:IsClient(), "Client events can only be accessed from the client") if not self.ClientEvent then - self.ClientEvent = ClientEvent(self.Id) :: any + self.ClientEvent = ClientEvent(self.Id, self.Unreliable) :: any end return self.ClientEvent :: any end -local function Event(Name: string, Validate: (...unknown) -> T...): Event +type EventOptions = { + Name: string, + Unreliable: boolean?, +} + +local function Event(Config: string | EventOptions, Validate: (...unknown) -> T...): Event + local Name, Unreliable + + if type(Config) == "string" then + Name = Config + Unreliable = false + else + Name = Config.Name + Unreliable = Config.Unreliable or false + end + assert(not Identifier.Exists(Name), "Cannot use same name twice") return { Id = Identifier.Shared(Name):Await(), Validate = Validate, + Unreliable = Unreliable, + ServerEvent = nil, ClientEvent = nil, diff --git a/lib/SharedEvent.luau b/lib/SharedEvent.luau deleted file mode 100644 index 029c408..0000000 --- a/lib/SharedEvent.luau +++ /dev/null @@ -1,217 +0,0 @@ -local Players = game:GetService("Players") -local RunService = game:GetService("RunService") - -local Identifier = require(script.Parent.Identifier) -local Spawn = require(script.Parent.Parent.Spawn) -local Signal = require(script.Parent.Parent.Signal) - -local Net = require(script.Parent.Net) - -type SharedBaseEvent = { - Id: string, - Reliable: boolean, - - FireClient: (self: SharedBaseEvent, Player: Player, T...) -> (), - FireAllClients: (self: SharedBaseEvent, T...) -> (), - FireAllClientsExcept: (self: SharedBaseEvent, Player: Player, T...) -> (), - FireClients: (self: SharedBaseEvent, Players: { Player }, T...) -> (), - - FireServer: (self: SharedBaseEvent, T...) -> (), -} - -export type SharedCallEvent = SharedBaseEvent & { - CallMode: "Call", - - Listener: ((...any) -> ())?, - - SetServerListener: (self: SharedCallEvent, Listener: (Player: Player, T...) -> ()) -> (), - SetClientListener: (self: SharedCallEvent, Listener: (T...) -> ()) -> (), -} - -export type SharedSignalEvent = SharedBaseEvent & { - CallMode: "Signal", - - Signal: Signal.Signal<...any>, - - OnServer: (self: SharedSignalEvent, Listener: (Player: Player, T...) -> ()) -> () -> (), - OnClient: (self: SharedSignalEvent, Listener: (T...) -> ()) -> () -> (), -} - -export type SharedEvent = SharedCallEvent | SharedSignalEvent - -local function FireClient(self: SharedBaseEvent, Player: Player, ...) - assert(RunService:IsServer(), "FireClient can only be called from the server") - - if self.Reliable then - Net.Server.SendReliableEvent(Player, self.Id, table.pack(...)) - else - Net.Server.SendUnreliableEvent(Player, self.Id, table.pack(...)) - end -end - -local function FireAllClients(self: SharedBaseEvent, ...) - assert(RunService:IsServer(), "FireAllClients can only be called from the server") - - local Args = table.pack(...) - - if self.Reliable then - for _, Player in Players:GetPlayers() do - Net.Server.SendReliableEvent(Player, self.Id, Args) - end - else - for _, Player in Players:GetPlayers() do - Net.Server.SendUnreliableEvent(Player, self.Id, Args) - end - end -end - -local function FireAllClientsExcept(self: SharedBaseEvent, Player: Player, ...) - assert(RunService:IsServer(), "FireAllClientsExcept can only be called from the server") - - local Args = table.pack(...) - - if self.Reliable then - for _, OtherPlayer in Players:GetPlayers() do - if OtherPlayer ~= Player then - Net.Server.SendReliableEvent(OtherPlayer, self.Id, Args) - end - end - else - for _, OtherPlayer in Players:GetPlayers() do - if OtherPlayer ~= Player then - Net.Server.SendUnreliableEvent(OtherPlayer, self.Id, Args) - end - end - end -end - -local function FireClients(self: SharedBaseEvent, Players: { Player }, ...) - assert(RunService:IsServer(), "FireClients can only be called from the server") - - local Args = table.pack(...) - - if self.Reliable then - for _, Player in Players do - Net.Server.SendReliableEvent(Player, self.Id, Args) - end - else - for _, Player in Players do - Net.Server.SendUnreliableEvent(Player, self.Id, Args) - end - end -end - -local function FireServer(self: SharedBaseEvent, ...) - assert(RunService:IsClient(), "FireServer can only be called from the client") - - if self.Reliable then - Net.Client.SendReliableEvent(self.Id, table.pack(...)) - else - Net.Client.SendUnreliableEvent(self.Id, table.pack(...)) - end -end - -local function SetServerListener(self: SharedCallEvent, Listener: (Player: Player, T...) -> ()) - assert(RunService:IsServer(), "SetServerListener can only be called from the server") - - self.Listener = Listener :: any -end - -local function SetClientListener(self: SharedCallEvent, Listener: (T...) -> ()) - assert(RunService:IsClient(), "SetClientListener can only be called from the client") - - self.Listener = Listener :: any -end - -local function OnServer(self: SharedSignalEvent, Listener: (Player: Player, T...) -> ()): () -> () - assert(RunService:IsServer(), "OnServer can only be called from the server") - - return self.Signal:Connect(Listener :: any) -end - -local function OnClient(self: SharedSignalEvent, Listener: (T...) -> ()): () -> () - assert(RunService:IsClient(), "OnClient can only be called from the client") - - return self.Signal:Connect(Listener :: any) -end - -type SharedEventOptions = { - Name: string, - Unreliable: boolean?, - - Validate: (...unknown) -> T..., -} - -type ConstructorType = - ((Options: SharedEventOptions & { CallMode: "Call"? }) -> SharedCallEvent) - & ((Options: SharedEventOptions & { CallMode: "Signal" }) -> SharedSignalEvent) - -local SharedEvent: ConstructorType = function(Options: any) - local self = { - Id = Identifier.Shared(Options.Name):Await(), - Reliable = not Options.Unreliable, - - FireClient = FireClient, - FireAllClients = FireAllClients, - FireAllClientsExcept = FireAllClientsExcept, - FireClients = FireClients, - - FireServer = FireServer, - } - - if Options.CallMode == "Call" then - self.CallMode = "Call" - - self.SetServerListener = SetServerListener - self.SetClientListener = SetClientListener - - self.Listener = nil :: (...any) -> ()? - - if RunService:IsServer() then - Net.Server.SetListener(self.Id, function(Player, Args) - Spawn(function(Player: Player, ...: any) - if self.Listener and pcall(Options.Validate, ...) then - self.Listener(Player, ...) - end - end, Player, table.unpack(Args)) - end) - else - Net.Client.SetListener(self.Id, function(Args) - Spawn(function(...: any) - if self.Listener and pcall(Options.Validate, ...) then - self.Listener(...) - end - end, table.unpack(Args)) - end) - end - elseif Options.CallMode == "Signal" then - self.CallMode = "Signal" - - self.Signal = Signal() - - self.OnServer = OnServer - self.OnClient = OnClient - - if RunService:IsServer() then - Net.Server.SetListener(self.Id, function(Player, Args) - Spawn(function(Player: Player, ...: any) - if pcall(Options.Validate, ...) then - self.Signal:Fire(Player, ...) - end - end, Player, table.unpack(Args)) - end) - else - Net.Client.SetListener(self.Id, function(Args) - Spawn(function(...: any) - if pcall(Options.Validate, ...) then - self.Signal:Fire(...) - end - end, table.unpack(Args)) - end) - end - end - - return self -end :: any - -return SharedEvent diff --git a/wally.toml b/wally.toml index 1c6a6d0..24ebb4e 100644 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] name = "red-blox/red" -version = "2.1.1" +version = "2.2.0-rc.1" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" license = "MIT"