RBLX Essentials Documentation

Overview

RBLX Essentials is a collection of modular, production-ready Roblox game systems distributed as plug-and-play packs. Each pack installs in minutes via a dedicated Studio plugin, requires no manual scripting to set up, and integrates automatically with the other packs in the ecosystem.

The goal is to give Roblox developers a solid, tested foundation so they can focus on what makes their game unique instead of rebuilding common systems from scratch.

Pack Catalogue

PackPackIdVersionPriceDescription
Easy Pet System EasyPetSystem 2.1.0 $14.99 Complete pet collection system with eggs, rarity tiers, hatching animations and pet following.
Sound System & Music Player SoundSystem 2.1.0 Free Centralized SFX and music infrastructure shared across all packs.
General UI Kit GeneralUIKit 2.1.0 $14.99 Tag-based UI framework with panels, buttons, tooltips and animations.
Datastore & Purchase System DatastorePurchaseSystem 2.1.0 Free Persistent player data and Robux purchase handling shared across all packs.
Easy Leaderstats & Leaderboards System EasyLeaderstatsSystem 2.0.0 $7.99 Tab-menu leaderstats and physical in-world global leaderboards, both plugin-managed.
Roulette System RouletteSystem 2.1.0 $7.99 Weighted-chance reward wheel with free spins, cooldowns and custom reward modules.
Time Rewards System TimeRewardsSystem 2.1.0 $4.99 A 7-day daily reward cycle plus timed in-session gifts, with custom reward modules.
Codes System CodesSystem 2.1.0 $4.99 Redeemable codes for in-game rewards, authored in Settings with expiry dates.
Notifications System NotificationsSystem 2.1.0 Free Toasts, chat lines and banners, with triggers any pack can fire through the kernel.

Dependency Matrix

The shared UI/runtime foundation ships embedded inside every pack — nothing to install separately. The table below shows the remaining dependencies and synergies between packs.

Sound System Datastore UI Kit Pet System Leaderstats
Sound System
Free
Pairs Pairs
Datastore & Purchase
Free
Pairs Pairs Pairs
General UI Kit
$14.99
Pairs Pairs Pairs
Easy Pet System
$14.99
Pairs Bundled Pairs
Easy Leaderstats
$7.99
Bundled
Bundled ships inside the pack
Pairs works great with

Architecture Philosophy

Modular. Each pack is a self-contained Rojo project. You install only what you need.

Plugin-managed. A pack arrives as one self-contained container you drag into your place. The plugin installs it into the services it belongs in, with anything it depends on installed first, in one click. Nothing is placed by hand and there is no install order to remember.

Configuration over code. Every setting lives in that pack's own Settings folder, and the plugin renders it as a panel built for that pack rather than a generic list of values. You change things in the plugin; you do not touch scripts.

Shared infrastructure. One kernel boots every pack in dependency order and hands each one the others' APIs. A pack reaches another only by declaring it, so there is no hidden coupling, and optional companions are resolved at runtime instead of being made mandatory.

Getting Started

Requirements: Roblox Studio (any recent version). API Services must be enabled in your place if you use Datastore & Purchase System or Easy Leaderstats & Leaderboards System.

Step 1: Install the Plugin

Download the RBLX Essentials Plugin (free) from Itch.io and drag the .rbxm file into Studio. It installs automatically to your Plugins folder. Restart Studio once.

The plugin appears as RBLX Essentials in the Plugins toolbar.


Step 2: Drag a Pack into Studio

  1. Buy and download any pack's .rbxm from Itch.io.
  2. Drag the file into Roblox Studio.
  3. Open the plugin (Plugins → RBLX Essentials) — it shows a banner for the container it just detected.

Each pack now ships fully self-contained — there is no separate Ecosystem Core download and no required install order between packs. Drag in whichever packs you own, in any order.


Step 3: Install the Pack

Click Install on the banner. That's it — the pack deploys itself to the correct services.

A couple of packs expect another pack's data to exist:

  • Easy Pet System and Easy Leaderstats & Leaderboards System read and write through Datastore & Purchase System — install that pack first if you're using either of them.
  • General UI Kit and Sound System have no install-order requirement; Sound System pairs with the other packs for audio but is optional everywhere.

Step 4: Enable API Services

If you are using Datastore & Purchase System or Easy Leaderstats & Leaderboards System, go to:

Game Settings → Security → Enable Studio Access to API Services

Without this, player data and global leaderboards will not save in Studio playtests.


Step 5: Configure

Select any installed pack in the plugin to open its settings panel. Change values directly in the panel, no script editing required.

Typical things to configure:

  • Easy Pet System: egg prices, drop rates, pet multipliers, gamepass IDs.
  • Datastore & Purchase System: auto-save interval, DataStore name.
  • Sound System: which Music Player template to use, enable/disable Music Player.

Step 6: Press Play

Hit Play in Studio. All packs initialise automatically. No bootstrap code, no wiring required.


Updating a Pack

Download the new version's .rbxm and drag it into Studio. The plugin shows an update banner for that pack — click Update. Your code is overwritten with the new version; your Settings are preserved.


Migrating from v1

Installing the v2 plugin replaces your old RBLX Essentials plugin file. Open it and every v1 pack it detects shows a Migrate banner: click Migrate to uninstall the v1 copy and capture its settings, then install the matching v2 .rbxm and click Apply mapped settings to carry your old configuration over automatically. If you had Ecosystem Core installed for v1, migrate it the same way — its runtime now ships embedded in each pack, so there's no v2 download for it.


Uninstalling a Pack

Select the pack in the plugin and click Uninstall Pack. The pack's deployed code is removed from all services.

Plugin

The plugin is the manager for the whole ecosystem. It finds the packs you have, installs them into the right places, resolves what they depend on, and gives each one a settings panel built for that pack. It is free, and it is how every pack is meant to be installed.

Every pack in one list

The plugin listing nine installed packs with their versions

Drop a pack file into your place and it appears here. One click installs it into the services it belongs in, with anything it depends on installed first and in the right order. The same list tells you which packs are behind the current version.

Pack Banners

BannerMeaning
InstallA pack container was dragged into Studio and is ready to deploy.
UpdateA newer version of an installed pack was dragged in. The code is replaced; your Settings are carried over.
MigrateA v1 install of this pack was detected. Migrating uninstalls the v1 copy and captures its settings for the v2 install.
Apply mapped settingsShown after installing a v2 pack following a migration. Applies the captured v1 settings onto it.

Settings built for each pack

This is the part that is not a generic form. Eight packs have a panel written for them, and what you edit is what you see.

The Roulette rewards panel with a live wheel preview above the seven slots

Roulette's wheel is the real one, redrawn as you change a slot. Time Rewards previews the streak rail. Notifications renders each notification card at its true size and puts a pencil beside it that makes it appear on screen and selects it in the Explorer. Datastore prints the finished save a new player will receive, as JSON you can select and copy.

A pet previewed in 3D inside the plugin, above its name, multiplier and rarity

Easy Pet System spins each pet in 3D while you set its rarity and multiplier, and tells you when an egg's drop table does not add up to 100%. General UI Kit lists your menus and HUD buttons. Sound System measures every sound's real loudness in decibels and can pull the outliers into line in one press.

Changes are written straight to the pack's Settings and take effect on the next Play. Everything the plugin does is an ordinary Studio edit, so Ctrl+Z works on all of it.

Updating without losing your settings

Drag the newer .rbxm in and press Update. The plugin snapshots your current Settings, installs the new version, and writes your values back over the new defaults.

  • A setting the new version no longer has is reported as orphaned rather than dropped in silence, so you can see what changed.
  • A setting whose name contains a dot is the one case the snapshot skips. Nothing shipped is named that way; if you added one yourself, rename it before updating.

Registry and Update Checks

On each refresh the plugin fetches a remote registry listing every available pack and its latest version. That powers two things:

  1. Catalogue listing. Packs you do not have yet appear in a separate "More from RBLX Essentials" section, so you can find them.
  2. Plugin update banner. If a newer plugin is out, a banner appears at the top.

The fetch is non-blocking: your local packs render immediately and the list fills in when the fetch returns, so the plugin is usable offline.

Kernel Coexistence

Every pack ships with its own copy of the shared runtime embedded inside its container. There is no separate Ecosystem Core to install first, and no install order to remember.

With several packs installed, the plugin keeps the newest copy in sync across all of them, and only ever moves forward: installing an older pack never puts an older runtime back. That is why updating any one pack brings the newest runtime to everything you have.

Uninstalling

Removes the pack from every service it was installed into. Its Settings go with it, so copy them somewhere first if you have spent time on them. Packs that were using it do not break; whatever they used it for simply goes quiet.

Guides

Short answers to the things people actually want to do. Each one is written for someone who has the plugin installed and a pack or two in their place, and none of them assumes you want to read the pack's source.

Where everything lives

Every pack installs into the same five places, always under a folder called RBLXEssentials. Nothing is ever scattered anywhere else, so if you cannot find something, it is in one of these.

ServerScriptService.RBLXEssentials
├── Kernel                          -- the one Script in the ecosystem
└── Packs.<PackId>                  -- server code, CustomRewards, Triggers

ReplicatedStorage.RBLXEssentials
├── Kernel                          -- shared modules and Remotes
└── Packs.<PackId>
    ├── manifest                    -- id, version, what it depends on
    ├── Client                      -- client code
    ├── Settings                    -- everything the plugin edits
    └── Assets                      -- templates the pack clones at runtime

StarterPlayer.StarterPlayerScripts.RBLXEssentials
└── KernelClient                    -- the one LocalScript

StarterGui.RBLXEssentials.<PackId>  -- the interfaces you edit
SoundService.RBLXEssentials         -- SFX and Music
Workspace.RBLXEssentials.<PackId>   -- demo parts, leaderboards, eggs

There is one Script and one LocalScript in the whole ecosystem, whatever number of packs you install. Everything else is a ModuleScript that the kernel boots in dependency order.

How do I edit a pack's interface?

Open StarterGui > RBLXEssentials > <PackId> and edit it like any other GUI in your place. It is real StarterGui content, not something built at runtime, so what you see in Studio is what a player gets. Change colours, fonts, corners, images, sizes, move things around: none of it needs code, and none of it needs the plugin.

Three rules keep a redesign working:

  1. Do not rename what the pack drives. Each pack's page lists the handful of names it fills in, like a Codes panel's TextBox or a day tile's Amount. Everything else is yours. Rename one of those and the pack warns in the Output instead of breaking.
  2. Size and position in scale, not offset. The whole kit is built that way, so an element is the same fraction of the screen on a phone as on a 4K monitor. Drag with the scale handles.
  3. Outlines are ScaledSize. A UIStroke with StrokeSizingMode = ScaledSize takes its Thickness as a fraction of the element's height, so a resized card keeps its border weight. If you add your own, match that.

Two packs make this easier still. General UI Kit's menus and Notifications System's templates each have a pencil beside their name in the plugin: click it and that panel or card appears on screen and is selected in the Explorer, so you can design it in place instead of hunting for it. Click it again when you are done, and the plugin will tell you if you left one visible, because Visible is saved with your place.

Two packs providing the same menu

Several packs ship their own copy of a menu so they work on their own. When General UI Kit is installed, its copy usually wins and the pack drives that one instead, so you get one menu rather than two.

Which copy wins is the AdoptPriority attribute on the panel: highest number wins, and the default is 0. If you have restyled the wrong copy and want yours to be the one used, raise its AdoptPriority.

How do I make a button do something of my own?

Set the button's Action attribute to a name, then add a function under that name in General UI Kit's CustomHandlers module:

-- ReplicatedStorage.RBLXEssentials.Packs.GeneralUIKit.CustomHandlers
-- Updates never overwrite this file.
return {
    Rebirth = function(button)
        -- runs when a button whose Action says "Rebirth" is clicked
        print(button.Name, "was pressed")
    end,
}

Opening and closing panels, prompting a game pass or a developer product, and inviting friends are already handled, so a handler of your own is only for the parts that are actually yours. The button object is passed in, so you can read your own attributes off it and use one handler for several buttons.

How do I add a new currency, and show it?

Four steps across four packs, none of them code.

  1. Save it. In the plugin's Datastore panel, under Default Data, open the Stats group and press + Variable. Name it Gems, pick N for number, press Add. The preview underneath shows you exactly what a new player will now receive. Its path is Stats.Gems, and that string is what everything else asks for.
  2. Show it on the HUD. Add a TextLabel to your GUI, tag it UI_DataBind, and give it a DataPath attribute of Stats.Gems. Add Prefix or Suffix for a symbol, and Format for Default, Commas or Short. It follows the value for the rest of the session with no script.
  3. Rank it. In Easy Leaderstats, add a leaderstat whose data key is Stats.Gems to put it in the Tab menu, or point a leaderboard at it to put it on a board in the world.
  4. Give it out. Any reward slot in Roulette, Time Rewards or Codes takes Type = Stat and Target = Stats.Gems. So a code, a spin and a daily reward can all pay in Gems without anything being wired between them.

A key you add reaches players who already exist, on their next load. A key you remove stays in their saved data, so deleting one here can never lose what someone earned.

How do I give a reward the pack doesn't offer?

Roulette System, Time Rewards System and Codes System all take the same kind of module. Drop a ModuleScript into that pack's CustomRewards folder under ServerScriptService > RBLXEssentials > Packs > <PackId>:

local MyReward = {}

-- `d` is the live save inside a data update: what you change here
-- is persisted and replicated for you. `amount` is the slot's Amount.
function MyReward.Grant(player, d, amount)
    d.Stats = d.Stats or {}
    d.Stats.Gems = (d.Stats.Gems or 0) + amount
end

return MyReward

Then set that slot's Type to Custom and pick your module from the Module dropdown, which lists whatever is in the folder. A slot pointing at a module that is not there is switched off at startup with a warning, rather than failing the first time a player earns it.

You are not limited to numbers. A Grant can hand out a tool, teleport someone, or set a flag your own code reads later.

How do I add a sound, or a music track?

Sounds live in SoundService > RBLXEssentials > SoundSystem, under SFX and Music. There are two ways in:

  • From the plugin. The Sounds panel has + Add sound: paste an asset id or a marketplace link, give it a name, press Add. That name is what you pass to SFX:Play, and what the dropdowns elsewhere in the plugin will offer you.
  • From the Explorer. Drop a Sound into either group. It is picked up immediately, including while the game is running.

