Robust DataStore: Player Save with Retry and Security
This prompt creates a complete Luau server Script for robust persistence of Roblox player data. The solution uses DataStoreService with UpdateAsync, automatic retries with exponential backoff, protection against invalid data, safe loading, staggered autosave, and saving when the server shuts down.
Ideal for developers who need to save coins, level, XP, serializable inventory, settings, and progression without trusting the client. The prompt also asks for adaptation to the Explorer and the existing RemoteEvents in the project, keeping the server as the authority over any persistent data.
Act as a senior Roblox Luau developer, specialized in secure, scalable, and resilient persistence systems using DataStoreService. Generate a complete system for robust saving of player data, adapted to the context of my game that I will provide below. The main result must be a single **Script** on the server, to be placed in **ServerScriptService** (for example, `PlayerDataService.server.lua`). Do not generate a LocalScript for persistence, and do not allow the client to decide, send, or directly overwrite currency, XP, inventory, damage, level, or any saved data. The server must be fully authoritative. If my context mentions RemoteEvents or RemoteFunctions, treat them only as request channels: strictly validate player, type, limits, permissions, and server state before changing data. If they are not needed for saving, do not create unnecessary remotes. Before writing the code, analyze the context that I will fill in. If any essential information is missing, adopt safe conventions and leave objective notes at the start of the response, without blocking delivery of the script. Preserve compatibility with existing objects and names when I provide them. ### CONTEXT OF MY GAME (I will fill in) - Game/system name: - Relevant objects and paths in the Explorer: - Data structure that must be saved (e.g., Coins, Gems, Level, XP, Inventory): - Where these values exist during the session (Attributes, leaderstats, Folder/ValueObjects, table, etc.): - Default values for a new player: - Existing RemoteEvents/RemoteFunctions and their purposes: - Progression/economy rules the server must respect: - Desired name for the key/DataStore (optional): - Restrictions or existing systems that cannot be changed: Implement a professional system using `DataStoreService:GetDataStore()` and `UpdateAsync()` to minimize the risk of overwriting between servers. Use one key per player based on `Player.UserId`, with a configurable prefix/versioning. Structure the data in a serializable table, without Instances, functions, userdata, or values not accepted by DataStore. Include `schemaVersion`, default values, and a normalization/migration function prepared for future versions. The script must: load data in `Players.PlayerAdded`; validate the return from `GetAsync`/`UpdateAsync`; distinguish a new player from a loading failure; prevent changes and unsafe saving when the session is not marked as loaded; keep a data cache by UserId on the server; apply loaded values to the objects/Attributes indicated in my context; and read the current values back into a sanitized table before saving. Never accept arbitrary inventory tables from the client. Validate numbers with reasonable limits, rounding when applicable, allowed item types, and maximum sizes for collections/strings to avoid corrupted or excessive data. Create a reusable save function with protection against concurrent calls per player. It must use retry with a configurable number of attempts, exponential waiting with a small jitter, `pcall`, useful warning messages, and a success/failure return. Do not make aggressive loops or unnecessary DataStore calls. Include periodic autosave with configurable interval and gradual distribution of operations to reduce budget spikes. Also save in `PlayerRemoving` and in `game:BindToClose()`, explaining in comments that shutdown is not an absolute guarantee and that autosave is essential. Avoid saving a player who was not loaded successfully or who is already being saved, except when a safe queue/pending strategy is necessary. Use current Luau APIs and syntax, `task.wait`, `task.spawn` when appropriate, and comments in Portuguese explaining important decisions. Do not use `wait()`, do not use nonexistent `DataStoreIncrementOptions`, do not use deprecated APIs, and do not invent Roblox properties. Handle errors without exposing private information to the client. If loading fails repeatedly, choose a conservative policy and document it in the code, such as preventing the player from entering with a clear message or keeping the session without persistence, justifying the choice. Deliver the response in this order: 1) a brief list of the assumptions adopted; 2) the complete Luau code, ready to paste, mandatorily inside a single markdown block ` ```lua `; 3) practical instructions for installing it in the Explorer, configuring the fields at the top of the script, and testing it in Roblox Studio. In the test instructions, include how to publish a test experience, enable **Enable Studio Access to API Services** only in the appropriate environment, test player join/leave, simulate temporary failures, and verify persisted data. Do not deliver pseudocode, incomplete snippets, external dependencies, or client code for saving data.