From ffd21a7a776873751c24e7e1ed4cf517564cd5f7 Mon Sep 17 00:00:00 2001 From: jackdotink Date: Sat, 30 Sep 2023 23:06:14 -0500 Subject: [PATCH] red 2.0.0 release! --- Test/Client/Client.client.luau | 29 +++++ Test/Server/Server.server.luau | 56 +++++++++ Test/Shared/ComplexEvent.luau | 11 ++ Test/Shared/EmptyEvent.luau | 7 ++ Test/Shared/ReadyEvent.luau | 7 ++ Test/Shared/SimpleEvent.luau | 8 ++ dev.project.json | 20 +++- docs/.vitepress/config.ts | 1 + docs/2.0/Red.md | 18 ++- docs/guide/events/client.md | 2 +- docs/guide/events/declaring.md | 10 +- docs/guide/events/server.md | 4 +- docs/guide/introduction/getting-started.md | 4 +- docs/guide/introduction/redblox.md | 16 +++ lib/ClientEvent.luau | 2 +- lib/Event.luau | 133 --------------------- lib/Event/Client.luau | 43 +++++++ lib/Event/Server.luau | 90 ++++++++++++++ lib/Event/init.luau | 54 +++++++++ lib/Function.luau | 39 ------ lib/Identifier.luau | 4 + lib/ServerEvent.luau | 8 +- lib/init.luau | 2 +- package-lock.json | 51 ++++++-- package.json | 2 +- wally.toml | 2 +- 26 files changed, 411 insertions(+), 212 deletions(-) create mode 100644 Test/Client/Client.client.luau create mode 100644 Test/Server/Server.server.luau create mode 100644 Test/Shared/ComplexEvent.luau create mode 100644 Test/Shared/EmptyEvent.luau create mode 100644 Test/Shared/ReadyEvent.luau create mode 100644 Test/Shared/SimpleEvent.luau create mode 100644 docs/guide/introduction/redblox.md delete mode 100644 lib/Event.luau create mode 100644 lib/Event/Client.luau create mode 100644 lib/Event/Server.luau create mode 100644 lib/Event/init.luau delete mode 100644 lib/Function.luau diff --git a/Test/Client/Client.client.luau b/Test/Client/Client.client.luau new file mode 100644 index 0000000..e2b11a0 --- /dev/null +++ b/Test/Client/Client.client.luau @@ -0,0 +1,29 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local ComplexEvent = require(ReplicatedStorage.ComplexEvent):Client() +local SimpleEvent = require(ReplicatedStorage.SimpleEvent):Client() +local EmptyEvent = require(ReplicatedStorage.EmptyEvent):Client() +local ReadyEvent = require(ReplicatedStorage.ReadyEvent):Client() + +print("Registering Listeners") +ComplexEvent:On(function(Value1, Value2, Value3) + print("ComplexEvent", Value1, Value2, Value3) +end) + +SimpleEvent:On(function(Value) + print("SimpleEvent", Value) +end) + +EmptyEvent:On(function() + print("EmptyEvent") +end) + +print("Firing Events") +ComplexEvent:Fire({ one = { "String Literal", 123 }, two = { 123, "String Literal" } }, "hello world again", 123) +SimpleEvent:Fire(123) +EmptyEvent:Fire() + +task.wait(1) + +print("Firing Ready") +ReadyEvent:Fire() diff --git a/Test/Server/Server.server.luau b/Test/Server/Server.server.luau new file mode 100644 index 0000000..b9a1320 --- /dev/null +++ b/Test/Server/Server.server.luau @@ -0,0 +1,56 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local ComplexEvent = require(ReplicatedStorage.ComplexEvent):Server() +local SimpleEvent = require(ReplicatedStorage.SimpleEvent):Server() +local EmptyEvent = require(ReplicatedStorage.EmptyEvent):Server() +local ReadyEvent = require(ReplicatedStorage.ReadyEvent):Server() + +print("Registering Listeners") +ComplexEvent:On(function(Player, Value1, Value2, Value3) + print("ComplexEvent", Player, Value1, Value2, Value3) +end) + +SimpleEvent:On(function(Player, Value) + print("SimpleEvent", Player, Value) +end) + +EmptyEvent:On(function(Player) + print("EmptyEvent", Player) +end) + +ReadyEvent:On(function(Player) + print(`[{Player.Name}] Ready, firing events!`) + + print(`[{Player.Name}] :Fire`) + SimpleEvent:Fire(Player, 1) + EmptyEvent:Fire(Player) + ComplexEvent:Fire(Player, nil, "hello world again", 1) + + print(`[{Player.Name}] :FireAll`) + SimpleEvent:FireAll(2) + EmptyEvent:FireAll() + ComplexEvent:FireAll({ one = { "String Literal", 123 }, two = { 123, "String Literal" } }, "hello world again", 2) + + print(`[{Player.Name}] :FireAllExcept`) + SimpleEvent:FireAllExcept(Player, 3) + EmptyEvent:FireAllExcept(Player) + ComplexEvent:FireAllExcept(Player, nil, "this should be skipped", 3) + + print(`[{Player.Name}] :FireList`) + SimpleEvent:FireList({ Player }, 4) + EmptyEvent:FireList({ Player }) + ComplexEvent:FireList({ Player }, { one = { "String Literal", 8 }, two = { 1, "String Literal" } }, "hi", 4) + + print(`[{Player.Name}] :FireWithFilter`) + SimpleEvent:FireWithFilter(function(OtherPlayer) + return OtherPlayer.Name == Player.Name + end, 5) + EmptyEvent:FireWithFilter(function(OtherPlayer) + return OtherPlayer.Name == Player.Name + end) + ComplexEvent:FireWithFilter(function(OtherPlayer) + return OtherPlayer.Name == Player.Name + end, { one = { "String Literal", 17 }, two = { 123, "String Literal" } }, "another string", 5) + + print(`[{Player.Name}] Done!`) +end) diff --git a/Test/Shared/ComplexEvent.luau b/Test/Shared/ComplexEvent.luau new file mode 100644 index 0000000..5eed90f --- /dev/null +++ b/Test/Shared/ComplexEvent.luau @@ -0,0 +1,11 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Red = require(ReplicatedStorage.Packages.Red) +local Guard = require(ReplicatedStorage.Packages.Guard) + +local ValueCheck = + Guard.Optional(Guard.Map(Guard.String, Guard.List(Guard.Or(Guard.Literal("String Literal"), Guard.Number)))) + +return Red.Event("ComplexEvent", function(Value1, Value2, Value3) + return ValueCheck(Value1), Guard.String(Value2), Guard.Number(Value3) +end) diff --git a/Test/Shared/EmptyEvent.luau b/Test/Shared/EmptyEvent.luau new file mode 100644 index 0000000..bd8c465 --- /dev/null +++ b/Test/Shared/EmptyEvent.luau @@ -0,0 +1,7 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Red = require(ReplicatedStorage.Packages.Red) + +return Red.Event("EmptyEvent", function() + return +end) diff --git a/Test/Shared/ReadyEvent.luau b/Test/Shared/ReadyEvent.luau new file mode 100644 index 0000000..a826cc6 --- /dev/null +++ b/Test/Shared/ReadyEvent.luau @@ -0,0 +1,7 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Red = require(ReplicatedStorage.Packages.Red) + +return Red.Event("ReadyEvent", function() + return +end) diff --git a/Test/Shared/SimpleEvent.luau b/Test/Shared/SimpleEvent.luau new file mode 100644 index 0000000..eaf1ffd --- /dev/null +++ b/Test/Shared/SimpleEvent.luau @@ -0,0 +1,8 @@ +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local Red = require(ReplicatedStorage.Packages.Red) +local Guard = require(ReplicatedStorage.Packages.Guard) + +return Red.Event("SimpleEvent", function(Value) + return Guard.Number(Value) +end) diff --git a/dev.project.json b/dev.project.json index 025e8d5..11776ab 100644 --- a/dev.project.json +++ b/dev.project.json @@ -1,9 +1,23 @@ { "name": "RedDev", "tree": { - "$path": "Packages", - "Red": { - "$path": "lib" + "$className": "DataModel", + "ServerScriptService": { + "$path": "Test/Server" + }, + "ReplicatedStorage": { + "$path": "Test/Shared", + "Packages": { + "$path": "Packages", + "Red": { + "$path": "lib" + } + } + }, + "StarterPlayer": { + "StarterPlayerScripts": { + "$path": "Test/Client" + } } } } \ No newline at end of file diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 0cd4ee6..fe1ae40 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -21,6 +21,7 @@ function sidebar() { items: [ { text: 'What is Red?', link: '/guide/introduction/what-is-red' }, { text: 'Getting Started', link: '/guide/introduction/getting-started' }, + { text: 'Redblox', link: '/guide/introduction/redblox' }, ] }, { diff --git a/docs/2.0/Red.md b/docs/2.0/Red.md index bd96b1e..a8cb3da 100644 --- a/docs/2.0/Red.md +++ b/docs/2.0/Red.md @@ -8,12 +8,16 @@ Creates a new [event](./Event/index) object. ```lua ( + Name: string, -- The name of the event Validate: (...unknown) -> T..., -- Validates event payloads - NameOverride: string?, -- Optional override for the name of the event ) -> Event ``` -This will create an event with the same name as the script calling the function. +This will create an event with the passed name. + +::: 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: @@ -22,7 +26,7 @@ The validation function is used to validate the type of the payloads. The functi 3. The callback must narrow the types of the arguments. ```lua -return Red.Event(function(Number, String) +return Red.Event("EventName", function(Number, String) return Guard.Number(Number), Guard.String(String) end) ``` @@ -30,11 +34,3 @@ end) ::: tip It is recommended to use [Guard](https://util.redblox.dev/guard) to typecheck payloads as it also narrows types. ::: - -If for whatever reason the name of the calling script cannot be used for the name of the event, you can pass in a string as the second argument to override the name. - -```lua -return Red.Event(function(Number, String) - return Guard.Number(Number), Guard.String(String) -end, "MyEvent") -``` diff --git a/docs/guide/events/client.md b/docs/guide/events/client.md index d00ff1c..f5b7c54 100644 --- a/docs/guide/events/client.md +++ b/docs/guide/events/client.md @@ -12,7 +12,7 @@ We'll also use the same event module as on the server. local Guard = require(Path.To.Guard) local Red = require(Path.To.Red) -return Red.Event(function(Number, String, Boolean) +return Red.Event("MyEvent", function(Number, String, Boolean) return Guard.Number(Number), Guard.String(String), Guard.Boolean(Boolean) end) ``` diff --git a/docs/guide/events/declaring.md b/docs/guide/events/declaring.md index f2fec3c..4be9b7c 100644 --- a/docs/guide/events/declaring.md +++ b/docs/guide/events/declaring.md @@ -7,7 +7,7 @@ Create a module in ReplicatedStorage. Inside the module import Red, and return a ```lua local Red = require(Path.To.Red) -return Red.Event(function() +return Red.Event("MostBasicEvent", function() return end) ``` @@ -15,7 +15,7 @@ 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. ::: danger -Make sure the module name is unique - modules with the same name will return the same event. If you need to create events without using module name, `Red.Event` has a second string argument that can be used to override the name of the event. These name overrides must still be unique. +Make sure the event name is unique - using the same name multiple times will result in an error. ::: ## Event Payloads @@ -25,7 +25,7 @@ We now have an event - but we can't send any data with it. To send a payload wit This callback is a validation check callback. This callback's purpose is to assert the types of the payload. ```lua -return Red.Event(function(Value) +return Red.Event("EventWithPayload", function(Value) assert(type(Value) == "number") return Value @@ -51,7 +51,7 @@ local Guard = require(Path.To.Guard) local Check = Guard.Map(Guard.String, Guard.List(Guard.Number)) -return Red.Event(function(Value) +return Red.Event("EventUsingGuard", function(Value) return Check(Value) end) ``` @@ -71,7 +71,7 @@ local PlayerCheck = function(Value: unknown) return Value end -return Red.Event(function(Action, Player) +return Red.Event("EventWithMultiplePayloads", function(Action, Player) return ActionCheck(Action), PlayerCheck(Player) end) ``` diff --git a/docs/guide/events/server.md b/docs/guide/events/server.md index 1c7ecf5..eb98844 100644 --- a/docs/guide/events/server.md +++ b/docs/guide/events/server.md @@ -16,7 +16,7 @@ For simplicity we'll be using the same event module throughout this guide. local Guard = require(Path.To.Guard) local Red = require(Path.To.Red) -return Red.Event(function(Number, String, Boolean) +return Red.Event("MyEvent", function(Number, String, Boolean) return Guard.Number(Number), Guard.String(String), Guard.Boolean(Boolean) end) ``` @@ -49,7 +49,7 @@ This will fire the event and payload to all players, and as such does not have a MyEvent:FireAllExcept(Player, 1, "Hello", true) ``` -This is the exact same as `FireAll`, *except* that it does not fire to the player passed as the first argument. +This is the exact same as `FireAll`, _except_ that it does not fire to the player passed as the first argument. ### FireList diff --git a/docs/guide/introduction/getting-started.md b/docs/guide/introduction/getting-started.md index 3a990aa..b2ec0a8 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-rc.8" +Red = "red-blox/red@2.0.0" ``` 3. Run `wally install` to install packages. @@ -23,7 +23,7 @@ Red = "red-blox/red@2.0.0-rc.8" return { studioWallyServer = "https://studio-wally.fewkz.com", packages = { - Red = "red-blox/red@2.0.0-rc.8" + Red = "red-blox/red@2.0.0" } } ``` diff --git a/docs/guide/introduction/redblox.md b/docs/guide/introduction/redblox.md new file mode 100644 index 0000000..f24e00b --- /dev/null +++ b/docs/guide/introduction/redblox.md @@ -0,0 +1,16 @@ +# Redblox + +The Redblox project contains the library Red, but also other useful libraries. + +- [Red](https://red.redblox.dev/) - A simple, fast, and powerful networking library. +- [Util](https://util.redblox.dev/) - A collection of small utilities for roblox. + - [Bin](https://util.redblox.dev/bin) - Manages cleanup for objects that cannot be garbage collected. + - [Clock](https://util.redblox.dev/clock) - Calls a function at consistent intervals. + - [Collection](https://util.redblox.dev/collection) - Handles instance addition and removal from collections. + - [Fetch](https://util.redblox.dev/fetch) - A Future based HTTP request utility similar to Javascript's fetch. + - [Future](https://util.redblox.dev/future) - A lightweight class to represent asynchronous functions. + - [Guard](https://util.redblox.dev/guard) - A runtime type checker with support for luau types. + - [Promise](https://util.redblox.dev/promise) - A Promise implementation that prioritizes speed and ease of use. + - [Ratelimit](https://util.redblox.dev/ratelimit) - Ratelimits many keys in a very intuitive interface. + - [Signal](https://util.redblox.dev/signal) - A signal implementation without connection objects. + - [Spawn](https://util.redblox.dev/spawn) - A shared "fast spawn" function that reuses threads. diff --git a/lib/ClientEvent.luau b/lib/ClientEvent.luau index fa0b6f5..1e61888 100644 --- a/lib/ClientEvent.luau +++ b/lib/ClientEvent.luau @@ -70,7 +70,7 @@ function ClientEvent.Start() debug.profilebegin("Red.ClientEvent.Outgoing") if ClientEvent.OutgoingMap.Call or ClientEvent.OutgoingMap.Fire then - Remote:FireServer(ClientEvent.OutgoingMap.Fire, ClientEvent.OutgoingMap.Call) + Remote:FireServer(ClientEvent.OutgoingMap.Fire or {}, ClientEvent.OutgoingMap.Call or {}) table.clear(ClientEvent.OutgoingMap) end diff --git a/lib/Event.luau b/lib/Event.luau deleted file mode 100644 index 070da7f..0000000 --- a/lib/Event.luau +++ /dev/null @@ -1,133 +0,0 @@ -local Players = game:GetService("Players") -local RunService = game:GetService("RunService") - -local Identifier = require(script.Parent.Identifier) -local ClientEvent = require(script.Parent.ClientEvent) -local ServerEvent = require(script.Parent.ServerEvent) - -local Server = {} -Server.__index = Server - -function Server.new(EventId: string, Validate: (...unknown) -> T...) - return setmetatable({ - EventId = EventId, - Validate = Validate, - - Listening = false, - }, Server) -end - -export type Server = typeof(Server.new("", function(): T... end)) - -function Server.Fire(self: Server, Player: Player, ...: T...) - ServerEvent.Fire(Player, self.EventId, table.pack(...)) -end - -function Server.FireAll(self: Server, ...: T...) - for _, Player in Players:GetPlayers() do - self:Fire(Player, ...) - end -end - -function Server.FireAllExcept(self: Server, Except: Player, ...: T...) - for _, Player in Players:GetPlayers() do - if Player ~= Except then - self:Fire(Player, ...) - end - end -end - -function Server.FireList(self: Server, PlayerList: { Player }, ...: T...) - for _, Player in PlayerList do - self:Fire(Player, ...) - end -end - -function Server.FireWithFilter(self: Server, Filter: (Player) -> boolean, ...: T...) - for _, Player in Players:GetPlayers() do - if Filter(Player) then - self:Fire(Player, ...) - end - end -end - -function Server.On(self: Server, Callback: (Player: Player, T...) -> ()) - assert(self.Listening == false, "Only one listener can be given for each event.") - self.Listening = true - - ServerEvent.Listen(self.EventId, function(Player, ...) - if pcall(self.Validate, ...) then - Callback(Player, ...) - end - end) -end - -local Client = {} -Client.__index = Client - -function Client.new(EventId: string, Validate: (...unknown) -> T...) - return setmetatable({ - EventId = EventId, - Validate = Validate, - - Listening = false, - }, Client) -end - -export type Client = typeof(Client.new("", function(): T... end)) - -function Client.Fire(self: Client, ...: T...) - ClientEvent.Fire(self.EventId, table.pack(...)) -end - -function Client.On(self: Client, Callback: (T...) -> ()) - assert(self.Listening == false, "Only one listener can be given for each event.") - self.Listening = true - - ClientEvent.Listen(self.EventId, Callback) -end - -local Event = {} -Event.__index = Event - -function Event.new(Validate: (...unknown) -> T..., NameOverride: string?) - local Name = NameOverride or debug.info(2, "s") - - if string.find(Name, "-") then - error("Event name cannot contain '-'") - end - - Name = string.gsub(Name, "%.", "_") - - return setmetatable({ - EventId = Identifier.Shared(Name):Await() :: string, - Validate = Validate, - - ServerEvent = nil :: Server?, - ClientEvent = nil :: Client?, - }, Event) -end - -export type Event = typeof(Event.new(function(): T... end)) - -function Event.Server(self: Event): Server - assert(RunService:IsServer(), "Cannot get server event from client.") - - if not self.ServerEvent then - self.ServerEvent = Server.new(self.EventId, self.Validate) - end - - return self.ServerEvent :: Server -end - -function Event.Client(self: Event): Client - assert(RunService:IsClient(), "Cannot get client event from server.") - - if not self.ClientEvent then - self.ClientEvent = Client.new(self.EventId, self.Validate) - end - - return self.ClientEvent :: Client -end - -return Event diff --git a/lib/Event/Client.luau b/lib/Event/Client.luau new file mode 100644 index 0000000..fad16df --- /dev/null +++ b/lib/Event/Client.luau @@ -0,0 +1,43 @@ +local ClientEvent = require(script.Parent.Parent.ClientEvent) + +local function PackArgs(...: any) + return { ... } +end + +export type Client = { + Id: string, + Listening: boolean, + Validate: (...unknown) -> T..., + + Fire: (self: Client, T...) -> (), + + On: (self: Client, Callback: (T...) -> ()) -> (), +} + +local function Fire(self: Client, ...) + ClientEvent.Fire(self.Id, PackArgs(...)) +end + +local function On(self: Client, Callback: (T...) -> ()) + assert(not self.Listening, "Event is already listening") + self.Listening = true + + ClientEvent.Listen(self.Id, function(...) + if pcall(self.Validate, ...) then + Callback(...) + end + end) +end + +local function Client(Id: string, Validate: (...unknown) -> T...): Client + return { + Id = Id, + Listening = false, + Validate = Validate, + + Fire = Fire, + On = On, + } :: any +end + +return Client diff --git a/lib/Event/Server.luau b/lib/Event/Server.luau new file mode 100644 index 0000000..5b69c88 --- /dev/null +++ b/lib/Event/Server.luau @@ -0,0 +1,90 @@ +local Players = game:GetService("Players") + +local ServerEvent = require(script.Parent.Parent.ServerEvent) + +local function PackArgs(...: any) + return { ... } +end + +export type Server = { + Id: string, + Listening: boolean, + Validate: (...unknown) -> T..., + + Fire: (self: Server, Player: Player, T...) -> (), + FireAll: (self: Server, T...) -> (), + FireAllExcept: (self: Server, Except: Player, T...) -> (), + FireList: (self: Server, List: { Player }, T...) -> (), + FireWithFilter: (self: Server, Filter: (Player) -> boolean, T...) -> (), + + On: (self: Server, Callback: (Player, T...) -> ()) -> (), +} + +local function Fire(self: Server, Player: Player, ...: T...) + ServerEvent.Fire(Player, self.Id, PackArgs(...)) +end + +local function FireAll(self: Server, ...: T...) + local Args = PackArgs(...) + + for _, Player in Players:GetPlayers() do + ServerEvent.Fire(Player, self.Id, Args) + end +end + +local function FireAllExcept(self: Server, Except: Player, ...: T...) + local Args = PackArgs(...) + + for _, Player in Players:GetPlayers() do + if Player ~= Except then + ServerEvent.Fire(Player, self.Id, Args) + end + end +end + +local function FireList(self: Server, List: { Player }, ...: T...) + local Args = PackArgs(...) + + for _, Player in List do + ServerEvent.Fire(Player, self.Id, Args) + end +end + +local function FireWithFilter(self: Server, Filter: (Player) -> boolean, ...: T...) + local Args = PackArgs(...) + + for _, Player in Players:GetPlayers() do + if Filter(Player) then + ServerEvent.Fire(Player, self.Id, Args) + end + end +end + +local function On(self: Server, Callback: (Player, T...) -> ()) + assert(not self.Listening, "Event is already listening") + self.Listening = true + + ServerEvent.Listen(self.Id, function(Player, ...) + if pcall(self.Validate, ...) then + Callback(Player, ...) + end + end) +end + +local function Server(Id: string, Validate: (...unknown) -> T...): Server + return { + Id = Id, + Listening = false, + Validate = Validate, + + Fire = Fire, + FireAll = FireAll, + FireAllExcept = FireAllExcept, + FireList = FireList, + FireWithFilter = FireWithFilter, + + On = On, + } :: any +end + +return Server diff --git a/lib/Event/init.luau b/lib/Event/init.luau new file mode 100644 index 0000000..61eef6b --- /dev/null +++ b/lib/Event/init.luau @@ -0,0 +1,54 @@ +local RunService = game:GetService("RunService") + +local Identifier = require(script.Parent.Identifier) + +local ServerEvent = require(script.Server) +local ClientEvent = require(script.Client) + +export type Event = { + Id: string, + Validate: (...unknown) -> T..., + + ServerEvent: ServerEvent.Server?, + ClientEvent: ClientEvent.Client?, + + Server: (self: Event) -> ServerEvent.Server, + Client: (self: Event) -> ClientEvent.Client, +} + +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) + end + + return self.ServerEvent :: any +end + +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, self.Validate) + end + + return self.ClientEvent :: any +end + +local function Event(Name: string, Validate: (...unknown) -> T...): Event + assert(not Identifier.Exists(Name), "Cannot create event with duplicate name!") + + return { + Id = Identifier.Shared(Name):Await(), + Validate = Validate, + + ServerEvent = nil, + ClientEvent = nil, + + Server = Server, + Client = Client, + } :: any +end + +return Event diff --git a/lib/Function.luau b/lib/Function.luau deleted file mode 100644 index 3b8f859..0000000 --- a/lib/Function.luau +++ /dev/null @@ -1,39 +0,0 @@ -local RunService = game:GetService("RunService") - -local Future = require(script.Parent.Parent.Future) - -local Identifier = require(script.Parent.Identifier) -local ServerEvent = require(script.Parent.ServerEvent) -local ClientEvent = require(script.Parent.ClientEvent) - -local Function = {} -Function.__index = Function - -function Function.new(Name: string, ArgsValidate: (...unknown) -> A..., ReturnsValidate: (...unknown) -> R...) - return setmetatable({ - EventId = Identifier.Shared(Name):Await() :: string, - - ArgsValidate = ArgsValidate, - ReturnsValidate = ReturnsValidate, - }, Function) -end - -export type Function = typeof(Function.new("", function(): A... end, function(): R... end)) - -function Function.Server(self: Function, Callback: (Player, A...) -> R...) - assert(RunService:IsServer(), "Cannot use server function from client.") - - ServerEvent.Listen(self.EventId, function(Player, ...) - if pcall(self.ArgsValidate, ...) then - return Callback(Player, ...) - end - end) -end - -function Function.Client(self: Function, ...: A...): typeof(Future.new(function(): R... end)) - assert(RunService:IsClient(), "Cannot use client function from server.") - - return ClientEvent.Call(self.EventId, table.pack(...)) -end - -return Function diff --git a/lib/Identifier.luau b/lib/Identifier.luau index 9634d51..ee8cc70 100644 --- a/lib/Identifier.luau +++ b/lib/Identifier.luau @@ -36,6 +36,10 @@ function Identifier.Shared(Name: string) end, Name) end +function Identifier.Exists(Name: string) + return RunService:IsServer() and Remote:GetAttribute(Name) ~= nil +end + function Identifier.Unique() NextUnique += 1 diff --git a/lib/ServerEvent.luau b/lib/ServerEvent.luau index 7b071a4..0e61fb8 100644 --- a/lib/ServerEvent.luau +++ b/lib/ServerEvent.luau @@ -13,8 +13,8 @@ type EventMap = { [string]: { Data } } ServerEvent.ListenerMap = {} :: { [string]: (Player, ...any) -> () } ServerEvent.OutgoingMap = {} :: { [Player]: { Fire: EventMap?, Call: { Data }? } } -local FireSectionCheck = Guard.Check(Guard.Optional(Guard.Map(Guard.String, Guard.List(Guard.Any)))) -local CallSectionCheck = Guard.Check(Guard.Optional(Guard.Map(Guard.String, Guard.List(Guard.Any)))) +local FireSectionCheck = Guard.Check(Guard.Map(Guard.String, Guard.List(Guard.Any))) +local CallSectionCheck = Guard.Check(Guard.Map(Guard.String, Guard.List(Guard.Any))) function ServerEvent.Start() debug.setmemorycategory("Red.ServerEvent.Start") @@ -24,7 +24,7 @@ function ServerEvent.Start() local FireSectionValid, FireSection = FireSectionCheck(UnknownFireSection) - if FireSectionValid and FireSection then + if FireSectionValid then debug.profilebegin("Red.ServerEvent.Start.MultipleFire") for EventId, CallList in FireSection do @@ -42,7 +42,7 @@ function ServerEvent.Start() local CallSectionValid, CallSection = CallSectionCheck(UnknownCallSection) - if CallSectionValid and CallSection then + if CallSectionValid then debug.profilebegin("Red.ServerEvent.Start.Call") for EventId, CallList in CallSection do diff --git a/lib/init.luau b/lib/init.luau index 6957af3..6d67583 100644 --- a/lib/init.luau +++ b/lib/init.luau @@ -39,5 +39,5 @@ else end return { - Event = require(script.Event).new, + Event = require(script.Event), } diff --git a/package-lock.json b/package-lock.json index 0ba7cc1..62877de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "vitepress": "^1.0.0-rc.10", + "vitepress": "^1.0.0-rc.20", "vue": "^3.3.4" } }, @@ -601,6 +601,28 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.2.tgz", + "integrity": "sha512-Tla7hH9oeXHOlJyBFdoqV61xWE9FZf/y2g+gFVwQ2vE1/eBzjUno5JCd3Hdb5oATve5OF6xNjZ/4VIZhVVx+hA==", + "dev": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.3.tgz", + "integrity": "sha512-T5k6kTXak79gwmIOaDF2UUQXFbnBE0zBUzF20pz7wDYu0RQMzWg+Ml/Pz50214NsFHBITkoi5VtdjFZnJ2ijjA==", + "dev": true + }, "node_modules/@types/web-bluetooth": { "version": "0.0.17", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", @@ -1060,9 +1082,9 @@ "dev": true }, "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -1203,25 +1225,38 @@ } }, "node_modules/vitepress": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.10.tgz", - "integrity": "sha512-+MsahIWqq5WUEmj6MR4obcKYbT7im07jZPCQPdNJExkeOSbOAJ4xypSLx88x7rvtzWHhHc5aXbOhCRvGEGjFrw==", + "version": "1.0.0-rc.20", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.20.tgz", + "integrity": "sha512-CykMUJ8JLxLcGWek0ew3wln4RYbsOd1+0YzXITTpajggpynm2S331TNkJVOkHrMRc6GYe3y4pS40GfgcW0ZwAw==", "dev": true, "dependencies": { "@docsearch/css": "^3.5.2", "@docsearch/js": "^3.5.2", + "@types/markdown-it": "^13.0.1", "@vue/devtools-api": "^6.5.0", "@vueuse/core": "^10.4.1", "@vueuse/integrations": "^10.4.1", "focus-trap": "^7.5.2", "mark.js": "8.11.1", "minisearch": "^6.1.0", - "shiki": "^0.14.3", + "shiki": "^0.14.4", "vite": "^4.4.9", "vue": "^3.3.4" }, "bin": { "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4.3.2", + "postcss": "^8.4.30" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } } }, "node_modules/vscode-oniguruma": { diff --git a/package.json b/package.json index a374483..8926e47 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "type": "module", "devDependencies": { - "vitepress": "^1.0.0-rc.10", + "vitepress": "^1.0.0-rc.20", "vue": "^3.3.4" }, "scripts": { diff --git a/wally.toml b/wally.toml index 7c56da4..236d3e1 100644 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] name = "red-blox/red" -version = "2.0.0-rc.11" +version = "2.0.0" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" license = "MIT"