I know what datastores are, and I have a pretty basic understanding of them, but the one thing I have never understood are keys
Your question is pretty vague, however, I will still explain from what I can tell you are asking.
Let's think of a datastore as a place with multiple boxes. Each box is locked, and to access it you need a 'key'.
So, in our game (as an example), we have a different key for every player. This means that each player has their own box to store data in. Let's make the key the player's user id because every player has a unique one.
Now, our player has a place in that datastore to have data stored in or retrieved from. How do we access it? The key required to open the box must have the same name as the box itself, so that way no other player can access another player's data.
In our script, we can access the 'box' and retrieve data by using :GetAsync()
, and set data using :SetAsync()
.
In a script, it would look like this:
local datastoreService = game:GetService("DataStoreService") -- Get the API local datastore = datastoreService:GetDataStore("TestDataStore") -- Can be anything, for this example it is 'TestDataStore' local key = "123456789" -- Just an example, in a real scenario you would want it to be the player's userid datastore:SetAsync(key, 5) -- We have stored the integer '5' into the box '123456789' in our datastore print(datastore:GetAsync(key)) -- This will print '5', because that is the data stored
Here is also a working example of how you can use datastores to save points:
local datastore = game:GetService("DataStoreService"):GetDataStore("PointsStore") game.Players.PlayerAdded:connect(function(player) local leaderstats = Instance.new("Folder", player) leaderstats.Name = "leaderstats" local points = Instance.new("IntValue", leaderstats) points.Name = "Points" local key = player.UserId -- Unique key for each player local data = datastore:GetAsync(key) or 0 -- If there is no data stored, make it 0 points.Value = data end) game.Players.PlayerRemoving:connect(function(player) local key = player.UserId -- Again, unique key datastore:SetAsync(key, player.leaderstats.Points.Value) end)
I've transferred this answer over to a gist, if you'd like to share this answer with someone else, direct them here.
Data stores are a service offered to developers which allow them to save data and fetch it from any server in the universe (game).
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).
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
.
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.
Method
GetAsync(string Key)
Sample call
local PlayerDataStore = game:GetService("DataStore"):GetDataStore("PlayerDataStore") game.Players.PlayerAdded:Connect(function(player) local playerData = PlayerDataStore:GetAsync(player.UserId) end)
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.
Method
SetAsync(string Key, variant Value)
Sample call
local PlayerDataStore = game:GetService("DataStore"):GetDataStore("PlayerDataStore") local ServerPlayerData = {} game.Players.PlayerRemoving:Connect(function(player) PlayerDataStore:SetAsync(player.UserId, ServerPlayerData[player]) end)
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.
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.
Data stores are assigned a request budget. If the number of requests made exceed this budget, the data store will throttle.
local DataStoreService = game:GetService("DataStoreService") local Players = game:GetService("Players") local PlayerDataStore = DataStoreService:GetDataStore("PlayerDataStore") local RETRY_LIMIT = 3 local RETRY_INTERVAL = 5 local function GetPlayerData(userId) local success, e = pcall(PlayerDataStore.GetAsync, PlayerDataStore, userId) -- Return player data if the request was successful. if success then return e end -- Check if the data store has throttled, if so, wait until the request budget is increased. while DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.GetAsync) < 1 do wait(1) warn("Data store budget depleted") end local currentRetries = 0 -- Repeat the request until the request is successful or the request limit is reached. repeat wait(RETRY_INTERVAL) currentRetries = currentRetries + 1 success, e = pcall(PlayerDataStore.GetAsync, PlayerDataStore, userId) if success then return e end until currentRetries == RETRY_LIMIT end Players.PlayerAdded:Connect(function(player) local playerData = GetPlayerData(player.UserId) end)
You've probably found youtube videos that recommend you assign multiple keys to one specific value, even the wiki is guilty of this:
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.
local PlayerDataSchema = { General = { Currency = 0, Experience = 0 }, Moderation = { AdminStatus = "Normal", ModerationHistory = {}, ModerationStatus = "None" }, PurchaseLog = {} }
If you're familiar with OOP you could also save an object, do whatever is intuitive for you.
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.
Keys in DataStorage are basically different Storage for different people. So a Key would be like Player1 and another would be Player2