Loading...
Loading...
Use when a Roblox game saves or loads player data, or when players report data not saving, lost progress, rollbacks, duplicated items or a purchase granted twice. Triggers: DataStoreService, UpdateAsync, SetAsync, BindToClose, ProfileStore, ProfileService, DataStore2, session locking, data schema or migration, Developer Product ProcessReceipt, player trading, OrderedDataStore leaderboards, MemoryStore, MessagingService, TeleportService and reserved servers, DataStore budgets and throttling (errors 301 to 306), 'You must publish this place to the web to access DataStore', and testing data code in an unpublished place.
npx skill4agent add scenario-labs/skills scenario-roblox-dataskill_getUpdateAsyncSetAsyncUseCache = falseKeyQueuePurchaseIdPurchaseGrantedLastSavedDataNotProcessedYetReconcileReconcileSchemaVersionSchemaVersionDataStoreServiceMockDataStoreProfileStore.MockOrderedDataStorerbx_data.budgetPlayerDataPlayer_<UserId>SchemaVersionPurchaseIdsscripts/agentkit/data/rbx_data.run(studio, code, dm, data=[...])rbx_data.build_place(dir)rbx_data.probe_services(studio, "Edit")"Server"DataCheck.report(template)DataCheck.key(key, {userId})rbx_data.budget(players, ccu, kb, saves_per_min)okPlayerDataStartSessionAsyncCancelAddUserIdReconcileOnSessionEndEndSessionsessionsDataStoreStatelocal Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ProfileStore = require(ServerStorage.Packages.ProfileStore)
local PlayerData = require(ServerStorage.DataKit.PlayerData)
local Migrate = require(ServerStorage.DataKit.Migrate)
local data = PlayerData.new({
ProfileStore = ProfileStore,
Players = Players,
storeName = "PlayerData",
template = { SchemaVersion = 2, Coins = 0, Inventory = {}, PurchaseIds = {} },
migrate = Migrate.new({
latest = 2,
steps = {
-- v1 kept owned items as an array of names; v2 counts them by id.
[2] = function(d: any)
d.Inventory = d.Inventory or {}
for _, item in ipairs(d.Items or {}) do
d.Inventory[item] = (d.Inventory[item] or 0) + 1
end
d.Items = nil
end,
},
}),
})
data:Start()Migrate.new{latest, steps}schemaPurchaseIdReceipts.new{getProfile, isPresent, handlers}ProcessReceiptreceiptsKeyQueue.new(store)verifykeyqueueTrade.paidTradingAllowedIsPaidItemTradingAllowedTrade.new{log = KeyQueue.new(tradeLogStore)}trade:Recover(profile)onLoadedtradeSafeTeleport.new{TeleportService}TeleportPartyteleportDataAudit.place(game)rbx_data.py audit place.rbxlPlayerData:RestoreVersionQueryOnLastSaverbx_data.rtbf_configrbx_run| Value | Relative to |
|---|---|
| Server read or write: 60 + 40 x players per minute | Per server, default, tunable |
| Experience read 300 + 40 x CCU, write 300 + 20 x CCU per minute | Whole experience, shared with Open Cloud |
| Both budgets |
| Per key: write 4 MB per minute, read 25 MB per minute | Rounded up to whole KB per request |
| Value 4,194,304 characters; key, name, scope 50 | Per key |
| Queue of 30 per request type, then errors 301 to 306 | Per server |
| GetAsync cache 4 s | Per data store instance |
| One version kept per key per UTC hour, 30 days after overwrite | Per key |
| Storage 500 MB + 1 MB x lifetime users | Compressed latest versions |
| ProfileStore autosave 300 s, steal 40 s, assume dead 630 s | Pinned source; assume dead compares |
| ProfileStore first retry 5 s, then every 10 s | While another server holds the lock |
| Profile load over 5 s | Treat as a bug (loleris) |
| BindToClose budget 30 s | Per server shutdown |
| MessagingService message 1 kB | Per publish |
run_offline.shrun_live.pyDataAudit| Mistake | Symptom | Fix |
|---|---|---|
| Rollbacks, duplicated items after server hops | ProfileStore through |
Key from | Data lost after a rename | |
| Old fields stranded, new fields empty | Migrate first |
| Retry by recalling the same write | Counters too high | Op id in the value, |
| Paid but not granted after a crash | |
| Handler that yields | Grant applied twice | Handlers run on a copy and must not yield |
| Two profile saves as a trade | Items duplicated or lost | |
| Script errors at startup in Studio | Probe inside pcall, then fall back to the mock |
| Awaiting MessagingService in an unpublished playtest | Thread hangs | Call it in |
Retrying a teleport with | Party split across servers | Reserve once, reuse the access code |
| Editing a ModuleScript's Source in Edit, then requiring it again | The old code keeps running | Replace the module with a Clone first |
ReceiptsTradeHttpService:JSONEncodeReserveServerAsyncReserveServer