Then play it from anywhere on the client:

local SFX = require(game.ReplicatedStorage.RBLXEssentials.Kernel.SFX)
SFX:Play("Press")
SFX:PlayOverlap("Gold_Pickup")  -- for things that fire fast

While you are in that panel: Measure all reads every sound's real loudness in decibels and Match all pulls the outliers to your target, which is the fastest way to stop one effect drowning out the rest.

How do I react to something happening?

When a player's numbers change

The save is mirrored onto the player as ordinary Value objects, so you can watch one from a LocalScript with no remotes:

local player = game.Players.LocalPlayer
local data = player:WaitForChild("PlayerData")
local cash = data:WaitForChild("Stats"):WaitForChild("Cash")

cash.Changed:Connect(function(value)
    print("cash is now", value)
end)

A DataReady BoolValue sits in the same folder and turns true once the save has arrived, if you would rather wait for the whole thing than for one key.

When somebody buys something

local hook = game:GetService("ServerScriptService").RBLXEssentials
    .Packs.DatastorePurchaseSystem:FindFirstChild("PurchaseCompleted")
if hook then
    hook.Event:Connect(function(player, kind, id, currencySpent)
        print(player.Name, "bought", kind, id)
    end)
end

It fires after the purchase is recorded as granted, and it also fires for FreeMode grants, so you can test the whole path in Studio without spending Robux.

When you want to say something

local Notify = require(game.ReplicatedStorage
    .RBLXEssentials.Packs.NotificationsSystem.Notify)

Notify.Toast(player, "TitleSubtitle", { Title = "Level up!", Subtitle = "You reached level 10" })
Notify.BannerAll({ Title = "Boss event", Subtitle = "The arena opens in 30 seconds" })

How do I update a pack without losing my settings?

Drag the new .rbxm into your place and press Update on that pack's card. The plugin takes a snapshot of your current Settings first, installs the new version, and writes your values back over the new defaults.

Two things worth knowing:

  • A setting the new version no longer has is reported as orphaned rather than silently dropped, so you can see what changed.
  • A setting whose name contains a dot is the one case the snapshot skips. None of the shipped settings are named that way; if you added one yourself, rename it before updating.

Updating one pack updates the shared runtime for all of them. The kernel travels inside every pack and only ever moves forward, so whichever pack you update brings the newest one to everything installed. Installing an older pack never puts an older kernel back.

How do I remove a pack?

Uninstall on its card removes it from every service it was installed into. Its Settings go with it, so if you have spent time configuring it, copy the Settings folder somewhere first.

Packs that depend on it keep working in the sense that they will not error, but whatever they used it for goes quiet: a pack that plays a sound goes silent, a pack that shows a notification shows nothing. The one exception is Datastore & Purchase System, which is a hard requirement for the packs that save anything.

Something isn't showing up

What you seeUsually
No HUD button for a pack you installedAnother pack owns the HUD and this one's copy is hidden as a duplicate. Look for its button on General UI Kit's rail before adding a second one.
Two of the same buttonTwo packs shipped the same menu and neither has a higher AdoptPriority. Raise it on the one you want.
A panel opens emptySomething the pack fills by name has been renamed. The Output says which.
No sound anywhereSound System is not installed. Every pack asks for sound whether or not it is there and gets silence if it is not.
No notifications from any packSame shape: Notifications System is not installed.
A reward slot does nothingIts Type is Stat or Custom with an empty Target, so it was switched off at startup. The Output names the slot.
A player joins with no dataThey are told to rejoin instead of starting empty. Their real save is untouched.

Almost everything above prints a warning in the Output window when it happens. Nothing in the ecosystem fails silently on purpose.

Datastore & Purchase System

PackId: DatastorePurchaseSystem  ·  Version: 2.1.0  ·  Requires: nothing

What It Does

This is where your players' progress lives, and the backbone every other pack saves through. There is one datastore for your whole game, so a code, a spin and an egg all pay into the same numbers with nothing wired between them.

  • Loads on join, saves on a timer, on leave and on shutdown.
  • An in-memory cache, so every read mid-session is instant.
  • Live replication to the client as Value objects, so showing a stat needs no remotes.
  • Game pass checks, cached and remembered in the save.
  • Developer product receipts handled safely: a receipt Roblox retries is confirmed rather than granted twice.
  • Retries with backoff, request-budget awareness and save smoothing, so a full server does not spend its budget in one burst.
  • Studio safety: a separate datastore version for testing, synthetic user ids for test clients, and one calm warning instead of error spam on an unpublished place.

Plugin Configuration

Default Data

The Default Data tree in the plugin

This is what a new player starts with. Groups hold variables, and a variable is a number, a string or a true/false. + Variable and + Group add them; the red cross removes them.

Adding a variable, with its type picker

The path you build here, Stats.Cash or Inventory.Slots, is the exact string every other pack asks for: a reward slot's Target, a leaderstat's data key, a label's DataPath. Add a key here and it appears in those pickers immediately.

A new key reaches players who already exist, on their next load: each saved profile is merged with the current defaults and anything missing is filled in. A changed default only affects new players. A removed key stays in profiles that already have it, so deleting one here can never lose what someone earned.

See exactly what a new player gets

The preview of a new player's finished save

The panel prints the finished save before you press Play, your own keys merged with the ones the packs add. It is selectable text, so it is also the fastest way to check a key is spelled the way your code spells it.

Datastore Settings

Datastore settings and the Wipe Studio Data button
SettingShips asWhat it does
DataStoreVersion"v1"Key suffix for the live datastore. Change it to start every player on a fresh slate.
StudioDataStoreVersion"v4"The suffix used while testing in Studio, kept separate so a playtest can never touch live player data.
AutoSaveInterval90Seconds between periodic saves per player, smoothed and jittered to respect the datastore budget.
Wipe Studio DatabuttonDeletes every key in the current Studio version, with a confirm step. A clean slate for testing. It never touches live data, and it needs a published place with Studio API access on.

Purchase Settings

The FreeMode toggle

FreeMode makes every pass read as owned and lets a product grant itself, with no Robux and no prompt. It is how you test a purchase path fifty times in an afternoon.

It ships enabled, for development. Turn it off before you go live.

Uninstalling

Uninstall Pack button

Removes the pack from every service. Saved player data is untouched: it lives in Roblox's datastore, not in your place.


That's everything from the plugin.

Below: reading and writing a save from code, showing values on the client, and handling purchases.

Getting the API

Every pack boots through the kernel and receives the others' APIs through ctx.require. Declare the dependency in your own pack's manifest and ask for it in init:

-- ReplicatedStorage.RBLXEssentials.Packs.MyPack.manifest
return { id = "MyPack", version = "1.0.0", deps = { "DatastorePurchaseSystem" } }

-- ServerScriptService.RBLXEssentials.Packs.MyPack
return function(ctx)
    local ds = ctx.require("DatastorePurchaseSystem")
end

The kernel guarantees this pack has finished booting before your init runs, so ctx.require never waits. Making a pack of your own is one folder and a manifest; the walkthrough is in the kernel reference that ships with the source.

Data API

ds.Get(player)

The player's whole save. The first call loads and caches; every call after that returns the cache instantly. Concurrent first calls are deduplicated, so everyone gets the same table.

local data = ds.Get(player)
print(data.Stats.Cash)

ds.Update(player, mutator)

Applies a change and pushes it to the client replica. The mutator gets the table directly.

ds.Update(player, function(data)
    data.Stats.Cash += 100
end)

It persists on the next save and when the player leaves. Change the table only through Update, or through the helpers below which call it, so the replica stays in step.

ds.GetValue · ds.SetValue · ds.Increment

Path-addressed shortcuts for the common cases. The path is the same text the plugin shows.

local cash = ds.GetValue(player, "Stats.Cash")   -- nil if the key is not there
ds.SetValue(player, "Nickname", "Ana")          -- creates missing groups on the way
ds.Increment(player, "Stats.Cash", 250)         -- returns the new value
ds.Increment(player, "Stats.Cash", -500, 0)     -- the last argument is a floor

Increment treats a missing key as 0 and refuses a key holding something other than a number. Both it and SetValue return (result, err) and warn on failure.

ds.Save(player)

Queues an early save ahead of the schedule, for the moments that matter. It returns immediately; the write happens in the background, respecting the budget.

ds.GetSafeUserId(player)

The id the pack keys data by. In Studio, a test client with no real UserId gets a stable synthetic one, so test data never collides with production keys. Use it whenever you key your own stores by player.

ds.SaveAllImmediate(maxWaitPerWrite?) · ds.IsFreeMode()

The first flushes every cached profile now, which the pack already does for you on shutdown. The second is true while FreeMode is on.

Reading data on the client

Every player's save is mirrored under their Player as Folders and Value objects, updated on every write. No remotes.

-- LocalScript
local player = game.Players.LocalPlayer
local playerData = player:WaitForChild("PlayerData")
playerData:WaitForChild("DataReady")  -- true once the first replication landed

local cash = playerData.Stats.Cash
cashLabel.Text = tostring(cash.Value)
cash:GetPropertyChangedSignal("Value"):Connect(function()
    cashLabel.Text = tostring(cash.Value)
end)

Numbers, strings and booleans arrive as NumberValue, StringValue and BoolValue; arrays arrive as children named [1], [2] and so on. The replica is read-only by convention: writing to these Values does not write back to the server.

If all you want is a label that shows a number, you do not need any of this. Tag it UI_DataBind and give it a DataPath, and General UI Kit does it for you.

Purchases

ds.HasPass(player, passId)

Checks the in-memory cache, then the player's save, then Roblox, persisting a confirmed ownership back to the save. Returns true for everyone while FreeMode is on, and the cache clears itself when someone buys a pass mid-session.

if ds.HasPass(player, 123456789) then
    -- give them the perk
end

ds.registerHandler(productId, callback)

One handler per developer product. It receives (player, receiptInfo) and must return true to confirm. Returning false or erroring makes Roblox retry later, so nothing is lost.

ds.registerHandler(987654321, function(player, receipt)
    ds.Increment(player, "Stats.Cash", 1000)
    return true
end)

Every granted receipt is remembered in the player's save, so a receipt Roblox re-delivers is confirmed without running your handler a second time. Product and pass ids are not stored by this pack: keep them where your own code needs them.

ds.GrantProduct(player, productId) · ds.InvalidatePass(player, passId)

The first runs a registered product handler with no real purchase, and only while FreeMode is on, which is how you test a reward flow in Studio. Your handler receives nil for receiptInfo on that path. The second clears a cached pass result; mid-session purchases already do it for you.

Knowing when somebody bought something

The pack publishes a BindableEvent anyone can listen to, without declaring a dependency:

local hook = game:GetService("ServerScriptService").RBLXEssentials
    .Packs.DatastorePurchaseSystem:FindFirstChild("PurchaseCompleted")
if hook then
    hook.Event:Connect(function(player, kind, id, currencySpent)
        print(player.Name, "bought", kind, id)
    end)
end

kind is "product" or "gamepass", and it fires after the purchase is recorded as granted. It fires for FreeMode grants too, so the whole path is testable in Studio. Notifications System uses this exact hook, which is why neither pack declares the other.

What a save looks like

{
    Meta = {
        SchemaVersion = 1
        -- plus, at runtime: FirstJoinDate, SessionStart,
        -- SessionId, LastSaveDate, SaveCount
    },
    Stats = {
        Cash = 0,
        Wins = 0
    },
    Pets = {
        Equipped = {},
        Owned    = {}
    },
    Purchases = {
        Gamepasses     = {},
        ReceiptHistory = {}   -- filled as products are bought
    }
}

Stats, and anything else you add, comes from Default Data above. Meta, Pets and Purchases are added by the packs unless you define them yourself.

When a save cannot be read

A player whose data fails to load is not dropped into the game with an empty one. They are told to rejoin, and their real save is still there when they do. This is the one case where doing nothing would quietly cost someone their progress.

Easy Pet System

PackId: EasyPetSystem  ·  Version: 2.1.0  ·  Dependencies: Datastore & Purchase Sound System (optional)

What It Does

Easy Pet System is a complete, production-ready pet system for Roblox. Players hatch pets from eggs, collect them across rarity tiers, merge duplicates into rarer versions, equip pets that follow them around the map, and earn multiplier bonuses.

  • Weighted rarity rolls for egg hatching (single and triple hatch).
  • Auto-hatch gamepass support.
  • Triple-hatch gamepass support.
  • Extra Pet Space gamepass support.
  • Three pet movement modes — Ground, Float (orbit), and Follow — switchable at runtime.
  • Merge 3 pets of the same type into the next rarity tier.
  • Merge All: auto-merges all eligible sets in one call.
  • Equip Best: automatically equips the highest-multiplier combination.
  • Multiplier API: sum or product of all equipped pet multipliers, accessible from any server script.
  • 28 included pet models across 5 rarities (fully replaceable).
  • BillboardGui egg hatching animations.

Plugin Configuration

Required: this pack uses Roblox DataStore. Go to Game Settings → Security → Enable Studio Access to API Services before testing, otherwise player data will not save.

All settings are managed directly from the plugin, no script editing needed.

Purchase Settings

Purchase Settings

Lists the game passes used by the pack. Each entry has a warning to replace the default ID with your own from the Roblox Creator Dashboard.

  • ExtraPetSpace: allows players to equip a 4th pet.
  • AutoHatch: allows players to auto-hatch eggs continuously.
  • TripleHatch: allows players to hatch 3 eggs at once.

Before testing: create these Game Passes in the Roblox Creator Dashboard for your experience and paste each ID into the corresponding field. Without real IDs, ownership checks won't work.

Pet Data

Pet Data settings
  • MaxOwned: maximum number of pets a player can have in their inventory.
  • MaxEquipped: maximum number of pets a player can have equipped at once (increased to 4 with the ExtraPetSpace pass).
  • MergeCount: number of pets of the same type required to perform a merge.
  • ProximityDistance: stud distance within which pets stay near the player.
  • FollowMode: how pets move relative to the player. Options: Ground, Orbit, Follow. Orbit is labelled "Float" in the in-game Settings panel.

Rarities

Rarities configuration

Rarities are defined in ReplicatedStorage > RBLXEssentials > EasyPetSystem > PetSystem > PetData > Rarities. You can add, remove, and rename rarities freely. Pets reference rarities by name.

