I've transferred this answer over to a gist, if you'd like to share this answer with someone else, direct them here.
What are data stores?
Data stores are a service offered to developers which allow them to save data and fetch it from any server in the universe (game).
How are data stores structured?
Data stores are structured similarly to dictionaries/records. They consist of key-value pairs. Each key is unique and acts like a "header" or "identifier", if you like, and can hold a value that can be converted or serialised into JSON. This means that you cannot explicitly set a key's value to a userdata (which includes but is not limited to Instances), and can only save primitive types (numbers, booleans, strings and tables containing only these types).
Example
- PlayerDataStore
98401192
| {Currency = 100, IsAdmin = false}
46383457
| {Currency = 238, IsAdmin = true}
The above example is a representation of a data store that holds a collection of key-value pairs which hold player data. The key should be a unique identifier relevant to the corresponding player, hence you can use the player's UserId
.
Making requests to a data store
Getting Data
You can fetch data from a data store using the GetAsync
method. It takes a string as it's key parameter, which will be used as a unique identifier for your data.
API
Method
GetAsync(string Key)
Sample call
1 | local PlayerDataStore = game:GetService( "DataStore" ):GetDataStore( "PlayerDataStore" ) |
3 | game.Players.PlayerAdded:Connect( function (player) |
4 | local playerData = PlayerDataStore:GetAsync(player.UserId) |
Setting Data
You can set a key's value using the SetAsync
method. It takes the key whose value will be set, and a primitive value as the arguments.
API
There are other member functions available to us which allow us to "Update" a data store field, but these are rarely needed and you can find them on the wiki here.
Error handling
Data store requests are prone to errors, and to have good design you must be able to handle errors effectively whenever they happen. Something as simple as wrapping the request around a pcall()
and repeating the request after an arbitrary interval should do the trick.
Throttling
Data stores are assigned a request budget. If the number of requests made exceed this budget, the data store will throttle.
Sample code
01 | local DataStoreService = game:GetService( "DataStoreService" ) |
02 | local Players = game:GetService( "Players" ) |
04 | local PlayerDataStore = DataStoreService:GetDataStore( "PlayerDataStore" ) |
07 | local RETRY_INTERVAL = 5 |
09 | local function GetPlayerData(userId) |
10 | local success, e = pcall (PlayerDataStore.GetAsync, PlayerDataStore, userId) |
13 | if success then return e end |
16 | while DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.GetAsync) < 1 do |
18 | warn( "Data store budget depleted" ) |
21 | local currentRetries = 0 |
26 | currentRetries = currentRetries + 1 |
28 | success, e = pcall (PlayerDataStore.GetAsync, PlayerDataStore, userId) |
33 | until currentRetries = = RETRY_LIMIT |
36 | Players.PlayerAdded:Connect( function (player) |
37 | local playerData = GetPlayerData(player.UserId) |
Good design
You've probably found youtube videos that recommend you assign multiple keys to one specific value, even the wiki is guilty of this:
1 | DataStore:SetAsync(player.UserId .. "_cash" , player.Cash.Value) |
It's simply bad design, you should be saving one dictionary per player, which contains all of the player's data. This reduces the total number of requests made, and consequently reduces the risk of throttling. This method also allows for scalability.
Here's a template or schema (blueprint of a data store) that one may use to save player data:
01 | local PlayerDataSchema = { |
07 | AdminStatus = "Normal" , |
08 | ModerationHistory = { } , |
09 | ModerationStatus = "None" |
If you're familiar with OOP you could also save an object, do whatever is intuitive for you.
Closing remarks
Remember to maintain good error handling, scalability and always design your data stores intuitively. If you're game is in production and your data stores are designed poorly, you may have issues later down the line.