Games often need to store persistent data between sessions, such as a player's level, experience points, money, inventory items, location, and more.
This tutorial shows how to create a basic data store, save player data, and read the data back into a game session.
Enable Studio access
By default, games tested in Studio cannot access data stores, so you must first enable them:
Publish the experience.
Choose File and Game Settings.
In the Security section, turn on Enable Studio Access to API Services and click Save.
Create a data store
Data stores require a unique name. This example creates a data store named PlayerGold that saves each player's gold in persistent storage:
Create a Script within ServerScriptService called GoldManager.
Data stores are managed by DataStoreService, so get the service:
local DataStoreService = game:GetService("DataStoreService")Call DataStoreService:GetDataStore() with the string "PlayerGold". This method accesses the PlayerGold data store if it already exists. If it doesn't exist, the method creates a new data store and names it PlayerGold.
local DataStoreService = game:GetService("DataStoreService")local goldStore = DataStoreService:GetDataStore("PlayerGold")
Save data
A data store is essentially a dictionary, like a Lua table. Each value in the data store is indexed by a unique key, which might be the player's unique UserId or simply a named string for a game promotion.
Key | Value |
---|---|
31250608 | 50 |
351675979 | 20 |
505306092 | 78000 |
To save player data in the data store:
Create a variable named playerUserID for the data store key. Then, use playerGold to store a player's starting gold amount.
local DataStoreService = game:GetService("DataStoreService")local goldStore = DataStoreService:GetDataStore("PlayerGold")-- Data store key and valuelocal playerUserID = 505306092local playerGold = 250To save data into the PlayerGold data store, call SetAsync within a protected call, passing the key and value variables previously created.
local DataStoreService = game:GetService("DataStoreService")local goldStore = DataStoreService:GetDataStore("PlayerGold")-- Data store key and valuelocal playerUserID = 505306092local playerGold = 250-- Set data store keylocal success, error = pcall(function()goldStore:SetAsync(playerUserID, playerGold)end)if not success thenwarn(error)end
Functions like SetAsync() are network calls that may occasionally fail. As shown above, pcall() is used to detect and handle when such failures occur.
In its most basic form, pcall() accepts a function and returns two values:
- The status, which is true if the function executed without errors, or false otherwise.
- The return value of the function or an error message.
The sample above checks status on line 13. If SetAsync() fails for any reason, the sample displays the error in the Output window.
Read data
To read data from a data store, call GetAsync() with the desired key name.
local DataStoreService = game:GetService("DataStoreService")
local goldStore = DataStoreService:GetDataStore("PlayerGold")
-- Data store key and value
local playerUserID = 505306092
local playerGold = 250
-- Set data store key
local setSuccess, errorMessage = pcall(function()
goldStore:SetAsync(playerUserID, playerGold)
end)
if not setSuccess then
warn(errorMessage)
end
-- Read data store key
local getSuccess, currentGold = pcall(function()
return goldStore:GetAsync(playerUserID)
end)
if getSuccess then
print(currentGold)
end
To test the script, click Run and notice the currentGold value printed to the Output window. Note that it may take a couple seconds, as the functions must connect to data store servers.
Read and save automatically
The previous script works, but has a fundamental problem: it includes hard-coded values for playerUserID and playerGold, so it doesn't support multiple players with different amounts of gold. A more realistic solution reads the gold value when the player connects to the experience and then saves it when the player leaves. This approach means connecting the data store calls to events from the Players service.
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local goldStore = DataStoreService:GetDataStore("PlayerGold")
-- Add gold values for each player to a local table to avoid hitting the data
-- store repeatedly.
local playerGold = {}
local function incrementGold(player, amount)
playerGold[player.UserId] += amount
end
local function onPlayerAdded(player)
-- Read data store key
local success, storedGold = pcall(function()
return goldStore:GetAsync(player.UserId)
end)
if success then
local currentGold
if storedGold then
currentGold = storedGold
else
currentGold = 0
end
playerGold[player.UserId] = currentGold
print(currentGold)
end
-- Test incrementing gold
incrementGold(player, 5)
end
local function onPlayerRemoving(player)
-- Set data store key
local success, err = pcall(function()
goldStore:SetAsync(player.UserId, playerGold[player.UserId])
end)
if not success then
warn(err)
end
-- Clean up entry so that the table doesn't grow for the lifespan of the server
playerGold[player.UserId] = nil
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
Read and save character position
To save player position, you work with the Character rather than the Player, but the principle is similar. This time, create a Script within ServerScriptService called PositionManager:
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local Workspace = game:GetService("Workspace")
local playerPositionStore = DataStoreService:GetDataStore("PlayerPositionStore")
local function positionHandler(player)
-- Load position on character add
player.CharacterAdded:Connect(function(character)
local success, coords = pcall(function()
return playerPositionStore:GetAsync(player.UserId)
end)
local position = Vector3.new(coords[1], coords[2], coords[3])
if success and position then
character:PivotTo(CFrame.new(position))
print("Loaded player position!")
else
warn("Failed to load position for player " .. player.Name .. ". Placing in default position.")
end
-- Handle player respawn on death
local humanoid = character:FindFirstChildOfClass("Humanoid")
humanoid.Died:Connect(function()
local spawnLocation = Workspace:FindFirstChild("SpawnLocation")
character:PivotTo(spawnLocation.CFrame)
end)
end)
-- Save position on character removal
player.CharacterRemoving:Connect(function(character)
local position = character:GetPivot().Position
local success, err = pcall(function()
playerPositionStore:SetAsync(player.UserId, {position.X, position.Y, position.Z})
print("Saved player position!")
end)
if not success then
warn("Failed to save position for player " .. player.Name .. ": " .. err)
end
end)
end
Players.PlayerAdded:Connect(positionHandler)
This script adds a new data store, playerPositionStore. Because data stores only store basic types rather than objects, you have to store X, Y, and Z coordinates as individual numbers rather than a single Vector3 or CFrame value. As you test your experience, move your character around. Note how your character returns to the same position the next time you test your experience.
Sample project
Now that you understand basic data store usage, test it out in the Gold Rush sample game. You can also edit the game in Studio and explore the enhanced GoldManager script, which displays gold as part of the UI and includes auto-saving.