Friday, January 4, 2013

Data Persistence: How-to.


Hello Robloxians! TechTeam911 here again, with a new scripting tutorial on how to use the powerful Roblox feature, Data Persistence.

History:
Data Persistence was introduced by John Shedletsky back in April of 2011 (Blog article). Before it was released, users who were fluent in Rbx.Lua would use their own algorithims to convert data into text. Players would then be able to save the converted data as a string of numbers/letters onto their computer for later reference. When a user wished to retrieve data, they would then copy the string they were given into another area, where it could be decrypted. A well known users that created pseudo data persistence was Mariostar6464.

How it works:
Data Persistence is very similar to a database program called SQL. SQL is a database in which data can be saved/retrieved on the fly. SQL is commonly used among websites where data is meant to be stored on a server for later use, such as MeepCity, Amazon, Roblox, and other popular sites that require you to sign-up. SQL requires forms on sites that use it to enter a key specific to that database, so that hackers cannot randomly read/write data from the database.

Fun fact: When you hear a person on Roblox bragging/claiming that they "SQL'D Roblox" - it means that they are claiming that they found the passwords to the Roblox database, and were able to change/view data. Users that claim to SQL sites are frequently bluffing - because it would take years to find out the password for an SQL server with a strong password. (Think 20 - 30 characters, alphabet, numbers, symbols, lower and upper case.) If the user has an average computer, it would take ages to preform that at one password per second. However, if they have a very, very powerful computer, and the resources/programs to do it (Guessing at 1000 password a second, without CAPTCHA), it would take around 5 days. But, most sites have methods in place to prevent users from guessing so fast.

Data Persistence in Roblox:

Data Persistence in Roblox is like MySQL. You can read and write data to the database by using the methods supplied by the Roblox developers. In an effort to prevent users from reading/writing unwanted data using Data Persistence, Roblox developers implemented a few requirements, which must be met in order to use Data Persistence.

1. "Data persistence methods cannot be called from within a LocalScript. You'll need to process the saving in a Script."

What it means: It means that when using Data Persistence, you have to use it inside a script, not a local script.

Why they did it: Local scripts are loaded into the client environment and run client-side, scripts are never loaded into the client environment, they are run server-side. It prevents users from stealing a working copy of a place, because the server-side scripts won't be copied over. It also prevents users from using exploits to tamper with their own data/data of other users.

2. "There's a limit on how much data can be stored per player per place."

