From 943c487cf23985917dc79434fbbccab82baeee67 Mon Sep 17 00:00:00 2001 From: Tazmondo Date: Tue, 2 Jan 2024 03:53:31 +0000 Subject: [PATCH 1/2] Add docs for Shared Events --- docs/.vitepress/config.ts | 3 + docs/2.0/Event/Client.md | 2 +- docs/2.0/Event/Server.md | 2 +- docs/2.0/Function.md | 4 +- docs/2.0/Red.md | 62 +++++++++ docs/2.0/SharedEvent.md | 110 ++++++++++++++++ docs/2.0/SharedSignalEvent.md | 110 ++++++++++++++++ docs/guide/events/sharedevent.md | 141 +++++++++++++++++++++ docs/guide/introduction/getting-started.md | 4 +- 9 files changed, 432 insertions(+), 6 deletions(-) create mode 100644 docs/2.0/SharedEvent.md create mode 100644 docs/2.0/SharedSignalEvent.md create mode 100644 docs/guide/events/sharedevent.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 45a86c7..dec0685 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -25,6 +25,7 @@ function sidebar() { { text: 'Declaring Events', link: '/guide/events/declaring' }, { text: 'Server Usage', link: '/guide/events/server' }, { text: 'Client Usage', link: '/guide/events/client' }, + { text: 'Shared Events', link: '/guide/events/sharedevent' } ] }, { @@ -47,6 +48,8 @@ function sidebar() { ] }, { text: 'Function', link: '/2.0/Function' }, + { text: 'SharedEvent', link: '/2.0/SharedEvent' }, + { text: 'SharedSignalEvent', link: '/2.0/SharedSignalEvent' } ], } ], diff --git a/docs/2.0/Event/Client.md b/docs/2.0/Event/Client.md index 5f84428..fd11a4d 100644 --- a/docs/2.0/Event/Client.md +++ b/docs/2.0/Event/Client.md @@ -24,7 +24,7 @@ Sets the event's listener. ```lua ( - Listener: (...T...) -> (), -- The listener + Listener: (T...) -> (), -- The listener ) -> () ``` diff --git a/docs/2.0/Event/Server.md b/docs/2.0/Event/Server.md index 948fb3f..6e89c2f 100644 --- a/docs/2.0/Event/Server.md +++ b/docs/2.0/Event/Server.md @@ -94,7 +94,7 @@ Sets the event's listener. ```lua ( - Function: (...: T...) -> (), -- The function to connect + Listener: (T...) -> (), -- The function to connect ) -> () ``` diff --git a/docs/2.0/Function.md b/docs/2.0/Function.md index f40b724..8acce1e 100644 --- a/docs/2.0/Function.md +++ b/docs/2.0/Function.md @@ -9,7 +9,7 @@ Sets the function's callback. ```lua ( Callback: (Player, A...) -> R... -- The callback to set. -) +) -> () ``` A singular callback must be set on the server to allow clients to call the function. This callback is given the arguments and must return the expected return values. This callback may only be set on the server, attempting to set it on the client will result in an error. @@ -33,7 +33,7 @@ Calls the function on the server. ```lua ( ...A: any -- The arguments to pass to the function. -): Future +) -> Future ``` A function is called on the client to call the function on the server. This method returns a [Future](https://util.redblox.dev/future) which can be used to await the return values or connect a function to be called when the return values are received. diff --git a/docs/2.0/Red.md b/docs/2.0/Red.md index 46efcc9..09aded4 100644 --- a/docs/2.0/Red.md +++ b/docs/2.0/Red.md @@ -72,3 +72,65 @@ end) ::: tip It is recommended to use [Guard](https://util.redblox.dev/guard) to typecheck as it also narrows types. ::: + +## SharedEvent +Creates a new [SharedEvent](./SharedEvent) object. +```lua +( + Name: string | { Name: string, Unreliable: boolean? }, -- Event config + Validate: (...unknown) -> T... -- Validates event payloads +) -> SharedCallEvent +``` +This will create a SharedEvent 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. +::: + +The validation function is used to validate the type of the payloads. The function has three rules: + +1. The callback returns the arguments in the same order they were passed in. +2. The callback must error if the arguments are invalid. +3. The callback must narrow the types of the arguments. + +```lua +return Red.SharedEvent("EventName", function(Number, String) + return Guard.Number(Number), Guard.String(String) +end) +``` +::: tip +It is recommended to use [Guard](https://util.redblox.dev/guard) to typecheck as it also narrows types. +::: + +## SharedSignalEvent +Creates a new [SharedSignalEvent](./SharedSignalEvent) object. +```lua +( + Name: string | { Name: string, Unreliable: boolean? }, -- Event config + Validate: (...unknown) -> T... -- Validates event payloads +) -> SharedSignalEvent +``` +This will create a SharedSignalEvent 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. +::: + +The validation function is used to validate the type of the payloads. The function has three rules: + +1. The callback returns the arguments in the same order they were passed in. +2. The callback must error if the arguments are invalid. +3. The callback must narrow the types of the arguments. + +```lua +return Red.SharedSignalEvent("EventName", function(Number, String) + return Guard.Number(Number), Guard.String(String) +end) +``` +::: tip +It is recommended to use [Guard](https://util.redblox.dev/guard) to typecheck as it also narrows types. +::: \ No newline at end of file diff --git a/docs/2.0/SharedEvent.md b/docs/2.0/SharedEvent.md new file mode 100644 index 0000000..3af1095 --- /dev/null +++ b/docs/2.0/SharedEvent.md @@ -0,0 +1,110 @@ +# SharedEvent +SharedEvents are built on the same backend networking as regular events. However, they do not require an explicit `:Server` or `:Client` call to use. + +Unlike [SharedSignalEvents](SharedSignalEvent), only one listener may be set at any time. Setting a new listener will overwrite the previous one, if it existed. + +## SetServerListener +Sets the server callback. +```lua +( + Listener: (Player: Player, T...) -> () -- The listener to register +) -> () +``` +This method sets the server listener. Errors if called from the client. +```lua +MyEvent:SetServerListener(function(Player, Value) + print(Player, Value) +end) +``` + +## SetClientListener +Sets the client callback. +```lua +( + Listener: (T...) -> () -- The listener to register +) -> () +``` +This method sets the client listener. Errors if called from the server. +```lua +MyEvent:SetClientListener(function(Value) + print(Value) +end) +``` + +## FireServer +Fires the event to the server. +```lua +( + ...: T... -- The event payload +) -> () +``` +This method fires the event to the server. Errors if called from the server. +```lua +MyEvent:FireServer("Hello, World!") +``` + +## FireClient +Fires the event to a specific player. +```lua +( + Player: Player, -- The target player + ...: T... -- The event payload +) -> () +``` +This method fires the event to a specific client. Errors if called from the client. +```lua +MyEvent:FireClient(player, "Hello, World!") +``` + +## FireAllClients +Fires the event to all players. +```lua +( + ...: T... -- The event payload +) -> () +``` +This method fires the event to all players. Errors if called from the client. +```lua +MyEvent:FireAllClients("Hello, World!") +``` + +## FireAllClientsExcept +Fires the event to every player **except** one. +```lua +( + Player: Player, -- Player to be ignored + ...: T... -- The event payload +) +``` +This method fires the event to every player except one. Errors if called from the client. +```lua +MyEvent:FireAllClientsExcept(player, "Hello, World!") +``` + +## FireClients +Fires the event to every player in the given list. +```lua +( + Players: { Player }, -- List of players to fire the event to + ...: T... -- The event payload +) -> () +``` +This method fires the event to every player in the list. Errors if called from the client. +```lua +MyServer:FireClients({ player }, "Hello, World!") +``` + +## FireFilteredClients +Fires the event to every player that passes the given filter function. +```lua +( + Filter: (Player) -> boolean -- Must return true for a player to receive the event. + ...: T... -- The event payload +) -> () +``` +This method fires the event to every player that passes the filter. Errors if called from the client. +```lua +MyServer:FireFilteredClients(function(player) + return player.Name == "Player1" +end, "Hello, World!") +``` \ No newline at end of file diff --git a/docs/2.0/SharedSignalEvent.md b/docs/2.0/SharedSignalEvent.md new file mode 100644 index 0000000..20cde76 --- /dev/null +++ b/docs/2.0/SharedSignalEvent.md @@ -0,0 +1,110 @@ +# SharedSignalEvent +SharedEvents are built on the same backend networking as regular events. However, they do not require an explicit `:Server` or `:Client` call to use. + +Unlike [SharedEvents](SharedEvent), SharedSignalEvents may have any number of listeners set. + +## OnServer +Sets the server callback. +```lua +( + Listener: (Player: Player, T...) -> () -- The listener to register +) -> () +``` +This method sets the server callback. Errors if called from the client. +```lua +MyEvent:OnServer(function(Player, Argument) + print(Player, Argument) +end) +``` + +## OnClient +Sets the client callback. +```lua +( + Listener: (T...) -> () -- The listener to register +) -> () +``` +This method sets the client callback. Errors if called from the server. +```lua +MyEvent:OnClient(function(Argument) + print(Argument) +end) +``` + +## FireServer +Fires the event to the server. +```lua +( + ...: T... -- The event payload +) -> () +``` +This method fires the event to the server. Errors if called from the server. +```lua +MyEvent:FireServer("Hello, World!") +``` + +## FireClient +Fires the event to a specific player. +```lua +( + Player: Player, -- The target player + ...: T... -- The event payload +) -> () +``` +This method fires the event to a specific client. Errors if called from the client. +```lua +MyEvent:FireClient(player, "Hello, World!") +``` + +## FireAllClients +Fires the event to all players. +```lua +( + ...: T... -- The event payload +) -> () +``` +This method fires the event to all players. Errors if called from the client. +```lua +MyEvent:FireAllClients("Hello, World!") +``` + +## FireAllClientsExcept +Fires the event to every player **except** one. +```lua +( + Player: Player, -- Player to be ignored + ...: T... -- The event payload +) +``` +This method fires the event to every player except one. Errors if called from the client. +```lua +MyEvent:FireAllClientsExcept(player, "Hello, World!") +``` + +## FireClients +Fires the event to every player in the given list. +```lua +( + Players: { Player }, -- List of players to fire the event to + ...: T... -- The event payload +) -> () +``` +This method fires the event to every player in the list. Errors if called from the client. +```lua +MyServer:FireClients({ player }, "Hello, World!") +``` + +## FireFilteredClients +Fires the event to every player that passes the given filter function. +```lua +( + Filter: (Player) -> boolean -- Must return true for a player to receive the event. + ...: T... -- The event payload +) -> () +``` +This method fires the event to every player that passes the filter. Errors if called from the client. +```lua +MyServer:FireFilteredClients(function(player) + return player.Name == "Player1" +end, "Hello, World!") +``` \ No newline at end of file diff --git a/docs/guide/events/sharedevent.md b/docs/guide/events/sharedevent.md new file mode 100644 index 0000000..205428d --- /dev/null +++ b/docs/guide/events/sharedevent.md @@ -0,0 +1,141 @@ +# Shared Events +Shared Events are simply a different way of using Events. They both use the same Red backend. + +They have the major benefit of not needing to explicitly call `Event:Server` or `Event:Client` before being usable. Both your server and client code can simply require them and start using them. + +There are two types of Shared Events: The default `SharedEvent`, and the Signal-based `SharedSignalEvent`. By default, Shared Events only allow you to set one callback function, but Shared Signal Events allow you to have any number of callbacks. + +This is marginally worse for performance, as it has to use a Signal to fire the callbacks - hence why it is not the default. + +::: warning +Setting the listener of a regular Shared Event twice will silently overwrite the first callback. +::: + +## Declaring Shared Events +Shared Events are declared in a similar way to regular Events. + +```lua +local Red = require(Path.To.Red) + +return Red.SharedEvent("UniqueEventName", function() + return +end) +``` + +However they also allow for another pattern, which is to declare multiple events in one file. This is possible with regular events, but it is finnicky as you still need to call `:Client` and `:Server`. It is much easier with Shared Events. + +```lua +local Red = require(Path.To.Red) +local Guard = require(Path.To.Guard) + +return { + BasicEvent = Red.SharedEvent("BasicEvent", function() + return + end), + + BasicSignalEvent = Red.SharedSignalEvent("BasicSignalEvent", function() + return + end), + + PayloadEvent = Red.SharedEvent("PayloadEvent", function(number) + return Guard.Number(number) + end), + + PayloadSignalEvent = Red.SharedSignalEvent("PayloadSignalEvent", function(number) + return Guard.Number(number) + end), + + UnreliableEvent = Red.SharedEvent({ + Name = "UnreliableEvent", + Unreliable = true + }, function(number) + return Guard.Number(number) + end) +} +``` + +## Listening to events +You use a different function to register listeners, depending on if it's a Signal Shared Event or not, as well as whether you're on the server or the client. + +### Client +```lua +-- Assuming the code snippet above is our events file +local Events = require(Path.To.Events) + +-- Shared Event +Events.PayloadEvent:SetClientListener(function(number) + print("Client Received!", number) +end) + +-- Shared Signal Event +-- You can have as many listeners as you want +Events.PayloadSignalEvent:OnClient(function(number) + print("Callback 1", number) +end) +Events.PayloadSignalEvent:OnClient(function(number) + print("Callback 2", number) +end) +``` + +### Server +```lua +-- Assuming the code snippet above is our events file +local Events = require(Path.To.Events) + +-- Shared Event +Events.PayloadEvent:SetServerListener(function(Player, number) + print("Server received from", Player, number) +end) + +-- Shared Signal Event +-- You can have as many listeners as you want +Events.PayloadSignalEvent:OnServer(function(Player, number) + print("Callback 1 from", Player, number) +end) +Events.PayloadSignalEvent:OnServer(function(Player, number) + print("Callback 2 from", Player, number) +end) +``` + +## Firing Events +Like regular Events, Shared Events offer a lot of different firing functions. + +These are identical for both types of Shared Event. + +### FireServer +This method fires the event to the server. Errors if called from the server. +```lua +MyEvent:FireServer("Hello, World!") +``` + +### FireClient +This method fires the event to a specific client. Errors if called from the client. +```lua +MyEvent:FireClient(player, "Hello, World!") +``` + +### FireAllClients +This method fires the event to all players. Errors if called from the client. +```lua +MyEvent:FireAllClients("Hello, World!") +``` + +### FireAllClientsExcept +This method fires the event to every player except one. Errors if called from the client. +```lua +MyEvent:FireAllClientsExcept(player, "Hello, World!") +``` + +### FireClients +This method fires the event to every player in the list. Errors if called from the client. +```lua +MyServer:FireClients({ player }, "Hello, World!") +``` + +### FireFilteredClients +This method fires the event to every player that passes the filter. Errors if called from the client. +```lua +MyServer:FireFilteredClients(function(player) + return player.Name == "Player1" +end, "Hello, World!") +``` \ No newline at end of file diff --git a/docs/guide/introduction/getting-started.md b/docs/guide/introduction/getting-started.md index b2ec0a8..4ae2bbf 100644 --- a/docs/guide/introduction/getting-started.md +++ b/docs/guide/introduction/getting-started.md @@ -9,7 +9,7 @@ Red is available for Rojo-based workflows and Studio workflows through wally. ```toml [dependencies] -Red = "red-blox/red@2.0.0" +Red = "red-blox/red@2.3.0" ``` 3. Run `wally install` to install packages. @@ -23,7 +23,7 @@ Red = "red-blox/red@2.0.0" return { studioWallyServer = "https://studio-wally.fewkz.com", packages = { - Red = "red-blox/red@2.0.0" + Red = "red-blox/red@2.3.0" } } ``` From 9f9bbf8acdf49ea2589758272886dd0dde8fa695 Mon Sep 17 00:00:00 2001 From: Tazmondo Date: Wed, 3 Jan 2024 01:38:16 +0000 Subject: [PATCH 2/2] Update function docs :Call now returns a success boolean --- docs/2.0/Function.md | 16 +++++++--------- docs/guide/functions.md | 12 +++++------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/2.0/Function.md b/docs/2.0/Function.md index 8acce1e..6443102 100644 --- a/docs/2.0/Function.md +++ b/docs/2.0/Function.md @@ -2,7 +2,7 @@ Functions allow RemoteFunction-like behavior in Red for calling client -> server. -## SetCallback +## SetCallback Sets the function's callback. @@ -22,24 +22,22 @@ Function:SetCallback(function(Player, Arg1, Arg2, Arg3) end) ``` -::: danger -If the callback errors the client will never recieve any value and will yield forever. **Doing this is a memory leak!** Do not rely on erroring not sending back values. -::: - -## Call +## Call Calls the function on the server. ```lua ( ...A: any -- The arguments to pass to the function. -) -> Future +) -> Future -- A success boolean followed by the returned values ``` -A function is called on the client to call the function on the server. This method returns a [Future](https://util.redblox.dev/future) which can be used to await the return values or connect a function to be called when the return values are received. +Functions can only be called from the client. The client must pass valid arguments to the function, and will be given back a [Future](https://util.redblox.dev/future) that completes with the returned values. + +It also returns a success boolean, similar to pcall, which is false if the server's callback function errored, and true if it was successful. ```lua local Function = require(Path.To.Function) -local Ret1, Ret2, Ret3 = Function:Call(Arg1, Arg2, Arg3):Await() +local Success, Ret1, Ret2, Ret3 = Function:Call(Arg1, Arg2, Arg3):Await() ``` diff --git a/docs/guide/functions.md b/docs/guide/functions.md index 4b22ee8..d9701d1 100644 --- a/docs/guide/functions.md +++ b/docs/guide/functions.md @@ -56,9 +56,9 @@ end) ::: -## Set Callback +## Setting the Callback -A singular callback must be set on the server to allow clients to call the event. This callback is given the arguments and must return the expected return values. +A singular callback must be set on the server to allow clients to call the function. This callback is given the arguments and must return the expected return values. This callback may only be set on the server, attempting to set it on the client will result in an error. ```lua local Function = require(Path.To.Function) @@ -68,16 +68,14 @@ Function:SetCallback(function(Player, Arg1, Arg2, Arg3) end) ``` -::: danger -If the callback errors then the client will never recieve any value and will yield forever. **Doing this is a memory leak!** Do not rely on erroring not sending back values. -::: - ## Calling Functions can only be called from the client. The client must pass valid arguments to the function, and will be given back a [Future](https://util.redblox.dev/future) that completes with the returned values. +It also returns a success boolean, similar to pcall, which is false if the server's callback function errored, and true if it was successful. + ```lua local Function = require(Path.To.Function) -local Ret1, Ret2, Ret3 = Function:Call("Hello", 1, true):Await() +local Success, Ret1, Ret2, Ret3 = Function:Call("Hello", 1, true):Await() ```