Data Persistence Tutorial

From Legacy Roblox Wiki
Jump to navigationJump to search

Ouch!
The following article, Data Persistence Tutorial, mentions a feature exclusive to certain versions of the client.
Specifically: Data persistence was added in 2011 and requires the launcher or revival to support it.


Introduction

This tutorial will help you understand and use Data Persistence effectively in your places.

Prerequisites

  • You must be proficient in using Lua in Roblox

What is Data Persistence?

Data Persistence is a way to save data to a player, and load it again if they join that place again, even on a different server. It is done completely with Lua, with calls to load and save data on a specific player. It is Per-User-Per-Place (PUPP), meaning you can't save anything to the server (for example if you wanted a high score board to work for every server, which isn't currently possible) but save data to specific players.

Why would this be useful?

Data Persistence opens up a vast number of possibilities, many of which you probably already wanted to do and some which may have already been achieved with messy other ways (such as typing in load codes). Here's a few of the things you could use with Data Persistence with:

  • Save Level, XP and Gold in RPGs
  • Save models people have built in building games
  • Save position a player has got to in an obby
  • Save score in all sorts of games

Using Data Persistence

Data Persistence only works in a Roblox game server. If you use solo mode or a local server to test your script, Data Persistence will not work.

DataReady

You cannot save data to a player immediately after a player has joined your place - you must wait until data saving is ready, by reading a value Roblox has added into every player called DataReady. When that is true, you can save and load data from the player.

For a quick and easy way of waiting for DataReady in a player, you can use the WaitForDataReady function, which makes your script wait until DataReady is true:

game.Players.PlayerAdded:connect(function(player)
	player:WaitForDataReady()
end)

That was easy, wasn't it? Those few lines won't achieve any saving or loading though! We need to use the methods in the player objects to save and load data from the player.

Getting started

Firstly, we will need a score in the player to save! I'm going to be creating a leaderboard, but only create the player's score after it has been loaded (we will cover this later). This is a tutorial on data persistence, not leaderboards, so I'm not going to explain the code. Here is the script so far:

game.Players.PlayerAdded:connect(function(player)
	player:WaitForDataReady()

	-- create leaderboard
	local ls = Instance.new("IntValue")
	ls.Name = "leaderstats"
	ls.Parent = player

	--create the score stat
	local score = Instance.new("IntValue")
	score.Name = "Score"
	score.Parent = ls
end)

This will create a leaderboard in the player. However, you may have noticed that we haven't set the score's value; we haven't loaded it yet. Let's do that now.

Saving and loading overview

You both load and save data with key/value pairs, where `key` is the string index you load and save it from. When loading and saving, the key you load a specific piece of data as must be the same you saved it as or you'll be loading something completely different and that probably hasn't been saved.

The save limit for a user in a place is 45,000 bytes, which shouldn't be a problem unless you saving absolutely masses of data.

The first thing you will need to know is that loading or saving data may not always work correctly,for a number of reasons. When using data persistence, you should place all data persistence calls in pcall()s so that if it doesn't work, it won't break the script and you can catch the error. More about this will be explained below.

Loading data

The methods to load data from the player are:

key, as explained previously, is the index we are going to be loading and saving the data from. So we can change the player's score's key easily, without going the entire script and finding where we load and save to the player, we will create a variable with the key to put at the beginning of the script.

scoreKey = "PlayerScore"

There we go. Now whenever we want to load or save the player's score, we will use scoreKey as the key.

As mentioned, we should put the load and save calls in pcall()s, so if it breaks, it won't crash the entire script. We can then print the error message if it doesn't work.

If a load function has nothing to load, it will return 0 (for LoadNumber), "" (for LoadString) or nil (for LoadInstance). This may be the same as their actual score in the game, so don't put a big error saying, "YOU HAVEN'T GOT ANYTHING SAVED!" because they may just have had 0 points/whatever you are saving anyway. If it does have something to load and loads successfully, it will return whatever it loaded.

