{"id":"lucasx150/simpleleaderboard","name":"simpleleaderboard","scope":"lucasx150","platform":"roblox","description":"Mirrored from the Wally registry.","version":"0.1.1","latest":"0.1.1","versions":["0.1.0","0.1.1"],"license":"MIT","licenseRating":"safe","licenseCaveats":["The package archive does not include its license text; the license is declared in its manifest metadata."],"licenseVerified":false,"dependencies":{},"integrity":"7eefdea33482f041a9294c4139b1ee0788e5616c730e2ba361ef7bff79200848","likes":0,"downloads":0,"install":"forest install lucasx150/simpleleaderboard","url":"https://forest.dev/p/roblox/lucasx150/simpleleaderboard","files":"https://api.forest.dev/ai/package/roblox/lucasx150/simpleleaderboard/files","readme":"# SimpleLeaderboard\n\nGlobal leaderboards - minimal setup required, but fully customizable if needed.\n\n## Usage\n\n```lua\nlocal SimpleLeaderboard = require(ReplicatedStorage.Packages.SimpleLeaderboard)\n\nSimpleLeaderboard.new({\n\tName = \"Cash\",\n\tGetStat = function(player)\n\t\treturn MyDataLayer.Get(player, \"Cash\")\n\tend,\n\tRenderer = SimpleLeaderboard.BoardRenderer.new({\n\t\tContainer = leaderboardGui.Frame.ScrollingFrame,\n\t\tTemplate = ReplicatedStorage.Templates.LeaderboardEntry,\n\t}),\n})\n```\n\nThe board starts automatically, saving online players' `Cash`\nevery `SaveInterval` seconds and refreshing the UI every `UpdateInterval`\nseconds.\n\n## `SimpleLeaderboard.new(config: BoardConfig): Board`\n\n| Field | Type | Default | Notes |\n|---|---|---|---|\n| `Name` | `string` | required | Used as the `OrderedDataStore` name unless `OrderedStore` is given |\n| `GetStat` | `(Player) -> number?` | required | Reads the current stat value for a player |\n| `Renderer` | `(entries: {LeaderboardEntry}) -> ()` | none | Updates the Leaderboard UI. Build one with `SimpleLeaderboard.BoardRenderer.new(...)`, or pass your own function. Omit it for a save-only board - no UI update loop runs |\n| `SaveCondition` | `(value: number) -> boolean` | always save | If `false`, the value will not be saved to the `OrderedDataStore` |\n| `Serializer` | `(value: any) -> number` | identity | Applied before writing to `OrderedDataStore` |\n| `Deserializer` | `(value: number) -> any` | identity | Inverse of `Serializer`, applied when reading entries back |\n| `PageSize` | `number` | `25` | OrderedDataStore page size |\n| `SaveInterval` | `number` (seconds) | `720` | How often players' stats are saved |\n| `UpdateInterval` | `number` (seconds) | `540` | How often the UI is refreshed |\n| `OrderedStore` | `OrderedDataStore` | `Based on Name` | |\n\n### Methods\n\n- `Board:Start()` - begins the save loop and the UI update loop. Boards start automatically by default.\n- `Board:Stop()` - halts both loops; safe to `Start()` again later.\n- `Board:Destroy()` - stops and permanently disables the board.\n\n## `SimpleLeaderboard.BoardRenderer.new(config: RendererConfig): Renderer`\n\nBuilds a default renderer for a `ScrollingFrame` of cloned template entries.\n\n| Field | Type | Default | Notes |\n|---|---|---|---|\n| `Container` | `ScrollingFrame` | required | |\n| `Template` | `GuiObject` | required unless a default is set | Needs `Title`/`Rank`/`Value` descendants for the default `GuiFormatter` |\n| `GuiFormatter` | `(GuiObject, entry: LeaderboardEntry) -> ()` | fills `Title`/`Rank`/`Value`, top-3 gold/green/teal highlight | Fills in a player's leaderboard entry - both its text and its appearance. The deserialized value is `entry.Value` |\n\n- `BoardRenderer.SetDefaultTemplate(template: GuiObject)` - sets a fallback `Template` so it doesn't need to be passed to every `RendererConfig`.\n- `BoardRenderer.SetDefaultGuiFormatter(formatter: GuiFormatter)` - same, for `GuiFormatter`.\n- `BoardRenderer.DefaultGuiFormatter` - the built-in formatter, so a custom one can call it and then adjust.\n\n## `SimpleLeaderboard.Serialize`\n\nPairs of `(Serializer, Deserializer)` for stats that don't fit directly into an `OrderedDataStore`'s integer storage:\n\n- `Serialize.Log(precision: number?)` - stores via `log10`, for stats that can exceed `2^63` (e.g. cash). Lossy past `precision` digits.\n- `Serialize.Negatives(bias: number?)` - shifts negative-capable stats into non-negative range.\n- `Serialize.Divide(factor: number?)` - divides by a fixed factor, for stats with more precision than integer storage allows (e.g. thousandths).\n\n## `SimpleLeaderboard.StatSource`\n\nA helper for data layers that store multiple stats under one general accessor\n(e.g. ProfileStore-style `profile.Data[key]`). Wrap it once and get a\n`GetStat` function per key, instead of writing a closure for every board.\n\n```lua\nlocal stats = SimpleLeaderboard.StatSource.new(function(player, key)\n\tlocal profile = ProfileStore:GetProfile(player)\n\treturn profile and profile.Data[key]\nend)\n\nSimpleLeaderboard.new({\n\tName = \"Cash\",\n\tGetStat = stats:GetStat(\"Cash\"),\n\t...\n})\n\nSimpleLeaderboard.new({\n\tName = \"Rebirths\",\n\tGetStat = stats:GetStat(\"Rebirths\"),\n\t...\n})\n```\n\n- `StatSource.new(source: (Player, key: string) -> number?): StatSource`\n- `StatSource:GetStat(key: string): StatGetter` - returns a `(Player) -> number?` bound to `key`, for use as a `BoardConfig.GetStat`.\n","readmeTruncated":false}