# [Radio](https://github.com/RoStrap/RoStrapUI/blob/master/Radio.lua)¶

Registers a Material Design Radio PseudoInstance which can be instantiated via `PseudoInstance.new("Radio")`

    local Resources = require(game:GetService("ReplicatedStorage"):WaitForChild("Resources"))
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")
    local Radio = PseudoInstance.new("Radio")

Only Global ZIndexBehavior is officially supported.

## Radio API¶

Radio inherits from PseudoInstance and SelectionController, so all properties, methods, and events of these can also be used on Checkboxes.

### Radio:SetChecked¶

void Radio:SetChecked(boolean Checked = not self.Checked)

Sets the `Checked` property and animates to the new state. Fires `OnChecked`

### Fields¶

#### Wrapped Properties¶

Properties which access its top-level ImageButton:

Property | Type
AnchorPoint | Vector2
Name | string
Parent | Instance
Position | UDim2
LayoutOrder | int
NextSelectionDown | Instance
NextSelectionLeft | Instance
NextSelectionRight | Instance
NextSelectionUp | Instance
ZIndex | int

#### SelectionController Properties¶

Property | Type | Description
---|---|---
Checked | Boolean | Whether the Radio is Checked
Disabled | Boolean | Whether the Radio is Disabled
PrimaryColor3 | Color3 | The Color3 of the Radio when Checked
Theme | Enumeration.MaterialTheme | "Dark" or "Light" colored frame when not Checked

#### Events¶

Event | Description
OnChecked | Fires after the `Checked` property was changed

###### Radio inherits from [PseudoInstance](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstance-api)¶

### Example¶

