Ledger
The top level module.
local Ledger = require(ServerStorage.Ledger)Ledger.New
Ledger.New<D>(Options: Config<D>) -> Store<D>Builds a store. Throws on anything wrong with the options, at build time, rather than letting it misbehave later.
| Option | Type | |
|---|---|---|
Name | string | Required. The datastore name, 1 to 47 characters. |
Reducer | (State, Op) -> State? | Required. See Writing a reducer. |
Default | D | Required. The fresh state table. |
Balance | string? | Names a number field for transfers. |
Migrations | { Migration }? | See Migrations. |
Keys | "Player" | "String" | Defaults to "Player". |
OnLoadFailed | ((Player, Reason) -> boolean)? | What to do when a load fails. See below. |
The name caps at 47 rather than the datastore's 50 because Ledger also creates <Name>_Tx for
transaction markers.
Default can't declare any key starting with _, and it has to be storable: numbers, strings,
booleans, tables and buffers.
Two stores can't share a name in one server. Call Store:Destroy() first if you need to rebuild
one.
OnLoadFailed
A load that fails kicks the player with "Your data failed to load, please rejoin". OnLoadFailed
takes that decision instead. It gets the player and the reason, and returns
whether it dealt with them. true and Ledger leaves them alone, false and it kicks.
local Store = Ledger.New({
Name = "PlayerData",
Reducer = Reducer,
Default = { Gold = 0 },
OnLoadFailed = function(Player: Player, Why: Ledger.Reason): boolean
if Why == Ledger.Reason.Behind then
-- this server is running an old build, send them to one that isn't
TeleportService:Teleport(game.PlaceId, Player)
return true
end
return false
end
})Behind means a newer build wrote the record, so a rejoin puts them on the same old server and fails
again. Unresolved is usually a datastore outage, where a rejoin is the right answer.
It can yield, so a teleport works. Anything waiting in WaitForLoaded is released before it runs.
If it throws, Ledger warns and kicks. If it returns true and the player is still in the server with
no data, Ledger warns about that too.
Only for Keys = "Player" stores. Passing it on a Keys = "String" store throws at build time.
Ledger.Reason
The ten reasons, as constants. See Reasons.
Ledger.Reason.Refused
Ledger.Reason.Busy
Ledger.Reason.Spent
Ledger.Reason.Held
Ledger.Reason.Unresolved
Ledger.Reason.Closed
Ledger.Reason.Backlog
Ledger.Reason.Full
Ledger.Reason.Invalid
Ledger.Reason.BehindLedger.UseMock
Ledger.UseMock(Options: { Players: number?, CCU: number?, Throttled: boolean? }?) -> ServicePoints every store at an in memory datastore, including ones you built before the call.
Call it one time. Each call hands back a fresh empty service, and every store moves onto it, so a second call anywhere in your game makes everything written up to that point unreadable. Ledger warns if it catches you doing it while stores are live.
| Option | ||
|---|---|---|
Players | number? | How many on this server. Defaults to 0. Sizes the per server budget. |
CCU | number? | How many in the whole experience. Defaults to Players. Sizes the experience budget. |
Throttled | boolean? | Defaults to true. |
Both budgets apply at the same time. The smaller one stops you first, the same as a real server. See Testing.
Ledger.UseReal
Ledger.UseReal() -> ()Back to the real DataStoreService. Only affects stores built after the call.
Ledger.UseClock
Ledger.UseClock(Reads: (() -> number)?, Sleeps: ((Seconds: number) -> ())?) -> ()Swaps the function Ledger reads the time from. Pass nil to go back to os.time.
Sleeps swaps the wait Ledger polls on, and defaults to task.wait. There is one poll, in
Session:CommitOp, where a commit waits on a transaction another server
parked on the key. Its deadline is read off your clock.
Ledger.Sweep
Ledger.Sweep() -> ()Forces a background maintenance pass right now: finish stranded transfers, settle stuck transaction legs, tidy up old markers. The sweeper does this on its own, so this is for tests and incidents.
Ledger.CloseAll
Ledger.CloseAll() -> ()Shuts everything down. Stops the sweeper, tells every queue to skip ahead to the last write, saves every live session, and yields until it's all done.
Put this in BindToClose and nothing else.
game:BindToClose(function()
Ledger.CloseAll()
end)Exported types
Ledger.Store<D>
Ledger.Session<S>
Ledger.Op
Ledger.Reducer<S>
Ledger.Reason
Ledger.KeyLike
Ledger.KeysMode
Ledger.TxLeg
Ledger.HoldOptions
Ledger.Migration
Ledger.Config<D>
Ledger.HistoryEntrySee Types.