Skip to content

Keep

Keep is Cluaupp player data: ProfileStore session locking, tagged replication buffers, path tokens, transient overlays, ordered lists, and trade escrow. Server code uses Keep.Server; clients use Keep.Client. Low-level DataStore access uses Keep.Store; player-to-player trades use Keep.Trade. Paths are generated into Keep.Paths / Keep.Server.Paths from your template.

Header: #include <clpp/libs/keep.clh>. Runtime: CluauppLibs.Keep.

  • Session-safe saves with mock mode, key prefixes, and buffer stats for tuning replication.
  • Merged reads (Get) vs persisted-only (GetPersisted) vs session-only transient layers.
  • Signals per path for UI (Gleam) and gameplay without polling.

When not to use: global non-player state (use Keep.Store directly or a custom store), or cross-place inventory without understanding ProfileStore limits.

#include <clpp/roblox.clh>
#include <clpp/libs/keep.clh>
[[server]]
void initServer() {
Keep.Server.Init(DataServiceOptions {
.Template = myDefaultData,
.StoreName = "PlayerData_v1",
});
Data data = Keep.Server.WaitFor(player);
data.Set(Keep.Server.Paths.Currencies.Coins, 100);
}
[[client]]
void initClient() {
Keep.Client.Init();
Data data = Keep.Client.WaitForData();
int coins = data.Get(Keep.Client.Paths.Currencies.Coins); // replicated value
}

Returns: DataServiceServer — server-only API (Init, WaitFor, Get, …). Unavailable stubs on client builds.

When: Authoritative data in [[server]] scripts.

Example

Keep.Server.Init(opts);

Returns: DataServiceClient — client API (Init, WaitForData, Get, …).

When: Local player data mirror in [[client]] scripts.

Example

Keep.Client.Init();

Returns: DataPath tree — path tokens for the active context (server or client compile).

When: Typed Get / Set without string paths.

Example

DataPath p = Keep.Paths.Currencies.Coins;

Returns: DataServiceEnum — e.g. Keep.Enum.OrderList.Asc / Desc for ordered lists.

When: GetOrderedList options.

Example

string asc = Keep.Enum.OrderList.Asc;

Returns: KeepStore module — ProfileStore-style StartSessionAsync, globals, messaging.

When: Non-player keys, tooling, or custom session logic.

Example

KeepStore store = KeepStore.New("Meta", tmpl);

Returns: KeepTrade — escrow Begin / Reserve / Commit / Abort / SendOffline. Reserve deducts into a per-trade vault; Commit credits with receipts and SaveWaits both profiles (rollback if either save fails).

When: Atomic trades between two loaded profiles.

Example

string id = Keep.Trade.Begin(profileA, profileB);

Returns: DataPath tree for the server template.

When: Same as Keep.Server.Paths after Init.

Example

DataPath coins = Keep.Server.Paths.Currencies.Coins;

Returns: DataServiceServerthis after starting replication and store binding.

When: Once per server experience with DataServiceOptions.

Example

Keep.Server.Init(DataServiceOptions { .Template = tmpl, .StoreName = "v1" });

Returns: Data — blocks until the player’s profile is loaded and replicated.

When: Join handlers that must not race empty data.

Example

Data data = Keep.Server.WaitFor(player);

Returns: Data or Luau nil if no session.

When: Non-yielding lookup after you know data exists.

Example

Data data = Keep.Server.Get(player);

Returns: booltrue when a live Data object exists for the player.

When: Guards before Get.

Example

bool ok = Keep.Server.HasData(player);

Returns: DataProfile — underlying store profile (save, release, trade APIs).

When: Keep.Trade or direct Profile:Save().

Example

DataProfile profile = Keep.Server.GetProfile(player);

Returns: DataBufferStats — aggregate replication metrics.

When: Server-wide bandwidth tuning.

Example

DataBufferStats s = Keep.Server.GetBufferStats();

DataServiceServer::GetBufferStats (player)

Section titled “DataServiceServer::GetBufferStats (player)”

Returns: DataBufferStats — per-player replication metrics.

When: Diagnosing one heavy client.

Example

DataBufferStats s = Keep.Server.GetBufferStats(player);

Returns: Luau nil. Tears down the server data service.

When: Test shutdown or place cleanup.

Example

Keep.Server.Destroy();

Returns: DataPath tree for the client template.

When: Client Get / signals.

Example

auto path = Keep.Client.Paths.Currencies.Coins;

