ai-behavior-trees-util…
Build a production behavior-tree runtime (Blackboard, action/condition leaves, sequence/selector/parallel composites, decorators) and a Utility AI system…
Persist player data in Roblox with DataStoreService: GetDataStore, GetAsync/ SetAsync/UpdateAsync/IncrementAsync wrapped in pcall, load-on-join and save-on-leave plus BindToClose, retries, and OrderedDataStore leaderboards. Use when saving or loading persistent data in a Roblox
$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill roblox-datastores --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/roblox-datastoresContext preview
The summary Claude sees to decide when to auto-load this skill.
Persist player data in Roblox with DataStoreService: GetDataStore, GetAsync/ SetAsync/UpdateAsync/IncrementAsync wrapped in pcall, load-on-join and save-on-leave plus BindToClose, retries, and OrderedDataStore leaderboards. Use when saving or loading persistent data in a Roblox
name: roblox-datastores description: > Persist player data in Roblox with DataStoreService: GetDataStore, GetAsync/ SetAsync/UpdateAsync/IncrementAsync wrapped in pcall, load-on-join and save-on-leave plus BindToClose, retries, and OrderedDataStore leaderboards. Use when saving or loading persistent data in a Roblox experience — when the user mentions DataStore, DataStoreService, GetAsync, SetAsync, UpdateAsync, save player data, or leaderboards. For general Luau scripting use roblox-luau.
Persist data across sessions in Roblox with `DataStoreService`: loading on join, saving on leave and shutdown, safe updates, retries, and ordered stores for leaderboards. Server-side only.
leaderboards, or fix data loss, overwrites, and throttling.
`SetAsync`, `UpdateAsync`, or `GetOrderedDataStore`.
**When *not* to use:** general scripting, services, remotes, the client/server split → `roblox-luau`. High-frequency temporary state (matchmaking, per-round) → memory stores (a different service). Engine-agnostic persistence theory → `save-systems`.
1. **Enable Studio access once.** File → Game Settings → Security → *Enable Studio Access to API Services* (use a test place; Studio hits live data). DataStores work only from server `Script`s, never `LocalScript`s. 2. **Get a store, then read/write by key.** `DataStoreService:GetDataStore("Name")`; key per player is usually `"Player_" .. player.UserId`. 3. **Wrap every call in `pcall`.** `GetAsync`/`SetAsync`/`UpdateAsync` are network calls that can fail; an unguarded failure errors the thread and risks data loss. 4. **Load on `PlayerAdded`, save on `PlayerRemoving`, and also `BindToClose`.** A leaving player and a shutting-down server both need a final save. 5. **Prefer `UpdateAsync` for read-modify-write** (multi-server safe) over `SetAsync` (blind overwrite). On a failed load, do **not** overwrite with defaults — abort the save so you don't wipe good data. 6. **Use `OrderedDataStore` for ranked data** (leaderboards) via `GetSortedAsync`. Test by joining, changing data, rejoining, and confirming it persisted.
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local store = DataStoreService:GetDataStore("PlayerData")
local DEFAULT = { Coins = 0, Level = 1 }
Players.PlayerAdded:Connect(function(player)
local key = "Player_" .. player.UserId
local ok, data = pcall(function()
return store:GetAsync(key)
end)
if not ok then
-- Load FAILED (network). Do not treat as a new player; flag so we never save
-- over their real data with defaults.
warn("Load failed for", player.Name, data)
player:SetAttribute("DataLoaded", false)
return
end
player:SetAttribute("DataLoaded", true)
local profile = data or DEFAULT -- nil == genuinely new player
applyToLeaderstats(player, profile)
end)-- UpdateAsync reads the latest value, then writes what the callback returns.
-- The callback MUST NOT yield (no task.wait, no further Async calls inside it).
local function savePlayer(player)
if player:GetAttribute("DataLoaded") == false then return end -- never overwrite on a bad load
local key = "Player_" .. player.UserId
local newData = gatherDataFor(player) -- a plain table of serializable values
local ok, err = pcall(function()
store:UpdateAsync(key, function(old)
-- merge/decide here; return nil to cancel the write
return newData
end)
end)
if not ok then warn("Save failed for", player.Name, err) end
endPlayers.PlayerRemoving:Connect(savePlayer)
-- BindToClose runs when the server shuts down; save everyone still in.
-- It has a limited time budget, so save in parallel and yield until done.
game:BindToClose(function()
local players = Players:GetPlayers()
local remaining = #players
if remaining == 0 then return end
for _, player in players do
task.spawn(function()
savePlayer(player)
remaining -= 1
end)
end
while remaining > 0 do task.wait() end
end)local function withRetry(fn, attempts)
attempts = attempts or 3
for i = 1, attempts do
local ok, result = pcall(fn)
if ok then return true, result end
if i < attempts then task.wait(2 ^ i) end -- 2s, 4s, ... backoff
end
return false
end
local ok, data = withRetry(function() return store:GetAsync(key) end)-- IncrementAsync is a convenience for integer read-modify-write (still wrap it).
local ok, newTotal = pcall(function()
return store:IncrementAsync("Visits_" .. player.UserId, 1)
end)local boards = DataStoreService:GetOrderedDataStore("Coins")
-- Write a player's score (call when it changes, not every frame).
pcall(function() boards:SetAsync("Player_" .. player.UserId, coins) end)
-- Read the top 10, descending.
local ok, pages = pcall(function()
return boards:GetSortedAsync(false, 10) -- ascending=false → highest first
end)
if ok then
for rank, entry in ipairs(pages:GetCurrentPage()) do
print(rank, entry.key, entry.value) -- entry.value is the number
end
end*load*, mark the session and refuse to *save* so defaults never overwrite real data.
<img src="docs/assets/banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.
Repo: gamedev-skills/awesome-gamedev-agent-skills
Build a production behavior-tree runtime (Blackboard, action/condition leaves, sequence/selector/parallel composites, decorators) and a Utility AI system…
Implement game audio practice — bus/mixer architecture and gain in decibels, ducking (sidechain), adaptive/dynamic music via layering and re-sequencing, SFX…
Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and level-bounds clamping; 3D third-person orbit with collision and…
Plan, generate, source, normalize, and validate cohesive visual game assets. Use for art direction, style bibles, sprites, tilesets, backgrounds, UI art,…
Build branching dialogue and narrative — a node/choice graph with conditions, variables, and localization hooks — and choose between authoring tools Ink and…
Design NPC and enemy decision-making with finite state machines, behavior trees, steering behaviors, and A* pathfinding — engine-neutral algorithms that pair…