So that you understand this part of the script - unless you are proficient with using pcall already - you should view the pcall page. We will load the key, check if it worked, then set the player's leaderboard score to what LoadNumber returned if it did. Here is what I have:

	local succ,ret = pcall(function() player:LoadNumber(scoreKey) end)
	if succ then
		if ret ~= 0 then
			player.leaderstats.Score.Value = ret
		else
			print("Nothing to load/score was 0")
		end
	else
		print("Error:",ret)
	end

Hooray! We've loaded the score that was saved! But wait - we haven't saved it yet!

Saving Data

Before we can load data from the player, we first need to save it. The functions to do this are:

As mentioned before, we have these key and value arguments in our functions. key is the string index we load and save them from.

You cannot save data to a player after s/he has left the game. As most times we will need to save data as they are leaving, we can use the PlayerRemoving event, which fires just before a player leaves. In this function, we save the player's score using SaveNumber.

game.Players.PlayerRemoving:connect(function(player)
	local succ,ret = pcall(function() player:SaveNumber(scoreKey,player.leaderstats.Score) end) --if a player leaves before the leaderstats have been created, as it's in a pcall it won't crash the script
	if succ then
		print("Successfully saved")
	else
		print("Saving error:",ret)
	end
end)

Complete Source

Using that script, we are now saving the player's score whenever he leaves, and loading it again when he rejoins! In case you found that difficult to follow, here's the complete source of that script:

scoreKey = "PlayerScore"

game.Players.PlayerAdded:connect(function(player)
	player:WaitForDataReady()

	-- create leaderboard
	local ls = Instance.new("IntValue")
	ls.Name = "leaderstats"
	ls.Parent = player

	--create the score stat
	local score = Instance.new("IntValue")
	score.Name = "Score"
	score.Parent = ls

	local succ,ret = pcall(function() player:LoadNumber(scoreKey) end)
	if succ then
		if ret ~= 0 then
			player.leaderstats.Score.Value = ret
		else
			print("Nothing to load/score was 0")
		end
	else
		print("Error:",ret)
	end

end)

game.Players.PlayerRemoving:connect(function(player)
	local succ,ret = pcall(function() player:SaveNumber(scoreKey,player.leaderstats.Score) end) --if a player leaves before the leaderstats have been created, as it's in a pcall it won't crash the script
	if succ then
		print("Successfully saved")
	else
		print("Saving error:",ret)
	end
end)

Quick Reference

Here's a reference for all of the data functions:

WaitForDataReady( )
Returns nil
Description: This method pauses your script until the saved data is ready to be accessed.
Member of: Player
SaveNumber( string key, float value )
Returns nil
Description: Saves a number value that can be reloaded from any server via LoadNumber. The DataCost taken up by a saved number can be removed by saving 0 as the value.
Member of: Player
SaveString( string key, string value )
Returns nil
Description: Saves a string that can be reloaded from any server via LoadString. The DataCost used by a saved string can be removed by saving an empty string ("") as the value.
Member of: Player
SaveInstance( string key, RBX.lua.Instance (Object) value )
Returns nil
Description: Saves a Roblox instance, such as a part that can be reloaded on another server via LoadInstance. The DataComplexity used is the Object's DataCost property. Saving nil removes the entry and frees up DataComplexity units..
Member of: Player
SaveBoolean( string key, bool value )
Returns nil
Description: Saves a boolean value that can be reloaded from any server via LoadBoolean.
Member of: Player
LoadNumber( string key )
Returns number
Description: Returns a number value that was previously saved to the player via SaveNumber with the same key. If the key doesn't exist, it returns 0, not nil.
Member of: Player
LoadString( string key )
Returns string
Description: Returns a string value that was previously saved to the player via SaveString with the same key. Returns an empty string ("") if key doesn't exist, not nil.
Member of: Player
LoadBoolean( string key )
Returns bool
Description: Returns a boolean value that was previously saved to the player via SaveBoolean with the same key. Returns false if the key doesn't exist, not nil.
Member of: Player
LoadInstance( string key )
Returns RBX.lua.Instance (Object)
Description: Loads an instance value that was previously saved to the player via SaveInstance.
Member of: Player


See also