[Click here for the example place](https://rostrap.github.io/assets/demos/RadioGroup.rbxl)

Demo code:

    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")

    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Color = Resources:LoadLibrary("Color")
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    local LocalPlayer repeat LocalPlayer = Players.LocalPlayer until LocalPlayer or not wait()
    local PlayerGui repeat PlayerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui") until PlayerGui or not wait()

    local Screen = Instance.new("ScreenGui", PlayerGui)
    local Frame = Instance.new("Frame", Screen)
    Frame.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
    Frame.BorderSizePixel = 0
    Frame.Size = UDim2.new(1, 0, 1, 0)

    local Template = PseudoInstance.new("Radio")
    Template.AnchorPoint = Vector2.new(0.5, 0.5)
    Template.Theme = "Dark"

    local RadioGroup = PseudoInstance.new("RadioGroup")

    local Choice1 = Template:Clone()
    Choice1.PrimaryColor3 = Color.Red[500]
    Choice1.Position = UDim2.new(0.5, 0, 0.5, -32)
    Choice1.Parent = Frame

    local Choice2 = Template:Clone()
    Choice2.PrimaryColor3 = Color.Yellow[500]
    Choice2.Position = UDim2.new(0.5, 0, 0.5, 0)
    Choice2.Parent = Frame

    local Choice3 = Template:Clone()
    Choice3.PrimaryColor3 = Color.Green[500]
    Choice3.Position = UDim2.new(0.5, 0, 0.5, 32)
    Choice3.Parent = Frame

    RadioGroup:Add(Choice1, "Apples")
    RadioGroup:Add(Choice2, "Bananas")
    RadioGroup:Add(Choice3, "Carrots")

    RadioGroup.SelectionChanged:Connect(print)


# [Normal](https://github.com/RoStrap/Math/blob/master/Normal.lua)¶

Random number generator along a Normal distribution curve

`number Normal(Average, StdDeviation)` returns: `Normal curve [-1, 1] * StdDeviation + Average`


# RoStrap¶

RoStrap is the name of a [Roblox plugin](https://www.roblox.com/library/725884332/RoStrap) and resource management system designed to expedite game development on Roblox. Its main goals are to increase code reusability and simplify the networking of resources **without penalty for not using features**. The plugin allows for easy access and installation of incredibly useful **open-source** libraries hosted on GitHub.

With the click of a button you can install any library:

Gain access to a variety of beautiful Material Design elements, like the [Choice Dialog](https://rostrap.github.io/Libraries/RoStrapUI/ChoiceDialog):

Or some very clickable Material Design [RippleButtons](https://rostrap.github.io/Libraries/RoStrapUI/RippleButton), [Checkboxes](https://rostrap.github.io/Libraries/RoStrapUI/Checkbox), and [Radio buttons](https://rostrap.github.io/Libraries/RoStrapUI/Radio):

And [much more](https://rostrap.github.io/Libraries/)!


# Contributing¶

If you wish to contribute a Library, simply submit a pull request to [Libraries.lua](https://github.com/RoStrap/Libraries/blob/master/Libraries.lua). Simply insert a new table with fields `URL` (you can leave off <https://github.com>) and an optional `Description` and you are good to go!

## Library Standards¶

Libraries contributed to RoStrap must be useful, reusable, and reasonably universal. The code must be stable, maintained, readable, and speedy. It must have a readme or documentation website that clearly outlines its use and includes functioning demo code. Images/Videos help.

Submitted Libraries may or may not be subject to code review.

All images must meet the [image standards](https://rostrap.github.io/Contributing/Image Standards)

## How to Package Libraries¶

To indicate to the plugin's installer that a Lua file is a descendant of another, simply make a folder with the name of the parent Lua file, and place the parent Lua file inside with the name "init" (or "main" or "_"). Here is [an example of a single Library that has ModuleScripts within it using this method.](https://github.com/evaera/EvLightning)

## Dependencies¶

Dependencies are detected by the plugin using [this handy script](https://github.com/RoStrap/Libraries/blob/GetDeps/GetDependencies.ignore.lua). It can detect from the following source code that `Tween` and `Maid` are the names a Library will need to have installed in order to work. If your source code intends to rely on dependencies from the RoStrap system, [the detection script](https://github.com/RoStrap/Libraries/blob/GetDeps/GetDependencies.ignore.lua) _must_ be able to successfully determine the dependencies of your Libraries.

    local ReplicatedStorage = game:GetService("ReplicatedStorage") -- You have to use game:GetService
    local Resources = require(ReplicatedStorage:WaitForChild("Resources")) -- You have to use WaitForChild
    local require = Resources.LoadLibrary -- You can localize LoadLibrary

    local Tween = require("Tween") -- Either of these work
    local Maid = Resources:LoadLibrary('Maid')

## RoStrap Discord¶

Questions? Comments? Concerns? Join us!

[ ](https://discord.gg/ZaT5RwV)


# [WeightedProbabilityFunction](https://github.com/RoStrap/Math/blob/master/WeightedProbabilityFunction.lua)¶

A syntactically pleasing way of creating functions which randomly pick an option based upon its relative probability

Example:

    local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Resources")).LoadLibrary
    local WeightedProbabilityFunction = require("WeightedProbabilityFunction")

    local CoinToss = WeightedProbabilityFunction.new {
        Heads = 0.5;
        Tails = 0.5;

    local DiceRoll = WeightedProbabilityFunction.new {
        -- These weights are relative to one another, so if they are all 1, they will each have a 1/6 chance
        [1] = 1;
        [2] = 1;
        [3] = 1;
        [4] = 1;
        [5] = 1;
        [6] = 1;

    local DiceRollOrCoinToss = WeightedProbabilityFunction.new {
        -- 9/10 of the time, toss a coin
        -- 1/10 of the time, roll a dice

        [CoinToss] = 9;
        [DiceRoll] = 1;

    local t = {}

    for i = 1, 10000 do
        local pick = DiceRollOrCoinToss()
        t[pick] = (t[pick] or 0) + 1
    end

    table.foreach(t, print)


# [Tween](https://github.com/RoStrap/Interpolation/blob/master/Tween.lua)¶

RoStrap's premier Tween Module built for Roblox. Allows you to write interpolation code faster with a clear and simple API.

## Declaration¶

First let's load the module:

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Tween = Resources:LoadLibrary("Tween")
    local Enumeration = Resources:LoadLibrary("Enumeration")

Once you've loaded the Tween Module, there are two ways to create a Tween. You can either interpolate a property of an object, or create a custom Tween with a function you want called each `Heartbeat` frame.

## Library API¶

### Tween¶

TweenObject Tween(Object Object, string PropertyName, Variant EndValue, Enumeration.EasingFunction EasingFunction, number Time, bool Override = false, function(TweenStatus) Callback = nil, [arg InitialParameter])

Tween function for tweening any property. Like `GuiObject:TweenPosition()` but the first two arguments are Object and Property. If InitialParameter isn't `nil`, it will be pushed to the first argument passed to the Callback.

    -- Localizing the `.Value` of an EasingFunction is fastest
    local OutQuad = Enumeration.EasingFunction.OutQuad.Value
    local Standard = Enumeration.EasingFunction.Standard.Value

    Tween(workspace.Part, "CFrame", CFrame.new(10, 10, 10), OutQuad, 2, true)
    Tween(workspace.Part, "Transparency", 1, Standard, 2, true)

`Override` works a bit differently in this system than in Roblox's. Roblox's override parameter should be named "Overridable" whereas this parameter is whether the new Tween should override any previous open Tweens. The same things can be accomplished with both, but this is a notable difference.

### Tween.new¶

TweenObject Tween.new(number Duration, string EasingFunctionName, function Callback, [arg InitialParameter])

Tweens created with `Tween.new` will call Callback every tween `Heartbeat` frame, with EasingFunction interpolating from 0 to 1 over the allotted duration. If InitialParameter isn't `nil`, it will be pushed to the first argument passed to the Callback.

    Tween.new(number Duration, string EasingFunctionName, function Callback)

    local Deceleration = Enumeration.EasingFunction.Deceleration.Value

    local newTween = Tween.new(0.5, Deceleration, function(x)
        print("This will be called with each 'Frame' of this tween")
    end)

## TweenObject API¶

### Tween:Resume¶

TweenObject Tween:Resume()

Resumes a Tween that was Stop()ed

### Tween:Stop¶

TweenObject Tween:Stop()

Stops a Tween

### Tween:Wait¶

TweenObject Tween:Wait()

Yields until Tween finishes interpolating

### Tween:Restart¶

TweenObject Tween:Restart()

Makes the Tween go back to timeElapsed = 0

### Fields¶

Field | Description | Type | Default value
---|---|---|---
Running | Whether the Tween is active | bool | true


# Website¶

The website is compiled from markdown (`.md`) files, so anyone can help write documentation for this website! Click [here](https://github.com/RoStrap/rostrap.github.io/tree/master/docs) to visit the repository where the markdown files are kept. The [documentation for the Signal library](https://rostrap.github.io/Libraries/Events/Signal/) follows the format that will be standard.


# [Color](https://github.com/RoStrap/RoStrapUI/blob/master/Color.lua)¶

Color utilities with [Material Design's 2014 Color Palette](https://material.io/design/color/#tools-for-picking-colors)

## Library API¶

### Color.toRGBString¶

string Color.toRGBString(Color3 Color [, number Alpha])

Returns a string representation of the rgb or rgba value for a given Color.

### Color.toHexString¶

string Color.toHexString(Color3 Color [, number Alpha])

Returns a string representation of the hexidecimal Color code for a given Color.

### Color.fromHex¶

Color3 Color.fromHex(<number, string> Hex)

Converts a 3-digit or 6-digit hex color to a Color3. Takes in a string of the form: "#FFFFFF" or "#FFF" or a 6-digit hexadecimal number (e.g. 0xFFFFFF)

### Color.toHex¶

number Color.toHex(Color3 Color)

Returns the Color in its hexidecimal-number form.

### Color.COLOR¶

The `Color` table contains all the colors from the [2014 Material Design Color Pallete](https://material.io/design/color/#tools-for-picking-colors). These Colors are structured like so:

    Cyan = {
        [50] = rgb(224, 247, 250);
        [100] = rgb(178, 235, 242);
        [200] = rgb(128, 222, 234);
        [300] = rgb(77, 208, 225);
        [400] = rgb(38, 198, 218);
        [500] = rgb(0, 188, 212);
        [600] = rgb(0, 172, 193);
        [700] = rgb(0, 151, 167);
        [800] = rgb(0, 131, 143);
        [900] = rgb(0, 96, 100);

        Accent = {
            [100] = rgb(132, 255, 255);
            [200] = rgb(24, 255, 255);
            [400] = rgb(0, 229, 255);
            [700] = rgb(0, 184, 212);
        };
    };

Example

    local Color = Resources:LoadLibrary("Color")
    local Cyan = Color.Cyan[500]
    local DarkCyan = Color.Cyan[900]
    local CyanAccent = Color.Cyan.Accent[700]


# [EasingFunctions](https://github.com/RoStrap/Interpolation/blob/master/EasingFunctions.lua)¶

A bunch of reuseable Easing Functions, including those from the Material Design specification ([Standard, Acceleration, and Deceleration](https://material.io/design/motion/speed.html#easing))

These are the available EasingFunctions:

Directionless | In | Out | InOut | OutIn
---|---|---|---|---
Linear | InQuad | OutQuad | InOutQuad | OutInQuad
Spring | InCubic | OutCubic | InOutCubic | OutInCubic
SoftSpring | InQuart | OutQuart | InOutQuart | OutInQuart
RevBack | InQuint | OutQuint | InOutQuint | OutInQuint
RidiculousWiggle | InSine | OutSine | InOutSine | OutInSine
Smooth | InExpo | OutExpo | InOutExpo | OutInExpo
Smoother | InCirc | OutCirc | InOutCirc | OutInCirc
Acceleration | InElastic | OutElastic | InOutElastic | OutInElastic
Deceleration | InBack | OutBack | InOutBack | OutInBack
Sharp | InBounce | OutBounce | InOutBounce | OutInBounce
Standard |  |  |  |

This library returns an array of these functions, with values corresponding to their Enumerations' values.

    local Enumeration = Resources:LoadLibrary("Enumeration")
    local EasingFunctions = Resources:LoadLibrary("EasingFunctions")

    local InSine = EasingFunctions[Enumeration.EasingFunction.InSine.Value]

    -- If you want an array of all EasingFunction Enumerations
    local EnumerationItems = Enumeration.EasingFunction:GetEnumerationItems()

    for i = 1, #EnumerationItems do
        local EnumerationItem = EnumerationItems[i]
        print(EnumerationItem, EasingFunctions[EnumerationItem.Value])
    end


# [Resources](https://github.com/RoStrap/Resources/blob/master/Resources.lua)¶

The core resource manager and library loader for RoStrap. It is designed to streamline the retrieval and networking of resources and centralize the loading of libraries.

## Demo¶

It can be used to require your libraries:

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local Date = Resources:LoadLibrary("Date")
    print(Date())  --> Sat Dec 31 15:38:01 2022

It can be used to manage your RemoteEvents and RemoteFunctions:

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Chatted = Resources:GetRemoteEvent("Chatted")
    local ClientLoaded = Resources:GetRemoteFunction("ClientLoaded")

## Functionality¶

Upon being [required](http://wiki.roblox.com/index.php?title=Global_namespace/Roblox_namespace#require) on the server for the first time, [Resources](https://github.com/RoStrap/Resources/blob/master/Resources.lua) moves libraries within `ServerStorage.Repository` to folder `ReplicatedStorage.Resources.Libraries`, where both the client and server can [require](http://wiki.roblox.com/index.php?title=Global_namespace/Roblox_namespace#require) them via the function `LoadLibrary`.

  * Only [Folders](http://wiki.roblox.com/index.php?title=API:Class/Folder) and libraries should go in this repository.
    * Folder heiarchy of libraries is ignored.
  * A [ModuleScript](http://wiki.roblox.com/index.php?title=API:Class/ModuleScript) and its descendants are considered a single library.
    * Thus, only parent [ModuleScripts](http://wiki.roblox.com/index.php?title=API:Class/ModuleScript) will be accessible via `LoadLibrary`
  * Anything within the Repository with "Server" (case-sensitive) in its name as well as its children will not be accessible to clients
    * This includes [Folders](http://wiki.roblox.com/index.php?title=API:Class/Folder), libraries, and descendants of libraries.
    * Server-only objects will be located within folder `ServerStorage.Resources`

Info

Internally, `LoadLibrary` caches and returns `require(Resources:GetLibrary(LibraryName))`. `GetLibrary` is the only function that can "retrieve" objects from both `ServerStorage` and `ReplicatedStorage`. This is because server libraries are added directly to its cache.

## API¶

All methods of [Resources](https://github.com/RoStrap/Resources/blob/master/Resources.lua) have hybrid syntax, meaning they can be called using either a `:` or `.`

### LoadLibrary¶

Variant Resources:LoadLibrary(string LibraryName)

A require-by-string function which internally calls `require(Resources:GetLibrary(string LibraryName))` and caches the results.

Example

    local SortedArray = Resources:LoadLibrary("SortedArray")

    -- Some people like to overwrite the default require function
    local require = Resources.LoadLibrary

    local Signal = require("Signal")

Info

This is the only function with a hard-coded name for its cache, so you may access cached require results using the following:

    -- Resources uses this table to cache LoadLibrary results
    local LoadedLibraries = Resources:GetLocalTable("LoadedLibraries")

### Get functions¶

Ref<Instance> Resources:GetCLASSNAME(string InstanceName)

Get functions can be procedurally generated by [Resources](https://github.com/RoStrap/Resources/blob/master/Resources.lua) at run-time in the form of `Resources:GetCLASSNAME(string InstanceName)`. These functions return an [Instance](http://wiki.roblox.com/index.php?title=API:Class/Instance) named `InstanceName` under `Resources:GetFolder(CLASSNAME:gsub("y$", "ie") .. "s")`. On the server, missing instances will be instantiated via [Instance.new](http://wiki.roblox.com/index.php?title=Instance_\(Data_Structure\)). On the client, the function will yield for the missing instances via [WaitForChild](http://wiki.roblox.com/index.php?title=API:Class/Instance/WaitForChild).

The following is the internal call tree. Keep in mind that these functions cache so the 3rd function will only run once per machine and `GetFolder` will only run once per function generation (e.g. the first time `GetRemoteEvent` is called).

Step | Object to search for | Function call
---|---|---
1 | RemoteEvent `Chatted` inside | GetRemoteEvent("Chatted")
2 | Folder `RemoteEvents` in | GetFolder("RemoteEvents")
3 | `Resources` | ROOT

Info

  * Caches results in `Resources:GetLocalTable(CLASSNAME:gsub("y$", "ie") .. "s")`

Example

Get functions can also manage instance types that aren't instantiable by `Instance.new`. However, these instances must be preinstalled in the locations in which they would otherwise be instantiated because they cannot be generated at run-time. This allows you to do things like the following:

    local Falchion = Resources:GetSword("Falchion")

#### GetLocal functions¶

Ref<Instance> Resources:GetLocalCLASSNAME(string InstanceName)

Get functions in the form of `GetLocalCLASSNAME` work in the same way as regular Get functions, except clients may also instantiate missing instances and `GetLocalFolder` searches in different locations. Not meant to be used very much.

**Machine** | LOCALSTORAGE | LOCALRESOURCES
---|---|---
**Server** | `ServerStorage` | `ServerStorage.Resources`
**Client** | `Players.LocalPlayer.PlayerScripts` | `Players.LocalPlayer.PlayerScripts.Resources`
Step | Object to search for | Function call
---|---|---
1 | BindableEvent `Attacking` inside | GetLocalBindableEvent("Attacking")
2 | Folder `BindableEvents` in | GetLocalFolder("BindableEvents")
3 | Folder `LOCALSTORAGE.Resources` | LOCALRESOURCES

Example

    local Attacking = Resources:GetLocalBindableEvent("Attacking")

Here is what the above code would do if ran by the server versus what it would do if ran by a client:

Server | Client

Warning

In Play-Solo Mode, all local objects will go under `ServerStorage`, as there is no difference between the client and server. If you use identical Local-function calls on the client and server, this could cause conflicts in Play-Solo. The same problem applies to caches, which have "Local" appended to the front of the string to differentiate from regular caches. `LOCALSTORAGE` is typically just for `GetLocalBindableEvent` calls and having a place to store server-only Libraries, which are under `GetLocalFolder("Resources")`.

### GetLocalTable¶

table Resources:GetLocalTable(string TableName)

A function which caches a local table by string, and returns said table. Does not replicate. Useful for eliminating the need for [_G](http://wiki.roblox.com/index.php?title=Global_namespace/Basic_functions#G) or ModuleScripts which return an empty table.

Note

Because this is hard-coded and not procedurally generated, it doesn't obey the rules of other GetLocal functions.

Example

    -- Can be used in any script on the same machine to get this table
    local MyItems = Resources:GetLocalTable("MyItems") -- {}

Info

Resources uses `GetLocalTable` internally, so you can access its internals using this function:

    -- Resources uses this table to cache LoadLibrary results
    local LoadedLibraries = Resources:GetLocalTable("LoadedLibraries")

## RoStrap Discord¶

Questions? Comments? Concerns? Join us!

[ ](https://discord.gg/ZaT5RwV)


# [Make](https://github.com/RoStrap/Helper/blob/master/Make.lua)¶

Shorthand for instance declarations.

You probably shouldn't use this function, but if you really really like it, here it is.

    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local Make = Resources:LoadLibrary("Make")

    Make("TextLabel"){
        TextSize = 11;
        Font = "Arial";
        Name = "Yup";
        Parent = workspace;

You can also create multiple instances at once. When you pass subsequent tables as parameters, the object created by the first table is Cloned and modified by the properties in its table. Each subsequent table generates a new instance.

    Make("TextLabel")({
        TextSize = 11;
        Font = "Arial";
        Name = "Yup";
        Parent = workspace;
    }, {
        Name = "So!";
        Font = "SourceSans";
    }, {
        Name = "Yellow!";
    })


# [Keys](https://github.com/RoStrap/Input/blob/master/Keys.lua)¶

A light-weight library for simplifying Key input.

## API¶

### Keys¶

The library returns a table `Keys`. Within this table, Keys from Enum.KeyCode can be indexed.

    local Resources = require(game:GetService("ReplicatedStorage"):WaitForChild("Resources"))
    local Keys = Resources:LoadLibrary("Keys")

    -- Each of these can be called with either a '.' or a ':', as it doesn't need 'self'
    Keys:Pause() -- Disconnects this module's InputEnded and InputBegan connections to UserInputService
    Keys:Resume() -- Reconnects what Pause disconnects

    local Q = Keys.Q -- returns a Key Object

### Key¶

These table objects contain two custom Signals (technically, they are interfaces). `KeyDown` and `KeyUp`

    local Q = Keys.Q -- returns a Key Object

    Q.KeyDown:Connect(function()
        print("Q was pressed!")
    end)

    Q.KeyUp:Connect(function()
        print("Q was let go!")
    end)

### Signals¶

In this module, `KeyDown` and `KeyUp` Signals have the following functions:

    local Shift = Keys.Shift.KeyDown
    local E = Keys.E.KeyDown

    E:Connect(function() -- Connects a function
        print("E!")
    end)

    E:Press() -- Fires open connections
    E:Fire() -- Same as Press()
    E:Wait() -- Yields until event fires

    ;(Shift + E):Connect(function() -- You can add 2 Keys together to get a combo event!
        print("Shift + E!")
        -- NOTE: Neither Shift nor E fire when (Shift + E) fires
        -- If you want to fire one or both of them, do Shift:Press() or E:Press()
    end)

## Overhead¶

This is an extremely light library. The tables within `Keys` are merely interface tables, and are not directly involved with calling connected Functions. The tables interface with a system that looks mostly like this:

    local KeyUps = {
        Q = function()
            print("Q was let up")
        end;

        E = function()
            print("E was let up")
        end

    UserInputService.InputEnded:Connect(function(Data, GuiInput)
        if not GuiInput and Data.KeyCode ~= Enum.KeyCode.Unknown then
            local Function = KeyUps[Data.KeyCode.Name]
            if Function then
                Function()
            end
        end
    end)


# [Sentry](https://github.com/RoStrap/Sentry)¶

A library for reporting errors/warnings/messages to your [Sentry account](https://sentry.io).

## Getting started¶

Sign up for [Sentry](https://sentry.io/) to get started.

Navigate to the `Projects` tab on the left-hand side.

In the top right, navigate to "Add new..." and select "Project"

Don't select a project or framework, since this Library isn't officially supported by Sentry (but don't worry, **it is still officially supported by RoStrap**). Give your project a name and create project!

Now you need to find your DSN. You will hopefully be shown the following screen, but if not you can always find these in the "Settings" tab.

You will need to locate a DSN that looks like this:

Next, install this library using [the RoStrap plugin](https://www.roblox.com/library/725884332/RoStrap), and navigate to the Server-side Sentry library.

Under the configuration, modify the `DSN` variable to your DSN

Now you have a working Sentry library!

## API¶

The API is the same for both the client and the server.

    local Sentry = Resources:LoadLibrary("Sentry")
    local Enumeration = Resources:LoadLibrary("Enumeration") -- Optional

    Sentry:Post(string Message [, string Traceback, <Enum, Enumeration> MessageType])
    -- Traceback defaults to debug.traceback()
    -- MessageType defaults to "Error"
    -- MessageType accepts any of the following Enumerations (or their corresponding numbers or strings)

    -- 0: Enumeration.IssueType.Debug
    -- 1: Enumeration.IssueType.Info
    -- 2: Enumeration.IssueType.Warning
    -- 3: Enumeration.IssueType.Error
    -- 4: Enumeration.IssueType.Fatal

    -- MessageType also accepts Roblox's MessageType Enums (but NOT their numbers or strings)
    -- Enum.MessageType.MessageError
    -- Enum.MessageType.MessageInfo
    -- Enum.MessageType.MessageOutput
    -- Enum.MessageType.MessageWarning

It is intended to be easily compatible with the `Try` library

    local Try = Resources:LoadLibrary("Try")
    local Sentry = Resources:LoadLibrary("Sentry")

    Try(function(x) error("test client error " .. x) end, "1")
        :Catch(Sentry.Post)

Here are your free Sentry rate limits:

Enjoy!


# [Signal](https://github.com/RoStrap/Events/blob/master/Signal.lua)¶

Summary

A class for creating API-compatible Roblox Events

This new version originally designed by Anaminus addresses two flaws in previous implementations:

  * Previous versions held a reference to the last set of fired arguments.
  * Arguments would be overridden if the signal was fired by a listener.

## Library API¶

### Signal.new¶

Signal Signal.new([function Constructor, function Destructor])

Returns a new signal. Receives optional constructor and destructor functions. The constructor is called when the number of listeners/threads becomes greater than 0. The destructor is called when then number of threads/listeners becomes 0. The values returned by the constructor are passed into the destructor.

Example

    local Signal = Resources:LoadLibrary("Signal")
    local Event = Signal.new()

## Signal API¶

### Signal:Fire¶

void Signal:Fire(...)

Fire the signal, passing the arguments to each listener and waiting threads. Arguments are **always** passed by reference.

### Signal:Wait¶

(...) Signal:Wait()

Block the current thread until the signal is fired. Returns the arguments passed to Fire.

### Signal:Connect¶

PseudoScriptConnection Signal:Connect(function Function [, any Argument])

Sets a function to be called when the signal is fired. The listener function receives the arguments passed to Fire. Returns a [PseudoScriptConnection](https://rostrap.github.io/Libraries/Events/Signal/#pseudoscriptconnection-api)

### Signal:Destroy¶

void Signal:Destroy()

Disconnects all listeners and becomes unassociated with currently blocked threads. The signal becomes unusable and ready to be garbage collected.

### Fields¶

Field | Description | Type | Default value
---|---|---|---
Event | Interface which can only access `Connect` and `Wait` | PseudoScriptSignal | N / A
Bindable | Dispatches scheduler-compatible Threads | BindableEvent | Instance.new("BindableEvent")
Arguments | Holds arguments for pending listener functions and Threads | table | { }
Connections | SignalConnections connected to the signal | table | { }
Constructor | Called when the number of listeners becomes greater than 0 | function | nil
Destructor | Called when then number of listeners becomes 0 | function | nil

## PseudoScriptConnection API¶

void PseudoScriptConnection:Disconnect()

Disconnects the listener, causing it to no longer be called when the signal is fired.

### Fields¶

Field | Description | Type | Default value
---|---|---|---
Connected | Whether the listener is connected | bool | true


# [RadioGroup](https://github.com/RoStrap/RoStrapUI/blob/master/RadioGroup.lua)¶

Registers a Material Design RadioGroup PseudoInstance which can be instantiated via `PseudoInstance.new("RadioGroup")`. It acts as an interface for a set of Radio elements.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")
    local RadioGroup = PseudoInstance.new("RadioGroup")

Only Global ZIndexBehavior is officially supported.

## RadioGroup API¶

### RadioGroup:Add¶

void RadioGroup:Add(RadioButton Item, variant Option)

Adds an Item to the group. The `Selection` returned by `GetSelection` will become the `Option` when `Item` is selected.

### RadioGroup:GetSelection¶

Option RadioGroup:GetSelection()

Returns the current Selected `Option`.

### Events¶

Event | Description
SelectionChanged | Fires after Selection changes with the new selected Option

### Example¶

[Click here for the example place](https://rostrap.github.io/assets/demos/RadioGroup.rbxl)

Demo code:

    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")

    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Color = Resources:LoadLibrary("Color")
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    local LocalPlayer repeat LocalPlayer = Players.LocalPlayer until LocalPlayer or not wait()
    local PlayerGui repeat PlayerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui") until PlayerGui or not wait()

    local Screen = Instance.new("ScreenGui", PlayerGui)
    local Frame = Instance.new("Frame", Screen)
    Frame.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
    Frame.BorderSizePixel = 0
    Frame.Size = UDim2.new(1, 0, 1, 0)

    local Template = PseudoInstance.new("Radio")
    Template.AnchorPoint = Vector2.new(0.5, 0.5)
    Template.Theme = "Dark"

    local RadioGroup = PseudoInstance.new("RadioGroup")

    local Choice1 = Template:Clone()
    Choice1.PrimaryColor3 = Color.Red[500]
    Choice1.Position = UDim2.new(0.5, 0, 0.5, -32)
    Choice1.Parent = Frame

    local Choice2 = Template:Clone()
    Choice2.PrimaryColor3 = Color.Yellow[500]
    Choice2.Position = UDim2.new(0.5, 0, 0.5, 0)
    Choice2.Parent = Frame

    local Choice3 = Template:Clone()
    Choice3.PrimaryColor3 = Color.Green[500]
    Choice3.Position = UDim2.new(0.5, 0, 0.5, 32)
    Choice3.Parent = Frame

    RadioGroup:Add(Choice1, "Apples")
    RadioGroup:Add(Choice2, "Bananas")
    RadioGroup:Add(Choice3, "Carrots")

    RadioGroup.SelectionChanged:Connect(print)


# [Checkbox](https://github.com/RoStrap/RoStrapUI/blob/master/Checkbox.lua)¶

Registers a Material Design Checkbox PseudoInstance which can be instantiated via `PseudoInstance.new("Checkbox")`

    local Resources = require(game:GetService("ReplicatedStorage"):WaitForChild("Resources"))
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")
    local Checkbox = PseudoInstance.new("Checkbox")

Only Global ZIndexBehavior is officially supported.

## Checkbox API¶

Checkbox inherits from PseudoInstance and SelectionController, so all properties, methods, and events of these can also be used on Checkboxes.

### Checkbox:SetChecked¶

void Checkbox:SetChecked(boolean Checked = not self.Checked)

Sets the `Checked` property and animates to the new state. Fires `OnChecked`

### Fields¶

#### Wrapped Properties¶

Properties which access its top-level ImageButton:

Property | Type
AnchorPoint | Vector2
Name | string
Parent | Instance
Position | UDim2
LayoutOrder | int
NextSelectionDown | Instance
NextSelectionLeft | Instance
NextSelectionRight | Instance
NextSelectionUp | Instance
ZIndex | int

#### SelectionController Properties¶

Property | Type | Description
---|---|---
Indeterminate | Boolean | Whether the Checkbox is Indeterminate
Checked | Boolean | Whether the Checkbox is Checked
Disabled | Boolean | Whether the Checkbox is Disabled
PrimaryColor3 | Color3 | The Color3 of the Checkbox when Checked
Theme | Enumeration.MaterialTheme | "Dark" or "Light" colored frame when not Checked

#### Events¶

Event | Description
OnChecked | Fires after the `Checked` property was changed

###### Checkbox inherits from [PseudoInstance](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstance-api)¶

### Example¶

[Click here for the example place](https://rostrap.github.io/assets/demos/Checkbox.rbxl)

Demo code:

    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")

    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Color = Resources:LoadLibrary("Color")
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    local LocalPlayer repeat LocalPlayer = Players.LocalPlayer until LocalPlayer or not wait()
    local PlayerGui repeat PlayerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui") until PlayerGui or not wait()

    local Screen = Instance.new("ScreenGui", PlayerGui)
    local Frame = Instance.new("Frame", Screen)
    Frame.BackgroundColor3 = Color.Grey[200]
    Frame.BorderSizePixel = 0
    Frame.Size = UDim2.new(1, 0, 1, 0)

    local ReceiveUpdates = PseudoInstance.new("Checkbox")
    ReceiveUpdates.PrimaryColor3 = Color.Teal[500]
    ReceiveUpdates.Checked = true
    ReceiveUpdates.OnChecked:Connect(function(On)
        print(On) -- On is the new value of `Checked`
    end)
    ReceiveUpdates.AnchorPoint = Vector2.new(0.5, 0.5)
    ReceiveUpdates.Position = UDim2.new(0.5, 0, 0.5, 0)
    ReceiveUpdates.Theme = "Light" -- "Dark" is also valid
    ReceiveUpdates.Parent = Frame


# [Enumeration](https://github.com/RoStrap/Classes/blob/master/Enumeration.lua)¶

Pure-lua Enumeration implementation that function identically to Roblox Enums, except one may declare their own. Enumerations are a set of named reference constants for when a value must be a discrete value from a list. For example:

    local Enumeration = Resources:LoadLibrary("Enumeration")

    Enumeration.ButtonType = {"Custom", "Flat", "Raised"}
    Enumeration.SelectionControllerType = {"Checkbox", "Radio", "Switch"}

    local Radio = Enumeration.SelectionControllerType.Radio

    print(Radio)
    print(Radio.EnumerationType)
    print(Radio.Name)
    print(Radio.Value)

    > Enumeration.SelectionControllerType.Radio
    > SelectionControllerType
    > Radio
    > 1

Enumerations have a `Value` equal to their index in the declarative array minus one:

Enumeration.ButtonType = { | "Custom" | "Flat" | "Raised" | }
---|---|---|---|---
| 0 | 1 | 2 |

In this implementation, we use `Enumeration` in the places where Roblox uses `Enum`:

    print("All Roblox Enums:")
    for i, EnumType in ipairs(Enum:GetEnums()) do
        print(i, EnumType)
        for j, EnumName in ipairs(EnumType:GetEnumItems()) do
            print("   ", j, EnumName)
        end
    end

    print("All RoStrap Enumerations:")
    for i, EnumerationType in ipairs(Enumeration:GetEnumerations()) do
        print(i, EnumerationType)
        for j, EnumName in ipairs(EnumerationType:GetEnumerationItems()) do
            print("   ", j, EnumName)
        end
    end


# [Table](https://github.com/RoStrap/DataTypes/blob/master/Table.lua)¶

A few utility functions which operate on Tables. Purposefully light.

## Library API¶

### Table.Move¶

a2 Table.Move(array a1, number f, number e, number t, array a2)

Moves elements [f, e] from array a1 into a2 starting at index t. Equivalent to Lua 5.3's `table.move`.

    -- @param table a1 from which to draw elements from range
    -- @param number f starting index for range
    -- @param number e ending index for range
    -- @param number t starting index to move elements from a1 within [f, e]
    -- @param table a2 the second table to move these elements to
    --  @default a2 = a1
    -- @returns a2

Example

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local Table = Resources:LoadLibrary("Table")
    local Debug = Resources:LoadLibrary("Debug")

    print(Debug.TableToString(Table.Move({1, 2, 3}, 1, 3, 2)))
    -- {1, 1, 2, 3}

    -- Explanation:
    -- Inserts {1, 2, 3} @ 2

### Table.Lock¶

userdata Table.Lock(table Table [, function __call])

Returns a userdata with read-only access to a table. `__call` is the function which may be set as its metamethod of the same name.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local Table = Resources:LoadLibrary("Table")
    local Debug = Resources:LoadLibrary("Debug")

    local t = Table.Lock {
        x = 5

    print(t.x) -- 5
    print(t.y) -- error!
    t.x = 2 -- error!
    t.y = 3 -- error!

    local bar = Table.Lock({
        x = 2;
        y = 3;
    }, Debug.TableToString)

    print(bar()) -- {x = 2, y = 3}


# [SortedArray](https://github.com/RoStrap/DataTypes/blob/master/SortedArray.lua)¶

A class to create sorted arrays. Must contain objects comparable to one another (that can use the `<` and `==` operators). Numbers and strings support these operators by default.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local SortedArray = Resources:LoadLibrary("SortedArray")

    local Array = SortedArray.new()

    -- Alternatively
    local Sorted = SortedArray.new{2, 1, 5, 3} -- Will get `table.sort`ed

## Library API¶

### SortedArray.new¶

table SortedArray.new(array InitialSet [, function SortFunction])

Instantiates and returns a new SortedArray, with optional parameters. \- `InitialSet`: Pass in an array of data which will be sorted upon instantiation. If this is omitted, an empty array is used. \- `SortFunction`: An optional comparison function which is used to customize the element sorting, which will be given two elements `a` and `b` from the array as parameters. The function should return a boolean value specifying whether the first argument should be before the second argument in the sequence. If no comparison function is passed, the Lua-default `a < b` sorting is used.

## SortedArray API¶

`SortedArray:Concat()` and `SortedArray:Unpack()` are equivalent to `table.concat` and `unpack` respectively.

### SortedArray:Insert¶

number SortedArray:Insert(Variant Element)

Inserts an element in the proper place which would preserve the array's orderedness. Returns the index the element was inserted.

    local Sorted = SortedArray.new{1, 2, 3, 5}
    print(Sorted:Insert(4)) -- 4
    print("{" .. Sorted:Concat(", ") .. "}")
    -- {1, 2, 3, 4, 5}

### SortedArray:Find¶

number SortedArray:Find(variant Signature [, function Eq, function Lt])

Finds an Element in a SortedArray and returns its position (or nil if non-existant).

`Signature` is the element to find or something that will be matched by the `Eq` function.

`Eq` is an optional function which checks for equality between the passed-in element and the other elements in the SortedArray.

`Lt` is an optional less-than comparison function, which falls back on the comparison passed in from `SortedArray.new`

Returns the numerical index of the element which was found, else nil.

### SortedArray:Copy¶

table SortedArray:Copy()

Returns a raw array with the same values.

### SortedArray:Clone¶

table SortedArray:Clone()

Returns a SortedArray clone.

### SortedArray:RemoveIndex¶

variant SortedArray:RemoveIndex(number Index)

Removes an element from index, returning that element. The same as `table.remove`.

### SortedArray:RemoveElement¶

variant SortedArray:RemoveElement(variant Signature, function Eq, function Lt)

Searches the array via `SortedArray:Find(Signature, Eq, Lt)`. If found, it removes the value and returns the value, otherwise returns nil. Only removes a single occurence.

    local Sorted = SortedArray.new{1, 2, 3, 3, 3, 3, 5}
    Sorted:RemoveElement(1)
    print("{" .. Sorted:Concat(", ") .. "}") -- {2, 3, 3, 3, 3, 5}
    while Sorted:RemoveElement(3) do end
    print("{" .. Sorted:Concat(", ") .. "}") -- {2, 5}

### SortedArray:SortIndex¶

number SortedArray:SortIndex(number Index)

Removes the value at Index and re-inserts it. This is useful for when a value may have updated in a way that could change it's position in a SortedArray. Returns Index.

### SortedArray:SortElement¶

number SortedArray:SortElement(variant Signature [, function Eq, function Lt])

Calls `RemoveElement(Signature, Eq, Lt)` and re-inserts the value. This is useful for when a value may have updated in a way that could change it's position in a SortedArray. Returns Index.

### SortedArray:Sort¶

void SortedArray:Sort()

Does `table.sort(self, Comparison_From_SortedArray_new)`


# [FastSpawn](https://github.com/RoStrap/Helper/blob/master/FastSpawn.lua)¶

Expensive method of running a function on a new thread without yielding a frame (like `spawn`) and works within Roblox's thread scheduler. If passed arguments, they will be passed by reference.

    local t = {}
    print(t)

    FastSpawn(function(...)
        wait(5)
        print(...)
    end, t)


# [Array](https://github.com/RoStrap/DataTypes/blob/master/Array.lua)¶

A few utility functions which can operate on Arrays. Purposefully light.

## Library API¶

### Array.Flatten¶

a1 Array.Flatten(array a1)

Takes in an array, a1, which may have arrays inside of it. Unpacks all arrays in their proper place into a1. Returns a1.

Example

    Array.Flatten{{1, 2}, 3, 4, {5, {6, {{7, 8}, 9}, 10}, 11}, {}, 12}
    --> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}

### Array.Contains¶

number Array.Contains(array a1, variant v)

Returns the index at which v exists within array a1.

Example

    print(Array.Contains({0, 1, 2}, 2)) --> 3
    print(Array.Contains({0, 1, 2}, 4)) --> nil


# [Shadow](https://github.com/RoStrap/RoStrapUI/blob/master/Shadow.lua)¶

Only Global ZIndexBehavior is officially supported.

Registers a Material Design Shadow PseudoInstance which can be instantiated via `PseudoInstance.new("Shadow")`

## ShadowElevation Enumeration¶

ShadowElevation | Castable number
Elevation0 | 0
Elevation1 | 1
Elevation2 | 2
Elevation3 | 3
Elevation4 | 4
Elevation6 | 6
Elevation8 | 8
Elevation9 | 9
Elevation12 | 12
Elevation16 | 16

## Shadow API¶

### Shadow:ChangeElevation¶

void Shadow:ChangeElevation(Enumeration.ShadowElevation Elevation [, number TweenTime = 0.175])

Animates a Shadow to a new Elevation over the course of `TweenTime` seconds.

### Fields¶

Property | Type | Description
---|---|---
Elevation | Enumeration.ShadowElevation | The Elevation of the Shadow
Transparency | number | How transparent the Shadow is

###### Shadow inherits from [PseudoInstance](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstance-api)¶


Several games both big and small use Janitor as well as many plugins.

  * [Armtastic](https://www.roblox.com/games/6242582774/SHOP-Armtastic-Alpha) by [Mullets Mafia Red](https://www.roblox.com/groups/9160772/Mullet-Mafia-Red#!/about)
  * [Be an Alien: Renewal](https://www.roblox.com/games/463915360/Be-an-Alien-Renewal) by [PeZsmistic](https://www.roblox.com/users/121643/profile)
  * [Benchmarker](https://www.roblox.com/library/5853950046/Benchmarker) by [boatbomber](https://www.roblox.com/users/33655127/profile/)
  * [Bloopville (NOT RELEASED)](https://www.roblox.com/games/1919575283/BloopVille0) by [BloopVille Team](https://www.bloopville.com/)
  * [RBLX04](https://www.roblox.com/games/5040794421/RBLX04-A-ROBLOX-2004-Simulation) by [movsb](https://www.roblox.com/games/5040794421/RBLX04-A-ROBLOX-2004-Simulation)
  * [RepoToRoblox](https://www.roblox.com/library/6284281701/RepoToRoblox) by [boatbomber](https://www.roblox.com/users/33655127/profile)
  * [Science Simulator](https://www.roblox.com/games/5414779423/5M-EVENT-Science-Simulator) by [Interbyte Studio](https://www.roblox.com/groups/5126818/Interbyte-Studio#!/about)
  * [Studio Tweaks](https://www.roblox.com/library/5601031949/Studio-Tweaks) by [PeZsmistic](https://www.roblox.com/users/121643/profile)
  * [Tropical Town Tycoon](https://www.roblox.com/games/8391439627/) by [Mightybull Games](https://www.roblox.com/groups/8573100/Mightybull-Games)

If you want your project featured here, leave a pull request! Make sure to change this file as well.


# Lydie (previously FrappéUI)
Version 1.0

> [!IMPORTANT]
> Lydie is currently being rebuilt from the ground up. This new version brings major changes, including a redesigned structure and updated design. Development on the current version is paused for now. For more details, check out the [Discord server](https://discord.gg/JSHRQkrafN).

## What is this?
This is the first iteration of the FrappéUI component library. Named after Lydie Malen from Atelier because she has a unique sense of art, so do we when we are creating Lydie.

Created 2-3 years ago, it was originally used for building the interface of Frappé's future games and toolkits.
Powered by Fusion and FusionRouter, it is a powerful component library with features such as hot theme switching.

Anyway, we have moved on and used other libraries due to some circumstances. As a result, this library is now
abandoned and will no longer be used in Frappé. I was not paid for the work put into this library and was initially
started as an unofficial project. I have decided to release this very version of Frappé as open-source software.

Given its creation date, the code and libraries used here are pretty much outdated, but it should still work with some
porting.

## License
MIT License.

## Author
7kayoh <git@7kayoh.net>

## Build version
Lydie v1.0.0 (Based on FrappéUI v1.2.1 git:630fda6)


An exceptionally fast, lightweight, and elegant open-source signal
library for Luau — with generic types and detailed documentation.

[](https://create.roblox.com/store/asset/118793070598362)  [](https://devforum.roblox.com/t/3552231)  [](https://alexxander.gitbook.io/signalplus)

# ⚡ Speed. Reliability. Elegance.
Signal+ is built for developers who demand performance without compromise.
It’s fast, reliable, and equipped with every feature that matters.

**It easily outperforms leading alternatives like
Good-, Lemon-, and FastSignal — in both design and execution.**

## Visit the [documentation](https://alexxander.gitbook.io/signalplus) to learn more!


1

Get the module

GitHub (Recommended)

Creator Store

[See Latest Release](https://github.com/AlexanderLindholt/SignalPlus/releases/latest)

  * Download the `.rbxm` file.

  * Find the file in your file explorer.

  * Drag the file into Roblox Studio.

**Bonus:** The package will auto-update if you enable it in the `PackageLink` inside.

[Get Roblox Asset](https://create.roblox.com/store/asset/118793070598362)

  * Click `Get Model`.

  * Open the ToolBox in Roblox Studio.

  * Go to the `Inventory` tab.

  * Click on `Signal+` to insert.

2

Place it

Find a great place for the module, where other scripts can reference it.

[NextIntroduction](https://alexxander.gitbook.io/signalplus/getting-started/introduction)

Last updated 3 months ago


When you require the module, it’ll return a function, which you can use to create signals:

Copy

    local Signal = require(script.SignalPlus)

    local signal = Signal() -- Creates a new signal.

You can then use methods to control and fire connections on the signal — just like BindableEvents and other signal alternatives. See the full API on the next page.

[PreviousInstallation](https://alexxander.gitbook.io/signalplus)[NextAPI Reference](https://alexxander.gitbook.io/signalplus/getting-started/api-reference)

Last updated 3 months ago


Should you be in doubt, here are the main ways to share signals between scripts:

Modules

You can return a table of signals in a ModuleScript, which multiple other scripts can then require and thereby access the same signals and communicate.

Scripts can also add or remove signals from the ModuleScript at any time.

Example:

Signals (ModuleScript)

Copy

    local Signal = require(script.SignalPlus)

    return {
        CoolSignal = Signal()
        SuperCoolSignal = Signal() :: Signal.Signal<number> -- Custom type :D

Some script

Copy

    local signals = require(script.Signals)
    local coolSignal = signals.CoolSignal

    task.wait(5)

    coolSignal:Fire()

Some other script

Copy

    local signals = require(script.Signals)
    local coolSignal = signals.CoolSignal

    coolSignal:Connect(function()
        print("CoolSignal fired!")
    end)

Built-in shared table (Roblox-specific)

You can store signals in the built-in shared table, which multiple other scripts can then access and thereby access the same signals and communicate.

Scripts can also add or remove signals from the shared table at any time.

Example:

Some script

Copy

    local Signal = require(script.SignalPlus)
    shared.CoolSignal = Signal()

    task.wait(5)

    shared.CoolSignal:Fire()

Some other script

Copy

    -- Just make sure that this part runs after
    -- the signal has been added to the shared table.
    shared.CoolSignal:Connect(function()
        print("CoolSignal fired!")
    end)

[PreviousGeneric types](https://alexxander.gitbook.io/signalplus/getting-started/custom-types)

Last updated 3 months ago


`Signal`:

Name

Type

Description

Connect

Method

Connects a function.  **Returns:** _Connection_

Once

Method

Connects a function, then auto-disconnects after the first call.  **Returns:** _Connection_

Wait

Method

Yields the calling thread until the next fire.

Fire

Method

Runs all connected functions and resumes all waiting threads.

DisconnectAll

Method

Erases all connections.

**Much faster than calling **`**Disconnect**`** on each.**

Destroy

Method

Erases all connections and methods.

**To fully erase, also remove all references to the signal.**

`Connection`:

Name

Type

Description

Disconnect

Method

Removes the connection from the signal.

**The connection’s data remains.**

Connected

Property

A boolean representing the connection state.

Signal

Property

A reference to the signal the connection is a part of.

[PreviousIntroduction](https://alexxander.gitbook.io/signalplus/getting-started/introduction)[NextGeneric types](https://alexxander.gitbook.io/signalplus/getting-started/custom-types)

Last updated 3 months ago


It’s super easy to define generic types for your signals with Signal+. This means you can have autocompletion for the [parameters](https://create.roblox.com/docs/tutorials/fundamentals/coding-2/use-parameters-and-events) in your signals:

You can provide your own parameters as shown in the screenshot above:

All methods (`Connect`, `Once`, `Wait`, and `Fire`) will display your desired parameters.

[PreviousAPI Reference](https://alexxander.gitbook.io/signalplus/getting-started/api-reference)[NextSharing signals](https://alexxander.gitbook.io/signalplus/getting-started/sharing-signals)

Last updated 3 months ago


# Spark
Spark is an input-action manager for Roblox.

## Features
- Supports button, 1D, and 2D inputs.
- Create virtual 1D/2D inputs using `VirtualAxis1d`/`VirtualAxis2d`.
- Rebind inputs using `Bindings`.

## Example
```lua
local actions = Actions.new({ "attack", "move" }):setRebuildBindings(function(bindings)
    bindings:bind("attack", Enum.UserInputType.MouseButton1)
    bindings:bind("move", VirtualAxis2d.wasd())
end)

actions:justPressedSignal("attack"):connect(function()
    print("Attacked!")
end)

RunService.Heartbeat:Connect(function()
    print("Moved", actions:clampedAxis2d("move"))
end)

To get started, visit the [docs](https://nezuo.github.io/spark).


Spark is an input-action manager for Roblox.

To get started, visit the [Getting Started](https://nezuo.github.io/spark/docs/GettingStarted) page.


Spark allows you to activate an action manually. This is especially useful to implement mobile touch controls.

For buttons, you can activate an action with `Actions:press`. Here's how you might implement a mobile button:

    local imageButton = Instance.new("ImageButton")

    local hold = nil

    imageButton.InputBegan:Connect(function(inputObject)
        if inputObject.UserInputState ~= Enum.UserInputState.Begin then
            -- UserInputState will be Change if the touch was dragged onto the button.
            return
        end

        if hold == nil then
            -- Press the button every frame before input is updated. If held inputs don't matter, you could call press without a loop.
            local connection = RunService.PreRender:Connect(function()
                actions:press("attack")
            end)

            hold = {
                connection = connection,
                inputObject = inputObject,
        end
    end)

    imageButton.InputEnded:Connect(function(inputObject)
        -- Only stop the hold if it's the same touch that started it.
        if hold ~= nil and hold.inputObject == inputObject then
            hold.connection:Disconnect()
            hold = nil
        end
    end)

For 1D/2D axis values like movement, camera movement, or camera zoom, use `Actions:moveAxis` and `Actions:moveAxis2d`.

    local thumbstickDirection = Vector2.one -- Get this value from your thumbstick.

    -- This will increase the 2D axis value of the move action by thumbstickDirection.
    -- It's reset every time `Actions:update` is called, so you need to call it every frame.
    actions:moveAxis2d("move", thumbstickDirection)

    local cameraZoomDelta = 0 -- Could be a value from a pinch motion

    actions:moveAxis("cameraZoom", cameraZoomDelta)


-- First we create the InputState object. This should only be created once.
    local inputState = InputState.new()

    -- Next, we create our Actions object.
    -- The casting is optional but will provide autocomplete/typechecking for the action names.
    local actions = (Actions.new({ "jump", "attack", "move" }) :: any) :: Spark.Actions<"jump" | "attack" | "move">

    -- Next, we set the callback to setup up the bindings for each action.
    -- This will be called once here and anytime actions:rebuildBindings() is called.
    actions:setRebuildBindings(function(bindings)
        bindings:bind("jump", Enum.KeyCode.Space, Enum.KeyCode.ButtonA)
        bindings:bind("attack", Enum.UserInputType.MouseButton1, Enum.KeyCode.ButtonR1)
        bindings:bind("move", VirtualAxis2d.wasd(), Enum.KeyCode.Thumbstick1)
    end)

    -- You need to update your Actions objects each frame before any code that reads from it.
    RunService:BindToRenderStep("Spark", Enum.RenderPriority.Input.Value, function()
        -- First we update our Actions object with the InputState.
        actions:update(inputState)

        -- InputState:clear needs to be called after updating all Actions objects.
        -- It resets the values of the mouse wheel and mouse movement so they don't persist across frames.
        inputState:clear()
    end)


<source media="(prefers-color-scheme: dark)" srcset="./gh-assets/wordmark-dark.svg" width="600px">
        <source media="(prefers-color-scheme: light)" srcset="/gh-assets/wordmark-light.svg" width="600px">

## 💥 Luau's Unstoppable Force

> [!WARNING]
> **Prvd 'M Wrong is unfinished.** Do not use Prvd 'M Wrong for production.

Luau has often meant navigating sprawling mazes of dependencies, grappling with
incomplete frameworks, and a challenging development experience.

No longer. Prvd 'M Wrong is the Luau provider framework, freeing you from
logistical nightmares so you can focus just on your project's logic.

Use Prvd 'M Wrong to structure your project with providers. Prvd 'M Wrong
delivers type-safe APIs, method lifecycles, and dependency injection.

Whether you prefer batteries to be included, or want to use your favorite
packages and patterns, Prvd 'M Wrong adapts to your needs.

Built with fantastic user extensibility, modular composability and runtime
integration, Prvd 'M Wrong can be dropped in for both new projects and existing
frameworks. In particular, Prvd 'M Wrong gleams anywhere Luau runs, from Roblox
to Lune.

All free from logistical nightmares and without sprawling mazes of bloaty
dependencies.

Want to prove them wrong?
[Get going with the Prvd 'M Wrong on rails tutorial,][Tutorial]
or [read through the library of example projects.][Examples]

[Tutorial]: https://prvdmwrong.luau.page/latest/tutorials
[Examples]: https://prvdmwrong.luau.page/latest/examples

## 🚀 Crash Course

Assuming a Roblox project, create a "Providers" folder in ServerScriptService.

Create a new server Script inside ServerScriptService, this is where we will
bootstrap Prvd 'M Wrong.

First, create and start a root. Roots are starting points for Prvd 'M Wrong:

```luau
-- replace with path to Prvd 'M Wrong!
local prvd = require(...)

local root = prvd.root()
    -- use all descendant module scripts inside our Providers folder
    :useModules(script.Parent:WaitForChild("Providers"):GetDescendants())
    -- start!
    :start()

-- when the server closes, the root should be finished.
game:BindToClose(function()
    root:finish()
end)

Now, let's create your first provider! Inside your "Providers" folder, create a
ModuleScript called "TimeProvider":

```luau
-- replace with path to Prvd 'M Wrong!
local prvd = require(...)

-- Providers are just defined as tables.
local TimeProvider = {}

-- When starting the root, Prvd 'M Wrong will construct each provider one by
-- one.
function TimeProvider.constructor(self: TimeProvider)
    self.time = 0
end

-- `start` will be called after all providers have constructed. `start` also
-- runs in it's own thread!
function TimeProvider.start(self: TimeProvider)
    while true do
        self.time += task.wait()
    end
end

-- Prvd 'M Wrong takes advantage of the Luau type solver, so your providers
-- enjoy type safety!
export type TimeProvider = typeof(TimeProvider)

-- Export the provider.
return prvd(TimeProvider)

Prvd 'M Wrong can also resolve your provider's dependencies as so the
dependencies it needs are constructed before the dependent. Create a new
"ServerTimeProvider" ModuleScript:

```luau
-- replace with path to Prvd 'M Wrong!
local prvd = require(...)

local ServerTimeProvider = {}

-- `dependencies` is a special table that Prvd 'M Wrong will read and collect
-- dependencies.
ServerTimeProvider.dependencies = {
    -- `depend` tells Prvd 'M Wrong this is a dependency that it should track.
    -- It also refines the type to the actual constructed provider!
    TimeProvider = prvd.depend(require("./TimeProvider"))

-- To use the dependencies, get it as the second argument of the constructor:
function ServerTimeProvider.constructor(
    self: ServerTimeProvider,
    dependencies: typeof(self.dependencies)
)
    -- Need to hold on dependencies for later? Just set it!
    self.timeProvider = dependencies.TimeProvider
end

-- Now you can access `TimeProvider` in other methods!
function ServerTimeProvider.start(self: ServerTimeProvider)
    while true do
        print(
            "The server has been alive for",
            math.round(self.timeProvider.time),
            "seconds!"
        )
    end
end

export type ServerTimeProvider = typeof(ServerTimeProvider)
return prvd(ServerTimeProvider)

With that, you've written your first Prvd 'M Wrong project, touching on all of
the major Prvd 'M Wrong concepts!

[In the tutorials][Tutorial], we'll dig deeper into what exactly all of this code
is doing. Feel free to review these code snippets as many times as you need.

## 🌏 Cultural Impact

Prvd 'M Wrong has a significant cultural impact following the archival of the
Knit framework. Many packages inspired by Prvd 'M Wrong, which you can use if
you're unsastified, as listed below:

- [Artwork](https://ratplier.github.io/artwork)
- [Saphhire (archived)](https://github.com/Mark-Marks/sapphire)

## 📝 License

Prvd 'M Wrong 0.2 is licensed under the Mozilla Public License 2.0.

Note that historical versions of Prvd 'M Wrong were licensed under the MIT
License and sometimes the Apache License 2.0.


# Home

The framework for everything roblox
Made with  by [@ratplier](https://github.com/ratplier)

framework inspired by [flamework](https://flamework.fireboltofdeath.dev/) and [prvdmwrong](https://prvdmwrong.github.io/prvdmwrong/latest/)

## **Boilerplate**

The bare minimal example of a provider , which can listen to any [lifecycle event](https://ratplier.github.io/artwork/tutorials/fundamentals/lifecycles)

    local artwork = require("@packages/artwork")

    local provider = {}
    artwork.register(provider)

## **Full Example**

A more in-depth example of a framework usage

    -- DataProvider.luau
    local artwork = require("@packages/artwork")

    local DataProvider = {
        registeredPlayers = {},
        data = {},

    function DataProvider.registerPlayer(player: Player)
        if DataProvider.registeredPlayers[player.UserId] then return end

        DataProvider.data[player.UserId] = {}
        DataProvider.registeredPlayers[player.UserId] = true
    end

    function DataProvider.setData(player: Player, key: string, value: any)
        DataProvider.registerPlayer(player)

        local playerData = DataProvider.data[player.UserId]
        playerData[key] = value
    end

    return artwork.register(DataProvider)

    -- CoinsProvider.luau
    local artwork = require("@packages/artwork")

    local CoinsProvider = {
        dataProvider = require("DataProvider")

    function CoinsProvider.setCoins(player: Player, amount: number)
        CoinsProvider.dataProvider.setData(player, "Coins", amount)
    end

    return artwork.register(CoinsProvider)

    -- main.server.luau
    local artwork = require("@packages/artwork")

    artwork.loadDescendants("@providers")
    local shutdown = artwork.ignite()

    game:BindToClose(shutdown)

Back to top


# Providers

Providers are objects that are registered with the framework. They work like every other `ModuleScript` in your code, but they can listen to lifecycles

You can define a provider like you would define a module

    local provider = {}
    provider.a = 1

    function provider.hello()
        print("Hello World!")
    end

### Registering a provider

To register a provider, you can use the [`artwork.register`](https://ratplier.github.io/artwork/tutorials/fundamentals/provider/refrence/core/#register) function to register a object for listening to lifecycles. the priority can also be passed but it is optional

    return artwork.register(provider, 1)

### Load Order

Dependency Injection

This framework will automatically determine the correct load order for you, so it is recommended to avoid setting this manually. The property will take priority over the automatic order but providers with the same load order will still run in the automatic order.

The higher the priority, the earlier the provider is registered on ignition. The default priority is `1` and providers with lower priority are registered after providers with a higher priority.

    return artwork.register(provider, 0) -- will run BEFORE default priority

    return artwork.register(provider, 2) -- will run AFTER default priority

Back to top


# Lifecycle Events

## Built-in Lifecycle Events

### onStart

The `onStart` lifecycle event is similar to `onInit`, however, instead of calling each event sequentially, they are all called at the same time. This means yielding, or failures, in onStart won't affect others.

### onInit

Avoid Use

You should always use onStart except in very rare circumstances where you need the unique behavior of onInit.

The `onInit` lifecycle event is one of the initialization events. This is only called once, and the function must complete successfully before the framework will call the next onInit or other events. It isnt recommended to yield in `onInit` since yielding will yield ignition. Errors and such will be logged but not pause execution.

### onStop

The `onStop` lifecycle event is the opposite of `onStart`. It is called once right before the framework is shutdown. This is your chance to cleanup any resources that were allocated in `onStart` or anytime during execution.

## Custom Lifecycle Events

### Custom Events

Custom lifecycle events are a powerful feature that allows you to create custom lifecycle events that can be used by any module. To create a custom lifecycle event you can use the `artwork.createLifecycle` method.

You can create client/server specific lifecycles with `artwork.createClientLifecycle` and `artwork.createServerLifecycle`

    local artwork = require("@packages/artwork")

    artwork.createLifecycle("onPlayerAdded", function(fire)
        Players.PlayerAdded:Connect(fire)
        for _, player in Players:GetPlayers() do
            fire(player)
        end
    end)

    -- then you can use the lifecycle event in any module like so:
    local provider = {}

    function provider.onPlayerAdded(player)
        print(`Player {player.Name} joined the game! ({player.UserId})`)
    end

    artwork.register(provider)

Back to top


# Preloading & Ignition

To start the framework, you should load the required modules by calling the `loadDescendants` method. The `loadDescendants` method is a generic module loader that can be used to load modules from a given parent instance. The method accepts a parent of type `Instance` and an optional `predicate` function, which can be used to filter the descendants that should be loaded. If a `predicate` is provided, only the descendants for which the predicate returns `true` will be loaded.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    artwork.loadDescendants(ReplicatedStorage.Shared)

    -- loading with custom predicate
    artwork.loadDescendants(ReplicatedStorage.Shared, function(instance)
        return instance:HasTag("ignore") == false
    end)

    -- custom loadChildren implementation
    -- this method is already built-in so this is a example
    local function loadChildren(parent, predicate)
        return artwork.loadDescendants(parent, function(descendant)
            local isChild = descendant.Parent == parent
            return isChild and (not predicate or predicate(descendant))
        end)
    end

    loadChildren(ReplicatedStorage.Modules)

### Ignition!

After loading the providers you can then ignite the framework! Igniting the framework will initialize all registered providers in the order of their priority. The priority of a provider is used to determine the order in which the providers are registered.

Warning

Loading providers will **NOT** register the module. The [loadDescendants](https://ratplier.github.io/artwork/refrence/core/#loaddescendants)/[loadChildren](https://ratplier.github.io/artwork/refrence/core/#loadchildren) methods dont have any special features other than loading modules. In order to register a provider, you must use the [`artwork.register`](https://ratplier.github.io/artwork/refrence/core/#register) method.

If you want to check if the framework is currently ignited, you can use the [`artwork.ignited`](https://ratplier.github.io/artwork/refrence/core/#ignited) method, which returns a boolean indicating whether the framework is currently ignited or not.

    artwork.ignite()

Back to top


# Modding

## Methods

### addListener

Registers an object as a listener. The API scans the object for string keys with function values and subscribes them to the corresponding lifecycle events.

    function Modding.addListener(object: object, ignoreList: { string }?)

**Parameters**

  * `object` (`table`): The listener object to register.
  * `ignoreList` (`{string}?`): An optional array of string keys to ignore. Callbacks with these names will not be registered.

    local MyListener = {
        onStart = function()
            print("MyListener has started!")
        end,

        onStop = function()
            print("MyListener is shutting down.")
        end

    -- Register the entire object as a listener
    Modding.addListener(MyListener)

### removeListener

Removes and cleans up all event subscriptions for a given listener object. This is crucial for preventing memory leaks when a listener is disabled or unloaded.

    function Modding.removeListener(object: object)

**Parameters**

  * `object` (`table`): The listener object to unregister.

    -- Assuming MyListener was added previously
    Modding.removeListener(MyListener)

### addLifecycleListener

A convenience function for adding a single event listener without creating a full object. It returns a `cleanup` function that can be called to remove the listener.

    function Modding.addLifecycleListener(name: string, callback: (...any) -> ()) -> (() -> ())

**Parameters**

  * `name` (`string`): The ID of the event to listen for.
  * `callback` (`function`): The function to execute when the event fires.

**Returns**

  * (`function`): A function that, when called, will remove the listener.

    local function onPlayerChat(player, message)
        print(player.Name .. ": " .. message)
    end

    -- Add the listener and store the disconnect function
    local disconnectChatListener = Modding.addLifecycleListener("onPlayerChat", onPlayerChat)

    -- Sometime later, to stop listening...
    disconnectChatListener()

### getListeners

Gets all the listeners under a specific id. The resulting array is not ordered.

    function Modding.getListeners(id: string) -> { object }

**Parameters**

  * `id` (`string`): The specific event ID to watch for.

**Returns**

  * (`{ object }`): An array of listener objects that implement the given event ID.

    local listeners = Modding.getListeners("onPlayerChat")

### onListenerAdded

Connects a callback that fires whenever a listener is added via `Modding.addListener`.

The function can operate in two modes: 1\. **Global:** If `id` is `nil`, the callback fires for _any_ listener added. 2\. **Specific:** If an `id` is provided, the callback fires only when a listener subscribing to that specific event ID is added.

In both modes, the callback will be immediately fired for all _already existing_ listeners that match the criteria.

    function Modding.onListenerAdded(callback: (object: object) -> (), id: string?) -> RBXScriptConnection

**Parameters**

  * `callback` (`function`): A function that receives the listener object that was added.
  * `id` (`string?`): Optional. The specific event ID to watch for.

**Returns**

  * (`RBXScriptConnection`): A connection object that can be disconnected to stop listening.

    -- Listen for any listener that implements "OnUpdate"
    local connection = Modding.onListenerAdded(function(listenerObject)
        print("A new listener for OnUpdate was just added!")
        -- Maybe do something with listenerObject here
    end, "OnUpdate")

    -- Listen for any new listener, regardless of its events
    Modding.onListenerAdded(function(listenerObject)
        print("A new listener was added to the system.")
    end)

    -- Stop listening after a while...
    connection:Disconnect()

### onListenerRemoved

Connects a callback that fires whenever a listener is removed via `Modding.removeListener`.

    function Modding.onListenerRemoved(callback: (object: object) -> (), id: string?) -> RBXScriptConnection

**Parameters**

  * `callback` (`function`): A function that receives the listener object that was added.
  * `id` (`string?`): Optional. The specific event ID to watch for.

**Returns**

  * (`RBXScriptConnection`): A connection object that can be disconnected to stop listening.

    -- Listen for when any listener with an "OnUpdate" event is removed
    local connection = Modding.onListenerRemoved(function(listenerObject)
        print("A listener for OnUpdate was removed.")
    end, "OnUpdate")

    -- Stop listening after a while...
    connection:Disconnect()

Back to top


# Installation

artwork is available to download as packages for your game. you can use `pesde` or `wally` to install the framework. you can also download a binary from the github releases or build it yourself.

## From pesde (recommended)

  1. Make sure you have [pesde](https://docs.pesde.dev/installation/) installed.
  2. Add the package

         pesde add ratplier/artwork

  3. Import the package

         local art = require(ReplicatedStorage.roblox_packages.artwork)

## From wally

  1. Make sure you have [wally](https://wally.run/install) installed.
  2. Add the package

         artwork = "ratplier/artwork@1.0.0"

  3. Import the package

         local art = require(ReplicatedStorage.Packages.artwork)

## From source

  1. Clone the artwork repository

         git clone https://github.com/ratplier/artwork.git
         cd artwork

  2. Install all tools

         rokit install

  3. Build with rojo

         rojo build -o out/artwork.rbxm

Back to top


# Core

## Types

### object

The `object` type represents a generic object in the framework. It is used as a base type for structures.

    type object = { [any]: any }

### predicate

The `predicate` type represents a function that takes a value of type `T` and returns a boolean.

    type predicate<T> = (T) -> boolean

## Methods

### loadDescendants

The `artwork.loadDescendants` method is used to load all modules within the descendants of a given parent instance. This method accepts a parent of type `Instance` and an optional `predicate` function, which can be used to filter the descendants that should be loaded. If a `predicate` is provided, only the descendants for which the predicate returns `true` will be loaded.

Information

This is a generic module loader, there are no extra features included in the source method.

    function artwork.loadDescendants(parent: Instance, predicate: predicate<Instance>?): void

### loadChildren

The `artwork.loadChildren` method is used to load all modules within the children of a given parent instance. This method accepts a parent of type `Instance` and an optional `predicate` function, which can be used to filter the children that should be loaded. If a `predicate` is provided, only the children for which the predicate returns `true` will be loaded.

Information

This is a generic module loader, there are no extra features included in the source method.

    function artwork.loadChildren(parent: Instance, predicate: predicate<Instance>?): void

### register

The `artwork.register` function registers a object for listening to lifecycles. The priority determines the order in which objects are registered when the framework is ignited. If no priority is specified, the default priority of `1` is used. The function returns the same table of members that was passed to it.

Watch out

The higher the priority, the earlier the provider is registered on ignition. The default priority is `1` and providers with lower priority are registered after providers with a higher priority.

    function artwork.register(members: object<T>, priority: number?): T

### listen

The `artwork.listen` method is used to listen to a specific lifecycle event. It returns a cleanup function to disconnect the listener.

    function artwork.listen(name: string, listener: (...any) -> ()): () -> ()

### ignited

The `artwork.ignited` method is used to check if the framework is currently ignited.

    function artwork.ignited(): boolean

### shutdown

The `artwork.shutdown` method is used to check if the framework is currently shutdown.

    function artwork.shutdown(): boolean

### ignite

The `artwork.ignite` method is used to start the framework by initializing all registered providers and setting up the necessary event listeners. It processes providers based on their priority and also returns a function to shutdown the framework.

    function artwork.ignite(): void

### extend

The `artwork.extend` method is used to extend a table with other tables. It merges all the tables with the highest priority being the last given argument. It is also fully strictly typed and is very useful in creating [extensions] for the library.

    function artwork.extend(...: object<any>): object<any>

## Properties

### version

The `artwork.version` method is used to get the current version of the framework. It returns a string in the format of `major.minor`.

    string artwork.version

Back to top


# Dependencies

To add dependencies to a provider, you can assign other registered providers to specific keys within your provider table. The key names are arbitrary, but using descriptive names is recommended.

To set up a dependency, first require the module where the dependency is defined, and then assign it to a key in your provider table. Ensure the dependency is registered before registering your provider to allow proper injection.

When done correctly, providers with fewer dependencies will load before those with many, ensuring an efficient loading order.

### Example

    local Dependency = {}

    function Depdendency.hello()
        print("Hello world!")
    end

    return artwork.register(Dependency)
    -- IMPORTANT: make sure to register your dependencies else
    -- the dependency injection will not work

    -- Provider.luau
    local Dependency = require("Dependency")
    local Provider = {
        myDependency = Dependency,

    Provider.myDependency.hello()

    return artwork.register(Provider)
    -- only when Dependency is registered, the dependency
    -- will be injected

Back to top


# Reflect

## Methods

### defineMetadata

Defines or updates a piece of metadata for an object. This is the primary function for writing metadata. If the object has not been seen by the API before, it will be registered automatically.

    function Reflect.defineMetadata(object: object, key: string, value: unknown)

**Parameters**

  * `object` (`table`): The target object to associate metadata with.
  * `key` (`string`): The key for the piece of metadata.
  * `value` (`any`): The value of the metadata to store.

    local player = { name = "Alice", health = 100 }

    -- Associate some metadata with the 'player' object
    Reflect.defineMetadata(player, "LastLogin", os.time())
    Reflect.defineMetadata(player, "IsAdmin", false)

    -- Note: The 'player' table itself remains unchanged.
    -- player.IsAdmin is still nil

### readMetadata

Reads a piece of metadata from an object for a given key.

    function Reflect.readMetadata(object: object, key: string): unknown

**Parameters**

  * `object` (`table`): The target object.
  * `key` (`string`): The key of the metadata to retrieve.

**Returns**

  * (`any`): The value of the metadata, or `nil` if the key or object does not have metadata defined.

    local isAdmin = Reflect.readMetadata(player, "IsAdmin")
    print("Is Admin:", isAdmin)
    -- > Is Admin: false

### getMetadata

Retrieves the entire metadata table for a given object. This is useful if you need to read multiple metadata keys at once. If the object has no metadata, it will be registered automatically.

    function Reflect.getMetadata(object: object): metadata

**Parameters**

  * `object` (`table`): The target object.

**Returns**

  * (`table`): The complete metadata table for the object. Changes to this table will be persisted.

    -- Get the whole metadata container
    local allMeta = Reflect.getMetadata(player)

    -- You can read from it...
    print("Is Admin:", allMeta.IsAdmin) -- > false

    -- ...or write to it directly
    allMeta.NewValue = "hello"
    print(Reflect.readMetadata(player, "NewValue")) -- > hello

### register

Explicitly registers an object with the Reflect API, preparing it for metadata storage.

Note

In normal usage, you do not need to call this function. `defineMetadata` and `getMetadata` will call it automatically. It's provided for edge cases where you might want to pre-register a batch of objects.

    function Reflect.register(object: object)

**Parameters**

  * `object` (`table`): The object to register.

    local someObject = {}
    Reflect.register(someObject)
    -- someObject is now known to the Reflect API

### getId

Gets the unique internal ID string generated for an object. This is mainly useful for debugging.

    function Reflect.getId(object: object): string

**Parameters**

  * `object` (`table`): The target object.

**Returns**

  * (`string`): The unique ID used by the API to track the object (e.g., `reflect:12345678`).

    local myObject = {}
    local id = Reflect.getId(myObject)
    print(id) -- e.g., "reflect:59103819"

Back to top