Data Persistence Tutorial: Difference between revisions

From Legacy Roblox Wiki
Jump to navigationJump to search
>Pighead10
Data Persistence tutorial
Added template
 
(37 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{CatUp|Tutorials}}
{{CatUp|Tutorials}}
 
{{NonStandard|reason=Data persistence was added in 2011 and requires the launcher or revival to support it.}}
==Introduction==
==Introduction==
This tutorial will help you understand and use Data Persistence effectively in your places.
This tutorial will help you understand and use Data Persistence effectively in your places.
==Prerequisites==  
==Prerequisites==  
*You must be proficient in using Lua in Roblox
*You must be proficient in using Lua in Roblox
*Data Persistence must be up and running! (as this is currently a very new feature, Roblox may bring it down for fixes)
===What is Data Persistence?===
===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.
[[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?===
===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:
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:
Line 15: Line 14:
*Save score in all sorts of games
*Save score in all sorts of games
==Using Data Persistence==
==Using Data Persistence==
{{EmphasisBox|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.|blue|dark=a}}
===DataReady===
===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.
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, use this piece of simple code.
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:
<pre>
<pre>
game.Players.PlayerAdded:connect(function(player)
game.Players.PlayerAdded:connect(function(player)
repeat wait(1) until player.DataReady --wait until DataReady is true (== true is not necessary)
player:WaitForDataReady()
end)
end)
</pre>
</pre>
Line 29: Line 29:
<pre>
<pre>
game.Players.PlayerAdded:connect(function(player)
game.Players.PlayerAdded:connect(function(player)
repeat wait(1) until player.DataReady -- wait until DataReady is true (== true is not necessary)
player:WaitForDataReady()


-- create leaderboard
-- create leaderboard
Line 44: Line 44:
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.
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===
===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.
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.
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====
====Loading data====
The methods to load data from the player are:
The methods to load data from the player are:
*[[LoadNumber]](string key)
*[[LoadNumber]]({{type|string}} <var>key</var>)
*[[LoadString]](string key)
*[[LoadString]]({{type|string}} <var>key</var>)
*[[LoadInstance]](string key)
*[[LoadInstance]]({{type|string}} <var>key</var>)
`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.
*[[LoadBoolean]]({{type|string}} <var>key</var>)
<pre> scoreKey = "PlayerScore" </pre>
<var>key</var>, 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.
There we go. Now whenever we want to load or save the player's score, we will use `scoreKey` as the key.
<syntaxhighlight lang="lua">scoreKey = "PlayerScore" </syntaxhighlight>
There we go. Now whenever we want to load or save the player's score, we will use <var>scoreKey</var> 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.
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.
Line 61: Line 64:


So that you understand this part of the script - unless you are proficient with using pcall already - you should view the [[Function_Dump/Core_Functions#pcall_.28f.2C_arg1.2C_.C2.B7.C2.B7.C2.B7.29|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:
So that you understand this part of the script - unless you are proficient with using pcall already - you should view the [[Function_Dump/Core_Functions#pcall_.28f.2C_arg1.2C_.C2.B7.C2.B7.C2.B7.29|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:
<pre>
<syntaxhighlight lang="lua">
local succ,ret = pcall(function() player:LoadNumber(scoreKey) end)
local succ,ret = pcall(function() player:LoadNumber(scoreKey) end)
if succ then
if succ then
Line 72: Line 75:
print("Error:",ret)
print("Error:",ret)
end
end
</pre>
</syntaxhighlight>
Hooray! We've loaded the score that was saved! But wait - we haven't saved it yet!
Hooray! We've loaded the score that was saved! But wait - we haven't saved it yet!
 
====Saving Data====
====Saving Data====
Before we can load data from the player, we first need to save it. The functions to do this are:
Before we can load data from the player, we first need to save it. The functions to do this are:
*[[SaveNumber]](string key,int value)
*[[SaveNumber]]({{type|string}} <var>key</var>, {{type|float}} <var>value</var>)
*[[SaveString]](string key,string value)
*[[SaveString]]({{type|string}} <var>key</var>, {{type|string}} <var>value</var>)
*[[SaveInstance]](string key,Instance value)
*[[SaveInstance]]({{type|string}} <var>key</var>, {{type|Instance|RBX.lua.Instance (Object)}} <var>value</var>)
As mentioned before, we have these `key` and `value` arguments in our functions. `key` is the '''string''' index we load and save them from.
*[[SaveBoolean]]({{type|string}} <var>key</var>, {{type|bool}} <var>value</var>)
As mentioned before, we have these <var>key</var> and <var>value</var> arguments in our functions. <var>key</var> is the {{type|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()`.
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)|PlayerRemoving]] event, which fires just before a player leaves. In this function, we save the player's score using [[SaveNumber (Method)|SaveNumber]].
<pre>
<syntaxhighlight lang="lua">
game.Players.PlayerRemoving:connect(function(player)
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
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
Line 92: Line 96:
end
end
end)
end)
</pre>
</syntaxhighlight>


===Complete Source===
===Complete Source===
Line 100: Line 104:


game.Players.PlayerAdded:connect(function(player)
game.Players.PlayerAdded:connect(function(player)
repeat wait(1) until player.DataReady -- wait until DataReady is true (== true is not necessary)
player:WaitForDataReady()


-- create leaderboard
-- create leaderboard
Line 137: Line 141:
==Quick Reference==
==Quick Reference==
Here's a reference for all of the data functions:
Here's a reference for all of the data functions:
{{Member/box|method|SaveNumber}}}}
 
{{Member/box|method|SaveString}}}}
{{Member/box|method|WaitForDataReady}}
{{Member/box|method|SaveInstance}}}}
{{Member/box|method|SaveNumber}}
{{Member/box|method|LoadNumber}}}}
{{Member/box|method|SaveString}}
{{Member/box|method|LoadString}}}}
{{Member/box|method|SaveInstance}}
{{Member/box|method|LoadInstance}}}}
{{Member/box|method|SaveBoolean}}
{{Member/box|method|LoadNumber}}
{{Member/box|method|LoadString}}
{{Member/box|method|LoadBoolean}}
{{Member/box|method|LoadInstance}}
 
{{clear floats}}


==See also==
==See also==

Latest revision as of 02:56, 17 May 2024

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