ValueTypeControls
ordernumberTier rank used for pet sorting in the Pets menu and for determining the merge target (next rarity up). Lower = weaker.
frameColorColor3Border/stroke colour of each pet slot in the egg billboard GUI, and the probability percentage text colour.
textColorColor3Pet name label text colour in the egg billboard GUI.
frameColor2Color3Read by PetConfig but not currently applied anywhere. Reserved for future use.
labelColorColor3Read by PetConfig but not currently applied anywhere. Reserved for future use.

These values are the ones that render. Each card's stroke takes the rarity's MainColor, its gradient runs MainColor to SecondaryColor, and the pet's name takes MenuTextColor, applied when the menu is built. The colours baked into the shipped templates match these, so the editor shows you what a player will see, but the data is what wins.

Pet Card Templates

The pet cards in the Pets menu are cloned from one template per rarity. With General UI Kit installed they live in its menu; without it, in the pack's own copy:

StarterGui > RBLXEssentials > GeneralUIKit > GeneralUIKitGUI
  > Menus > Pets > Container > PetContainer > Container

Each template is a Frame named [Rarity]PetTemplate (e.g. CommonPetTemplate, EpicPetTemplate) and tagged in CollectionService with the same name. When a pet card is rendered, the system resolves the correct template by tag:

CollectionService:GetTagged(petDef.rarity .. "PetTemplate")

The matching template is cloned and filled with that pet's data, then recoloured from the rarity's own values. Restyle a template freely: shape, corners, layout and fonts are yours, and the three colour slots are overwritten from the rarity so the card always matches the rest of the pack.

The Pets menu in game, with a card per rarity colour

To add a new rarity, create a new Frame in the container, name it [NewRarity]PetTemplate, and apply the CollectionService tag [NewRarity]PetTemplate to it.

Pets

Pets configuration

The full list of pets in the game. Each pet has a preview image (the 3D model), a name, a rarity, and a multiplier value. You can add new pets, delete existing ones, or change any of their properties here.

Eggs

Eggs configuration

Defines all eggs available in the game. Each egg has a name, a price, a currency (Cash or Robux), and a drop percentage table listing which pets can be hatched from it and at what probability. The total must add up to 100%.

At the bottom you can create a new Cash Egg or a Robux Egg.

Robux Eggs

Robux egg configuration

Robux eggs use robuxPrice (the display price in Robux) and a productId (the Developer Product ID from the Roblox Creator Dashboard) instead of a cash price.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into, demo eggs included. Saved pets are untouched: they live in the player's data, not in your place.


That's everything from the plugin.

If you're not planning to modify the pack's code or call its APIs from your own scripts, you can stop here. The sections below are for developers who want to integrate the system directly into their own code.

API

Declare "EasyPetSystem" in your own pack's manifest.deps and ask the kernel for it:

local pets = ctx.require("EasyPetSystem")

pets.GetTotalMultiplier(player, multiply?)

Returns the combined multiplier of all equipped pets.

-- Sum all equipped multipliers (e.g. 1 + 1.5 + 2 = 4.5)
local total = pets.GetTotalMultiplier(player)

-- Multiply all equipped multipliers (e.g. 1 * 1.5 * 2 = 3.0)
local product = pets.GetTotalMultiplier(player, true)

-- Apply to a reward
local reward = baseCash * pets.GetTotalMultiplier(player)

pets.HatchEgg(player, eggId, count, isAuto?)

Hatches one or three pets from the specified egg. Returns a result table.

local result = pets.HatchEgg(player, "Tier1Egg", 1)
if result.ok then
    for _, pet in result.pets do
        print(pet.id, pet.rarity, pet.multiplier)
    end
end

Parameters:

  • eggId (string): must match a key in the Eggs configuration.
  • count (number): 1 for single hatch, 3 for triple (requires TripleHatch pass).
  • isAuto (bool, optional): true for auto-hatch (requires AutoHatch pass).

Failure reasons: "invalid_egg", "insufficient_funds", "inventory_full", "need_triple_pass", "need_auto_pass", "cooldown".

Each pet in result.pets has: uid, id, rarity, multiplier, createdAt.

pets.EquipPet(player, uid)

Equips a pet by its unique ID. Players can have up to 3 pets equipped (4 with the ExtraPetSpace pass).

pets.UnequipPet(player, uid)

Unequips a pet by its unique ID.

pets.DeletePet(player, uid)

Deletes a pet from the player's inventory. The pet must be unequipped first.

pets.MergePets(player, petId)

Merges 3 pets of the same type into one pet of the next rarity tier. Non-equipped pets are consumed first; if there aren't enough, the remaining merge slots are filled from equipped pets, which are unequipped before being consumed.

local result = pets.MergePets(player, "Cat")
if result.ok then
    print("Merged into:", result.pet.rarity)
end

Failure reasons: "no_next_tier", "not_enough", "invalid_pet".

pets.MergeAll(player)

Merges all eligible pet sets in a single call.

local result = pets.MergeAll(player)
print("Merges performed:", result.mergeCount)

pets.EquipBest(player)

Unequips all current pets and re-equips the combination with the highest total multiplier, up to the equip limit.

Configuration

Pets, eggs, rarities, and core limits are all data-driven — edit them in the Explorer or through the plugin's settings panel without touching code. Pet movement behaviour (radius, speed, bobbing) is tuned in one editable Lua module, PetVisualsConfig (see below).

Key Settings

SettingDefaultDescription
MaxOwned50Maximum pets a player can hold in their inventory.
MaxEquipped3Base number of pets a player can equip simultaneously.
MergeCount3Number of pets required to perform a merge.
FollowMode"Ground"How pets move relative to the player. Options: "Ground", "Orbit", "Follow". Orbit is labelled "Float" in the in-game Settings panel.
ProximityDistance7Stud distance within which pets stay near the player.

Switching Follow Mode at Runtime

The active movement mode is controlled by a PetFollowMode attribute on the local player. Set it from any LocalScript — the Settings panel does exactly this:

local Players = game:GetService("Players")

Players.LocalPlayer:SetAttribute("PetFollowMode", "Orbit") -- "Ground", "Orbit", or "Follow"

Pets react immediately; no rejoin or respawn required. If the attribute is unset, the mode falls back to PetVisualsConfig.DefaultMode.

Tuning Pet Movement: PetVisualsConfig

ReplicatedStorage > RBLXEssentials > EasyPetSystem > PetSystem > PetVisualsConfig is an editable ModuleScript that defines every movement mode and its parameters:

return {
    DefaultMode = "Ground",
    Modes = {
        Orbit = {
            label       = "Float", -- name shown in the Settings panel
            Radius      = 4,       -- studs from character center
            Speed       = 45,      -- degrees per second
            FloatHeight = 2.5,     -- studs above HumanoidRootPart
            BobAmount   = 0.25,    -- vertical bob amplitude (studs)
            BobSpeed    = 1.2,     -- bob frequency (Hz)
            LerpAlpha   = 0.15,
        },
        Ground = {
            label       = "Ground",
            Radius      = 4,
            Speed       = 35,
            FloatHeight = -2,      -- floor level relative to HumanoidRootPart
            BobAmount   = 0,
            BobSpeed    = 0,
            LerpAlpha   = 0.15,
        },
        Follow = {
            label       = "Follow",
            Spacing     = 2.5,     -- studs between each pet in the trail
            FloatHeight = -2,
            LerpAlpha   = 0.1,
        },
    },
}

Adjust radius, orbit speed, float height, bobbing, or trail spacing here to change how equipped pets move.

Reading Configuration in Code: PetConfig

ReplicatedStorage > RBLXEssentials > EasyPetSystem > PetSystem > PetConfig loads the full PetData tree into a plain Lua table, usable from both client and server:

local PetConfig = require(ReplicatedStorage.RBLXEssentials.EasyPetSystem.PetSystem.PetConfig)

print(PetConfig.MaxOwned, PetConfig.MaxEquipped, PetConfig.MergeCount)
print(PetConfig.ProximityDistance, PetConfig.FollowMode)

local catDef = PetConfig.Pets["Cat"]        -- { name, rarity, multiplier }
local eggDef = PetConfig.Eggs["Tier1Egg"]   -- { name, price, currency, pets, robuxPrice?, productId? }
local rarity = PetConfig.Rarities["Epic"]   -- { order, frameColor, frameColor2, textColor, labelColor }

PetConfig.getNextRarityPets(petId) returns the merge candidates for a pet — the { id, percent } entries from the same egg at the next rarity tier — or nil if the pet is already the highest rarity in its egg:

local candidates = PetConfig.getNextRarityPets("Cat")
if candidates then
    for _, entry in candidates do
        print(entry.id, entry.percent)
    end
end

Gamepasses

Configure these pass IDs in the plugin settings to enable the corresponding features:

GamepassEffect
TripleHatchAllows hatching 3 eggs at once.
AutoHatchAllows continuous auto-hatching.
ExtraPetSpaceIncreases the equip limit from 3 to 4.

Client Integration: Remotes

On startup, the Bootstrap server script publishes the client-facing surface under ReplicatedStorage > PetSystem:

PetSystem > Remotes

RemoteTypeInvoked with
BuyAndHatchEggRemoteFunction{ eggId = "Tier1Egg", count = 1, isAuto = false }
GrantFreeProductRemoteFunction
EquipPetRemoteFunctionpet uid
UnequipPetRemoteFunctionpet uid
DeletePetRemoteFunctionpet uid
MergePetsRemoteFunctionpetId
EquipBestRemoteFunction
MergeAllRemoteFunction
PremiumHatchResultRemoteEventfired server → client with Robux hatch results

PetSystem > PassConfig — a folder the built-in UI reads for pass gating: NumberValues TripleHatch, AutoHatch, ExtraPetSpace (holding the pass IDs) and a BoolValue FreeMode (when true, all pass checks are bypassed).

Use these remotes to trigger pet actions from your own UI:

local Remotes = ReplicatedStorage:WaitForChild("PetSystem"):WaitForChild("Remotes")

local result = Remotes.BuyAndHatchEgg:InvokeServer({
    eggId = "Tier1Egg",
    count = 1,
    isAuto = false,
})
if result.ok then
    print("Hatched:", result.pets[1].id)
end

Adding Egg Models to the Map

Egg models in Workspace are discovered by tag, so you can place as many as you like. Each egg Model must:

  1. Be tagged Egg in CollectionService.
  2. Have an EggId string attribute matching a key in the Eggs configuration (e.g. "Tier2Egg").
  3. Contain a part named Gui holding a BillboardGui tagged EggBoard — this is the hatch interface players interact with.

The simplest workflow: duplicate one of the included egg models, change its EggId attribute, and swap the mesh. The billboard, proximity detection, and hatch buttons wire themselves up automatically.

Hatch Toast

The hatch-reveal toast (the full-screen pet reveal after hatching) is exposed as a client module at ReplicatedStorage > RBLXEssentials > EasyPetSystem > PetSystem > HatchToast, so you can trigger it from your own flows:

local HatchToast = require(ReplicatedStorage.RBLXEssentials.EasyPetSystem.PetSystem.HatchToast)

if not HatchToast.IsBusy() then
    HatchToast.ShowSingle(petData)   -- one pet reveal
    -- or
    HatchToast.ShowTriple(pets)      -- three-pet reveal (triple hatch)
end
  • HatchToast.IsBusy() — returns true while a reveal is currently playing.
  • HatchToast.ShowSingle(petData) — shows the reveal for one pet (a pet table with id, rarity, etc.).
  • HatchToast.ShowTriple(pets) — shows the three-slot reveal for an array of pets.

Included Pets

28 pets across 5 rarities sourced from a Roblox asset pack. All pet models are fully replaceable, you can add, remove, or swap them at any time without code changes.

RarityMultiplier Range
Commonx1 to x500
Uncommonx2 to x750
Rarex4 to x80,000
Epicx25 to x200,000
Legendaryx400 to x1,500,000

Sound System & Music Player

PackId: SoundSystem  ·  Version: 2.1.0  ·  Requires: nothing

What It Does

Sound System ships 42 sound effects, fifteen music tracks and seven music-player templates, including Studded. Every player has open and hide controls that preserve playback. Music and SFX use separate volume groups, so muting one leaves the other alone.

It is also what gives every other pack its sound. Every pack in the ecosystem asks for a sound whether or not this one is installed, and gets silence if it is not, so adding it later turns audio on across all of them at once, with nothing to wire.

Plugin Configuration

General and Music Player

The General and Music Player sections in the plugin
SettingDefaultWhat it does
SFXEnabledonGlobal kill switch. With it off, every sound effect in the game is silent, for every player.
MusicPlayerEnabledonWith it off, the music player is hidden and no music plays. For games that do not want one.
TemplateTemplate 1Which of the seven templates the player uses. The panel previews the one you pick.

The six original templates now include open and hide controls, and a seventh Studded template is included. They are ordinary frames in StarterGui: duplicate one, keep its MusicPlayerTemplate tag, and restyle it to add your own. The image below shows the original six designs.

All six music player skins

Music

The fifteen music tracks with per-song volume

Fifteen songs ship. Each carries its own volume, so a track mastered louder than the rest can be pulled down on its own instead of turning the whole playlist down. The group volume above them moves all of them together.

SFX

The eighteen sound effects, measured, with outliers flagged

42 effects ship, covering money, purchases, hovers, presses, level ups, pickups, impacts, movement and water. The 24 additions are optional sounds; they are not assigned to gameplay automatically. The plugin measures each one's loudness in decibels and flags the ones that miss your target with a Match button; Match all pulls every outlier into line at once.

WindLoop, UI_Confirm_01, Water_Splash_10 and Movement_Swish_04 are deferred from 2.1.0 because their current Roblox audio IDs could not be distributed.

+ Add sound takes an asset id, an rbxassetid:// string or a marketplace link, plus a name. That name is what you pass to SFX:Play, and what every sound dropdown elsewhere in the plugin will then offer you.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into. Every other pack goes silent rather than breaking.


That's everything from the plugin.

Below: playing sounds from your own code, the music player's structure, and the volume controls.

Where the sounds live

SoundService > RBLXEssentials > SoundSystem > SFX     -- sound effects
SoundService > RBLXEssentials > SoundSystem > Music   -- music tracks

