PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience

Saving Player Data

Saving Player Data

Nov 06 2018, 11:24 AM PST 15 min

Roblox allows data to be saved on its servers. The primary purpose of this feature is to store player data between sessions, keeping their stats, inventory, and other data intact. In this tutorial, we’ll create a system that automatically stores a player’s money and experience so that it can be retrieved when they play the game again.

Creating a Data Module

Before we dive into data storage, let’s set up the system which keeps track of a player’s money and experience during a game session. We’ll start by creating a ModuleScript, a special type of script that can be referenced from other scripts. An ideal place for this ModuleScript is within ServerStorage.

-- Set up table to return to any script that requires this module script
local PlayerStatManager = {}

-- Table to hold player information for the current session
local sessionData = {}

local AUTOSAVE_INTERVAL = 60

-- Function that other scripts can call to change a player's stats
function PlayerStatManager:ChangeStat(player, statName, value)
	assert(typeof(sessionData[playerUserId][statName]) == typeof(value), "ChangeStat error: types do not match")
	local playerUserId = "Player_" .. player.UserId
	if typeof(sessionData[playerUserId][statName]) == "number" then
		sessionData[playerUserId][statName] = sessionData[playerUserId][statName] + value
	else
		sessionData[playerUserId][statName] = value
	end
end

-- Function to add player to the 'sessionData' table
local function setupPlayerData(player)
	local playerUserId = "Player_" .. player.UserId
	sessionData[playerUserId] = {Money=0, Experience=0}
end

-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)

return PlayerStatManager

Saving Player Data

Now let’s start storing actual information using data stores!

Initialize the Data Store

First we’ll create a variable for the data store in the same ModuleScript and call DataStoreService/GetDataStore|GetDataStore() to open a new PlayerData data store.

-- Set up table to return to any script that requires this module script
local PlayerStatManager = {}

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

-- Table to hold player information for the current session
local sessionData = {}

local AUTOSAVE_INTERVAL = 60

Read/Write Initial Data

Next, let’s change how our setupPlayerData() function works. Currently, it just creates new data for a player when they join the game, but that data isn’t saved anywhere. Now, with access to the playerData data store, we can call GlobalDataStore/UpdateAsync|UpdateAsync() to check if it’s holding any updated information for the player. If that call returns valid data, we save it to the sessionData table; otherwise we save new player data to the sessionData table.

-- Function to add player to the 'sessionData' table
local function setupPlayerData(player)
	local playerUserId = "Player_" .. player.UserId
	local data
	playerData:UpdateAsync(playerUserId, function(playerData)
		data = playerData
	end)

	if data then
		-- Data exists for this player
		sessionData[playerUserId] = data
	else
		-- Data store is working, but no current data for this player
		sessionData[playerUserId] = {Money=0, Experience=0}
	end
end

-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)

Save Data on Exit

It’s good practice to save a player’s data when they exit the game. This can be done with a new savePlayerData() function bound to the Players/PlayerRemoving|PlayerRemoving event.

-- Function to save player's data
local function savePlayerData(playerUserId)
	if sessionData[playerUserId] then
		playerData:SetAsync(playerUserId, sessionData[playerUserId])
	end
end

-- Function to save player data on exit
local function saveOnExit(player)
	local playerUserId = "Player_" .. player.UserId
	savePlayerData(playerUserId)
end

-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)

-- Connect 'saveOnExit()' function to 'PlayerRemoving' event
game.Players.PlayerRemoving:Connect(saveOnExit)

Auto-Save

Lastly, it’s useful to make the system handle unexpected events like game crashes. We can do this with a function that waits for AUTOSAVE_INTERVAL seconds (60), loops through all of the players in the sessionData table, and uses the savePlayerData() function to save the current information. Note that this function is initially run in a coroutine using the spawn() function.

-- Function to periodically save player data
local function autoSave()
	while wait(AUTOSAVE_INTERVAL) do
		for playerUserId, data in pairs(sessionData) do
			savePlayerData(playerUserId)
		end
	end
end

-- Start running 'autoSave()' function in the background
spawn(autoSave)

-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)

With all of the above implemented, we now have a simple stat saving system that automatically saves our players’ data!

Handling Data Store Failures

Requests to data stores, like all network calls, may occasionally fail due to poor connectivity or other issues. As you learned in the Articles/Data store|Data Stores article, these calls should be wrapped in pcall() to catch and handle errors.

Completed Script

The following script includes error handling in all necessary areas, indicated by the highlighted lines.

-- Set up table to return to any script that requires this module script
local PlayerStatManager = {}
 
local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")
 
-- Table to hold player information for the current session
local sessionData = {}
 
local AUTOSAVE_INTERVAL = 60
 
-- Function that other scripts can call to change a player's stats
function PlayerStatManager:ChangeStat(player, statName, value)
	assert(typeof(sessionData[playerUserId][statName]) == typeof(value), "ChangeStat error: types do not match")
	local playerUserId = "Player_" .. player.UserId
	if typeof(sessionData[playerUserId][statName]) == "number" then
		sessionData[playerUserId][statName] = sessionData[playerUserId][statName] + value
	else
		sessionData[playerUserId][statName] = value
	end
end

-- Function to add player to the 'sessionData' table
local function setupPlayerData(player)
	local playerUserId = "Player_" .. player.UserId
	local data
	local success, err = pcall(function()
		playerData:UpdateAsync(playerUserId, function(playerData)
			data = playerData
		end)
	end)

	if success then
		if data then
			-- Data exists for this player
			sessionData[playerUserId] = data
		else
			-- Data store is working, but no current data for this player
			sessionData[playerUserId] = {Money=0, Experience=0}
		end
	else
		warn("Cannot set up data for player!")
	end
end

-- Function to save player's data
local function savePlayerData(playerUserId)
	if sessionData[playerUserId] then
		local success, err = pcall(function()
			playerData:SetAsync(playerUserId, sessionData[playerUserId])
		end)
		if not success then
			warn("Cannot save data for player!")
		end
	end
end

-- Function to save player data on exit
local function saveOnExit(player)
	local playerUserId = "Player_" .. player.UserId
	savePlayerData(playerUserId)
end
 
-- Function to periodically save player data
local function autoSave()
	while wait(AUTOSAVE_INTERVAL) do
		for playerUserId, data in pairs(sessionData) do
			savePlayerData(playerUserId)
		end
	end
end
 
-- Start running 'autoSave()' function in the background
spawn(autoSave)
 
-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)
 
-- Connect 'saveOnExit()' function to 'PlayerRemoving' event
game.Players.PlayerRemoving:Connect(saveOnExit)
 
return PlayerStatManager

Where next? Feel free to copy the script above into your game and customize it, using your own stat names and types, different starting values, and more. Remember to also explore the full power and potential of data stores in the Articles/Data store|Data Stores article.

Tags:
  • data
  • stats