Returns: Data — local player data facade after starting client replication.

When: Once in client bootstrap (often before WaitForData).

Example

Data data = Keep.Client.Init();

Returns: Data — yields until the first full snapshot arrives.

When: HUD and input that need non-nil paths.

Example

Data data = Keep.Client.WaitForData();

Returns: Data or nil if not ready.

When: Polling without yield.

Example

Data data = Keep.Client.Get();

Returns: DataBufferStats — client receive stats.

When: Debugging replication volume locally.

Example

DataBufferStats s = Keep.Client.GetBufferStats();

Returns: Luau nil. Stops client data service.

When: Teardown.

Example

Keep.Client.Destroy();

Returns: RBXScriptSignal — fires on any persisted/merged change (runtime Spark-backed).

When: Whole-profile UI refresh.

Example

data.Changed~>Connect(func () { refresh(); });

Returns: bool field — true after Destroy.

When: Ignoring signals after release.

Example

if (!data.Destroyed) { /* use data */ }

Returns: auto — merged view (persisted + transient) at optional path, or root table when omitted.

When: Gameplay reads that should see session overlays.

Example

int coins = data.Get(Keep.Client.Paths.Currencies.Coins); // 100

Returns: auto — value at DataPath path.

When: Typed path tokens.

Example

auto v = data.Get(path);

Returns: auto — saved ProfileStore slice only.

When: Auditing what will flush to DataStore.

Example

int saved = data.GetPersisted(path);

Returns: auto at path.

When: Persisted subtree.

Example

auto v = data.GetPersisted(path);

Returns: auto — transient overlay value or nil.

When: Buffs, round-only state not in save template.

Example

auto buff = data.GetTransient(path);

Returns: auto at path.

When: Addressed transient reads.

Example

auto t = data.GetTransient(path);

Returns: bool — any transient layer present (root or subtree).

When: Branching UI for temporary stats.

Example

bool has = data.HasTransient();

Returns: bool at path.

When: Specific transient key.

Example

bool has = data.HasTransient(path);

Returns: Luau nil. Writes persisted path (replicates, saves).

When: Authoritative progression on server.

Example

data.Set(path, 50); // coins become 50

Returns: Luau nil. Overlay only until cleared or session ends.

When: Round modifiers.

Example

data.SetTransient(path, 2);

Returns: auto — new value after fn(old) (persisted).

When: Atomic read-modify-write.

Example

data.Update(path, func (int old) { return old + 1; }); // increments

Returns: auto — new transient value after fn.

When: Mutable session-only fields.

Example

data.UpdateTransient(path, func (int old) { return old + 1; });

Returns: Luau nil. Drops all transient overlays.

When: Round end.

Example

data.ClearTransient();

Returns: Luau nil. Clears one transient path.

When: Remove one buff.

Example

data.ClearTransient(path);

Returns: Luau nil. Appends or inserts into persisted array path.

When: Inventory lines, quest lists.

Example

data.ArrayInsert(path, item);

Returns: Luau nil. Inserts at index.

When: Ordered insert.

Example

data.ArrayInsert(path, item, 1);

Returns: Luau nil. Transient array append.

When: Session-only lists.

Example

data.ArrayInsertTransient(path, item);

Data::ArrayInsertTransient (path, value, index)

Section titled “Data::ArrayInsertTransient (path, value, index)”

Returns: Luau nil. Indexed transient insert.

When: Ordered transient arrays.

Example

data.ArrayInsertTransient(path, item, 2);

Returns: auto — removed element from persisted array.

When: Consume stack items.

Example

auto removed = data.ArrayRemove(path, 1);

Returns: auto — removed transient element.

When: Session list edits.

Example

auto removed = data.ArrayRemoveTransient(path, 1);

Returns: auto — ordered entries per OrderedListOptions (key, order, limit).

When: Leaderboards stored in data.

Example

auto rows = data.GetOrderedList(path, OrderedListOptions { .key = "Score", .order = Keep.Enum.OrderList.Desc, .limit = 10 });

Returns: auto — ordered list sorted by key ascending.

When: Priority queues in data.

Example

auto list = data.GetOrderedListWithPriority(path, "Priority");

Data::GetOrderedListWithPriority (path, key, order)

Section titled “Data::GetOrderedListWithPriority (path, key, order)”

Returns: auto — with explicit order string (Asc / Desc).

When: Custom sort direction.

Example

