[ ocornut ](https://github.com/ocornut) / ** [imgui](https://github.com/ocornut/imgui) ** Public

  * ### Uh oh!

There was an error while loading. [Please reload this page](https://github.com/ocornut/imgui/wiki/About-the-IMGUI-paradigm/95bc6ad90ce1538c3bda7d457a4bda33541efc41).

  * [ Notifications ](https://github.com/login?return_to=%2Focornut%2Fimgui) You must be signed in to change notification settings
  * [ Fork 11.6k ](https://github.com/login?return_to=%2Focornut%2Fimgui)
  * [ Star  72.2k ](https://github.com/login?return_to=%2Focornut%2Fimgui)

# About the IMGUI paradigm

Jump to bottom

omar edited this page Sep 14, 2022 · [47 revisions](https://github.com/ocornut/imgui/wiki/About-the-IMGUI-paradigm/_history)

THIS IS WIP/DRAFT / [Discuss this article](https://github.com/ocornut/imgui/issues/3815)

### Why

**This is an attempt at explaining what the IMGUI paradigm stands for, and what it could be**.

Please note the References section where other articles have been written about this.

Proponent of the IMGUI paradigm have noticed that it was widely misunderstood, over and over. As of ~~Feb 2021~~ August 2022, even the [IMGUI Wikipedia page](https://en.wikipedia.org/wiki/Immediate_mode_GUI) is completely off the mark and incorrect. There are reasons for that:

  * the acronym is misleading (read below).
  * people didn't do a good enough job explaining or documenting what IMGUI means.
  * they are different interpretation of what IMGUI means.
  * many popular IMGUI implementation have made similar design choices, making it more confusing what is actually the soul and backbone of the IMGUI paradigm vs a set of implementation choices.
  * the naming and success of "Dear ImGui" blurred the line a little bit further (should have used another name).

The acronym is very misleading because "immediate-mode" was initially coined as a reference to obsolete graphics API which made it very easy to render contents. Some people still incorrectly assume that IMGUIs are often rendering using those API. The choice of that word got us into years, maybe decades of misunderstanding.

From now on, IMGUI will stand for "Incredibly Magic Graphics User Interface" ;)

The second purpose of this page should be to make it clear that there is a large space to explore in UI programming.

### The Pitch of Dear ImGui

From [README](https://github.com/ocornut/imgui#the-pitch), notice the four first points:

  * "Minimize state synchronization."
  * "Minimize state storage on user side."
  * "Minimize setup and maintenance."
  * "Easy to use to create dynamic UI which are the reflection of a dynamic data set."

While they don't constitute a definition of the IMGUI paradigm, they may give a good feeling of some of the advantages usually associated to the IMGUI paradigm.

### History

The acronym IMGUI was coined by Casey Muratori in this video in 2005:
<https://www.youtube.com/watch?v=Z1qyvQsjK5Y> (see also [blog](https://caseymuratori.com/blog_0001))

_"I’ve also seen lots of people getting into arguments about immediate-mode vs. retained-mode GUI APIs. I think this has something to do with how simple the IMGUI concept is, as it leads people to think they understand it, and then they proceed to get into heated arguments as if they actually know what they’re talking about. I rarely see this problem when I’m talking about anything even mildly complicated, like quaternions."_

Casey's work on formulating and popularizing research on this topic has been pivotal and invaluable. His video and the forums that followed helped people gather around the concept and talk about it.

The concept has been privately researched before and after. [Thierry Excoffier's Zero Memory Widget](https://web.archive.org/web/20220516003813/https://perso.univ-lyon1.fr/thierry.excoffier/ZMW/) and his [research report](https://web.archive.org/web/20220516004122/https://perso.univ-lyon1.fr/thierry.excoffier/ZMW/rr_2003_03_11.pdf) in 2003 are also very notable although they didn't reach the game development sphere back then.

### Do we need a definition?

_"I kind of wish there was more radical experimentation in this space"_ ([tweet](https://twitter.com/pervognsen/status/1361241939593416705))
_"But, I need to store a variable therefore it isn't IMGUI anymore!!!"_ (many people)

Nowadays I am starting to believe that the term has caused more harm than benefits, because it suggests there are two camps "IMGUI" vs "RMGUI". The reality is there is a continuum of possibilities over multiple unrelated axises. Many of them haven't been explored enough, as popular IMGUI libraries have been designed for certain needs and not others. Many of them are at reach as extension of existing popular frameworks. Some are likely to only ever exist as part of yet undeveloped IMGUI frameworks.

The existence of those two terms effectively has hindered both discussions and research. The fact that we are even trying to put an official definition to a label is hindering people's imagination about the many possible areas of researches to be done in UI space.

### Half of a definition

@ocornut's attempt for a definition (WIP, 2021)

What we care about:

  * IMGUI refers to the API: literally the interface between the application and the UI system.
    * The API tries to minimize the application having to retain data related to the UI system.
    * The API tries to minimize the UI system having to retain data related to the application.
  * IMGUI does NOT refer to the implementation. Whatever happens inside the UI system doesn't matter.

This is in comparison with typical RMGUI ("retained-mode UI"):

  * RMGUI APIs often have the application retain artifacts from the UI system (e.g. maintain references to UI objects).
  * RMGUI APIs often require synchronization mechanisms because the UI system retain more application data.

What it doesn't stands for:

  * IMGUI does not mean that the library doesn't retain data.
  * IMGUI does not mean that stuff are drawn immediately.
  * IMGUI does not mean it needs a continuous loop nor need to refresh continuously.
  * IMGUI does not mean it needs to refresh all-or-nothing.
  * IMGUI does not mean that states are polled.
  * IMGUI does not mean the UI doesn't look "native" or looks a certain way.
  * IMGUI does not mean it cannot animate.
  * IMGUI does not mean it needs a GPU to render.
  * IMGUI does not mean that the library can or cannot support feature x or y.
  * IMGUI does not mean that the library is more or less portable.

TODO: Each of those points should be explained with a paragraph. We could also describe how common UI libraries (of all types) stand on a given axis. We could also describe less explored paths and envision what new UI libraries could do.

It's particularly important we develop this section to dissociate the promise (sometimes unfulfilled) vs current implementation. The full-feature bell-and-whistle promise is more likely to be ever fulfilled by a UI library if people understand that it is possible.

### Examples

_TODO_

### Modeless write-up

March 2022: <https://news.ycombinator.com/item?id=30814797>

_"Immediate mode is a style of API where important state is kept in user code [editor note: this should be "user side" not "user code"] instead of being retained inside the API implementation. For example, an immediate mode GUI checkbox implementation does not store a boolean value determining whether the checkbox is checked. Instead, user code passes that information as a function parameter whenever the UI needs to be drawn. Even the fact that a checkbox exists on screen is not stored in the GUI library. The checkbox is simply drawn when user code requests it each frame."_

_"Counter intuitively this is often less complicated than the traditional "retained mode" style of GUI libraries because there is no duplication of state. That means no setup or teardown of widget object trees, no syncing of state between GUI objects and application code, no hooking up or removing event handlers, no "data binding", etc. The structure and function of your UI is naturally expressed in the code of the functions that draw it, instead of in ephemeral and opaque object trees that only exist in RAM after they're constructed at runtime. You retain control over the event loop and you define the order in which everything happens rather than receiving callbacks in some uncertain order from someone else's event dispatching code."_

_"Crucially, "immediate mode" is a statement about the interface of the library, not the internals. Common objections of the form "immediate mode GUIs can't support X" or "immediate mode GUIs are inefficient because Y" are generally false and based on a misconception that immediate mode GUIs are forbidden from retaining any internal state at all. It is perfectly normal for an immediate mode library to retain various types of internal state for efficiency or other reasons, and this is fine as long as the state stored in user code remains the source of truth. This can even go as far as internally constructing a whole retained widget tree and maintaining it via React-like tree diffing."_

### Vurtun's write-up

[@vurtun](https://github.com/vurtun) said very eloquently around April 2019:

_"I usually don't like to write about immediate mode UI since there are a lot of preconceived notions about them. Addressing all of them is hard since often that is required since they all are thrown at once. For example a single implementation of a concept does not define a concept (dear imgui is a immediate mode UI not all imgui are like dear imgui)._

_Everything that can be done with "retained" gui can by definition be done by an immediate mode gui (I hate this whole divide at this point). Then we have state. For some reason like andrew already said I often see confusion that immediate mode means stateless. Immediate mode has nothing to do with how or if state is stored. Immediate mode in guis doesn't remove having two data representation. One on the calling side and one in the library. Even dear imgui and nuklear both have internal state they keep track of. Rather at its core immediate mode is about how this state is updated. In classical gui the state of the application was synchronized to the gui state by modification. On the other hand immediate mode is closer to functional programming. Instead of mutating state, all previous state is immutable and new state can only be generated by taking the previous state and applying new changes. Both in dear imgui and nuklear there is very little previous state and most is build up every "frame"._

_Next up handling widget events (press,clicked,...). Immediate mode has nothing to do with events vs polling for input changes. The famously convenient if (button(...)). You can even have retained libraries that use polling instead of events._

_Then we have "immediate mode bundles layouting, input and rendering". Once again only dear imgui and nuklear do this. At this point all my imguis have multiple passes. For example if I cache state for layouting I have (layout, input, render). Any change in the input or render pass will will jump back to layouting. If I don't cache state for layouting I still have two passes (input, render) to prevent glitches. To make sure that performance is not a problem I make sure that only those elements that are actually visible are updated, which is important for datastructures (list, trees). Invisible widgets are not only not rendered but also not layouted or have input handling. In a current implementation they are not even iterated over._

_Another argument I've read is that immediate mode cannot be used with OOP. While I try to stay away from that term since at this point it has taken so many meaning that it is basically worthless, what I hear it in context of is having composability for widgets. Once again just because dear imgui and nuklear don't have does not mean it has to be or even should be that way. For example the imgui we have at work supports composability and just like retain mode UIs you can put any widget inside any other. Want a slider inside a tab you can just compose them (not that it makes sense in this case)._

_Getting to game vs. tool ui. Disclaimer: I spend most of my time on tool ui however I played around with game ui or rather the concept behind it in my immediate/retain mode hybrid (<https://gist.github.com/vurtun/c5b0374c27d2f5e9905bfbe7431d9dc0>)._

_Personally for me tool and game ui are two completely different use cases. Interestingly at work we actually have a single immediate mode core that is used for both game as well as tool ui and it still works. However our advantage is that our UI designer can code. Immediate mode biggest advantage is that state mutation is extremely simple and easy. However in games all UI (including stuff like animations) outside datastructures likes list are know at compile time or loading time. So most advantages of immediate mode doesn't hold up (outside mentioned datastructures). Interestingly for datastructures you can even have an immediate mode API inside a retained state UI. So having the best of both worlds by having a hybrid._

_Finally state handling. For games this is easier since most state is known beforehand and can be reserved/preallocated. For tool ui it is a bit more complicated. First of it is possible to have state per widget. That is what we currently do at keen core. The state itself is lifetime tracked and freed if not used anymore. However what is actually possible as well is having a concept of an window/view and managing state outside the library. By that I mean a tabbed window with content. Like for example a log view or scene view storing all needed state for the UI and the actually data needed (log entries for the log view). Instead of trying to do state handling on the widget level it makes a lot more sense to have state management inside the view itself, since unlike the actually widgets the view itself has an easy to track lifetime. Just to clarify the view in the library API would just an handle while the actual view implementation would store the state._

_A lot in ui depends on the problem on hand so I feel there is no silver bullet. But what is actually great about UI is that it is possible to write a version fitting the problem. I hope if anything is taken from this rant is that "imgui" currently entangles a lot of concepts that however don't define it. So everytime it does not work out immediate mode as a grand concept itself is tossed aside instead of reevaluating the parts that did not work out."_

### References

  * [Johannes 'johno' Norneby's article](http://www.johno.se/book/imgui.html), 2007.
  * [A presentation by Rickard Gustafsson and Johannes Algelind](http://www.cse.chalmers.se/edu/year/2011/course/TDA361/Advanced%20Computer%20Graphics/IMGUI.pdf), 2011.
  * [Jari Komppa's tutorial on building an IMGUI library](http://iki.fi/sol/imgui/), @jarikomppa, 2006.
  * [Casey Muratori's original video that popularized the concept](https://mollyrocket.com/861), 2005.
  * [Sean Barrett's article in Game Developer Magazine](https://archive.org/details/GDM_September_2005) (Page 34), September 2005.
  * [Nicolas Guillemot's CppCon'16 flash-talk about Dear ImGui](https://www.youtube.com/watch?v=LSRJ1jZq90k), 2016.
  * [Thierry Excoffier's ZMV (Zero Memory Widget)](http://perso.univ-lyon1.fr/thierry.excoffier/ZMW/), 2004.
  * [Unity's IMGUI](https://docs.unity3d.com/Manual/GUIScriptingGuide.html)

### Clone this wiki locally


# Adonis-Plugins
A repository of Adonis plugins made by me or community members.

Feel free to submit your own, just be sure to include information about the plugin in a comment at the top of it, such as author and description.

When someone asks me to make a plugin for some niche use case that doesn't fit in the main script, I'll probably put it here as a plugin.

If you have an issue with a plugin, or need help setting it up, make sure you contact the AUTHOR. If I (Sceleratis/Davey_Bones) did not make it, do not ask me about it because I probably can't help you.


# Sera
This is a Low-level schematized serialization library for Roblox for handling limited size dictionaries with primitive types. This library aims to do as few operations (table lookups, comparisons, etc.) as possible to achieve it's result while staying a good-enough serialization library.

Sera's intended purpose is to help with serialization schemas when handling high-efficiency game entity replication to clients. Note that this is not a module for beginners and you should at least know the basics of using the [`buffer`](https://create.roblox.com/docs/reference/engine/libraries/buffer) class.

## Perks:
- Fast.
- Efficient-enough representation of a dictionary inside a `buffer`.
- `Sera.Serialize`, `Sera.Push`, `Sera.DeltaSerialize` and `Sera.DeltaPush` outputs can be combined inside a large buffer - `Sera.Deserialize` and `Sera.DeltaDeserialize` understand when an entry ends given a buffer and offset and returns a new offset for the developer to continue reading the buffer.
- `Sera.DeltaSerialize` and `Sera.DeltaPush` treats a schema as an enumerator and serializes an incomplete dictionary with added (n + 1) bytes in the output where "n" is the number of fields present.

## Limitations:
- Schemas can only hold up to 255 fields in Sera and it's going to stay that way - you may fork the project and easily change this for yourself.
- `Sera.Serialize` and `Sera.DeltaSerialize` will fail if resulting buffer size is larger than  the constant `BIG_BUFFER_SIZE` inside the `Sera` module - This value should be moderate, but within reason since that's the memory `Sera` will take and never release during runtime.

[Source Code (Single ModuleScript)](https://github.com/MadStudioRoblox/Sera/blob/main/Sera.luau)

[Longer Example Code](https://github.com/MadStudioRoblox/Sera/blob/main/Test.luau)

[Join the discussion in the Roblox Developer Forum (click here)](https://devforum.roblox.com/t/sera-low-level-schematized-serialization-library/3304206)

***This is an experimental project - there will be no guarantees for backwards-compatibility on further updates***

## Short Example:

```lua

local Sera = require(game.ReplicatedStorage.Sera)

local Schema = Sera.Schema({
    Health = Sera.Int16,
    Position = Sera.Vector3,
    Name = Sera.String8,
})

local serialized, error_message = Sera.Serialize(Schema, {
    Health = 150,
    Position = Vector3.new(1001, 69, 0.1),
    Name = "loleris",
})

if error_message ~= nil then
    error(error_message)
end

local deserialized = Sera.Deserialize(Schema, serialized :: buffer)

print(`Serialized buffer size: {buffer.len(serialized :: buffer)} bytes`)
print(`Deserialized:`, deserialized)


# Fluent Renewed

![Fluent Renewed Title](Assets/darkmode.png#gh-dark-mode-only)
![Fluent Renewed Title](Assets/darkmode.png#gh-light-mode-only)

## ⚡ Features

- Modern design
- Many customization options
- Almost any UI Element you would ever need

## 🔌 Installation

You can load Fluent through a GitHub Release:

```lua
local Library = loadstring(game:GetService("HttpService"):GetAsync("https://github.com/ActualMasterOogway/Fluent-Renewed/releases/latest/download/Fluent.luau", true))()

```lua
local Library = loadstring(game:HttpGetAsync("https://github.com/ActualMasterOogway/Fluent-Renewed/releases/latest/download/Fluent.luau", true))()

## 📜 Usage

[Example Script the studio environment](https://github.com/ActualMasterOogway/Fluent-Renewed/blob/master/Example.client.luau)

[Example Script for an exploit environment](https://github.com/ActualMasterOogway/Fluent-Renewed/blob/master/Example.luau)

## Credits

- [Master Oogway](https://github.com/ActualMasterOogway/Fluent-Renewed) - The master mind behind Fluent Renewed
- [dawid](https://github.com/dawid-scripts/Fluent) - The master mind behind Fluent
- [Lucide](https://github.com/lucide-icons), [Phosphor](https://github.com/phosphor-icons) - The sexy icons
- [richie0866/remote-spy](https://github.com/richie0866/remote-spy) - Assets for the UI, some of the code
- [violin-suzutsuki/LinoriaLib](https://github.com/violin-suzutsuki/LinoriaLib) - Code for most of the elements, save manager
- [7kayoh/Acrylic](https://github.com/7kayoh/Acrylic) - Porting richie0866's acrylic module to lua
- [Latte Softworks & Kotera](https://github.com/latte-soft/wax/) - Bundler
- [Pepsied-5229/Pepsi-UI-Library](https://github.com/Pepsied-5229/Pepsi-UI-Library) - Inspiration for new features, some of the code


# UltimateList
A library to declaratively create virtualized lists in Roblox React.

Read the documentation [**here**](https://kampfkarren.github.io/ultimate-list).

## Example usage
```luau
return React.createElement(UltimateList.Components.ScrollingFrame, {
    dataSource = UltimateList.DataSources.array(letters),

    dimensions = UltimateList.Dimensions.consistentSize(48),

    renderer = UltimateList.Renderers.byState(function(value)
        return React.createElement("TextLabel", {
            BackgroundColor3 = Color3.new(1, 1, 1),
            Font = Enum.Font.BuilderSansBold,
            Text = value,
            TextColor3 = Color3.new(0, 0, 0),
            TextSize = 36,
            Size = UDim2.fromScale(1, 1),
        })
    end),

    direction = "y",
})

## Installation
UltimateList is available on Wally under [`kampfkarren/ultimate-list`](https://wally.run/package/kampfkarren/ultimate-list).


# Rostar

    Dead simple fully managed Rojo helper for Roblox projects

## Description

Rostar is a command-line tool that unpacks/packs Roblox place files (`rbxl`/`rbxlx`) into model files and `.lua` scripts in the filesystem, for use with [Rojo](https://rojo.space/). It is useful to both developers that prefer Roblox Studio, but also to Rojo users that would like a fully managed workflow without a hassle.

## Documentation

https://tacheometry.github.io/Rostar


### Fully managed Rojo

Gain the benefits of [fully managed Rojo](https://rojo.space/docs/v7/workflows/#fully-managed-rojo). Models and scripts are stored as files, and are able to be tracked with Git.

### Rojo compatible

Other project editors do not need to have Rostar installed to be able to work on the project.

### No extra configuration

Rostar does not require any extra configuration. Specify an input rbxl/rbxlx file and it will extracted into the filesystem with ease.


# Janitor

Janitor library. This branch is for the thread safe version of Janitor that does not use a global state.

[Original](https://github.com/RoStrap/Events/blob/master/Janitor.lua) was made by [Validark](https://github.com/Validark), however he doesn't really maintain that version anymore. It does have all the [original documentation](https://rostrap.github.io/Libraries/Events/Janitor/) for it though.

[Now on roblox-ts!](https://www.npmjs.com/package/@rbxts/janitor)

## Projects that use Janitor

If your project uses Janitor, leave a PR on the readme!

- [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/)
- [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 [a great friend](https://www.roblox.com/users/33655127/profile) (boatbomber lol)
- [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)
- [Bloopville (NOT RELEASED)](https://www.roblox.com/games/1919575283/BloopVille0) by [BloopVille Team](https://www.bloopville.com/)
- [Tropical Town Tycoon](https://www.roblox.com/games/8391439627/) by [Mightybull Games](https://www.roblox.com/groups/8573100/Mightybull-Games)

## Why use Janitor?

- Janitor makes dealing with garbage collection much less annoying and stressful because it manages them all in a nice interface.
- `Janitor:Add` returns whatever was added, which Maid doesn't.
- `Janitor:Add` also accepts a custom method, if you want to call `:Stop` on a `Tween`. You can see this being used in the [JanitorPromise](https://github.com/howmanysmall/Janitor/blob/main/src/Promise/init.lua#L100) library.
- `Janitor:Add` also accepts a custom reference to store under, which keeps the api more consistent. (`Maid.A = X` and `Maid:GiveTask(Y)` vs `Janitor:Add(X, nil, "A")` and `Janitor:Add(Y)`)
- Janitor also allows linking to an Instance, so when the Instance is destroyed, the Janitor cleans up everything along with it.

### Some less important benefits

- Runs a little better than Maid does.

## Performance

Janitor runs incredibly well. It is quite a bit faster than [Maid](https://github.com/Quenty/NevermoreEngine/blob/version2/Modules/Shared/Events/Maid.lua) and around as fast as [Dumpster](https://gist.github.com/Fraktality/f0ab4ad950698e9f08bb01bea486845e). You can run the benchmark for yourself using [boatbomber's benchmark plugin](https://devforum.roblox.com/t/benchmarker-plugin-compare-function-speeds-with-graphs-percentiles-and-more/829912) and the bench found [here](https://github.com/boatbomber/BenchmarkerLibrary).

![Benchmark results](https://cdn.discordapp.com/attachments/507950082285502465/807365433388433408/unknown.png)

Benchmarks ran on an R9 3900X with 32GB of DDR4-3600 RAM.


### Custom cleanup methods

Other garbage collection implementations such as Maid don't give you a choice with how you clean up objects. You can either give a function, a connection, an Instance, or a table with a Destroy method. Janitor allows you to specify how you clean up objects, so you can cancel a Tween when the Janitor is cleaned up.

### Promise Support

Janitor supports adding Promises to it natively, which will then cancel if the Janitor is cleaned up. This makes Janitor the only library of its kind with support for this implementation of Promises.

### Instances linking

Janitor's LinkToInstance method allows you to cleanup the Janitor when a linked Instance is destroyed. This allows you to execute behavior on destruction as well as cleanup objects quickly.


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

Light-weight, flexible object for cleaning up connections, instances, or anything. This implementation covers all use cases, as it doesn't force you to rely on naive typechecking to guess how an instance should be cleaned up. Instead, the developer may specify any behavior for any object.

    local Janitor = Resources:LoadLibrary("Janitor")

## Library API¶

### Janitor.new¶

Janitor Janitor.new()

Instantiates a new Janitor object

## Janitor API¶

### Janitor:Add¶

any Janitor:Add(any Object, <string, true> MethodName = "Destroy" [, string Index])

Adds an `Object` to Janitor for later cleanup, where `MethodName` is the key of the method within `Object` which should be called at cleanup time. If the `MethodName` is `true` the `Object` itself will be called instead. If passed an index it will occupy a namespace which can be Remove()d or overwritten. Returns the Object.

Example

    local Obliterator = Janitor.new()

    -- Queue the Part to be Destroyed at Cleanup time
    Obliterator:Add(workspace.Part, "Destroy")

    -- Queue function to be called with `true` MethodName
    Obliterator:Add(print, true)

    -- This implementation allows you to specify behavior for any object
    Obliterator:Add(Tween.new(0.5, 0, print), "Stop")

    -- By passing an Index, the Object will occupy a namespace
    -- If "CurrentTween" already exists, it will call :Remove("CurrentTween") before writing
    Obliterator:Add(Tween.new(0.5, 0, print), "Stop", "CurrentTween")

Note

Objects not given an explicit `MethodName` will be passed into the `typeof` function for a very naive typecheck. RBXConnections will be assigned to "Disconnect", functions will be assigned to `true`, and everything else will default to "Destroy". Not recommended, but hey, you do you.

### Janitor:Remove¶

void Janitor:Remove(string Index)

Cleans up whatever Object was set to this namespace by the 3rd parameter of [:Add()](https://rostrap.github.io/Libraries/Events/Janitor/#janitoradd)

Example

    Obliterator:Remove("CurrentTween")

### Janitor:Cleanup¶

void Janitor:Cleanup()

Calls each Object's `MethodName` (or calls the Object if `MethodName == true`) and removes them from the Janitor. Also clears the namespace. This function is also called when you call a Janitor Object (so it can be used as a destructor callback).

Example

    Obliterator:Cleanup()
    Obliterator()

### Janitor:LinkToInstance¶

RbxScriptConnection Janitor:LinkToInstance(RbxObject Instance [, boolean AllowMultiple])

"Links" this Janitor to an Instance, such that the Janitor will `Cleanup` when the Instance is `Destroyed()` and garbage collected. A Janitor may only be linked to one instance at a time, unless `AllowMultiple` is true.

When called with a truthy `AllowMultiple` parameter, the Janitor will "link" the Instance without overwriting any previous links, and will also not be overwritable. When called with a falsy `AllowMultiple` parameter, the Janitor will overwrite the previous link which was also called with a falsy `AllowMultiple` parameter, if applicable.

Example

    local Janitor = Janitor.new()

    Janitor:Add(function()
        print("Cleaning up!")
    end)

    do
        local Folder = Instance.new("Folder")
        Janitor:LinkToInstance(Folder)
        Folder:Destroy()
    end

    -- Cleaning up!


Here are some quick links to get started using Janitor:

  * [Installation guide](https://howmanysmall.github.io/Janitor/docs/installation)
  * [**API Docs**](https://howmanysmall.github.io/Janitor/api/Janitor)
  * [Why use Janitor?](https://howmanysmall.github.io/Janitor/docs/why-use-janitor)
  * [Who uses Janitor?](https://howmanysmall.github.io/Janitor/docs/who-uses-janitor)


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

Used to create Bezier functions.

## API¶

    local EasingFunc = Bezier.new(0.17, 0.67, 0.83, 0.67)

Test and generate Bezier curves here at [cubic-bezier.com](http://cubic-bezier.com/) or at [greweb.me](http://greweb.me/bezier-easing-editor/example/) Credit: Math borrowed from [here](https://github.com/gre/bezier-easing)


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

A two-step, single-choice Dialog with built-in Replication.

This library registers a Material Design `ChoiceDialog` PseudoInstance which can be instantiated via `PseudoInstance.new("ChoiceDialog")`.

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

## ChoiceDialog API¶

ChoiceDialog inherits from [ReplicatedPseudoInstance](https://rostrap.github.io/Libraries/Classes/ReplicatedPseudoInstance)

### Fields¶

Property | Type | Description
---|---|---
PrimaryColor3 | Color3 | The color of the radio/ripple buttons
Options | array of strings | The options presented
HeaderText | string | The Dialog title
DismissText | string | The Dismiss text (the left button)
ConfirmText | string | The Confirm text (the right button)

### Events¶

Event | Description | Signature
---|---|---
OnConfirmed | Fires after the ChoiceDialog was Confirmed/Dismissed: if confirmed, passes in string `OptionChosen` | (Player Player, string Choice)

###### ChoiceDialog also 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/ChoiceDialog.rbxl)

Demo code:

    -- This code is valid on the client and server
    -- Just make sure that both call `Resources:LoadLibrary("ReplicatedPseudoInstance")`

    -- If this is in a Script, it will Replicate this ChoiceDialog to every
    -- Player in the game and everyone who joins
    -- If this is in a LocalScript, it will generate it on the client only

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

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

    Resources:LoadLibrary("ReplicatedPseudoInstance")

    local PrimaryColor3 = Color.Teal[500] -- This is just a Color3 value

    local Dialog = PseudoInstance.new("ChoiceDialog")
    Dialog.HeaderText = "Repository Location"
    Dialog.Options = {"ServerStorage", "ServerScriptService"}
    Dialog.DismissText = "CANCEL"
    Dialog.ConfirmText = "INSTALL"
    Dialog.PrimaryColor3 = PrimaryColor3

    Dialog.OnConfirmed:Connect(function(Player, Choice)
        print(Player, Choice)

        if Choice then
            -- Choice is a string of the option they chose
        else
            -- They chose Dismiss, so Choice is false
        end
    end)

    Dialog.Parent = ReplicatedStorage


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

A reimplementation of the vanilla Lua os.date function built upon the one exposed by RobloxLua

    local Date = Resources:LoadLibrary("Date")

Demo:

    -- ISO 8601:
    print(Date("%FT%T"))              -- 2020-01-01T01:03:05
    print(Date("%Y-%m-%dT%H:%M:%S"))  -- 2020-01-01T01:03:05
    print(Date("%FT%T%#z"))           -- 2020-01-01T01:03:05-05:00

    -- Time:
    print(Date("%T")) -- 08:37:43

    -- Date:
    print(Date("%D")) -- 01/12/20

`Date` functions just like the vanilla Lua `os.date` function, except padding can be toggled by inserting a '#' like so:

    print(Date("%#x", os.time()))

String reference:

    The following patterns will be replaced by their tags below:
    %c = "%a %b %e %X %Y"
    %D = "%m/%d/%y"
    %F = "%Y-%m-%d"
    %n = "\n"
    %R = "%H:%M"
    %r = "%I:%M:%S %p"
    %T = "%H:%M:%S"
    %t = "\t"
    %v = "%e-%b-%Y"
    %X = "%T"
    %x = "%D"

    %#c = "%#x, %#X"
    %#r = "%#I:%M:%S %#p"
    %#T = "%#H:%M:%S"
    %#X = "%#T"
    %#x = "%A, %B %#d, %#Y"

    The following tags will be replaced as follows:
    %% = the character `%´
    %a = abbreviated weekday name (e.g., Wed)
    %A = full weekday name (e.g., Wednesday)
    %b = abbreviated month name (e.g., Sep)
    %B = full month name (e.g., September)
    %C = century: (year / 100) (padded)
    %d = day of the month (16) [01-31]
    %e = day of month as decimal number [ 1, 31]
    %g = Same year as in %G, but as a decimal number without century [00, 99]
    %G = a 4-digit year as a decimal number with century
    %H = hour, using a 24-hour clock (23) [00-23]
    %I = hour, using a 12-hour clock (11) [01-12]
    %j = day of year [001-366]
    %k = Hour in 24-hour format [ 0, 23]
    %l = Hour in 12-hour format [ 1, 12]
    %m = month (09) [01, 12]
    %M = minute (48) [00, 59]
    %p = either "am" or "pm" ('#' makes it uppercase)
    %s = Day of year suffix: e.g. 12th, 31st, 22nd
    %S = Second as decimal number [00, 59]
    %u = ISO 8601 weekday as number with Monday as 1 [1, 7]
    %U = Week of year, Sunday Based [00, 53]
    %V = week number of year (Monday as beginning of week) [01, 53]
    %w = weekday (3) [0-6 = Sunday-Saturday]
    %W = Week of year with Monday as first day of week [0, 53]
    %y = two-digit year (98) [00, 99]
    %Y = full year (1998)
    %z = Time zone offset from UTC in the form [+-]%02Hours%02Minutes, e.g. +0500

Example:

    print(Date("It is currently %#r"))
    --> It is currently 1:41:20 am


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

Calls functions on an interval along `os.time` (for cross-server simultaneous calls)

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

    -- SyncedPoller.new(number Interval, function Func)
    SyncedPoller.new(10, print)

Calls a function every `Interval` seconds, whenever `(os.time() % Interval == 0)`. Functions are called with the current `os.time()` (with `tick()` precision).


# [Try](https://github.com/Validark/RBX-Try-Library/tree/patch-4)¶

(DEPRECATED) An asynchronous pcall-wrapper library for controlling the flow of error-prone, interdependent functions.

## How to use¶

Upon requiring the Library, it returns a function called Try:

    -- Without RoStrap
    local Try = require(TryLibrary)

    -- With RoStrap
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))

    local Try = Resources:LoadLibrary("Try")

### API¶

`Attempt Try(Function, ...)`

`Try` calls `pcall(Function, ...)` **on a separate thread**, and returns a table Object called an `Attempt`.

#### Attempt¶

`Attempt :Then(Callback)`

This method takes a callback of the form `<function, callable table> Callback(...)`, and pcalls it **if the previous pcall didn't error**, with `...` being that which was returned by that `pcall`. This also returns the attempt, for further chaining.

    local HttpService = game:GetService('HttpService')

    Try(wait, 0.1)

        -- Try hashing the time
        :Then(function (Delta, ElapsedTime)
            return HttpService:GetAsync('http://md5.jsontest.com/?text=' .. Delta)
        end)

        -- Try decoding the response
        :Then(function (RawResponse)
            return HttpService:JSONDecode(RawResponse)
        end)

        -- Print the decoded response data
        :Then(function (Response)
            print('Input:', Response.original, '\nMD5:', Response.md5)
        end)

`Attempt :Catch([string Patterns...], Callback)`

This method takes a callback of the form `Variant Callback(string Error, string Stack, Attempt FailedAttempt)`, and pcalls it **if the previous pcall had an error**. Errors can be optionally filtered by providing a list of [patterns which the error should match](http://wiki.roblox.com/index.php?title=String_pattern#Simple_matching), otherwise all errors are caught by the function. Once an attempt's error is caught, it will not be caught by the next chained `:Catch` method, unless `Callback` itself has an error. The attempt is then returned for chaining.

If the first returned value from the attempt is an `Attempt`, it will be executed and the method will process its errors.

    local HttpService = game:GetService('HttpService')

    Try(HttpService.GetAsync, HttpService, 'http://httpstat.us/404')
        :Then(function (Data)
            print('Found', Data)
        end)

        -- Catch when the URL doesn't exist
        :Catch('HTTP 404', function (Error, Stack, Attempt)
            warn('Not found, error:', Error)
        end)

        -- Catch any other error
        :Catch(function (Error, Stack, Attempt)
            warn('Unknown error:', Error)
        end)

[httpstat.us](http://httpstat.us/) is a good way to test Http request errors.

`Attempt :Retry()`

This method can only be called within a `Catch` callback. It retries the last function called in the chain before the error (with the same old arguments). `Attempt.RetryCount` is incremented each time the attempt is retried, and is reset after a `Retry` `pcall` doesn't error.

You can use this method to retry a sequence of interdependent function calls that fail, and even limit the number of, or space out, retries. For example:

    local HttpService = game:GetService('HttpService')

    Try(HttpService.GetAsync, HttpService, 'http://httpstat.us/503')
        :Then(function (Data)
            print('Found', Data)
        end)

        -- Catch when the server is having issues and retry
        :Catch('HTTP 503', 'Timeout was reached', function (Error, Stack, Attempt)

            -- Limit the number of retries to 3
            if Attempt.RetryCount < 3 then

                -- Space out each retry
                local BackoffTime = Attempt.RetryCount * 3 + 3
                warn('Retrying in', BackoffTime, 'seconds...')
                wait(BackoffTime)

                -- Retry the attempt
                return Attempt:Retry()

            -- Give up if retry limit reached
            else
                warn('Failed')
            end

        end)

        -- Catch any other errors
        :Catch(function (Error, Stack, Attempt)
            warn('Unknown error:', Error)
        end)

`Attempt :Wait()`

This method yields until all of the pcalls before it have finished running.

    Try(wait, 0.5)
        :Then(wait)
        :Wait()
    print("Hello!") -- Runs after all of the threads finish

A `:Wait()` can go anywhere in the Chain:

    local Attempt = Try(wait, 2)

    print("Hey!") -- This runs immediately after Try is called on a separate thread

    Attempt
        :Wait() -- Wait until this Attempt's thread finishes yielding
        :Then(function(...) -- This is still on a separate thread
            wait(1)
            print("This was returned by wait(2)", ...)
        end)
    print("The Attempt has finished yielding!")


# Overview¶

What follows is an overview of the [libraries installable by RoStrap](https://github.com/RoStrap/Libraries/blob/master/Libraries.lua):

## RoStrap-owned Libraries by category¶

#### Core¶

###### [Resources](https://rostrap.github.io/Resources) \- The core resource manager and library loader for RoStrap. Centralizes library loading and simplifies resource management.

#### Classes¶

###### [Enumeration](https://rostrap.github.io/Libraries/Classes/Enumeration) \- A Lua implementation of Roblox Enums with the ability to declare new ones

    Enumeration.SelectionControllerType = {"Checkbox", "Radio", "Switch"}
    local Radio = Enumeration.SelectionControllerType.Radio

    print(Radio) --> Enumeration.SelectionControllerType.Radio
    print(Radio.EnumerationType) --> SelectionControllerType
    print(Radio.Name) --> Radio
    print(Radio.Value) --> 1

###### [PseudoInstance](https://rostrap.github.io/Libraries/Classes/PseudoInstance) \- A library for declaring black-boxed classes similar to Roblox Instances

###### [ReplicatedPseudoInstance](https://rostrap.github.io/Libraries/Classes/ReplicatedPseudoInstance) \- An extension of PseudoInstance with an automatic replication system

#### DataTypes¶

###### [Array](https://rostrap.github.io/Libraries/DataTypes/Array) \- A few utility functions which can operate on Arrays

###### [SortedArray](https://rostrap.github.io/Libraries/DataTypes/SortedArray) \- A class for sorted arrays using the binary search algorithm

###### [Table](https://rostrap.github.io/Libraries/DataTypes/Table) \- A few utility functions which operate on Tables, notably Table.Lock

#### Debugging¶

###### [Debug](https://rostrap.github.io/Libraries/Debugging/Debug) \- Better error/warn functions and an implementation of TableToString

###### [Typer](https://rostrap.github.io/Libraries/Debugging/Typer) \- Type Checker and Function Signature Assigner

    local AddPlayer = Typer.AssignSignature(Typer.InstanceWhichIsAPlayer, function(plr)
        print(plr)
    end)

    AddPlayer(game:GetService("Players").LocalPlayer) -- good!
    AddPlayer(10) -- [Typer] {AddPlayer} bad argument #1: expected Instance which is a Player, got number 10

#### Events¶

###### [Janitor](https://rostrap.github.io/Libraries/Events/Janitor) \- Light-weight, flexible object for cleaning up connections, instances, or anything. Intended to reach all use cases

###### [Signal](https://rostrap.github.io/Libraries/Events/Signal) \- A class for creating API-compatible Roblox Events

#### Helper¶

###### [FastSpawn](https://rostrap.github.io/Libraries/Helper/FastSpawn) \- Expensive method of running a function on a new thread without yielding a frame (like spawn) and works within Roblox's thread scheduler (`coroutine.resume(coroutine.create(Func))` is now preferred)

###### [Make](https://rostrap.github.io/Libraries/Helper/Make) \- Shorthand for instance declarations

#### Input¶

###### [Keys](https://rostrap.github.io/Libraries/Input/Keys) \- Simplifies Key input bindings

    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)

#### Interpolation¶

###### [Bezier](https://rostrap.github.io/Libraries/Interpolation/Bezier) \- Lua implementation of (2D) [Bezier curves](http://cubic-bezier.com/)

    local EasingFunc = Bezier.new(0.17, 0.67, 0.83, 0.67)

###### [EasingFunctions](https://rostrap.github.io/Libraries/Interpolation/EasingFunctions) \- 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))

###### [Lerps](https://rostrap.github.io/Libraries/Interpolation/Lerps) \- Holds all Lerp functions for Roblox objects, with a special visually-linear color lerping function

###### [Tween](https://rostrap.github.io/Libraries/Interpolation/Tween) \- Tweening library modeled after the [TweenPosition](http://duckduckgo.com/?q=!rowiki+TweenPosition) method built upon [Lerps](https://rostrap.github.io/Libraries/Interpolation/Lerps) and [EasingFunctions](https://rostrap.github.io/Libraries/Interpolation/EasingFunctions)

    Tween(workspace.Part, "Transparency", 1, Standard, 2, true)

#### Math¶

###### [BigNum](https://rostrap.github.io/Libraries/Math/BigNum) \- A BigNum with Fractions implementation for big-integer calculations, optimized for DataStore storage and networking.

    print(BigNum.new("1025123") ^ BigNum.new("9"))
    --> 1250212389547080859766804627957328989496357747253559363

###### [Leveler](https://rostrap.github.io/Libraries/Math/Leveler) \- Level and Experience class, because why not?

###### [Normal](https://rostrap.github.io/Libraries/Math/Normal) \- Random number generator along a Normal distribution curve

###### [WeightedProbabilityFunction](https://rostrap.github.io/Libraries/Math/WeightedProbabilityFunction) \- A syntactically pleasing way of creating functions which randomly pick an option based upon its relative probability

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

    print(CoinToss()) --> Heads

#### RoStrapUI¶

###### [AsymmetricTransformation](https://rostrap.github.io/Libraries/RoStrapUI/AsymmetricTransformation) \- Transform function for Paper from the Material Design specifications

###### [Checkbox](https://rostrap.github.io/Libraries/RoStrapUI/Checkbox) \- A Material Design checkbox element

###### [ChoiceDialog](https://rostrap.github.io/Libraries/RoStrapUI/ChoiceDialog) \- A Material Design ChoiceDialog element with built-in replication

###### [Color](https://rostrap.github.io/Libraries/RoStrapUI/Color) \- Material Design's 2014 Color Palette

###### [Radio](https://rostrap.github.io/Libraries/RoStrapUI/Radio) \- A Material Design radio element

###### [RadioGroup](https://rostrap.github.io/Libraries/RoStrapUI/RadioGroup) \- A wrapper class for a group of radio elements

###### [RippleButton](https://rostrap.github.io/Libraries/RoStrapUI/RippleButton) \- A Material Design button element

###### [Rippler](https://rostrap.github.io/Libraries/RoStrapUI/Rippler) \- A Material design class responsible for creating ripples

###### [SelectionController](https://rostrap.github.io/Libraries/RoStrapUI/SelectionController) \- A class from which the checkbox and radio classes inherit

###### [Shadow](https://rostrap.github.io/Libraries/RoStrapUI/Shadow) \- A Shadow/Elevation rendering PseudoInstance

###### [Snackbar](https://rostrap.github.io/Libraries/RoStrapUI/Snackbar) \- A Material Design Snackbar element with built-in replication

#### Time¶

###### [Date](https://rostrap.github.io/Libraries/Time/Date) \- A reimplementation of the vanilla Lua os.date function built upon the one exposed by RobloxLua

    print(Date("ISO 8601: %FT%T%#z")) --> ISO 8601: 2020-12-31T01:03:05-05:00

###### [SyncedPoller](https://rostrap.github.io/Libraries/Time/SyncedPoller) \- Calls functions on an interval along `os.time` (for cross-server simultaneous calls)

#### Sentry¶

###### [Sentry](https://rostrap.github.io/Libraries/Sentry) \- A library for reporting errors/warnings/messages to your [Sentry account](https://sentry.io)

###### [Try](https://rostrap.github.io/Libraries/Try) \- (DEPRECATED in favor of Promise) An asynchronous pcall-wrapper library for controlling the flow of error-prone, interdependent functions.

## Roblox-owned Libraries¶

###### [Roact](https://github.com/Roblox/roact) \- A declarative UI library similar to Facebook's React

###### [Rodux](https://github.com/Roblox/rodux) \- A state management library inspired by Redux

###### [Roact-Rodux](https://github.com/Roblox/roact-rodux) \- An ergonomic binding between Roact and Rodux

## Highly trusted Libraries¶

###### [Promise](https://github.com/LPGhatguy/roblox-lua-promise) \- An implementation of Promise similar to Promise/A+

###### [Aurora](https://github.com/evaera/Aurora) \- Manages status effects (known as "Auras")

###### [DataStoreService](https://github.com/buildthomas/MockDataStoreService) \- An implementation of Roblox's DataStoreService in Lua for seamless offline development & testing

###### [Cmdr](https://github.com/evaera/Cmdr) \- A fully extensible and type-safe admin command console

[](https://giant.gfycat.com/HatefulTanAzurewingedmagpie.mp4)

###### [EvLightning](https://github.com/evaera/EvLightning) \- Realistic-looking lightning bolt generator

###### [RadialImage](https://github.com/evaera/RadialSpriteSheetGenerator) \- A library which displays radial progress indicators using a sprite sheet generated by a nifty tool

## Third-Party Libraries¶

###### [Ready](https://github.com/EmeraldSlash/RbxReady) \- A Library for yielding until an object's descendants have finished replicating

## Libraries Lacking documentation¶

###### [ConditionalPoller](https://github.com/RoStrap/Time/blob/master/ConditionalPoller.lua) \- A class that continually calls a callback as long as a condition is true

###### [ReplicatedValue](https://github.com/RoStrap/Events/blob/master/ReplicatedValue.lua) \- A server-authoritative value replicated across the network

###### [Spring](https://github.com/RoStrap/Math/blob/master/Spring.lua) \- Simulates the motion of a critically damped spring

## RoStrap Discord¶

###### Questions? Comments? Concerns? Join us!

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


# Getting Started¶

To begin using RoStrap, simply install it by clicking the icon below and pressing the `Install` button:

[](https://www.roblox.com/library/725884332/RoStrap)

After installing, open a place and enable Http requests from Home → Game Settings → Security.

Next, navigate to the `PLUGINS` tab. Click the RoStrap logo.

The plugin will install the [Resources](https://github.com/RoStrap/Resources/blob/master/Resources.lua) library and create an example "Repository" folder in [ServerStorage](http://wiki.roblox.com/index.php?title=API:Class/ServerStorage) from which libraries may be loaded using `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`

Example

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

    local Keys = Resources:LoadLibrary("Keys") -- Keys
    local Thin = Resources:LoadLibrary("Thin") -- Error! Not a valid library

# Libraries¶

Whenever a developer updates their Library on GitHub, you will see an update button appear (under the `INSTALLED` tab). Installing or updating a library automatically installs its dependencies.

Updates are detected when your source code doesn't match the source code of the latest version of GitHub, excluding whitespace, comments, and _configurable variables_. Configurable variables (as RoStrap recognizes them) have to look like this:

    local ALL_CAMEL_CASE = "SINGLE-LINE-VALUE"


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

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

Only Global ZIndexBehavior is officially supported.

## Rippler API¶

### Rippler:Down¶

Tween Rippler:Down([number X, number Y])

Animates a Circular Ripple at position UDim2.new(0, X, 0, Y) within Container. Defaults to the center. Returns the Tween Object which is Tweening the `Size` of the Ripple.

### Rippler:Up¶

void Rippler:Up()

Fades the currently open Ripple out

### Rippler:Ripple¶

void Rippler:Ripple([number X, number Y, number Duration = 0.15])

Calls `Rippler:Down(X, Y)` and calls `Rippler:Up()` after `Duration`

### Fields¶

Property | Type | Description
---|---|---
Style | Enumeration.RipplerStyle | Full, Icon, or Round
BorderRadius | Enumeration.BorderRadius | How rounded the corners should be (0, 2, 4, or 8)
RippleFadeDuration | number | How long it takes for a Ripple to Fade
MaxRippleDiameter | number | A cap at how large the Ripples can get (defaults to math.huge)
RippleExpandDuration | number | How long it takes a Ripple to expand to full size
RippleColor3 | Color3 | The Color of the Ripples
RippleTransparency | number | The Transparency of the Ripples
Container | GuiObject | The parent to which Ripples are Parented

When the `Style` is set to `Enumeration.RipplerStyle.Icon`, it will constrain the diameter of the Ripples to twice the height of the Container.


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

Standard RoStrap debugging library. Contains better error/warn functions and an implementation of `TableToString`.

    local Debug = Resources:LoadLibrary("Debug")

## Library API¶

### Debug.TableToString¶

string Debug.TableToString(table Table [, bool Multiline = false, string TableName])

Pretty self-explanatory. Table is the table to convert into a string. String TableName puts `local TableName =` at the beginning. Multiline makes it multiline. Returns a string-readable version of Table.

### Debug.DirectoryToString¶

string Debug.DirectoryToString(RbxObject Object)

A fixed version of GetFullName.

### Debug.Inspect¶

string Debug.Inspect(any Object)

Returns a string representation of Object

### Debug.EscapeString¶

string Debug.EscapeString(string String)

Turns strings into Lua-readable format. Returns Objects location in proper Lua format. Useful for when you are doing string-intensive coding. Those minus signs are so tricky!

### Debug.AlphabeticalOrder¶

function Debug.AlphabeticalOrder(table Dictionary)

Iteration function that iterates over a dictionary in alphabetical order. Dictionary is that which will be iterated over in alphabetical order. Not case-sensitive.

Example

    for Key, Value in next, Debug.AlphabeticalOrder{Apple = true, Noodles = 5, Soup = false} do
        print(Key, Value)
    end

### Debug.Error¶

void Debug.Error(string ErrorMessage, ... strings argumentsToFormatIn)

Standard RoStrap Erroring system. Prefixing ErrorMessage with '!' makes it expect the `[error origin script].Name` as first parameter in `{...}`. Past the initial Error string, subsequent arguments get unpacked in a string.format of the error string. Assert falls back on Error. Error blames the latest item on the traceback as the cause of the error. Error makes it clear which Library and function are being misused.

### Debug.Assert¶

Condition Debug.Assert(Variant Condition, ... strings TupleToSendToError)

Returns `Condition or Debug.Error(...)`

### Debug.Warn¶

void Debug.Warn(string ErrorMessage, ... strings argumentsToFormatIn)

Functions the same as Debug.Error, but internally calls warn instead of error.

### Debug.UnionIteratorFunctions¶

function Debug.UnionIteratorFunctions(functions ...)

Unions iterator functions in the order they are passed in, without duplicates from overlap.

    for i, v in Debug.UnionIteratorFunctions(ipairs, pairs){1,2,3,4, a = 1, b = 2} do
        print(i, v)
    end


# Bundling RoStrapUI with a plugin

Let's say we want to Bundle ChoiceDialog with a plugin. Specifically, an "Object to Lua" plugin.

We're going to want all of our libraries under the same Object. In this case we are going to place our libraries under the main Script of the plugin.

Next, we are going to run a script that will replace the Resources functions with some simple replacements.

Enable your command bar (under the View tab) and paste the following script in. Make sure to change `PARENT_OF_LIBRARIES` to fit your use case.

    local PARENT_OF_LIBRARIES = workspace.ObjectsToLua

    local LocalTables = {}
    for _, Object in next, PARENT_OF_LIBRARIES:GetDescendants() do
        if Object:IsA("LuaSourceContainer") then
            local Source, Reps = Object.Source
                :gsub("local Resources = [^\r\n]+[\r\n]", "\nlocal function _Load_Library_(Name)\n\treturn require(script.Parent[Name])\nend\n")
                :gsub("Resources:LoadLibrary(%b())", "_Load_Library_%1")
                :gsub("Resources:GetLocalTable(%b())", function(x)
                    local Name = "LocalTable" .. x:sub(3, -3)

                    if not LocalTables[Name] then
                        LocalTables[Name] = true
                        local Mod = Instance.new("ModuleScript", PARENT_OF_LIBRARIES)
                        Mod.Source = "return {}\n"
                        Mod.Name = Name
                    end

                    return "_Load_Library_(\"" .. Name .. "\")"
                end)
                :gsub("Resources:Get(%a+)(%b())", function(x, y)
                    return "_Resource_Get_(\"" .. x .. "\", " .. y:sub(2, -2) .. ")"
                end)

            if Reps > 0 then
                Source = "local function _Resource_Get_(ClassName, Name, Parent)\n\tlocal IsPlugin = script.Parent.Parent.ClassName == \"Plugin\"\n\n\tif not Parent then\n\t\tParent = _Resource_Get_(\"Folder\", ClassName .. \"Folder\", game:GetService(IsPlugin and \"CoreGui\" or \"ReplicatedStorage\"))\n\tend\n\n\tif IsPlugin or game:GetService(\"RunService\"):IsServer() then\n\t\tlocal Object = Instance.new(ClassName)\n\t\tObject.Name = Name\n\t\tObject.Parent = Parent\n\t\treturn Object\n\telse\n\t\treturn Parent:WaitForChild(Name)\n\tend\nend\n\n" .. Source
            end

            Object.Source = Source
        end
    end

This code should work for any of the libraries under the RoStrap organization, with the exception of the `Sentry` library.

Now we can write some code for the plugin:

    require(script.ChoiceDialog) -- Preload the libraries
    require(script.ReplicatedPseudoInstance)
    local PseudoInstance = require(script.PseudoInstance)

    local Dialog = PseudoInstance.new("ChoiceDialog")
    Dialog.HeaderText = "Script Class"
    Dialog.Options = {"ModuleScript", "Script", "LocalScript"}
    Dialog.DismissText = "CANCEL"
    Dialog.ConfirmText = "CONVERT"
    Dialog.PrimaryColor3 = PrimaryColor3
    Dialog.Parent = game

    local _, Choice = Dialog.OnConfirmed:Wait()

    print(Choice)

Add some glue, and you've got yourself a plugin running RoStrapUI! Publish your plugin and try it out. Also be sure to check out my RbxObject2Lua plugin by clicking the following image:

[](https://www.roblox.com/library/2307140444/Obj2Lua)


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

Level and Experience class.

## Library API¶

### Leveler.new¶

LevelerObject Leveler.new(number PointsToStartWith = 0)

Returns a Leveler Object.

## Leveler API¶

### Leveler:Award¶

self Leveler:Award(number Points = 1)

Awards Points to Leveler, recalculating its Fields.

Fields:

Field | Description | Type
---|---|---
Total | Total Exp | number
Lvl | Current level | number
Exp | Current Exp within level | number
Next | Total Exp required to advance from this level to the next level | number
Percent | Decimal progress to next level | number

Run this little demo and you will learn everything you need to know.

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

    local Kills = Leveler.new()
    table.foreach(Kills, print)
    print()

    Kills:Award()
    table.foreach(Kills, print)
    print()

    Kills:Award(6)
    table.foreach(Kills, print)


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

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

Only Global ZIndexBehavior is officially supported.

## RippleButton API¶

RippleButtons work very similarly to regular TextButtons or ImageButtons. Just set the fields to whatever you want.

### Fields¶

#### Wrapped Properties¶

Properties which access its top-level ImageButton:

Property | Type
AnchorPoint | Vector2
Active | boolean
Name | string
Parent | Instance
Size | UDim2
Position | UDim2
TextTransparency | string
LayoutOrder | int
NextSelectionDown | Instance
NextSelectionLeft | Instance
NextSelectionRight | Instance
NextSelectionUp | Instance
Visible | boolean
ZIndex | int

Properties which access its TextLabel:

Property | Type
Font | Enum.Font
Text | string
TextSize | number
TextXAlignment | Enum.TextXAlignment
TextYAlignment | Enum.TextYAlignment

Properties which access its Shadow:

Property | Type
Elevation | Enumeration.Elevation

#### RippleButton Properties¶

Property | Type | Description
---|---|---
Disabled | Boolean | Whether the RippleButton is Disabled
Tooltip | string | The tip to display upon hover, with "" being the disabled value
BorderRadius | Enumeration.BorderRadius | How rounded the corners should be (0, 2, 4, or 8)
Style | Enumeration.ButtonStyle | "Flat", "Outlined", or "Contained" style
PrimaryColor3 | Color3 | The color of the text in Flat and Outlined styles or the color of the background in a Contained style when not Disabled
SecondaryColor3 | Color3 | The color of the text in Raised style, by popular request, despite my insistance to conform to Material Design. Not the original intention.

#### Events¶

Event | Description
OnPressed | Fires after the Button was tapped or left-clicked
OnRightPressed | Fires after the Button was right-clicked
OnMiddlePressed | Fires after the Button was middle-clicked

###### RippleButton 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/RippleButton.rbxl)

Demo code:

Example

    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 Flat = PseudoInstance.new("RippleButton")
    Flat.AnchorPoint = Vector2.new(0.5, 0.5)
    Flat.Size = UDim2.new(0, 83, 0, 36)
    Flat.Position = UDim2.new(0.5, 0, 0.5, -36 - 16)
    Flat.PrimaryColor3 = Color.Teal[500]
    Flat.BorderRadius = 4
    Flat.Style = "Flat"
    Flat.Text = "SUBMIT"
    Flat.Parent = Frame

    local Outlined = Flat:Clone()
    Outlined.Style = "Outlined"
    Outlined.Position = UDim2.new(0.5, 0, 0.5, 0)
    Outlined.Parent = Frame

    local Contained = Flat:Clone()
    Contained.Style = "Contained"
    Contained.Position = UDim2.new(0.5, 0, 0.5, 36 + 16)
    Contained.Parent = Frame

    Flat.OnPressed:Connect(function()
        print("Pressed Flat")
    end)

    Outlined.OnPressed:Connect(function()
        print("Pressed Outlined")
    end)

    Contained.OnPressed:Connect(function()
        print("Pressed Contained")
    end)


# Image Standards¶

If uploading icons, images must be 2x size so that high-density screens can still appreciate the beautiful icons. Icons should also be white (so that you can change ImageColor3 to whatever you want, including black).

For example, for a 24x24 image (toggle_on) from [material.io](https://material.io/tools/icons/) we would download the following.

Go with the 48x48 image in the 2x folder.

Pixels of 100% transparency (0% opacity/alpha) must have white color values.

Bad | Good

To fix this in GIMP, do the following. First, create a new layer:

Make sure it is filled with white pixels:

Next, cut out all of the pixels, leaving only transparent white pixels. To do this, press `R` (open select tool), click on the image, hit `Ctrl + A` (select all), then hit `Ctrl + X` (cut). Click and drag on this transparent white layer and make sure it is on the bottom. Then `Right Click` \+ `Merge Down`

Make sure to `save color values from transparent pixels` when you export the image.


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

The Class from which Checkbox and Radio inherit.


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

Summary

Rigidly defined PseudoInstance class system based upon Roblox Instances. The goal is to create encapsulated instances whose state is simply a function of its externally accessible properties. In other words, a cloned instance should render exactly the same as the original instance.

## Library API¶

### PseudoInstance.new¶

PseudoInstance PseudoInstance.new(string ClassName, ...)

Instantiates a new PseudoInstance of type `ClassName`. Arguments (...) are passed to the `Init` constructor.

Example

    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    local Shadow = PseudoInstance.new("Shadow")
    Shadow.Elevation = 8 -- this will cast to Enumeration.ShadowElevation.Elevation8
    Shadow.Parent = Background -- Whatever Gui you wish

Note

Only classes which have been [Registered](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstanceregister) may be instantiated. If you attempt to instantiate a class which has not been [Registered](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstanceregister), it will call `Resources:LoadLibrary(ClassName)` and try again.

### PseudoInstance.Make¶

PseudoInstance PseudoInstance.Make(string ClassName, dictionary Properties, ...)

`PseudoInstance.new` wrapper which automatically assigns properties in dictionary `Properties`. Arguments passed to (...) must be dictionaries of Properties too. The only downside is you cannot pass any parameters to the `Init` constructor, but you probably don't need to. [See the Make library's documentation for more information, as it works identically.](https://rostrap.github.io/Libraries/Helper/Make/)

Example

    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    local Shadow = PseudoInstance.Make("Shadow", {
        Elevation = 8; -- this will cast to Enumeration.ShadowElevation.Elevation8
        Parent = Background; -- Parent is set last
    })

### PseudoInstance:Register¶

PseudoTemplate PseudoInstance:Register(string ClassName, table Template, table InheritsFrom = PseudoInstance)

Registers a new class of PseudoInstances which can be instantiated via `PseudoInstance.new(ClassName)`. Please read all the documentation in the example below:

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Resources = require(ReplicatedStorage:WaitForChild("Resources"))
    local Typer = Resources:LoadLibrary("Typer")
    local Enumeration = Resources:LoadLibrary("Enumeration")
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")

    Enumeration.PastaStyle = {"Spaghetti", "Fusilli", "Penne", "Rigatoni", "Rotelle"}

    local ClassNameTemplate = PseudoInstance:Register("ClassName", {
        Events = {
            -- These are the events (see: Signal) of the PseudoInstance. These can be Connect()ed to,
            -- Fire()d, Wait()ed on, etc

            -- implicit numerical indeces define an Event member without Constuctors or Destructors
            "OnPressed";

            OnRightPressed = function(self)
                -- self is the PseudoInstance for which this Event is being constructed.
                -- Must return two functions, one constuctor and
                -- one destructor with which to call Signal.new (see docs)
                return function()
                end, function()
                end
            end;
        };

        Internals = {
            -- These values are internal-only and not externally accessible

            -- implicit numerical indeces are set by default to false, to be defined at run-time
            "NumChildren";

            Set = function(self, Bool) -- These can be set to anything
                return Bool == true and 1 or 0
            end;
        };

        Methods = {
            Kill = function(self)
                -- Methods and the Init functions are sent an internal-access table version of self

                self.OnPressed:Fire() -- Internal Signal-access has full control
                return self:Set(true)
            end;

            Destroy = function(self, ...)
                print("We are being destroyed!")

                -- self:super() calls the superclass's method by the name of the first parameter
                -- In this case, this calls Superclass.Destroy(self, ...)
                self:super("Destroy", ...)
            end;

            -- When set to 0, it makes the class Abstract
            FunctionWhichMustBeImplementedByAClassThatInheritsFromThisOne = 0;
        };

        Properties = {
            -- Can be set to a Typer value for typechecking
            Name = Typer.String;

            -- This is equivalent to the previous declaration
            Name = Typer.AssignSignature(2, Typer.String, function(self, Value)
                self:rawset("Name", Value)
            end)

            -- Can also be constrained to a certain EnumerationType
            Style = Typer.EnumerationOfTypePastaStyle;

            Parent = function(self, Parent)
                -- Always called with the Object and the Property value it is being set to

                -- Do stuff
                self:rawset("Parent", Parent) -- Make sure to rawset the Property to what it should be
            end;
        };

        Init = function(self)
            -- Called by PseudoInstance.new()

            -- Every PseudoInstance comes with a built-in Janitor which is called upon Destruction
            self.Janitor:Add(workspace.ChildAdded:Connect(function() end))

            -- Sets externally-accessible Value of PseudoInstance directly
            -- without calling a Property setting function
            -- This can also be used for read-only values
            self:rawset("IsPlayer", true)

            -- Must be in your init function, to run the constructor in the superclass
            -- Will not exist after the Init function runs if the constructors resolved properly
            self:superinit()
        end;
    })

    -- Will Error, because `ClassName` is Abstract
    print(pcall(PseudoInstance.new, "ClassName"))

    local MyClass = PseudoInstance:Register("MyClass", {
        Methods = {
            FunctionWhichMustBeImplementedByAClassThatInheritsFromThisOne = function(self)
            end;
        };

        Init = function(self)
            self:superinit()
        end;
    }, ClassNameTemplate)

    local MyInstance = PseudoInstance.new("MyClass")
    MyInstance.Style = "Rigatoni" -- Gets cast to Enumeration
    MyInstance.Style = 3 -- Gets cast to Rigatoni
    MyInstance:Kill()

    -- Externally, one only may Connect() and Wait() on events
    MyInstance.OnPressed:Connect(print)

    -- Error: Cannot `Fire` externally.
    print(pcall(MyInstance.OnPressed.Fire, MyInstance.OnPressed))

    print(pcall(function()
        MyInstance:Set(true)
    end)) -- Set does not exist externally

## PseudoInstance API¶

Properties | type
Archivable | Boolean
Parent | OptionalInstance
Name | String
ClassName | _read-only_ String
Methods
Clone
GetFullName
IsDescendantOf
GetPropertyChangedSignal
FindFirstChild
GetChildren
IsA
Destroy
Events
Changed


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

Type Checker and Function Signature Assigner

## Library API¶

### Typer.AssignSignature¶

function Typer.AssignSignature([number ParameterIndexToStartChecking = 1, ] types ... , function Callback)

Returns a function which checks to make sure the arguments passed in exactly match the parameters specified. If they match, it will call `Callback`, otherwise it will error, stating which parameter caused the error and what was expected.

Each parameter corresponds to the parameter with which the function is being called. Parameters should be arrays containing all valid strings which may be returned by `typeof(parameter)`. For example: `{"string", "number", "nil"}` would allow the corresponding parameter to be a `string`, `number`, or `nil`. Optionally, a table may have a `[string TypeNameKey] = function Callback` which may be called to determine whether a parameter is of the type `TypeNameKey`.

The first parameter may optionally be a positive integer which is the first parameter `AssignSignature` should start checking.

Example

    local Typer = Resources:LoadLibrary("Typer")

    local function IsButton(Value, TypeOfString)
        -- Value is the Parameter
        -- TypeOfString is the string returned by typeof(Value)

        return TypeOfString == "Instance" and (Value:IsA("TextButton") or Value:IsA("ImageButton"))
    end

    local foo = Typer.AssignSignature({"string", "number", "nil", Button = IsButton}, function(s)
        -- s can be a string, number, nil, or Button (TextButton or ImageButton)
        -- Will error if `s` is none of the aforementioned
        -- Will error if 2 or more parameters are passed in

        return s
    end)

    foo(2) -- 2
    foo("Hello") -- Hello
    foo({}) -- [Typer] {Foo} bad argument #1: expected Button or string or number or nil, got table {}

    local bar = Typer.AssignSignature(2, {"string"}, {"number"}, function(p1, p2, p3)
        -- The 2 signifies that the first checked parameter will be the second one
        -- Makes sure the 2nd parameter is a string, 3rd is a number
        -- The first parameter is not checked

        print(p1, p2, p3)

    end)

    bar(false, "", 1)

### Typer.Check¶

boolean Typer.Check(table PotentialType, any Parameter [, <string, number> ArgumentName])

Checks if an individual Parameter matches a PotentialType in the same way `Typer.AssignSignature` does. Returns `Parameter` if it's a truthy value. If it isn't a truthy value, return true. ArgumentName is just the name or number of the argument being Checked which can be used in the error message.

Example

    local Debug = Resources:LoadLibrary("Debug")
    local Typer = Resources:LoadLibrary("Typer")

    local function foo(a)
        -- `assert` can be used instead of Debug.Assert
        return Debug.Assert(Typer.Check({"number"}, a, 1))
    end

    foo(2) -- 2
    foo("Noodle") -- [Script] {Foo} bad argument #1: expected number, got string Noodle

### Typer.MapDefinition¶

function Typer.MapDefinition(table Definition)

Returns a function which returns the table if it matches the definition, or `false, errorMessage` if it doesn't.

Example

    local PlayerDefinition = Typer.MapDefinition {
        Name = Typer.String;
        UserId = Typer.PositiveInteger;

    local Player1 = {
        Name = "Validark";
        UserId = 2966752;

    Player1 = assert(PlayerDefinition(Player1))

    -- Can also be used with AssignSignature
    Typer.AssignSignature({Player = PlayerDefinition}, function(...)
        print(...)
    end)(Player1)

### Typer.TYPE¶

table Typer.TypeString

Typer will procedurally generate tables which can be cast to valid types. These may be used with `Typer.AssignSignature` or `Typer.Check` to avoid duplicate table definitions. These tables may also be called directly, since their metatable `__call` is set to `Typer.Check`

    assert(Typer.String("Yup"))
    assert(Typer.String(1))

    assert(Typer.Number(1))
    assert(Typer.Boolean(true))
    assert(Typer.Function(function() end)
    assert(Typer.Userdata(newproxy(false)))

    local foo = Typer.AssignSignature(Typer.String, function(a)
        print(a)
    end)

    foo("Hello, world!")

#### Built-in Types¶

Key | Matches
Nil | type(Object) = "nil"
Boolean | type(Object) = "boolean"
Number | type(Object) = "number"
String | type(Object) = "string"
Userdata | type(Object) = "userdata"
Function | type(Object) = "function"
Thread | type(Object) = "thread"
Table | type(Object) = "table"

Typer accepts anything returned by `typeof`

#### Custom Types¶

Key | Matches
Any | Anything
Array | A non-empty table with only numeric keys
Dictionary | A non-empty table with only non-numeric keys
EmptyTable | An empty table
NonNil | Any value which isn't nil
Integer | Any whole number
PositiveInteger | An integer higher than 0
NegativeInteger | An integer lower than 0
NonPositiveInteger | An integer lower than 0 or 0
NonNegativeInteger | An integer higher than 0 or 0
PositiveNumber | A number higher than 0
NegativeNumber | A number lower than 0
NonPositiveNumber | A number lower than 0 or 0
NonNegativeNumber | A number higher than 0 or 0
Truthy | Any value which isn't false or nil
Falsy | False or nil
Enum | An EnumType or EnumItem
EnumType | A roblox EnumType
EnumItem | An EnumItem
True | true
False | false

#### Types from parsed strings¶

Types separated by "Or" will function as expected.

    assert(Typer.NumberOrString(2))
    assert(Typer.OptionalBooleanOrNumberOrString(2))

#### Prefixes¶

Prefix | Matches
Optional | Accepts `nil` as well
InstanceOfClass | Returns `Object.ClassName == ClassName`
InstanceWhichIsA | Returns `Object:IsA(ClassName)`
EnumOfType | Returns the (Roblox) Enum if it can be cast to it via its string, number, or reference
EnumerationOfType | Returns `EnumerationType:Cast(Object)`
DictionaryOf | Ensures it's a dictionary with values of a given type
ArrayOf | Ensures it's an array with values of a given type
TableOf | Ensures it's a table with values of a given type

Warning

You may not nest prefixes, but you may use multiple on the top level using "Or"

Example

    assert(Typer.ArrayOfStringsOrDictionaryOfNumbers{"a", "b", "c"})

    assert(Typer.ArrayOfStringsOrDictionaryOfNumbers{
        a = 1;
        b = 2;
        c = 3;
    })


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

Transform function for Paper from the Material Design specifications

`AsymmetricTransformation(GuiObject Button, UDim2 EndSize)`

Only supports Offsets, doesn't support Scaling


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

A BigNum with Fractions implementation for big-integer calculations, optimized for DataStore storage and networking.

    local BigNum = Resources:LoadLibrary("BigNum")
    print(BigNum.new("1025123") ^ BigNum.new("9"))
    --> 1250212389547080859766804627957328989496357747253559363

The BigNum library utilizes Lua's doubles to hold place values, called "radix" (similar to digits but in a different base). Each double represents a single "radix" of the BigNum. By default, the BigNum library instantiates BigNums with 32 radix (256 bytes) to do calculations. This is big enough for numbers as large as `7.76e230` (see `BigNum:GetRange()`). If you need integers higher than that, simply pass in how many doubles it should allocate.

    -- Explicitly allocate 128 doubles (1024 bytes) for each BigNum
    print(BigNum.new("8", 128) ^ BigNum.new("900", 128))

Otherwise, you can externally set the default number of allocated radix:

    BigNum:SetDefaultRadix(64)

The library also ships with a data type called BigNumFractions, which can be used for exact calculations with Fractions.

## Library API¶

### BigNum.new¶

BigNum BigNum.new(string Base10Number [, integer Radix])

Returns a new BigNum with the allocated number of Radix (defaults to 32)

### BigNum.fromString64¶

BigNum BigNum.fromString64(string Base64String)

Meant to instantiate BigNums from strings returned via the `toString64` method.

### BigNum.newFraction¶

BigNumFraction BigNum.newFraction(BigNum Numerator, BigNum Denominator)

Instantiates a new Fraction in the form `(Numerator / Denominator)`

### BigNum:GetRange¶

string BigNum:GetRange(integer Radix)

Returns a string representation of approximately how large of integers can be represented with a given number of Radix

    print(BigNum:GetRange(128))
    --> +/- 2.90e924

### BigNum:SetDefaultRadix¶

void BigNum:SetDefaultRadix(integer DefaultRadix)

Sets the default number of Radix allocated for each number (default is 32)

## BigNum API¶

These are the methods which operate on BigNums themselves.

### BigNum:toString64¶

string BigNum:toString64()

Returns a string-base64 encoding of the BigNum, which can be reproduced using `BigNum.fromString64`

### BigNum:toConstantForm¶

void BigNum:toConstantForm(integer NumberOfDoublesPerLine = 16)

Creates a file (or on Roblox, a script in the Lighting) with a constant representation of the BigNum. This is useful for directly instantiating BigNums with Big values which would take a lot of processing at run-time to convert from a base10 string to the internal representation.

Looks like this:

    local CONSTANT_NUMBER = BigNum.new{
        0, 0, 154059, 3375645, 903522, 1038360, 13086355, 12524895, 10900259, 11967773, 5580376, 3468437, 579970, 6766011, 8397479, 8219103,
        8206478, 4226184, 11579046, 8128945, 3969135, 407937, 10824426, 1462231, 7523964, 16399268, 11691710, 13651477, 10604193, 2791442, 9280589, 5604105

### BigNum:stringify¶

string BigNum:stringify()

Returns a representation of the internal format of how BigNums are stored in their array.

## BigNumFraction API¶

### BigNumFraction:Reduce¶

BigNumFraction BigNumFraction:Reduce()

Reduces the Fraction and returns it.

### BigNumFraction:toScientificNotation¶

string BigNumFraction:toScientificNotation(integer NumDigitsAfterDecimalPoint = 2)

Returns a string representation of the Fraction in scientific notation with the given number of digits after the decimal. If there aren't enough digits to format it will just return the unformatted string.

## Demo¶

Here the library is used to calculate the [Birthday problem](https://en.wikipedia.org/wiki/Birthday_problem).

    local BigNum = Resources:LoadLibrary("BigNum")
    local _1 = BigNum.newFraction("1", "1")
    local DaysInYear = BigNum.new("365")

    local function BirthdayProblem(NumPeople)
        local Chance = BigNum.newFraction(DaysInYear, DaysInYear)

        for i = 2, NumPeople do
            Chance = Chance * BigNum.newFraction(DaysInYear + (1 - i), DaysInYear)
        end

        return _1 - Chance
    end

    print(BirthdayProblem(70):toScientificNotation(6))
    --> 2.291582e179 / 2.293509e179

## Writing Optimal Code with BigNum¶

Sometimes, you will need to perform a massive calculation which might take up too much time to calculate on Roblox without having the thread scheduler cut you off.

What follows is a list of what this library is optimized for and what this library is slow at.

Optimized for:

  * Any code given to you via the `toConstantForm` method

  * Encoding/Decoding for DataStores/Replication using `toString64` and `fromString64`

Runs slowly:

  * Printing massive numbers (in Base 10) a lot (Never use GetRange at run-time!)

  * Creating BigNums from a Base10String at run-time (toConstantForm is recommended)

  * Using more Radix than the library can be performant with. In my tests, 32 radix approaches native speeds, with going too far above 128 leading to unstability in intensive calculations

## Probably asked Questions¶

_What if I want to know what's after the decimal point?_

**Use Fractions!**

_How does this library work?_

**It uses a modified version of two's-complement numbers (but in base 2^24).**

_Why are calculations done in base 2^24?_

**Because the highest integer representable by a double is 2^53. 2^24 is the nicest high number which won't result in the internal calculations reaching higher than 2^53. Multiplying two numbers with the highest radix and the highest previous value can result in: (2^24 - 1)(2^24 - 1) + (2^24 - 1)**


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

A [Snackbar](https://material.io/design/components/snackbars.html) is a light-weight feedback bar appearing from the bottom of the screen. This implementation has built-in replication.

This library registers a Material Design `Snackbar` PseudoInstance which can be instantiated via `PseudoInstance.new("Snackbar")`.

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

## Snackbar API¶

Snackbar inherits from [ReplicatedPseudoInstance](https://rostrap.github.io/Libraries/Classes/ReplicatedPseudoInstance).

Only one can be present on the screen at once.

### Fields¶

Property | Type | Description
---|---|---
Text | string | The main text of the snackbar (required)
ActionText | string | The text on the button. `""` will remove the button

### Events¶

Event | Description | Signature
---|---|---
OnAction | Fires after the Action button on the Snackbar was pressed | (Player Player)

###### Snackbar also 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/Snackbar.rbxl)

Demo code:

    -- This code is valid on the client and server
    -- Just make sure that both call `Resources:LoadLibrary("ReplicatedPseudoInstance")`

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

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

    local Color = Resources:LoadLibrary("Color")

    local Snackbar = PseudoInstance.new("Snackbar")
    Snackbar.ActionText = "CANCEL"
    Snackbar.OnAction:Connect(function()
        print("Canceled!")
    end)

    -- If this is in a Script, it will Replicate this ChoiceDialog to every
    -- Player in the game and everyone who joins
    -- If this is in a LocalScript, it will generate it on the client only

    local PrimaryColor3 = Color.Teal[500] -- This is just a Color3 value

    local Dialog = PseudoInstance.new("ChoiceDialog")
    Dialog.HeaderText = "Repository Location"
    Dialog.Options = {"ServerStorage", "ServerScriptService"}
    Dialog.DismissText = "CANCEL"
    Dialog.ConfirmText = "INSTALL"
    Dialog.PrimaryColor3 = PrimaryColor3

    Dialog.OnConfirmed:Connect(function(Player, Choice)
        print(Player, Choice)
        Snackbar.Text = "Chosen: " .. Choice
        Snackbar.Parent = Player

        if Choice then
            -- Choice is a string of the option they chose
        else
            -- They chose Dismiss, so Choice is false
        end
    end)

    Dialog.Parent = ReplicatedStorage


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

Holds all Lerp functions for Roblox objects, with a special color lerping function by Fractality.

Contains functions which take arguments `(variant beginning, variant last, number alpha [0, 1])` and returns a value in between `beginning` and `last` inclusive where `0` is `beginning` and `1` is `last`.

Contains CIELUV color lerping, which looks like this:


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

Summary

A ReplicatedPseudoInstance is an Instance which, when instantiated on the server, has built-in replication. While the parent is set to `Workspace`, `ReplicatedStorage`, or descendants of these, or set to `Players`, it will be Replicated to all players (including those who join the server later). If it is parented to an individual player or a descendant of an individual Player, it will replicate solely to that Player.

If a property changes on the server, clients will be sent the new property's value and update their local version of the instance with the new property.

Events which are fired on the client (by replicating instances originating from the server) are automatically fired on the server via a RemoteEvent.

ReplicatedPseudoInstances which are Destroyed on the server have their destruction replicated.

Inherits from [PseudoInstance](https://rostrap.github.io/Libraries/Classes/PseudoInstance)

Warning

In order for replication to work, the `ReplicatedPseudoInstance` library **must** be loaded on the client.

Warning

All events fired on a client must have `LocalPlayer` as the first parameter. On the server, it will use the automatically-passed PlayerFrom parameter (which is built-in to RemoteEvents)

Example

To make a library inherit from ReplicatedPseudoInstance, simply pass it in as the third parameter to [PseudoInstance:Register](https://rostrap.github.io/Libraries/Classes/PseudoInstance/#pseudoinstanceregister). [ChoiceDialog](https://github.com/RoStrap/RoStrapUI/blob/master/ChoiceDialog.lua) is a good example of a ReplicatedPseudoInstance, but here is a re-implementation of the ClickDetector object (but also passes along what face of an Object you clicked.)

    -- ClickDetector Class
    -- @author Validark

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

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

    local Debug = Resources:LoadLibrary("Debug")
    local Enumeration = Resources:LoadLibrary("Enumeration")
    local SortedArray = Resources:LoadLibrary("SortedArray")
    local PseudoInstance = Resources:LoadLibrary("PseudoInstance")
    local ReplicatedPseudoInstance = Resources:LoadLibrary("ReplicatedPseudoInstance")

    local Parts = setmetatable({}, {__mode = "k"})
    local LocalPlayer

    if RunService:IsClient() then
        repeat LocalPlayer = Players.LocalPlayer until LocalPlayer or not wait()
        local Mouse = Players.LocalPlayer:GetMouse()
        local Targets = {}
        local MouseDowns = {}
        local CurrentMoval = 0
        local MaximumInteger = 2 ^ 53

        local function MouseMoved()
            local Target = Mouse.Target
            local ActiveIcon

            while Target do
                local Detectors = Parts[Target]
                if Detectors then
                    for i = 1, #Detectors do
                        local Detector = Detectors[i]
                        if Detector.MaxActivationDistance >= LocalPlayer:DistanceFromCharacter(Target.Position) then
                            if not Targets[Detector] then
                                Detector.MouseHoverEnter:Fire(LocalPlayer)
                            end
                            Targets[Detector] = CurrentMoval
                            ActiveIcon = true
                            Mouse.Icon = Detector.CursorIcon
                        end
                    end
                end
                Target = Target.Parent
            end

            for Detector, Moval in next, Targets do
                if Moval ~= CurrentMoval then
                    Targets[Detector] = nil
                    MouseDowns[Detector] = nil
                    if not ActiveIcon then
                        Mouse.Icon = ""
                    end
                    Detector.MouseHoverLeave:Fire(LocalPlayer)
                end
            end

            CurrentMoval = (CurrentMoval + 1) % MaximumInteger
        end

        Mouse.Move:Connect(MouseMoved)
        local Connection = Workspace.CurrentCamera:GetPropertyChangedSignal("CFrame"):Connect(MouseMoved)
        Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
            Connection:Disconnect()
            Connection = Workspace.CurrentCamera:GetPropertyChangedSignal("CFrame"):Connect(MouseMoved)
        end)

        for i = 1, 2 do
            local Event = i == 1 and "MouseClick" or "RightMouseClick"

            Mouse["Button" .. i .. "Down"]:Connect(function()
                local Target = Mouse.Target

                while Target do
                    local Detectors = Parts[Target]
                    if Detectors then
                        for a = 1, #Detectors do
                            local Detector = Detectors[a]
                            if Detector.MaxActivationDistance >= LocalPlayer:DistanceFromCharacter(Target.Position) then
                                MouseDowns[Detector] = i
                            end
                        end
                    end
                    Target = Target.Parent
                end
            end)

            Mouse["Button" .. i .. "Up"]:Connect(function()
                for Detector, Down in next, MouseDowns do
                    if Down == i then
                        Detector[Event]:Fire(LocalPlayer, Mouse.TargetSurface)
                    end
                end
            end)
        end
    end

    local function CompareClickDetectors(a, b)
        return a.__id < b.__id
    end

    return PseudoInstance:Register("ClickDetector", {
        Internals = {};

        Storage = {};

        Properties = {
            MaxActivationDistance = Enumeration.ValueType.Number;
            CursorIcon = Enumeration.ValueType.String;

            Parent = function(self, PVInstance)
                if PVInstance == nil then
                    return true
                elseif typeof(PVInstance) == "Instance" and PVInstance:IsA("PVInstance") then
                    self.Janitor:LinkToInstance(PVInstance)
                    if LocalPlayer then
                        local OldParent = self.Parent
                        if OldParent ~= PVInstance then
                            if OldParent then
                                local Data = Parts[PVInstance]

                                if #Data == 1 and Data[1] == self then
                                    Parts[PVInstance] = nil
                                else
                                    Data:RemoveElement(self)
                                end
                            end

                            local Data = Parts[PVInstance]
                            if Data then
                                Data:Insert(self)
                            else
                                Parts[PVInstance] = SortedArray.new({self}, CompareClickDetectors)
                            end
                        end
                    end
                    return true
                else
                    Debug.Error("The Parent of a " .. self.ClassName .. " must either be nil or a PVInstance")
                end
            end;
        };

        Events = {"MouseClick", "MouseHoverEnter", "MouseHoverLeave", "RightMouseClick"}; -- If these values are indexed from a ClickDetector, it will return a `Signal`

        Methods = {};

        Init = function(self, Id)
            self.MaxActivationDistance = 32
            self.CursorIcon = "rbxassetid://1727841997" -- 1727849582
            self:superinit(Id)
        end;
    }, ReplicatedPseudoInstance)