What it means: You can store up to 45000 bytes of data, per player, per place. If your curious as to how to figure out how much saving something would take up in terms of bytes, see this Roblox Wikipedia article (http://wiki.roblox.com/index.php/DataComplexity)

Why they did it: Roblox hosts data on a service offered by Amazon called AmazonS3. AmazonS3 allows a certain amount of storage with a certain amount of bandwidth for a certain amount each month. If Roblox goes over the limit Amazon sets, then they move into the next tier, which forces Roblox to pay more per month.

Consider it this way. If 100,000 users use data-persistence, and they upload a car that is 40 bytes in size, that's 4,000,000 bytes. 4 million bytes is 4 gigabytes. Roblox has millions of users. If 3 million users make a car that is 40 bytes in size, that's 120,000,000 bytes. 120,000,000 bytes is 120 gigabytes. Roblox set a limit as to how much data could be stored, because on a large-scale with millions of users, users uploading data to Amazon's servers could get out of hand quickly, and toss the Roblox budget into chaos.

45,000 bytes is more than enough for the average player. 45,000 bytes is the average WTRB (Welcome To Roblox Building) creation - with all the parts/scripts included. If 3 million users upload 45,000 bytes of data, that's 135,000,000,000 (135 billion) bytes of data. 135 billion bytes of data is 135,000 gigabytes of data, which is 135 terabytes. I currently do not know how many users play Roblox, so 3 million is an example.

3. "Data is specific to both the player and the place - it is not possible to save data to a player that is shared between places (such as a keeping data after a teleport), nor is it possible to save data to a place which is shared between players (such as saving high scores)"

What it means: Currently, Data Persistence is limited to being able to save data to only the player, and only to the place where the data was saved at. Currently, Roblox developers are considering an idea in which users can load/save data between places (http://blog.roblox.com/2012/01/the-big-bang-at-roblox-universe-creation/). This can be used to create multiple chapter RPG games that span across multiple games, being able to make a script that bans users from all places if necessary, and making models in-game that can be transferred across multiple places.

Now that the limitations are out of the way, lets look at the methods that will become handy while you are working with Data Persistence.

Most important thing about Data Persistence to remember is called, "Keys". Keys are what you save your data to, and what you use to find it later on. Think of it like a safety deposit box. You put your stuff in it, you can take your stuff out, and you can do what you want with it. If you forget where you put your safety deposit box, how will you put/take stuff out?

Worst of all, if you forget the key, there's no way to find out what it is later on. Currently, Roblox is lacking a Data Persistence management system for places, but that is something we should expect in the near future (Data Persistence is still new, so they're ironing out the kinks/improving on it.)

LoadBoolean()

How it's used: A boolean is a true or false statement. As the name says, it loads a true or false statement. This can be used to tell if a user was specified a permanent administrator while in a server by the owner, or if a user is banned etc.

How to use it:
When loading and saving data, the first thing you always must do is check to see if the data is ready to be accessed. If you try to access the data without waiting first, the script will toss you an error. It's like trying to make a car that is off accelerate - you can't do it.

Example: This loads a saved boolean called, "Ban"
game.Players.PlayerAdded:connect(function(player) -- Player entered the game.
player:WaitForDataReady() -- Crucial, you have to wait for the data server to be ready before retrieving.
local ban = player:LoadBoolean(Ban) -- Load the boolean called, "Ban".
if ban == false then -- Is the loaded Boolean false? It also returns false if Ban was never saved.
print ("Player is not banned") -- Player is not banned, self-explanatory.
else -- What if it isn't false?
print "Player is banned" -- The player is indeed banned.
player:Destroy() -- Removes the player
end -- End tag for the if ban == false statement.
end) -- End tag for the function.

LoadNumber()

How it's used: LoadNumber is as it describes - it loads a saved number. If the number doesn't exist, then it automatically returns 0. This is helpful when making persistent leaderboards and saving stats, such as time played, or how many kills a person has.

How to use it: Remember the rule with accessing data - always wait for the data to load first. If when you try to load a number, and it ends up it was never saved, it automatically returns 0.

Example: This loads a number called, "Kills" into a leaderboard.
game.Players.PlayerAdded:connect(function(player) -- Player entered the game.
player:WaitForDataReady() -- Wait for the data to be ready.
local leaderboard = Instance.new("Model") -- Create a new model.
leaderboard.Name = "leaderboard" -- Name it, "leaderboard".
leaderboard.Parent = player -- Put it inside the player.
local kills = Instance.new("IntValue") -- Create a new IntValue (Used for leaderboards)
kills.Name = "Kills" -- Name it kills
local killNum = player:LoadNumber(Kills) -- Try to load a number saved under the key, "Kills"
kills.Value = killNum -- Set the value of kills to killNum (If it didn't load, it would be 0, so it doesn't matter.)
kills.Parent = leaderboard -- Parent kills to the leaderboard
end) -- End for the function

LoadString()

How it's used: Lets say you want to load and save a specific thing about a player, so you can look at it later on. LoadString loads a string with the key you supply.

How to use it: Remember rule - wait for data to load first before accessing it. If you never saved a string under the key your trying to load, it'll return a blank string.

Example: This loads a string under the key, "ThingIenjoy" and makes a message with it, letting the world know what that user likes.

game.Players.PlayerAdded:connect(function(player) -- Player entered game.
player:WaitForDataReady() -- Wait for the data to be ready.
local thingPlayerEnjoys = player:LoadString("ThingIEnjoy") -- Load a string under the key, "ThingIEnjoy".
if thingPlayerEnjoys ~= "" then -- Make sure that it isn't blank.
local m = Instance.new("Message") -- Create a new message.
m.Name = "PlayerLikesThis" -- Name it, "PlayerLikesThis".
m.Text = (""..player.Name.." enjoys "..thingPlayerEnjoys.."") Set the text of the message to, "Playernamehere enjoys thing that the player enjoys".
m.Parent = game.Workspace -- Parent it to workspace.
game:GetService("Debris"):AddItem(m,5) -- Make the message vanish after 5 seconds.
end -- End for the if statement
end) -- End the function

LoadInstance()

How it's used: Lets say you have this elite game where users get to build their own cars and race them. A user spends an hour working on this drag-car, but he doesn't want to let his work vanish once he's gone. With Data Persistence, it is possible to save his work, and load it later on.

How to use it: Remember the rule (Need I remind you once more?) Also, in terms of how much data you can store, you used to be able to use something called DataCost on instances, but the developers locked it for some reason. If your thing is too big, it won't save - big being the maximum no. of parts in a WTRB creation (Huge house, mansion etc.) Also,

Example: This loads an instance under the key, "SavedCar" and puts it into workspace.

game.Players.PlayerAdded:connect(function(player) -- Player entered game.
player:WaitForDataReady() -- Wait for data to be ready.
local savedCar = player:LoadInstance("SavedCar") -- Loads an instance called, "SavedCar".
if savedCar ~= nil then -- Make sure that the car actually exists before we try to tinker with it.
savedCar.Parent = game.Workspace -- Parent it to workspace.
savedCar:MakeJoints() -- Make the joints of the car, otherwise it might fall apart.
savedCar:MoveTo(player.Character.Torso.Position + Vector3.new(0,5,0)) -- Move the car to the torso position of the player, and move it five studs up. Watch it fall on the player's head, lol.
end -- End for the if statement.
end) -- End for the function.

SaveBoolean()

How it's used: Lets say you want to ban a player from your place forever. You can save a boolean to him, and use the earlier example to see if he has indeed been banned before.

How to use it: Remember the rule. Booleans can only be true/false.

Example: This saves a boolean under the key, "Ban" to players that aren't epic enough to join our 1337 game.

game.Players.PlayerAdded:connect(function(player) -- Player entered.
player:WaitForDataReady() -- Wait for the data to load
if player.Name ~= "TechTeam911" or player.Name ~= "arbirator" then -- Is this person worthy enough to join?
print("Not epic enough")
player:SaveBoolean("Ban",true) -- Saves a boolean set to true under the key, "Ban".
player:Destroy() -- Destroy the player.
end -- End for the if statement.
end) -- End for the function.

SaveNumber()

How it's used: You were pwning noobs at your awesome game, and you want to keep track of all your pwnsome kills. Using SaveNumber, along with my earlier example, you can keep track of their kills.

How to use it: Remember the rule. Numbers are numbers, letters/symbols are obviously not numbers.

Example: This saves the player's kills to load later on.

game.Players.PlayerLeaving:connect(function(player) -- A player is leaving
player:WaitForDataReady() -- Wait for data to be ready to be accessed.
player:SaveNumber("Kills",player.leaderboard.Kills.Value) -- Save the player's kills under the key, "Kills".
end) -- End tag for the function.

SaveString()

How it's used: Lets say that you want to save a specific comment with a player, like something that they like. Using SaveString, you can do so.

How to use: Remember the rule. Strings are letters/numbers/symbols.

Example: This saves a string called, "pie" under the player when they leave. Using the earlier example with LoadString, it will say, "Usernamehere enjoys pie"

game.Players.PlayerLeaving:connect(function(player) -- When a player is leaving.
player:WaitForDataReady() -- Wait for data to be ready.
player:SaveString("ThingIEnjoy","pie") -- Saves the string, "pie" under the key, "ThingsIEnjoy".
end) -- End for the function.

SaveInstance()

How it's used: A user is building an awesome car in your car making game, but he doesn't want to lose that 1337 creation. Using SaveInstance, you can save that user's car and load it later LoadInstance().

How to use it: Remember rule. Total saved data to player cannot be over 40 kilobytes for everything.

Example: This saves a model in workspace called, "Car" into the player for later loading.

game.Players.PlayerLeaving:connect(function(player) -- Player is leaving
player:WaitForDataReady() -- Wait for the data to be ready.
player:SaveInstance("SavedCar",game.Workspace.Car) -- Saves a model to the player, the model being an object in workspace called, "Car" under the key, "SavedCar".
end) -- End of function.

Also, if you need to erase an instance, you can go, player:SaveInstance("keynamehere",nil). That allows you to erase an instance if necessary.

Summary:
Data Persistence is a very powerful feature that Roblox has offered users. New scripters may find Data Persistence difficult to understand, but they will come to understand it as their scripting skills increase. Data Persistence is bound to be upgraded, because as Roblox becomes older, newer features/upgrades are released. John Shedletsky once suggested the idea of a Data Persistence control panel, and it is under consideration.

This has been a scripting tutorial by TechTeam911 - I hope you enjoyed it! I will release more scripting tutorials as the time passes by.

2 comments:

Write your comment here... OR Flingi will eat you