They are edit-time content, not something built at runtime, so you can browse and replace them in the Explorer. Sounds are looked up by name: drop a Sound into the right group and it is available immediately, including one added while the game is running. Each sound's SoundGroup is assigned for you, so it obeys the player's volume without being told.

Playing a sound

From any LocalScript of your own:

local SFX = require(game.ReplicatedStorage.RBLXEssentials.Kernel.SFX)

-- Plays by name. Restarts if it is already playing.
SFX:Play("Press")

-- Clones it, so several can overlap. Each clone cleans
-- itself up when it finishes. Use this for anything that
-- can fire in quick succession.
SFX:PlayOverlap("Gold_Pickup")

This is the kernel's own sound facade, which every pack calls too. It is a silent no-op when Sound System is not installed, so code written against it never errors on a place that does not have the pack.

Pitch variation and owned loops

Inside a client pack module initialized by the Kernel, declare SoundSystem in manifest.deps, then obtain it with ctx.require("SoundSystem"). PlayOneShot(name, {volume = 0.7, pitch = 1.05}) plays a 2D copy without changing the template. PlayLoop(name, options) returns an owned loop: update its volume or PlaybackSpeed and call Destroy() when finished. Supply your own loop asset.

Both APIs may return nil while loading, muted or at their voice limits. Volume multiplies the template volume (0–2); pitch is absolute PlaybackSpeed (0.5–2). One-shots allow eight voices and expire after eight seconds; loops have a separate limit of two and no automatic eight-second expiry.

At a place in the world

Positional playback is on the pack's server API, reached through the kernel after declaring "SoundSystem" in your own pack's manifest.deps:

local sound = ctx.require("SoundSystem")

-- Everyone hears it, wherever they are.
sound.PlayAt("Money_1", part.Position)

-- With a range, it falls off with distance instead.
sound.PlayAt("Money_1", part.Position, 30)

-- Or for one player only.
sound.PlayAtForPlayer(player, "Gem_Pickup", part.Position, 30)

A ranged sound gets an invisible host part at that spot, rolls off with LinearSquare, and the part is destroyed when the sound ends. Without a range it plays globally, cloned straight into the SFX group. Either way it stays in that group, so the player's own volume and mute still apply.

Hearing all three before you write anything

Five parts ship in Workspace > RBLXEssentials > SoundSystem > Demo, each with a prompt on it and no code in it at all. Between them they cover every way of playing a sound: straight into your ear, out of a point in the world, and out of a point for one player only, the last two with and without a falloff range. Walk up, press the prompt, hear the difference, then delete the folder.

The music player

Drop your tracks into the Music group and the client does the rest: it builds the playlist, starts on a random track, advances when one ends, and keeps up if tracks are added or removed while the game runs.

The UI is tag-based. Tag any Frame with MusicPlayerFrame and give it this structure:

Frame  (tagged MusicPlayerFrame)
└── Container
    ├── SongName   (TextLabel)
    ├── Duration   (TextLabel)
    └── Buttons
        ├── Mute   (ImageButton with an "Icon" child)
        └── Next   (ImageButton)

Everything is wired for you: the song's name, the elapsed and total time updated live, a mute that swaps its icon, and a skip. You can tag more than one frame, for example one on the HUD and one inside a settings panel. They share a single playback state, so muting in one updates every other instantly, and frames added or removed at runtime are picked up.

Volume controls

The pack registers six actions you can put on any button by setting its Action attribute. No code.

ActionWhat it does
ToggleMusicMuteMutes and unmutes music, restoring the previous volume.
ToggleSFXMuteThe same for sound effects.
MusicVolumeUp / MusicVolumeDownSteps music volume.
SFXVolumeUp / SFXVolumeDownSteps SFX volume.

Volume moves along a nine step ladder from 0.04 to 1.0, so each press is a change you can hear rather than a linear crawl. The handlers also drive optional feedback: a mute button can show an ON/OFF status, and a frame with a VolumeGroup attribute of "Music" or "SFX" holding Volume1 to Volume9 segments renders as a live volume bar.

Everything stays in step across surfaces, because the music player's mute and the settings menu's mute are moving the same SoundGroup.

The pack ships its own Settings panel and the button that opens it, so the volume controls exist even with no other pack installed. With General UI Kit present, its Settings panel wins and these controls appear there instead. One menu, whichever pack provided it.

General UI Kit

PackId: GeneralUIKit  ·  Version: 2.1.0  ·  Requires: nothing

What It Does

General UI Kit is the menu system your game was going to need anyway, and the framework the rest of the ecosystem plugs into. Behaviour is declared with CollectionService tags and Instance attributes, so adding a panel or a button means tagging something, not editing a script.

It ships with fourteen menus and twelve HUD buttons already built, and provides:

  • Panel management: open, close, exclusive mode, animated transitions.
  • Button sounds, hover scaling and cursor-following tooltips.
  • Decorative animations: rotation, hover bob, idle bob, colour cycling.
  • An effect icon system with tooltip descriptions.
  • Live data binding, so a label shows and follows a saved value with no script.
  • Typing sounds on text boxes.
  • A place for your own button actions that pack updates never overwrite.

How It Works

The kernel discovers tagged elements anywhere in the local PlayerGui, both at startup and as they arrive. Tags say what an element is; attributes say how it behaves. Tag a Frame UI_Panel, set PanelId = "Shop", and it is a panel.

Because discovery is by tag rather than by path, every pack's menus land in the same system. When two packs ship the same menu, the one with the higher AdoptPriority wins and the other is left alone, so you get one Shop rather than two.

Button clicks are dispatched by action name. The built-in actions are handled for you; anything else goes to your own CustomHandlers module.

Plugin Configuration

Menus

The fourteen menus listed in the plugin

Every menu in your place, with the button that opens each one. Rename one, delete one, add one. Two of them are opened by other packs from code rather than by a button, and the list says so.

The pencil beside a menu makes it appear on screen and selects it in the Explorer, so you can design it in place instead of hunting for it. Click it again when you are done; the plugin warns you if you leave one visible, because Visible is saved with your place.

HUD Buttons

The twelve HUD buttons in the plugin One HUD button open, showing its six fields

A button is six fields: what it is called, which menu it opens, its icon, whether it shows at all, whether it has a tooltip and what that tooltip says. Show in HUD hides a button without deleting it, which is how you keep a menu your own code opens while taking its button off the screen.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into. Packs that were using its menus fall back to their own copies.


That's everything from the plugin.

If you're not planning to modify the pack's code or call its APIs from your own scripts, you can stop here. The sections below are for developers who want to integrate the system directly into their own code.

Tag Reference

UI_Panel

A toggleable menu panel (Frame or ScreenGui).

AttributeTypeRequiredDescription
PanelIdstringYesUnique identifier for this panel.
ExclusiveboolNoDefault: true. When an exclusive panel opens, it closes every other open panel that is also exclusive. Set to false to let the panel stay open alongside others — non-exclusive panels are never auto-closed.
AnimationstringNoPanel open animation. Default: "Bounce". See below for available animations.

Panel animations. The PanelAnimations loader aggregates animation modules from every installed RBLX Essentials pack, so the available set grows with your installed packs:

  • "SpinReveal" — ships with General UI Kit.
  • "Bounce" (the default) — ships embedded with every pack's shared runtime, so it's always available.

A pack contributes more by putting a ModuleScript that returns function(menu: GuiObject) in its own Client/PanelAnimations/ folder. The module's name becomes the Animation attribute value.

UI_Button

A clickable button (TextButton or ImageButton) that dispatches an action.

AttributeTypeRequiredDescription
ActionstringYesAction name. The built-in ones below are handled for you; anything else is dispatched to your CustomHandlers module on click.
TargetPanelstringNoThe PanelId to act on. Only read when Action is TogglePanel or ClosePanel — it has no effect with other actions.
ShowTooltipboolNoIf true, shows the shared Button tooltip on hover (see UI_Tooltip).
TooltipTextstringNoText shown in the Button tooltip. Defaults to the button's Name.
BuySoundboolNoIf true, plays the purchase sound instead of the standard click sound.

Linking a button to a panel:

To open or close a panel from a button, set Action = "TogglePanel" and TargetPanel = "<PanelId>" on the button. No handler is needed; it is one of the built-in actions.

Example: a button that toggles the Shop panel.

  • Panel frame: tag UI_Panel, attributes PanelId = "Shop", Exclusive = true
  • Button: tag UI_Button, attributes Action = "TogglePanel", TargetPanel = "Shop"

Use Action = "ClosePanel" (with the same TargetPanel) for a dedicated close button inside the panel. If you omit TargetPanel on a ClosePanel button, it closes the nearest ancestor panel — handy for a generic X button.

Icon hover effect: a direct child of the button named Icon scales up slightly on hover and back on leave. Nothing to tag.

Every GuiButton is wired, tagged or not, so a button of your own gets the click and hover sounds for free. Two tags opt out: UI_Toggle, which is handled separately, and UI_Blocker, for the invisible full-screen sheet a modal panel puts behind itself. A blocker is a button only so that it swallows clicks, so it gets no sound, no tooltip and no icon tween.

UI_Animated

A decorative UI element with a looping animation.

AttributeTypeRequiredDescription
AnimationstringYes"Rotate" (a continuous spin), "HoverBob" (a float on hover), "SineBob" (a constant bob and rock) or "RainbowCycle" (cycles the background colour).
SpeednumberNoDuration in seconds of one full rotation — larger values spin slower. Default: 15. Only used by Rotate; ignored by HoverBob.

UI_Tooltip

A shared tooltip frame. There is one tooltip per type, registered by its TooltipType attribute — every UI_Button with ShowTooltip uses the single Button tooltip, and every UI_EffectIcon uses the single Effect tooltip. You do not create a tooltip per button.

AttributeTypeRequiredDescription
TooltipTypestringYes"Button" (follows the cursor near a hovered button) or "Effect" (anchors beside a hovered effect icon, with screen-edge bounds checking).

Tooltip content contract. The labels a tooltip fills in are found either by name or by tag, anywhere inside the tooltip frame:

TagWherePurpose
ButtonName, by nameInside the Button tooltipTextLabel that receives the button's TooltipText, or its Name when none is set.
GeneralUIKit_EffectNameLabelInside the Effect tooltipTextLabel that receives the effect icon's Name.
GeneralUIKit_EffectDescriptionLabelInside the Effect tooltipTextLabel that receives the effect icon's Description.
Icon, by nameA direct child of any buttonScales up on hover.

UI_EffectIcon

An icon representing a buff or game effect. Hovering it shows the shared Effect tooltip with the icon's Name as the title and Description as the body.

AttributeTypeRequiredDescription
DescriptionstringYesText displayed in the Effect tooltip on hover.

UI_TypeSound

Apply to a TextBox to play a typing sound as the player types. The sound plays only when the text gets longer — backspace and deletions are silent. No attributes required.

UI_DataBind

Apply to a TextLabel or TextButton to live-bind its text to player data. The label updates automatically whenever the bound value changes — no polling, no manual refresh.

AttributeTypeRequiredDescription
DataPathstringYesDot-path into the replicated PlayerData folder, e.g. Stats.Cash.
PrefixstringNoText prepended to the value, e.g. "$".
SuffixstringNoText appended to the value, e.g. " pts".
FormatstringNoNumber formatting: Default (raw), Short (12.5K, 3.2M, 1.1B…), Full (floored integer), Commas (1,250,000). Non-numeric values are shown as-is.

Example: a cash counter in your HUD.

  • TextLabel: tag UI_DataBind, attributes DataPath = "Stats.Cash", Prefix = "$", Format = "Short"

Your own button actions

Set a button's Action attribute to a name, then add a function under that name in the pack's CustomHandlers module. Updates to the pack never overwrite that file.

-- ReplicatedStorage.RBLXEssentials.Packs.GeneralUIKit.CustomHandlers
return {
    OpenShop = function(button)
        print(button.Name, "was pressed")
    end,

    BuyItem = function(button)
        local productId = button:GetAttribute("ProductId")
        game:GetService("MarketplaceService"):PromptProductPurchase(
            game.Players.LocalPlayer, productId
        )
    end,
}

The button itself is passed in, so you can read your own attributes off it and drive several buttons from one handler. An entry whose name matches a built-in action replaces it.

Built-in Actions

These are handled for you. You do not need a handler for any of them.

