ROBLOX Scripting How To: Data Persistence

From Legacy Roblox Wiki
Jump to navigationJump to search

Welcome to one of the first of many to come guides from ROBLOX on cool new scripting features!

In this guide I'll be showing you how to use Data Persistence in your place. As a general warning, you'll probably want to be a least somewhat proficient at scripting; in other words, this shouldn't be your first script. It's not extremely difficult, though, so don't put off learning it! It's a great feature that'll majorly enhance your game!


What is Data Persistence?

Have you created a game that players earn money in? That has checkpoints (like an obby) or maybe you've made a building place where people make their own creation, or maybe another type of place- It doesn't matter! Data Persistence will probably come in very handy with any place!


These are all great examples of places that can benefit from Data Persistence. Data Persistence is the ability to save some data (like a number, or a string, or a brick) for a player in your game, so when they come back you can load in what they had last time they visited your place (such as the amount of money they earned, or a level they gained, etc.)


How do I use Data Persistence?

Persistence is completely done via Lua, with calls on the individual player to save/load a piece of data for a particular place, as this is a Per User Per Place (PUPP) paradigm. In simpler terms, this means for each place, each player can have saved data. The model for this data is key/value pairs, with the key being a string you specify (more on this in the example code), and the value is determined by the call.


The following are our current calls for saving different types of data, and each of these calls can be found under Player. Due to this, you can not save/load from players not in the same server as the script executing the function call.

SaveBoolean(String key, Bool value)

SaveNumber(String key, Number value)

SaveString(String key, String value)

SaveInstance(String key, Instance value)


All of these calls do essentially the same thing, except they each will save different types of data, in accordance with the call name. Let's look at the arguments we pass these functions:

String key - Think of this as a literal key that you would use to unlock a door. Whatever you call this key - "Money", "Experience", maybe even "Pink Elephant" will be the key for loading (unlocking) any data you save later.

Value - this is the actual thing that you want to save for the player. What this is depends on the functions you call, if you call SaveBoolean(), you'll want to use a Boolean (true/false) value. If you use some data type other than the one specified by the function, you will get an error!


For each save function, we have a corresponding load function:

LoadBoolean(String key)

LoadNumber(String key)

LoadString(String key)

LoadInstance(String key)


If you use the same key you defined while saving a value from above, you will now get the same value you saved returned from each of these functions.


Things to be Aware Of

What is save/load Instance all about?

These functions will save any object that can be placed under workspace (including all of the object’s descendants), which also brings up a key point: All of the save functions have a data cap on how large the saves can be, if this cap is reached no new data will be saved to that particular key (we believe the size of each to be sufficient currently, but if we see a need to raise it in the future, we will consider raising).


What's with this Player.DataReady stuff?

This is a very important part of data persistence, which will we show more of in the Examples below. On the Player object there is now a read-only Boolean value called DataReady. This value indicates whether the player is ready to have save/load functions called on him or her. When a player initially joins a game, DataReady is false. After a few seconds, DataReady will typically become true (at this point you can freely call save/load functions). The best way to determine when DataReady goes from false to true is to use the WaitForDataReady function:

player:WaitForDataReady()

This function will wait until DataReady on player is true. Note: if DataReady never becomes true, we will sit and wait indefinitely on this property, effectively stopping the script from executing.


Why you should use pcall with Data Persistence

If you're not familiar with pcall, you definitely will be after using persistence. Pcall stands for "protected call", and rightfully so. Any code executed inside of a pcall will not cause a script to crash. Rather, the pcall will return with the error message the failed code has output. Why is this important in respect to Data Persistence? You should always wrap your save/load calls in a pcall, just in case the call fails, you can handle the error in your script and continue on. Here is an example:

local playerScore
if not pcall(function() playerScore = player:LoadNumber("Score") end) then
   playerScore = 0
end

The above code tries to load a number into playerScore with the key "Score" on the player object. If for some reason we can't load the number (maybe we have never saved something to the key "Score", maybe Data Persistence goes down in the middle of the game, etc.), we can now recover by checking if the pcall succeeds. In this example, if we fail we just assign a player score of 0 and allow the script to keep running.

Examples

Loading example

The first place that used persistence was Welcome to ROBLOX Building, so naturally we thought it would be a good piece of example code. Models that players build are saved, so when they come back they can continue to build on their creations. Consider the following code snippet:

player:WaitForDataReady()

local savedBuildingArea
local success = pcall(function() savedBuildingArea = player:LoadInstance("PlayerArea") end)

if not success or savedBuildingArea == nil or not savedBuildingArea:FindFirstChild("BasePlate") then
   createNewPlayerArea(area)
else
   local offset = area.BasePlate.CFrame.p - savedBuildingArea.BasePlate.CFrame.p
   area.BasePlate.Parent = nil
   savedBuildingArea:TranslateBy(offset)
   savedBuildingArea.Parent = area
   savedBuildingArea:MakeJoints()
end


First we call ":WaitForDataReady()" - If you've not read the above guide on this, it basically just waits until the player is ready to have data loaded and saved. Next, we try to load in a key titled “PlayerArea” for our particular player. Putting this in a pcall is a good idea, if we get an error (maybe this is the player’s first visit to your place, see Things to be Aware Of for more info) we can handle that situation. If we fail at loading "PlayerArea", our script then creates a new playerArea. Otherwise, if we succeed in loading a previous playerArea, we set it up for use by the player.


Saving example

function removePlayer(player)
   if not pcall(function() player:SaveInstance("PlayerArea", takenAreas.PlayerArea) end) then
      print("error saving")
   end
end

game.Players.PlayerRemoving:connect(function(player)
   removePlayer(player)
end)


The above script simply tries to save an Instance "takenAreas.PlayerArea" to the key “PlayerArea” for the player simply called “player” when he or she leaves the game (typically it is good practice to only save when a player leaves your game, saves done in game don't actually get pushed out until the player leaves, thus saving in the middle of playing doesn't do much). Once again we do this with a pcall, just in case persistence isn’t working properly we can handle the error. Saving for bools, numbers and strings work the same way, except it will throw an error if you try to pass the functions anything except for the type they specify respectively.


I hope that was pretty straightforward, if you have any questions please feel free to send Jeditkacheff a Private Message on ROBLOX here.

It may also be a good idea to post on the Scripting Helpers forum here.

Happy persisting!

See Also

Tutorials

Methods