auto list = data.GetOrderedListWithPriority(path, "Priority", Keep.Enum.OrderList.Asc);

Returns: RBXScriptSignal — value changes at path.

When: Binding one stat to UI.

Example

data.GetChangedSignal(path)~>Connect(func (auto newV, auto oldV) {});

Returns: RBXScriptSignal — subtree path changes.

When: Nested tables.

Example

data.GetPathChangedSignal(path)~>Connect(func () {});

Returns: RBXScriptSignal — dictionary key or index updates.

When: Map-like data.

Example

data.GetIndexChangedSignal(path)~>Connect(func (auto key, auto newV, auto oldV) {});

Returns: RBXScriptSignal(index, value) on insert.

When: List UI append animations.

Example

data.GetArrayInsertedSignal(path)~>Connect(func (int i, auto v) {});

Returns: RBXScriptSignal(index, value) on remove.

When: List UI removals.

Example

data.GetArrayRemovedSignal(path)~>Connect(func (int i, auto v) {});

Returns: DataPath — typed path token for IntelliSense.

When: Feeding generics from a base path.

Example

DataPath typed = data.Typed(Keep.Server.Paths.Currencies.Coins);

Returns: Luau nil. Ends session and disconnects listeners.

When: Player leaving custom handlers before engine teardown.

Example

data.Destroy();

Returns: KeepStore — named store with template tmpl for reconciliation.

When: Custom keys outside the player service.

Example

KeepStore store = KeepStore.New("Global", defaultTable);

Returns: Luau nil. Registers a global constant on the store module.

When: Shared configuration values referenced by store logic.

Example

KeepStore.SetConstant("Season", 2);

Returns: DataProfile — loaded session for key (yields).

When: Manual profile lifecycle.

Example

DataProfile profile = store.StartSessionAsync("Player_123");

Returns: auto — raw DataStore value for key (yields).

When: One-off reads without session.

Example

auto raw = store.GetAsync("Player_123");

Returns: booltrue if MessagingService publish succeeded.

When: Cross-server messages (trades, mail).

Example

bool sent = store.MessageAsync(key, payload);

Returns: booltrue if removal succeeded.

When: GDPR wipe or reset key.

Example

bool ok = store.RemoveAsync(key);

Returned from store version listing helpers in the runtime; CL++ type exposes iteration.

Returns: auto — version metadata at index.

When: Random access in a version list.

Example

auto info = query.Get(1);

Returns: auto — array of all version entries collected.

When: Bulk inspection.

Example

auto all = query.List();

Returns: auto — next version entry, or nil when exhausted.

When: Sequential walk.

Example

auto item = query.Next();

Returns: string — trade id, or empty string if either profile already has an active trade.

When: Starting a two-player escrow session.

Example

string tradeId = Keep.Trade.Begin(profileA, profileB); // non-empty guid

Returns: booltrue if amount of kind/ref was deducted from spendable data into the trade vault. A second reserve on the same kind|ref for this trade fails.

When: Locking currencies/items before commit. Spendable Get after Reserve already shows the lower balance, so a second spend cannot duplicate.

Example

bool ok = Keep.Trade.Reserve(profile, "Currency", "Coins", 10, tradeId);

Returns: booltrue if both offer lists moved from escrow with receipt ids and both SaveWaits succeeded. A failed save restores both in-memory snapshots and retries persist so a crash cannot duplicate.

When: Finalizing a trade on one server.

Example

bool done = Keep.Trade.Commit(store, profileA, profileB, offersA, offersB);

Returns: Luau nil. Refunds remaining reserved rows for tradeId and clears ActiveTradeId.

When: Cancel or disconnect mid-trade. EndSession / player leave also call AbortIfActive before the last save.

Example

Keep.Trade.Abort(profile, tradeId);

Returns: Luau nil. Aborts whatever ActiveTradeId is set.

When: Leave handlers; you usually do not call this yourself.

Keep.Trade.AbortIfActive(profile);

Returns: booltrue if trade payload was messaged to offline key.

When: Completing credit when recipient is not in-server.

Example

bool sent = Keep.Trade.SendOffline(store, key, payload);

DataBufferStats: Bytes, Messages, LastPacketBytes, LastUtilization (numbers). OrderedListOptions: key, order, limit. KeepTradeOffer: Kind, Ref, Amount. DataPath is an opaque path token. DataProfile is the session handle: IsActive, Reconcile, AddUserId, Save (async), SaveWait (blocks until the DataStore write finishes), EndSession.