ActionEffect
TogglePanelToggles the panel named in TargetPanel.
ClosePanelCloses the panel named in TargetPanel, or the button's nearest ancestor panel if TargetPanel is unset.
InviteFriendsPrompts the SocialService friend-invite dialog (permission-checked; fails silently if invites aren't available).

Notes

  • Sound System is optional. Every sound this pack asks for goes through the kernel, which is silent until Sound System registers itself. Install it and the whole kit gains its clicks, hovers and typing sounds at once; leave it out and nothing errors.
  • Every GuiButton is wired for click and hover sounds with no tag needed. To exclude one, such as an invisible click blocker, tag it UI_Blocker.
  • Tooltips are throttled at 16ms intervals to avoid layout thrashing.
  • Every connection is cleaned up when the GUI is destroyed, so respawns and reloads leave nothing behind.

Easy Leaderstats & Leaderboards System

PackId: EasyLeaderstatsSystem  ·  Version: 2.0.0  ·  Dependencies: Datastore & Purchase System

What It Does

Easy Leaderstats & Leaderboards System adds two related features to your game:

  • Leaderstats: the in-game stat list shown in the Tab/escape menu. Values are read directly from player data (provided by the Datastore & Purchase System) and kept in sync automatically.
  • Global Leaderboards: physical in-world boards that display the all-time top players for any stat, stored in Roblox OrderedDataStores and refreshed on a configurable interval.

Both features are fully configuration-driven from the plugin. No script editing is needed to add or remove stats and leaderboards.

Plugin Configuration

Selecting the pack in the plugin opens a settings panel with two sections: Leaderstats and Leaderboards.

Leaderstats

Easy Leaderstats plugin settings - Leaderstats section

Each stat entry corresponds to one row in the Tab menu. Entries are grouped by the data group they belong to (e.g. Stats).

FieldDescription
DisplayNameLabel shown in the Tab menu (e.g. Cash, Wins).
OrderSort order within the stat list. Lower numbers appear first.
DataKeyDot-path into PlayerData (e.g. Stats.Cash). Read-only; derived from Datastore Default Data.
DisplayFormatOptional formatting keyword applied to the raw number (see the keyword table below). If empty, Default is used.
PrefixOptional text prepended to the displayed value (e.g. $).
SuffixOptional text appended to the displayed value (e.g. wins).

DisplayFormat accepts these keywords (not printf-style format strings):

KeywordBehaviour
DefaultRaw number, displayed as-is.
ShortAbbreviated with a suffix tier: 1.5K, 2.3M — tiers go K, M, B, T, Qa, Qi.
FullFloored integer (52500.952500).
CommasThousands separators (5250052,500).

These fields compose left-to-right: Prefix + formatted value + Suffix. When a Prefix or Suffix is set and the format is Default, the value is formatted as Full (a floored integer string) so the affix can wrap it.

DisplayFormatPrefixSuffixRaw valueResult
Default(empty)(empty)5250052500
Short(empty)(empty)5250052.5K
Short(empty)(empty)23000002.3M
Commas$(empty)52500$52,500
Default$(empty)52500.9$52500 (treated as Full because a prefix is set)
Full(empty) wins77 wins

+ Add Stat creates a new entry. The DataKey is selected from the existing variables in your Datastore Default Data, so stats must be defined there first.

Leaderboards

Global Settings

Easy Leaderstats plugin settings - Global Settings
SettingDefaultDescription
RefreshInterval60Seconds between board refreshes.
MaxEntries50Maximum rows shown per board.

Leaderboards

Easy Leaderstats plugin settings - Leaderboards entries Leaderboard model in workspace

Each entry drives one physical in-world leaderboard board. Boards are discovered via the CollectionService tag ELS_Leaderboard: any Model in workspace with that tag and an ELS_LeaderboardId attribute appears here automatically. Models can be placed anywhere in the workspace hierarchy.

FieldDescription
StatPathDot-path into PlayerData for the stat this board tracks (e.g. Stats.Cash). Changing this renames the board automatically.
TemplateWhich of the three designs this board uses, from a dropdown. Changing it swaps the model in the world while keeping its position and orientation.
ValueFormatHow values are displayed: Raw (52500), Comma (52,500), or Abbreviated (52.5K).
EnabledWhen OFF, this leaderboard is skipped entirely.
Leaderboard templates

+ Add Leaderboard asks for a name, a stat path and a template, then clones that template into Workspace > RBLXEssentials > EasyLeaderstatsSystem, gives it its own id, and creates the matching Settings entry. The new model is selected in Studio for you, so you can drag it where it belongs.

Delete Leaderboard removes both the Settings entry and the workspace model.

Duplicating a Board in Studio

If you duplicate a leaderboard Model in Studio, the plugin detects the duplicate GUID and automatically assigns the clone a new unique ID and a new Settings entry.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into, and takes the demo boards out of the workspace with it.


That's everything from the plugin.

The sections below cover how it works underneath and what to expect at runtime.

How It Works

Leaderstats

A leaderstats folder is created under each player as they join, with one entry per configured stat. The value is read from that player's replicated save using the DataKey path, and a changed signal keeps it in step for the rest of the session. You never write to leaderstats yourself: change the saved value and the row follows.

The leaderstat type follows the source value in PlayerData:

Source typeLeaderstat created
IntValue / NumberValueIntValue — or StringValue when a custom DisplayFormat, Prefix, or Suffix is set, so the formatted text can be displayed.
StringValueStringValuePrefix/Suffix are still applied to the string.
BoolValueBoolValue — shown as true/false.

Global Leaderboards

Stats are written to OrderedDataStores in three situations:

  1. On join: once PlayerData is loaded and all configured stat values are non-nil, stats are written. Polls up to 10 seconds waiting for data readiness.
  2. On leave: stats are written immediately when the player disconnects.
  3. Periodically: every 5 minutes, all current players' stats are written to keep leaderboard data fresh for long sessions.

Board UI is refreshed every RefreshInterval seconds. The first refresh runs 5 seconds after server start to allow player data to load. During each refresh, the top MaxEntries players are read from the OrderedDataStore and the SurfaceGui list on the in-world board is rebuilt. A countdown timer on each board shows when the next refresh will occur.

Per-board display settings have sensible fallbacks, all derived from the last segment of StatPath (e.g. Stats.CashCash):

  • Empty TitleLabelTextTOP <STAT> (e.g. TOP CASH).
  • Empty VariableNameText<STAT> (e.g. CASH).
  • Empty or unrecognized ValueFormatRaw. Abbreviated covers the K, M, and B tiers.

OrderedDataStore Naming

Store names are derived automatically from the stat path: GlobalLeaderboard_ followed by the dot-path with dots replaced by underscores.

-- e.g. Stats.Cash → GlobalLeaderboard_Stats_Cash

Inspecting the raw store: values are stored with a +1 offset (subtracted back on read), so a stored 52501 means the player's stat is 52500. Stats ≤ 0 are not written to the store at all.

Dummy Avatars

The top three players on each board wear their real Roblox avatar on Dummy1, Dummy2 and Dummy3 beside it, playing a looping emote.

The scale comes from the rig itself. Each template builds its podium dummies at its own size, and the pack measures that before applying anything, so an avatar lands at the size the template was designed for rather than at one number shared by every board. A podium you scaled yourself is measured the same way and keeps working.

A rank whose player has not changed since the last refresh is left alone, so only a genuine change costs a GetHumanoidDescriptionFromUserId call.

Settings Structure

All settings live under ReplicatedStorage > RBLXEssentials > Packs > EasyLeaderstatsSystem > Settings, and the plugin is the way to edit them.

Settings/
├── Leaderstats/
│   └── [GroupName]/
│       └── [StatName]/        — one folder per stat
│           ├── DataKey        StringValue  dot-path into PlayerData (read-only)
│           ├── DisplayName    StringValue  label shown in Tab menu
│           ├── Order          NumberValue  sort order
│           ├── DisplayFormat  StringValue  Default | Short | Full | Commas
│           ├── Prefix         StringValue  optional prefix text
│           └── Suffix         StringValue  optional suffix text
└── Leaderboards/
    ├── RefreshInterval        NumberValue  seconds between refreshes (default: 60)
    ├── MaxEntries             NumberValue  max rows per board (default: 50)
    └── [EntryName]/           — one folder per leaderboard
        ├── StatPath           StringValue  dot-path into PlayerData
        ├── Template           StringValue  name of template in Templates/ folder
        ├── Enabled            BoolValue    if false, board is skipped
        ├── ValueFormat        StringValue  Raw | Comma | Abbreviated
        ├── TitleLabelText     StringValue  text shown on TopPart title label
        └── VariableNameText   StringValue  column header text on the board list

The three board designs live beside Settings, in the pack's own Assets folder. They are the catalogue + Add Leaderboard clones from; nothing reads them at runtime.

ReplicatedStorage.RBLXEssentials.Packs.EasyLeaderstatsSystem
├── Settings/
└── Assets/
    └── Templates/
        ├── LeaderboardTemplate1   [Model]
        ├── LeaderboardTemplate2   [Model]
        └── LeaderboardTemplate3   [Model]

Each leaderboard Settings folder also has an ELS_LeaderboardId attribute (GUID string) that links it to its workspace model.

In-World Board Setup

A board is any Model in the workspace tagged ELS_Leaderboard, wherever you put it. New ones are cloned from the design named in the board's Template setting and dropped into Workspace > RBLXEssentials > EasyLeaderstatsSystem for tidiness, which is created if it is not there. Changing Template later swaps the model in place, keeping its position and orientation.

Copy a board in Studio and it is found. Boards are discovered by tag rather than from a list you maintain, so a duplicate gets a new id and its own Settings entry, and you point it at a different stat.

The system locates UI elements by ELS_Role attribute, not by path, so the internal hierarchy of the model can vary between templates.

[StatPath leaf] Leaderboard  [Model]
├── BoardModel
│   ├── BoardBase  [Part]
│   │   └── Leaderboard  [SurfaceGui]
│   │       ├── Rank   [TextLabel, header]  ELS_Role = HeaderRank
│   │       ├── Name   [TextLabel, header]  ELS_Role = HeaderName
│   │       ├── Value  [TextLabel, header]  ELS_Role = HeaderValue  (shows VariableNameText)
│   │       └── List   [ScrollingFrame]     ELS_Role = List
│   │           └── Template  [Frame, cloned per entry, must stay hidden]
│   │               └── Frame
│   │                   ├── Rank, Name, Value  [TextLabel]
│   │                   └── Icon  [ImageLabel]
│   └── TopPart  [Part]
│       └── Leaderboard  [SurfaceGui]
│           └── TextLabel  ELS_Role = TitleLabel  (shows TitleLabelText)
├── Dummy1 / Dummy2 / Dummy3  [Model, R15 rigs for top-3 avatars]
└── Timer  [Part]
    └── GUI  [SurfaceGui]
        └── Label  [TextLabel, shows "Refresh in: Xs"]
ELS_RoleElementPopulated with
TitleLabelTextLabel on TopPart boardTitleLabelText setting (e.g. TOP CASH)
ListScrollingFrame containing entry rows(used as parent for cloned entries)
HeaderRankRank header TextLabel# (static, set by template)
HeaderNameName header TextLabelPlayer name (static, set by template)
HeaderValueValue header TextLabelVariableNameText setting (e.g. CASH)

Each board Model also carries these attributes (managed by the plugin):

AttributeTypePurpose
ELS_LeaderboardIdstring (GUID)Stable unique link to the Settings entry.
ELS_StatPathstringDot-path into PlayerData (e.g. Stats.Cash).

Requirements

  • Datastore & Purchase System must be installed and providing PlayerData for each player.
  • API Services must be enabled (Game Settings → Security → Enable Studio Access to API Services) for OrderedDataStores to work in Studio.

Notifications System

PackId: NotificationsSystem  ·  Version: 2.1.0  ·  Requires: nothing

What It Does

This is the pack that tells your players things. Three surfaces, five ready-made reasons to use them, and one line of code for anything else you want to announce.

  • Toasts. Cards that slide in, stack, and leave on their own. Four shapes ship: title only, title and subtitle, image and title, and image with both.
  • Chat lines. A system message in a colour you choose. Cheap, unmissable, and it never covers the screen.
  • Banner. A large centred card for the things that matter: a restart, an event going live, a message from you. One line or two.
Four toasts stacked in game

Installed on its own it already announces five things with no code at all: a player's friend joining with their headshot, someone buying with Robux (a line to the server plus a confirmation to the buyer), your rotating announcements, and anyone joining or leaving (off by default, since most places already do this their own way).

Every other RBLX Essentials pack lights up when this one is installed: a code redeemed, a pet arriving from something other than an egg, a free spin coming up, a session gift becoming claimable. None of them require it. They simply go quiet without it.

Plugin Configuration

Templates

The seven templates listed in the plugin

Seven templates ship. Expanding one shows the card itself at its real proportions with its true pixel size underneath. Beside its name is the pencil, the same one General UI Kit uses for menus: click it and the template appears on screen and is selected in the Explorer, so you can drag it where you want and design it in Studio. Click it again when you are done. The plugin warns you if you leave one visible, because Visible is saved with your place.

A template expanded, with its render preview and every attribute

Triggers

The four triggers with their on/off switches
TriggerWhat it doesShips
FriendJoinedA toast, with their headshot, when a player's friend joins.On
PurchasesA server-wide chat line plus the buyer's own confirmation toast. Wants Datastore & Purchase System.On
AnnouncementsThe rotating messages, on a timer.On
JoinLeaveA chat line for anyone joining or leaving.Off

Each row carries a ? you can hover for what that trigger does, and the section prints the folder path so you can find the file. A trigger's wording lives at the top of its own module, in a marked Config block. To change "Friend joined!", open FriendJoined and edit the line.

Announcements

The rotating announcement messages
FieldWhat it does
IntervalMinutesMinutes between messages. Anything under half a minute is treated as half a minute.
SurfaceChat or Banner.
ShufflePick at random instead of in order. It never repeats the message just shown.
Message1..NThe messages themselves. Add and Delete are in the plugin; blank ones are skipped.

Every message field understands three tokens: {player}, {item} and {price}. Anything else is left on screen exactly as you typed it, so a mistyped token is visible rather than silently blank. Turning the whole rotation off is the Announcements switch in Triggers, not a field here.

Uninstalling

Uninstall Pack button

That's everything from the plugin.

Below: how to restyle a template, write your own trigger, and announce your own things from code.

Designing the templates

The seven live in StarterGui > RBLXEssentials > NotificationsSystem > NotificationsSystemGUI > Templates and are yours to move, resize and restyle. Change BackgroundColor3 on a template frame to recolour the whole card.

A restyled notification template

They are drawn in General UI Kit's visual language, so they sit beside your menus rather than beside a different product. Their borders are UIStrokes on StrokeSizingMode = ScaledSize, which makes Thickness a fraction of the frame's height rather than a number of pixels: resize a card and its border keeps its proportions. That is how the whole kit is built.

Every Position and Size is in scale, never offset, with a UIAspectRatioConstraint holding each card's shape. A card is the same fraction of the screen on a phone as on a 4K monitor. Keep it that way when you move them: drag with the scale handles, not the offset ones.

Where they appear, and what queues behind what

Where you put a template is where its notifications appear. StackGroup decides who queues behind whom: give two templates the same group name and their notifications form one column, taking turns instead of landing on top of each other, whatever mix of heights they are. Leave a group empty and that template stacks on its own.

Out of the box the four toasts share the group Toasts, so a friend-joined card and a reward card queue in one column. The two banners share Banner. So a "friend joined" card can live in the bottom-left while a reward card comes down from the top-centre, and they never fight, as long as their groups differ.

Nothing ever waits for a slot. When a template is already showing its MaxOnScreen limit, the next one dismisses the oldest of that kind early and takes its place. A notification held back until a slot opened would arrive describing something that had already stopped being true.

Making your own template

Duplicate one in the Explorer and rename it. That is the whole procedure: the new name appears in the plugin immediately and can be used anywhere a template is named. It works because a template carries its own behaviour, as attributes on the frame:

AttributeWhat it does
DurationSeconds on screen.
StackDirectionDown, Up, Left or Right. Leave it on Auto and the stack grows away from the nearest edge.
MaxOnScreenHow many of this type show at once. At the limit a new one replaces the oldest. 0 means no limit.
SoundA sound from Sound System's SFX folder, picked from a dropdown of the ones you actually have. Silent, not broken, without that pack.
AnimationSlide, Fade or Pop.
StackGroupWhich column this template queues in.
SlideFromWhich edge the card slides in from. Only used when Animation is Slide, and it changes the entry only: the card still stops where you put it.

The renderer fills three children by name: Title, Subtitle and Image. Image is a container, a round plate with a ring holding an ImageLabel called Content. The plate clips, so a picture that is not square is masked to the circle, and the plate is what shows while a headshot loads. Delete one and that field simply is not drawn, so your redesign cannot break the pack. Add one to a template that had none and it starts being filled.

The two that behave differently

The banners size themselves. Their width follows the longest line, from a floor of 420px up to 900px, past which the text truncates rather than running off the screen. A 40px margin sits either side of the words, and since the card hugs its text that margin is the only thing between the words and the border. It is the number to change if they look cramped. Their labels are the only ones in the pack with TextScaled off, because a self-sizing card needs the box to fit the text rather than the other way round.

The centre banner in game

Alert is the one that is not a card. A single line of red text at the bottom centre, no fill, no border, no shadow. It stacks upward, because it hangs off the bottom edge. Use it when something could not be done: not enough money, a full inventory, a pass they do not own. It leaves after three seconds rather than five, because a refusal is a correction rather than news, and it plays Code_Error instead of the reward sound.

Its navy outline is not decoration. With no card behind it, that UIStroke is the only thing separating red text from whatever colour your game happens to be. Keep it if you restyle.

Easy Pet System already speaks through it: try to open an egg without enough Cash, or equip a fourth pet, and the line you see is this template.

Everything else in the kit is TextScaled, which means the height of the label decides how big the text renders, not TextSize. Make a label taller to make its text bigger.

Announcing your own things

From any Script or LocalScript of your own:

local Notify = require(game:GetService("ReplicatedStorage")
    .RBLXEssentials.Packs.NotificationsSystem.Notify)

-- server
Notify.Toast(player, "TitleSubtitle", { Title = "Well done", Subtitle = "You found the secret!" })
Notify.ToastAll("TitleOnly", { Title = "The boss has spawned!" })
Notify.Chat("Double XP is live for the next hour!", Color3.fromRGB(255, 215, 0))
Notify.ChatTo(player, "Welcome back!")
Notify.Banner(player, { Title = "Round starting", Subtitle = "Get to the arena" })
Notify.BannerAll({ Title = "Server restarting in 5 minutes" })

-- client: the same names, without the player
Notify.Toast("TitleOnly", { Title = "Saved!" })

The data table can carry Title, Subtitle, Image (an asset id), ImageUserId (resolved to that player's headshot) and Color.

It can also override the template's behaviour, for that one notification. Pass any of Duration, MaxOnScreen, Sound, Animation or SlideFrom and it wins over what the frame says:

Notify.Toast(player, "TitleOnly", { Title = "Saved!", Duration = 1, Animation = "Pop" })

The names are the template's own attribute names, so there is nothing extra to learn. A value the renderer cannot use is ignored and the template's own setting stands, so a typo costs that call its override rather than the message. Passing a Subtitle to a title-only card is harmless.

StackDirection is the one behaviour you cannot override per call, on purpose: direction belongs to a column rather than to a card. Set it on the template, where it describes the column.

Writing a trigger

Drop a ModuleScript into ServerScriptService > RBLXEssentials > Packs > NotificationsSystem > Triggers and it appears in the plugin with its own switch. Nothing else to wire:

local Players = game:GetService("Players")

local MyTrigger = {}

function MyTrigger.Start(ctx, trove)
    trove:Connect(Players.PlayerAdded, function(player)
        ctx.kernel.Notify:Toast(player, "TitleOnly", { Title = "Welcome!" })
    end)
end

return MyTrigger
  • trove cleans up with the pack, so connect through it and you have nothing to disconnect.
  • Start must not yield. Do slow work in your own task.spawn.
  • Give the module a Description attribute and that text becomes its ? tooltip in the plugin.
  • A module with no Enabled attribute is on: you added it because you wanted it. The plugin writes the attribute the first time you flip the switch.
  • A module that fails to load, or errors while starting, is skipped with a warning. It cannot take the other triggers down with it.

For pack authors

A pack does not use the module above. It calls the kernel facade:

ctx.kernel.Notify:Toast(player, "TitleSubtitle", { Title = "…" })  -- server
ctx.kernel.Notify:Toast("TitleSubtitle", { Title = "…" })          -- client

ctx.kernel.Notify is a silent no-op until Notifications System registers itself as its backend. That is why no pack declares this one in manifest.deps and why none of them checks whether it is installed. The asymmetry is deliberate: a pack wants silence when the pack is absent, and a person writing code wants to be told, so the front-door module errors at the require line instead.

Hooking purchases yourself

Datastore & Purchase System publishes a BindableEvent that this pack listens to, and so can you:

local hook = game:GetService("ServerScriptService").RBLXEssentials
    .Packs.DatastorePurchaseSystem:FindFirstChild("PurchaseCompleted")
if hook then
    hook.Event:Connect(function(player, kind, id, currencySpent)
        print(player.Name, "bought", kind, id, "for", currencySpent)
    end)
end

kind is "product" or "gamepass", and it fires after the purchase is recorded as granted. It also fires for FreeMode grants, which is how you test a purchase announcement in Studio without spending Robux.

Menus

None. This is the only pack with no panel and no HUD button, because there is nothing to open.

Roulette System

PackId: RouletteSystem  ·  Version: 2.1.0  ·  Requires: Datastore & Purchase System

What It Does

Roulette System adds a spin-to-win wheel. Players spend spins to land on rewards, earn a free spin on a cooldown, and can buy more with developer products. All of it is configured from the plugin.

  • Weighted random rewards: Cash, Diamonds, more Spins, any number in the player's save (Stat), or a module you write (Custom). A random pet from an Easy Pet System egg ships ready to use.
  • A free spin cooldown whose clock starts when a player reaches zero spins, and a spin lands when it runs out.
  • +1 / +3 / +10 spin developer products, wired from the plugin.
  • Seven wheel slots with a name, an icon, a type, an amount and a weight. Typing a name or icon in the plugin redraws the wheel in Studio as you type.
  • If the client never reports the wheel finished, the server grants the reward anyway after 10 seconds, and on leave.

Plugin Configuration

Spins

Spin settings in the plugin
SettingDefaultWhat it does
StartingSpins1Spins a brand new player starts with.
MaxSpins0Cap on spins in hand. 0 means no cap.
FreeSpinCooldown900Seconds until a free spin once the player runs out.

Dev Products

Developer product ids in the plugin

Paste the developer product id each button should sell. 0 means nothing is sold and the button warns instead. The receipts are handled by Datastore & Purchase System, so a purchase is granted once and recorded like any other.

Rewards

The seven reward slots with a live wheel preview

The wheel at the top is the real one, redrawn as you edit. Each slot has five fields:

One reward slot expanded
FieldWhat it does
TypeCash, Diamonds, Spins, Stat (any dot path in Target, such as Stats.Gems) or Custom (a module named in Target).
TargetOnly for Stat and Custom. For Custom the plugin lists your reward modules as a dropdown.
AmountHow much is granted. For Custom, how many times Grant runs.
WeightRelative chance. A slot at 100 lands twice as often as one at 50. 0 never lands.
DisplayNameText on the slot, applied to the GUI in Studio as you type and again at play time.
IconImage on the slot (rbxassetid://…). Each slot ships with its own.

Cash and Diamonds never go below zero. An unknown type, or a Stat or Custom slot with no target, is switched off at startup with a warning in the Output.

What ships in the seven slots

SlotTypeAmountWeight
Reward1Cash100100
Reward2Cash25080
Reward3Cash50050
Reward4Cash100030
Reward5Diamonds1040
Reward6Custom, RandomPet120
Reward7Spins160

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into. Your settings go with it.


That's everything from the plugin.

If you are not writing custom rewards or calling the pack from your own code, you can stop here.

Where things live

ReplicatedStorage.RBLXEssentials.Packs.RouletteSystem
├── manifest, Config, RewardLabels, Client, Actions/Apply Rewards
└── Settings/  Spins · Dev Products · Rewards/Reward1..7
ServerScriptService.RBLXEssentials.Packs.RouletteSystem
├── Roulette                        -- the rules
└── CustomRewards/RandomPet, GrantPet
StarterGui.RBLXEssentials.RouletteSystem.RouletteSystemGUI

With General UI Kit installed, its own Spin menu and HUD button win adoption and the wheel matches the rest of your UI. Without it the pack's own copy is used. They are the same design.

What it saves

The pack writes its own keys on join. There is nothing to add to Datastore's Default Data.

d.Roulette.Spins        -- spins in hand
d.Roulette.LastSpinTime -- os.time() when they ran out (0 = no countdown)

Both are mirrored to player.PlayerData.Roulette so the client can read them.

Custom Rewards

Drop a ModuleScript next to GrantPet under ServerScriptService > RBLXEssentials > Packs > RouletteSystem > CustomRewards:

local MyReward = {}

function MyReward.Grant(player, d)
    -- d is the live save inside a data update; changes persist
    d.Stats = d.Stats or {}
    d.Stats.Gems = (d.Stats.Gems or 0) + 1
end

return MyReward

Set the slot's Type to Custom and pick it from the Module dropdown. Amount is how many times Grant runs per spin. A slot pointing at a module that is not there is switched off at boot with a warning.

Two modules ship with the pack, both wanting Easy Pet System:

  • RandomPet is slot 6's default. It hatches one pet from an Easy Pet System egg using that egg's own odds, free, with the usual hatch toast. EGG_ID at the top picks the egg (Tier1Egg as shipped) and FALLBACK is what it grants when Easy Pet System is absent (25 Diamonds, or nil for nothing).
  • GrantPet grants one specific pet, named in PET_ID.

API

Declare "RouletteSystem" in your pack's manifest.deps, then:

local roulette = ctx.require("RouletteSystem")

-- capped by MaxSpins; false if the amount is not positive
roulette.addSpins(player, 5)

-- 0 until the player's save has loaded
roulette.getSpins(player)

-- true, or false plus a reason you never show a player
local ok, why = roulette.canSpin(player)

Remotes

Created by the kernel under ReplicatedStorage > RBLXEssentials > Kernel > Remotes > RouletteSystem.

RemoteTypeDescription
RequestSpinRemoteFunctionSpends a spin and returns { Success = true, Reward = { Index, DisplayName, Icon } } or { Success = false, Error }. The reward is held until the wheel finishes.
AnimationCompleteRemoteEventGrants the held reward. A second RequestSpin grants it too, as do the 10 second timeout and leaving.

The free spin countdown is worked out on the client from Settings > Spins > FreeSpinCooldown, which replicates like any other setting. There is no separate config folder and no claim remote.

Sounds

Roulette_Click as the wheel passes each slot, Roulette_Reward when it lands. Both ship in Sound System's SFX group and play through the kernel, so they are silent rather than broken when that pack is not installed.

Time Rewards System

PackId: TimeRewardsSystem  ·  Version: 2.1.0  ·  Requires: Datastore & Purchase System

What It Does

Two reward systems that run side by side: a seven day streak for coming back, and six timed gifts for staying. Types, amounts, icons and timers are all plugin settings.

  • A seven day cycle on a cooldown you choose. Missing a day does not reset the cycle, it only pauses it.
  • Six session gifts that unlock at fixed minutes of unbroken playtime. Session progress lives in memory and starts again when the player rejoins, because these are rewards for staying.
  • Badges on both HUD buttons light up when something is claimable, and the daily panel opens by itself on join when a reward is waiting.
  • The session icon counts down to the next gift on the HUD itself. It clears once a gift is waiting, because the badge says that better than a clock at 00:00.
  • The current day announces itself: its tile grows, takes the gradient, and its icon bobs. The emphasis follows the cycle, so it moves on its own.

Plugin Configuration

Every reward slot, day or gift, shares the same four fields:

FieldWhat it does
TypeCash, Diamonds, Spins, Stat or Custom.
TargetOnly for Stat (a data path such as Stats.Gems) or Custom, where the plugin lists your reward modules as a dropdown.
AmountHow much is granted. For Custom, how many times your module runs.
IconImage on the tile (rbxassetid://…).

Tile labels are written from those fields: +500 for a currency, +1 SPIN for spins, +5 Gems for a stat. Custom is the exception, because there is no number to show and a module name is not for players, so the tile keeps whatever label you author on it. The shipped Day 7 reads "LEGENDARY PET".

Session Gifts

The six session gifts in the plugin

Gift slots add one field, IntervalMinutes: the minutes of unbroken session playtime before that gift unlocks.

GiftIntervalTypeAmount
15 minCash10
210 minCash25
315 minCash50
430 minCash75
545 minCash100
660 minDiamonds10

Daily Rewards

The seven daily rewards and the streak rail preview
SettingDefaultWhat it does
DailyResetModeHoursHours gives each player a private clock started by their own claim. Midnight unlocks everyone together at 00:00 UTC, so someone who claims at 23:50 can claim again ten minutes later.
DailyRewardCooldownHours24Hours between claims. Ignored when the mode is Midnight.
DayTypeAmount
1Cash100
2Cash200
3Spins1
4Diamonds20
5Cash500
6Diamonds100
7Custom, GrantPet1

One day, open

A single day expanded in the plugin

An unknown Type, or a Stat or Custom slot with no Target, is switched off at startup with a warning in the Output, rather than failing the first time a player tries to claim it.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into. Your settings go with it.


That's everything from the plugin.

If you are not writing custom rewards or calling the pack from your own code, you can stop here.

What it saves

The daily half persists one key, created on join if it is not there:

data.TimeRewards = {
    Daily = {
        Streak    = 1,  -- position in the cycle (1..7), wraps after the last day
        LastClaim = 0,  -- os.time() of the last successful claim
    }
}

Streak is a position in the cycle, not a count of consecutive days. It advances by one per claim and wraps. Someone who claims Day 3 and disappears for a month comes back to Day 4, because the cooldown is the only gate.

Session state (when they joined, which gifts they took) lives on the server for the length of the session and is never written to the datastore.

Custom Rewards

Drop a ModuleScript into ServerScriptService > RBLXEssentials > Packs > TimeRewardsSystem > CustomRewards:

local MyReward = {}

-- `d` is the live save inside a data update: changes persist and
-- replicate on their own. `amount` is the slot's Amount field.
function MyReward.Grant(player, d, amount)
    d.Stats = d.Stats or {}
    d.Stats.Gems = (d.Stats.Gems or 0) + amount
end

return MyReward
Choosing a custom reward module in the plugin

Set the slot's Type to Custom and pick it from the Module dropdown. A GrantPet example ships with the pack: it grants a pet of the tier named in PET_RARITY (Legendary as shipped) rather than one hardcoded pet, so the reward keeps matching its label after you edit your pet list. Set PET_ID instead to pin one exact pet.

The menus

The pack ships its own Rewards menu and HUD button so it works on its own. With General UI Kit installed, its copies win adoption and this pack drives those instead. The daily panel lives in General UI Kit only, because the current-day emphasis is applied from a template at runtime and has to follow the cycle rather than be frozen on one tile.

PartPurpose
Container/Gifts/Gift<N>/ButtonFrameClaim face: ClaimButton, TimeLeftText
Container/Gifts/Gift<N>/RewardFrameReward face once claimed: Reward/ImageLabel, Reward/AmountText
Container/ClaimedRewardsThe "n of total claimed" line
Container/Days/Day<N>Day tile: DayLabel, Icon, Amount, ClaimButton/StatusText
Container/ProgressTrack, Fill (streak over days), ProgressText
Container/FooterDailyStreak, TimeLeft

Reusing the day's motion anywhere

The bob on the current day is the same one General UI Kit gives any element, so you can put it on something of your own without code: tag it UI_Animated and set Animation = "SineBob". Day 7's sunburst spins the same way with Animation = "Rotate".

To retune the day icon itself, put any of IdleBobAmplitude (scale), IdleBobRotation (degrees) or IdleBobPeriod (seconds per cycle) as attributes on the panel's Container/Days frame.

API

local rewards = ctx.require("TimeRewardsSystem")

-- Where they are in the cycle, and how long until the next.
local daily = rewards.getDaily(player)
print(daily.dayIndex, daily.cooldownRemaining)

-- Second return: the new state, or why it was refused.
local ok, result = rewards.claimDaily(player)

-- The session half, by gift number.
rewards.getSession(player)
rewards.claim(player, 3)

A refusal is a reason, never a sentence, so the wording on screen stays yours: NotReady, AlreadyClaimed, OnCooldown, NoSuchGift, BadIndex, NoData.

Remotes

Created on demand under ReplicatedStorage > RBLXEssentials > Kernel > Remotes > TimeRewardsSystem.

RemoteTypeDescription
ClaimGiftRemoteFunctionClaims a session gift by index.
GetSessionRemoteFunctionThe caller's session state, for the first paint and re-opens.
ClaimDailyRemoteFunctionClaims the current day.
GetDailyRemoteFunctionThe caller's daily state, including the remaining cooldown.

Every claim is validated on the server against the same rules the panel shows. The client is never trusted for eligibility.

Codes System

PackId: CodesSystem  ·  Version: 2.1.0  ·  Requires: Datastore & Purchase System

What It Does

Codes System lets you hand out redeemable codes for a launch, a milestone, a video or a Discord drop, and lets players claim them from a menu. You write the codes in the plugin; each player can redeem each one once.

  • Any number of codes, each granting Cash, Diamonds, any other saved Stat, Spins for the Roulette wheel, or a Custom reward you write yourself.
  • Case and spaces do not matter. launch, LAUNCH and  Launch  are the same code, so a player who pastes one with a trailing space still gets their reward. The box upper-cases as they type, so what they see is what will be matched.
  • An optional expiry per code, to the day.
  • One redemption per player, remembered in their save.
  • Guessing is throttled server-side, and a correct code clears the penalty immediately.
  • Every word the panel can say is a setting you edit, so the pack speaks your game's language.

Codes are authored in the plugin, not fetched from anywhere. A new code means opening the place in Studio, adding it, and republishing. In exchange there is nothing to host, nothing to authorise, and no second place to look when something is wrong.

Plugin Configuration

The code list

The Codes list in the plugin

One row per code: the code itself, what it grants, and a dot saying whether it is live, expired or refused. Add code and Delete code live here too, so the list grows and shrinks without touching the Explorer, and both are ordinary Ctrl+Z.

One code, open

A single code expanded, showing its fields
FieldWhat it does
CodeWhat the player types. Write it however you want it read; matching ignores case and surrounding spaces.
TypeCash, Diamonds, Spins, Stat or Custom.
TargetOnly for Stat (a data path such as Stats.Gems) or Custom, where the plugin lists your reward modules as a dropdown.
AmountHow much is granted. For Custom, how many times your module runs.
ExpiresYYYY-MM-DD in UTC, or empty for a code that never expires. The plugin gives you a calendar for it.

Reward types

The five reward types in the dropdown

Spins only does anything with Roulette System installed, and Custom lists whatever modules you have dropped into the pack's CustomRewards folder. Neither is a declared dependency: a code whose pack is missing warns in the output instead of breaking the redemption.

Expiry

Picking an expiry date from the calendar

Expires is the day the code stops working, not its last day. 2026-09-01 means it dies at 00:00 UTC on the 1st, so 31 August is the last day anyone can redeem it. The plugin prints that last day under the field, so you never have to hold the rule in your head.

If you want a code to stop working on a date, set Expires rather than deleting it. The code stays visible in your settings and its history stays intact.

The codes that ship

SlotCodeGrants
Code1LAUNCH1000 Cash
Code2WELCOME50 Diamonds
Code3FREESPINS5 roulette spins
Code4LEGENDARYa Legendary pet, through GrantPet

They are examples. Change or delete them before you ship. None of them expires.

What the panel says

The Messages section in the plugin

Settings/Messages holds every word a player can be told, one field per outcome.

FieldDefault
RedeemedRedeemed {code}!
EmptyType a code first!
NoSuchCodeThat code doesn't exist.
AlreadyRedeemedYou've already used that code.
ExpiredThat code has expired.
CodeDisabledThat code isn't available.
TooFastSlow down a moment, then try again.
NoDataYour data is still loading. Try again.
GrantFailedSomething went wrong. Try again.

Redeemed may contain {code}, which is replaced with the code that was redeemed. A field you clear falls back to its default rather than saying nothing, and so does a field that is missing entirely.

Uninstalling

Uninstall Pack button

Removes the pack from every service it was installed into. Your settings go with it, so export them first if you want them back.


That's everything from the plugin.

If you are not planning to write custom rewards or call the pack from your own code, you can stop here.

What it saves

One key, created on join if it is not there:

data.Codes = {
    Redeemed = { LAUNCH = true, WELCOME = true },
}

A set of the codes this player has used, keyed in upper case. Renaming a code in Settings lets everyone redeem it again, which is correct: it is a different code.

Custom Rewards

Drop a ModuleScript into ServerScriptService > RBLXEssentials > Packs > CodesSystem > CustomRewards. It returns a table with a Grant function:

local MyReward = {}

-- `d` is the live player-data table inside a datastore update:
-- changes persist and replicate on their own. `amount` is the
-- code's Amount field.
function MyReward.Grant(player, d, amount)
    d.Stats = d.Stats or {}
    d.Stats.Gems = (d.Stats.Gems or 0) + amount
end

return MyReward

Set the code's Type to Custom and pick your module from the Module dropdown, which lists whatever is in that folder.

A custom reward module selected in the plugin

A GrantPet example ships with the pack. It grants a pet of the tier named in PET_RARITY (Legendary as shipped) rather than one hardcoded pet, so the reward keeps matching its label after you edit your pet list. Set PET_ID instead to pin one exact pet.

The menu

The pack ships its own Codes menu and HUD button so it works on its own. With General UI Kit installed, General UI Kit's copy wins and this pack drives that one instead: one menu, whichever pack provided it.

Inside the panel, the pack drives these by name:

PartPurpose
Container/Code/TextBoxWhere the player types. Upper-cased on every keystroke, with the caret left where it was.
Container/ButtonRedeem.
Container/ErrorTextRefusals.
Container/SuccessTextConfirmations.

Rename any of these and the pack degrades rather than breaking: it warns in the output and, if only the two labels are missing, keeps redeeming silently. Everything else in the panel is yours to restyle.

API

Another pack reaches this one through the kernel, after declaring it in its own manifest.deps:

local codes = ctx.require("CodesSystem")

-- ok, reason | { Code, Type, Amount }
codes.redeem(player, "LAUNCH")

-- boolean; case does not matter here either
codes.hasRedeemed(player, "launch")

Refusals come back as machine reasons, never as sentences: Empty, NoSuchCode, AlreadyRedeemed, Expired, CodeDisabled, TooFast, NoData, GrantFailed. The caller decides the wording.

Remotes

Created on demand under ReplicatedStorage > RBLXEssentials > Kernel > Remotes > CodesSystem.

RemoteTypeDescription
RedeemRemoteFunctionRedeems the given text. Returns { Success = true, Code, Type, Amount } or { Success = false, Error }.

Every redemption is validated server-side against the same rules the panel shows. The client is never trusted, and the throttle lives on the server rather than in the button.

Changelog

Version history for all packs and the plugin.

Version 2.1.0 Released 2026-09-15

RBLX Essentials Plugin 2.1.0 — Released 2026-09-15

  • Choose installed UI templates from a floating dropdown. Original and Studded UI retain separate project edits when you switch between them.
  • Menus and Buttons editors now follow the selected interface. Preview tiles scale with the miniature, and tooltips stay inside the plugin window.
  • Import the free Studded UI visual kit as a General UI Kit template. A Get more UI templates link provides a selectable address for the template catalog.
  • Studded UI remains visible as an installed template without opening a separate settings panel. Select and configure the active interface through General UI Kit.
  • Edit interactive menu pages directly in Studio: switch between Collection, Eggs and Index from the menu editor without running gameplay scripts.

Sound System & Music Player 2.1.0 — Released 2026-09-15

  • All six music-player templates now include an open button and a hide control; a seventh Studded template is included. Showing and hiding the player preserves music playback.
  • Add optional PlayOneShot and PlayLoop client APIs with per-play volume and pitch, separate voice limits and cleanup when the owner is removed or SFX are disabled.
  • Recover player controls when their GUI arrives late, preserve the selected template, and prevent duplicate initialization or stale UI connections.
  • Expand the optional SFX library from 18 to 42 sounds: 24 additions selected from sounds used in Stone Skipping. No extra gameplay mappings are enabled automatically.
  • The standalone Settings interface matches the current General UI Kit template.
  • Four initially selected sounds are deferred because their current Roblox audio IDs cannot be distributed: WindLoop, UI_Confirm_01, Water_Splash_10 and Movement_Swish_04.

Datastore & Purchase System 2.1.0 — Released 2026-09-15

  • Add SaveAndWait with confirmed storage results, frozen save snapshots, retry backoff and safer same-server rejoin handling.
  • Add GUID session ownership to reject displaced writers, plus read-only ReadSaved and StoreName APIs.
  • Add registerProfileHandler: profile rewards and purchase receipts are saved together before confirming a purchase. Duplicate receipts remain recognized without automatic history eviction.
  • HasPassStrict checks stored entitlements as well as Roblox ownership independently of FreeMode. Legacy handlers remain available, but arbitrary external effects are not made transactional.

General UI Kit 2.1.0 — Released 2026-09-15

  • Add Studded UI presentation support for the separately distributed free visual kit, including responsive typography, centered fixed-size stud textures and authored button behavior.
  • Support optional gain-to-counter visual feedback when Notifications System 2.1.0 is installed.
  • Show the configured demonstration Rebirth requirement and avoid rotation that breaks masking in affected panel animations. The requirement display does not add a server-side Rebirth economy.
  • Studded roulette offers animate the button, icon and price together from the center without shifting adjacent offers. Green prices and a static white circular rim improve readability while spinning.
  • The default Pets menu now includes Collection, Eggs and Index, with rarity-colored borders, consistent viewport framing and two-row equipment actions.

Easy Pet System 2.1.0 — Released 2026-09-15

  • Add stored egg inventory, one-at-a-time opening through the native hatch reveal, and permanent species discovery for optional Collection, Eggs and Index tabs.
  • Add GrantPet and GrantEggs APIs, profile helpers and a Changed signal, with complete-batch capacity checks and optional receipt-based delivery.
  • Fusion consumes exactly the required number of copies. Stable inventory IDs and saved multipliers improve equipment ranking and prevent unnecessary preview rebuilding.
  • Improve late GUI/data recovery, follower cleanup and frame-rate-independent smoothing. Keep disabled pass IDs disabled in FreeMode.
  • Robux eggs use confirmed profile purchase handlers; ordinary cash-hatch requests cannot grant Robux eggs. Requires DataStore 2.1.0.
  • The standalone Pets interface includes the revised default Collection, Eggs and Index layout. Stable grid sizing prevents a layout feedback loop when browsing tiers.

Roulette System 2.1.0 — Released 2026-09-15

  • Paid spin bundles use DataStore profile purchase handlers. A bundle is granted in full or remains pending when there is insufficient capacity; requires DataStore 2.1.0.
  • The included RandomPet reward uses the shared pet inventory and discovery helpers, retaining its existing diamond fallback when a pet cannot be granted.

Time Rewards System 2.1.0 — Released 2026-09-15

  • Explicit custom-reward failure no longer consumes a session or daily claim. Optional preparation runs before profile updates, while existing callbacks that return nil remain compatible.
  • The included pet reward validates the full batch and records species discoveries through Easy Pet System helpers.
  • Support the revised daily reward presentation and claim states. Session rewards still reset on a normal new session; AFK reconnection persistence is not included in this update.

Codes System 2.1.0 — Released 2026-09-15

  • Explicit custom-reward failure no longer consumes a code. Optional preparation resolves dependencies before profile updates, while existing nil-returning callbacks remain compatible.
  • The included GrantPet reward validates the whole batch and records discoveries through Easy Pet System helpers instead of maintaining its own inventory schema.

Notifications System 2.1.0 — Released 2026-09-15

  • Add CounterFlyout and a CounterEffects template for icons and amounts moving toward a GUI counter; optional tagged data bindings can trigger feedback on positive gains.
  • Support independent entrance and exit directions, optional colors and native text fields in notification templates.
  • Recover a late notification GUI with cancelable waits and clean up removed views. Counter flyouts are visual feedback, not delayed counter updates or a transaction ledger.

Compatibility and updating

  • Update DataStore, Easy Pet System and Roulette together. Easy Pet System 2.1.0 and Roulette 2.1.0 require DataStore 2.1.0 for profile purchase handlers.
  • The included pet reward examples in Codes, Time Rewards and Roulette require Easy Pet System 2.1.0 when used. Pets remain an optional companion for other rewards.
  • Studded UI requires General UI Kit 2.1.0 and Plugin 2.1.0 for template selection and interaction. The free download alone is a visual kit.
  • Back up customized pack folders before updating: variant switching preserves edits within a project, but pack installation still replaces pack folders and only restores compatible Settings values.
  • Coordinate the DataStore rollout and restart old servers. Older writers do not honor the new session ownership protocol; do not change StoreVersion or wipe profiles to perform this update.

Unchanged: Kernel 2.1.0; EasyLeaderstatsSystem 2.0.0.

🔌 Plugin
v2.0.0 Latest
  • Fresh codebase: the plugin is now an installer/control panel over self-contained pack containers instead of scanning ServerStorage for loose pack folders. It's still simply "RBLX Essentials" — it replaces the v1 plugin entirely.
  • Drag a pack's .rbxm into Studio and the plugin shows a banner to Install, Update, or Migrate it — no manual moving between Workspace and ServerStorage, no install ordering between packs.
  • Detected v1 installs get a Migrate banner: uninstalls the v1 copy, captures its settings, and offers to apply them onto the new v2 pack once installed.
  • Ecosystem Core is no longer a separate pack the plugin manages — its runtime ships embedded inside every pack, and the plugin keeps the newest copy in sync automatically.
  • Dedicated Easy Leaderstats & Leaderboards System boards section: add, rename, and delete leaderboards directly from the plugin.
v1.0.11
  • Settings panel: sections containing ModuleScript children now render each module as a read-only labeled row (with file icon) so users can see what's inside collection-style folders (e.g. TradeSystem > TradeableCollections)
  • Settings panel: section header subtitle (showing the ServerStorage > ... > Settings > SectionName path) now also displays for sections that contain ModuleScript children — makes it discoverable where to drop new module files
v1.0.10
  • Fixed uninstall leaving Menus panel containers tagged with the uninstalled pack's RBLXSourcePack after re-merge: unmergePackFromEC now re-tags the panel root with the alternate pack's id; stale tag cleared if no alternate exists
  • Fixed deepMergeInto allowing GeneralUIKit to replace untagged EC-owned instances (UIGridLayout, UIAspectRatioConstraint, etc.) — protected from replacement by all packs
  • Time Rewards System: dedicated plugin renderer for Daily Rewards and Session Gifts sections
  • Time Rewards System: Type dropdown populated dynamically from DS Default Data stats and any ModuleScript names in Settings/CustomRewards/
  • Time Rewards System: each Day/Gift slot is a collapsible box with Type dropdown, Amount field, Icon field with live image preview, and IntervalMinutes (session gifts only)
  • Time Rewards System: Amount and Icon edits sync live to StarterGui UI
  • Time Rewards System: notification badges on shortcut buttons activate when a reward becomes claimable
v1.0.9
  • Pack version label moved from top-right to bottom-right of the pack card
  • Fixed cross-pack UI contamination on uninstall: deepMergeInto now skips leaf instances tagged with a different RBLXSourcePack
  • Fixed install-order asymmetry for shared containers (e.g. Shop): removed over-broad container-level block in deepMergeInto that prevented a second pack from recursing into a container already tagged by the first pack — leaf protection is sufficient
  • Fixed install-order asymmetry for shared containers: setSourcePackRecursive no longer tags Folder instances — Folders are treated as shared structural containers
  • Fixed uninstall of one pack destroying shared Menus panels and Interactables buttons also owned by another installed pack: uninstall now checks SS for other installed packs claiming the same panel/button and either re-merges from their source (Menus) or re-tags in place (Interactables) before cleanup runs
  • Fixed uninstall of one pack removing shared Menus panel items (e.g. Top/Title, Frame/UIStroke, Frame/UIPadding) that were tagged by the uninstalling pack during install: after unmergeItems, the panel is re-merged from the other pack's source to restore any items that were removed
v1.0.8
  • Fixed pack uninstall breaking EC Interactables layout: deepMergeInto no longer replaces untagged EC-owned instances (UIGridLayout, UIAspectRatioConstraint, etc.) with pack-tagged copies
  • RouletteSystem: added Rewards section to SettingsPanel — collapsible, one box per reward slot (fixed 8 slots)
  • RouletteSystem rewards: DisplayName field with live sync to StarterGui RewardText label
  • RouletteSystem rewards: Icon field with inline image preview, live sync to StarterGui Icon image
  • RouletteSystem rewards: Type dropdown (Cash / Diamonds / Spins / any ModuleScript name in CustomRewards)
  • RouletteSystem rewards: Amount field (visible for all types; for Custom = number of times the module is called)
  • RouletteSystem rewards: Chance % field — input weight, live = X% probability label that recalculates across all slots when any weight changes
  • RouletteSystem rewards: generic Settings renderer skips Rewards and CustomRewards folders to avoid duplicate sections
v1.0.7
  • ELS leaderboards: multiple template support — templates live in ServerStorage/.../EasyLeaderstatsSystem/Templates/
  • ELS leaderboards: Template dropdown per leaderboard entry; changing template swaps the workspace model while preserving position and orientation
  • ELS leaderboards: back-fills missing Template value on existing entries automatically when opening the settings panel
  • ELS leaderboards: fixed Delete Leaderboard deleting the ServerStorage copy instead of the workspace model
  • ELS leaderboards: fixed template swap causing a duplicate entry in the plugin UI until refresh
v1.0.6
  • ELS leaderboards: fixed model discovery to only match workspace descendants, preventing false matches against ServerStorage template copies
v1.0.5
  • ELS leaderboards: tag-based model discovery via ELS_Leaderboard CollectionService tag — models can be placed anywhere in workspace
  • Fixed duplicate leaderboard entries when SS template copies were also tagged
v1.0.4
  • ELS leaderboards: template-agnostic board discovery via ELS_Role attributes (no hardcoded UI paths)
  • Added TitleLabelText and VariableNameText per-leaderboard settings; defaults to TOP X / X from StatPath
  • Removed DisplayName field from leaderboard entries (model lookup is now GUID-based)
  • StatPath and ValueFormat dropdowns now show a ▼ arrow indicator
v1.0.3
  • Added Easy Leaderstats & Leaderboards System settings panel: Leaderstats and Global Leaderboards configuration
  • Leaderboards section: workspace-driven discovery, Add/Delete Leaderboard, ValueFormat picker, bidirectional DisplayName sync
v1.0.2
  • Navigation Manager: each panel row now shows a live thumbnail preview of the panel
  • Navigation Manager: added "Add New Panel & Button" action button for General UI Kit
v1.0.1
  • Added Navigation Manager section to SettingsPanel for General UI Kit: configure per-panel Exclusive/Animation and assign buttons to panels
  • Fixed Installer: when Ecosystem Core installs with General UI Kit already present, PanelAnimations children are now additively merged instead of dropped
v1.0.0
  • Initial release: Scanner, Verifier, Installer, PacksPanel, SettingsPanel
🎡 Roulette System
v2.0.0 Latest
  • First release. A spin-to-win wheel with seven weighted slots, configured entirely from the plugin, with the wheel redrawing in Studio as you edit it.
  • Free spins on a cooldown that starts when a player runs out, and +1 / +3 / +10 spin developer products wired from the plugin.
  • Custom reward modules, with RandomPet shipping ready to hatch from an Easy Pet System egg using that egg's own odds.
  • If the client never reports the wheel finished, the server grants the reward anyway after ten seconds, and on leave.
🎁 Time Rewards System
v2.0.0 Latest
  • First release. A seven day streak and six timed session gifts, both fully configured from the plugin.
  • Missing a day pauses the cycle rather than resetting it, and the daily reset is either a private per-player clock or midnight UTC for everyone.
  • Badges on both HUD buttons light up when something is claimable, and the session icon counts down to the next gift on the HUD itself.
  • The current day announces itself and the emphasis follows the cycle, so it moves on its own.
🎟️ Codes System
v2.0.0 Latest
  • First release. Redeemable codes authored in the plugin, one redemption per player, remembered in their save.
  • Case and surrounding spaces are ignored, and the box upper-cases as the player types, so what they see is what will be matched.
  • Optional expiry per code, to the day, with the plugin naming the last usable day under the field.
  • Every word the panel can say is a setting, and guessing is throttled server-side.
🔔 Notifications System
v2.0.0 Latest
  • First release. Three surfaces (toasts, chat lines and a centre banner) with seven templates you can move, restyle and duplicate.
  • Five things announced with no code: a friend joining, a Robux purchase, your rotating announcements, and joins and leaves.
  • A template carries its own behaviour as attributes, so duplicating one in the Explorer produces a working new one.
  • Every other pack lights up when this is installed and goes quiet without it, because none of them declares it as a dependency.
🎨 General UI Kit
v2.0.0 Latest
  • Converted to a self-contained v2 pack: the shared UI runtime now ships embedded, installed and managed entirely by the new plugin — no separate Ecosystem Core install.
  • Sound System is now optional (it was hard-required in v1): buttons and panels stay silent without it, everything else still works.
  • Designed panels and nav buttons ship visible and editable in Studio before you press Play, instead of being cloned from a hidden template at runtime.
  • Settings are preserved across updates from the plugin, and carried forward automatically when migrating from a v1 install.
v1.0.12
  • Added EffectTooltipListener.client.lua: handles UI_EffectIcon hover tooltips independently of GUIManager, so effect tooltips keep working when EC's minimal GUIManager replaces GeneralUIKit's during install merge
  • Refactored GUIManager tooltip/icon lookups to use CollectionService tags with a new findTaggedDescendant helper — survives hierarchy changes (wrappers, re-parenting) in consumer games
  • Added GeneralUIKit_ tag prefix: GeneralUIKit_ButtonIcon, GeneralUIKit_ButtonNameLabel, GeneralUIKit_EffectNameLabel, GeneralUIKit_EffectDescriptionLabel — applied to bundled tooltip labels so the refactor works out of the box
  • Added Scripts/init.meta.json with ignoreUnknownInstances: true so Rojo no longer deletes in-place scripts not mirrored to disk
v1.0.11
  • Updated bundled Sound System to latest version
v1.0.10
  • Added ScrollbarScaler LocalScript: scales ScrollBarThickness proportionally to viewport height (reference: 1080p)
  • Applied ZIndex pre-order traversal to all panels
v1.0.9
  • Refactored PanelAnimations to modular folder architecture; loader auto-discovers animations from sibling packs at runtime
  • Added SpinReveal animation as a child ModuleScript
  • Fixed SpinReveal position bug: uses origPos instead of hardcoded Y values (prevented AnchorPoint from appearing to change)
  • Fixed SpinReveal compatibility: works with Frame, not just CanvasGroup
v1.0.0
  • Initial release
🐾 Easy Pet System
v2.0.0 Latest
  • Converted to a self-contained v2 pack: the shared UI runtime now ships embedded, installed and managed entirely by the new plugin — no separate Ecosystem Core install.
  • Designed Pets/Shop panels, toggle buttons, hatch toasts, and cash HUD ship visible and editable in Studio before you press Play, instead of being cloned from a hidden template at runtime.
  • Demo eggs ship placed in Workspace on install — delete them once you've swapped in your own.
  • Settings are preserved across updates from the plugin, and carried forward automatically when migrating from a v1 install. Player pet data is untouched.
v1.0.16
  • Updated bundled Sound System to latest version
v1.0.15
  • Updated Ecosystem Core to the latest version
v1.0.14
  • No public changelog for this version
🔊 Sound System
v2.0.0 Latest
  • Converted to a self-contained v2 pack: the shared runtime now ships embedded, installed and managed entirely by the new plugin — no separate Ecosystem Core install.
  • All SFX/music sounds and the Music Player GUI ship visible and editable in Studio, browsable and replaceable directly in the editor.
  • Becomes the shared audio backend for every other pack, so General UI Kit and Easy Pet System stay audible without any extra setup.
  • Settings are preserved across updates from the plugin, and carried forward automatically when migrating from a v1 install.
v1.0.13
  • Updated Ecosystem Core to the latest version
v1.0.12
  • No public changelog for this version
💾 Datastore & Purchase System
v2.0.0 Latest
  • Converted to a self-contained v2 pack: the shared runtime now ships embedded, installed and managed entirely by the new plugin.
  • Same DataStores, keys, and schema as v1 — existing player data is unaffected by the upgrade.
  • Receipt handling hardened against duplicate purchase processing.
  • Settings are preserved across updates from the plugin, and carried forward automatically when migrating from a v1 install.
v1.0.7
  • Fixed a data-loss race in DataStoreManager:Get: two concurrent loads for the same player could silently replace the cached data table, discarding custom fields written between them
  • Manager:Get now deduplicates in-flight loads — all concurrent callers receive the same canonical table
v1.0.6
  • Updated Ecosystem Core to the latest version
v1.0.5
  • No public changelog for this version
🏆 Easy Leaderstats & Leaderboards System
v2.0.0 Latest
  • Converted to a self-contained v2 pack: the shared runtime now ships embedded, installed and managed entirely by the new plugin.
  • Demo Cash and Wins leaderboards ship placed in Workspace on install, ready to use or delete.
  • Settings are preserved across updates from the plugin, and carried forward automatically when migrating from a v1 install. Your placed boards and their OrderedDataStore standings are untouched.
v1.0.4
  • Multiple leaderboard templates: templates stored in ServerStorage/.../EasyLeaderstatsSystem/Templates/; each leaderboard entry records which template it uses
v1.0.3
  • Initial release: Leaderstats + Global Leaderboards with plugin-managed configuration
  • Workspace-driven leaderboard discovery via ELS_LeaderboardId GUID attribute
  • ValueFormat per board: Raw, Comma, Abbreviated
  • Dummy avatar system for top-3 players with emote animations
  • Immediate board refresh on server start; stat writes on join, leave, and every 5 minutes