Friday, January 18, 2013

Learning the Data Persistence API

Data Persistence Tutorial


Introduction

Data Persistence. A scary phrase that makes most people afraid to even approach it. However, I have good news: It's not actually as complicated as it sounds! In this tutorial, I will guide you through the basics of the data persistence API[1] on ROBLOX.

The Data Persistence API allows you to save and load data for a player in one specific level. For an example of how this is helpful, consider the following: You have a player in your game. The player earns 1,000 points after playing for a good hour. However, you want the player to still have the 1,000 points after he leaves and comes back to your game, no matter what server he joins. The Data API allows this to happen, as long as the player comes to that same game again (it can still be a different server though, but must be the same game).

NOTE: This tutorial is for people who already have a basic understanding of scripting in Lua[2] on ROBLOX. If you don't know scripting very well yet, I highly encourage you to continue on learning more about it before diving into this tutorial. There are great ROBLOX Lua tutorials on the main Wiki site.



Getting Started

First of all, let's learn the methods used in the API. The whole API is built into the Player object. The methods that will be used to control the API are as following:

In that list, we have three categories of things. Loading, saving, and waiting. Before we do ANY saving or loading, we must make sure that the player object is ready to save and load data. That's where the WaitForDataReady method comes into play.

Quick examples:

  • player:SaveBoolean("myBoolean", true)
  • player:SaveInstance("myInstance", game.Workspace.MyModel)
  • player:SaveString("myString", "Hello world!")
  • player:SaveNumber("myNumber", 32)
  • local b = player:LoadBoolean("myBoolean")
  • local object = player:LoadInstance("myInstance")
  • local str = player:LoadString("myString")
  • local num = player:LoadNumber("myNumber")


Typically, you would do most of the saving and loading when the player first enters the game. Here is some pseudocode for that:


function PlayerEntered(player)
   player:WaitForDataReady() -- Await data ready
   -- Add loading/saving methods here
end

game.Players.PlayerAdded:connect(PlayerEntered)

That snippet of coding is the bare-bones of working with the data API. Thankfully, the rest of the process isn't very difficult either! Let us look at an example of using both saving and loading. In this example, we can keep track of the number of times the player has visited the game:


-- All under the PlayerEntered block:

player:WaitForDataReady()

-- Load the number of visits currently
-- If nothing has been saved, it will return 0
local numberVisits = player:LoadNumber("visits")

-- Increase the number by one:
numberVisits = (numberVisits + 1)

-- Save the new number of visits:
player:SaveNumber("visits"numberVisits)

Advanced: Auto-Saving

A lot of times, you want to be able to allow stats to save automatically, that way the player never has to worry about pressing any Save button all the time. A lot of users like to use the PlayerRemoving method to trigger saving, however that method can sometimes run into issues and not properly save all the data. Therefore, I prefer to tell people to take advantage of the Changed event in different value holder objects, like the IntValue object.

In the scenario below, we have a snippet of a script in the PlayerEntered block where we create a stat holder and then set a Changed event in order to save the value every time it changes:


-- All under the PlayerEntered block:

player:WaitForDataReady()

-- Create the value holder:
local points = Instance.new("IntValue", player)
points.Name = "Points"

-- Load points:
points.Value = player:LoadNumber("points")

-- Auto-save technique using the Changed event:
points.Changed:connect(function(newValue)
   player:SaveNumber("points", newValue)
end)


--------------------------------------------
-- How to avoid saving too often:

local lastValue = points.Value
points.Changed:connect(function(newValue)
   lastValue = newValue
   Delay(1, function()
      if (lastValue == newValue) then
         player:SaveNumber("points", newValue)
      end
   end)
end)

[1] API: Application Programming Interface. Usually a library of program code that lets you program off of.
[2] Lua: A programming language commonly implemented for scripting on programs

3 comments: