Datastore Tutorial for Beginners

Hey amazing developers! I’ve created a tutorial for new people regarding the datastore and how to save multiple/singular data. This tutorial will cover alot of things so if you’re new make sure you have your water ready!

In this tutorial im gonna try my best to explain every single line as possible assuming that you’re really new to coding, but if you already know some basic stuff, This tutorial is gonna go faster for you! :smiley:

For any improvements for this topic/thread please comment down below so I can improve it and make it better for future readers!

Btw, the tutorial is under this code, this just shows what you’re gonna learn today!


-- // Assigning variables //
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("MyDataStore") -- This can be changed to whatever you want

local function saveData(player) -- The functions that saves data
	
	local tableToSave = {
	    player.leaderstats.Money.Value; -- First value from the table
	    player.leaderstats.Coins.Value -- Second value from the table
	}
	
	local success, err = pcall(function()
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
	end)
	
	if success then -- If the data has been saved
		print("Data has been saved!")
	else -- Else if the save failed
		print("Data hasn't been saved!")
		warn(err)		
	end
end

game.Players.PlayerAdded:Connect(function(player) -- When a player joins the game
	
-- // Assigning player stats //
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local Money = Instance.new("IntValue")
	Money.Name = "Money"
	Money.Parent = leaderstats
	
	local Coins = Instance.new("IntValue")
	Coins.Name = "Coins"
	Coins.Parent = leaderstats
	
	local data -- We will define the data here so we can use it later, this data is the table we saved
	local success, err = pcall(function()

		data = dataStore:GetAsync(player.UserId) -- Get the data from the datastore
		
	end)
	
	if success then -- If there were no errors and player loaded the data
		
		Money.Value = data[1] -- Set the money to the first value of the table (data)
		Coins.Value = data[2] -- Set the coins to the second value of the table (data)
		
	else -- The player didn't load in the data, and probably is a new player
		print("The player has no data!") -- The default will be set to 0
	end

end)

game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
    local success, err  = pcall(function()
	    saveData(player) -- Save the data
    end)

    if success then
        print("Data has been saved")
    else
        print("Data has not been saved!")
    end
end)

game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
		local success, err  = pcall(function()
	         saveData(player) -- Save the data
        end)

        if success then
            print("Data has been saved")
        else
            print("Data has not been saved!")
        end
	end
end)

Here I know it’s confusing so i will explain each part

Introduction

Introduction:

Welcome everyone to my datastore tutorial, in here i will explain every basic part of data stores and how you can use it in different ways!

First of all you need to have API Services Enabled in your game settings.

To Get there, press Home > Game Settings > Security > Check the Enable studio access to api services

That’s what it should look like!

Now make a server script (normal script) and put it into Server Script Service!

Okay so before we get into the main datastore learning, we need to define variables, and to do so we will get the DataStoreService and then we will make our dataStore (trust me it’s not confusing):

local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("MyDataStore") -- This can be changed to whatever you want

As you see above, you can change the “MyDataStore” to whatever you would like to call your Datastore (you can be as creative as you want)

After you’re done with this, make sure you save the game and publish it!

-----------------------------------------------------------------------------------------------------------------------------------------
END OF INTRODUCTION
-----------------------------------------------------------------------------------------------------------------------------------------

Part1 - Assigning the function
local function saveData(player) -- The functions that saves data
	
	local tableToSave = {
	    player.leaderstats.Money.Value, -- First value from the table
	    player.leaderstats.Coins.Value -- Second value from the table
	}
	
	local success, err = pcall(function()
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
	end)
	
	if success then
		print("Data has been saved!")
	else
		print("Data hasn't been saved!")
		warn(err)		
	end
end

In here, we are determining the table that we want to save (tableToSave) and inside we are putting the data that you want to save,

So basically – player.leaderstats.Money.Value is gonna insert the players Money.Value into the table, same with Coins, and you can add as much stuff here as you want.

If you don’t know how we got the player, i will tell you later

Now we will use our dataStore (which i assigned at the beginning of the entire script) to save the table, so we do dataStore:SetAsync, Now SetAsync is a built in function that allows us to call the dataStore and tell it that we want to save something, in our case the “tableToSave”, So i say

dataStore:SetAsync(player.UserId, tableToSave)

The “player.UserId” is the key, aka. We want to save the table with the players Id as the unique key, following it with the table we want to save…

Now you might have realized this is all a function with a passing variable saveData(player) – “player” and we never called the function which i will tell you in part 2 and 3

But before that we first need to use a pcall function to make sure the script doesn’t find an error.

Pcall is basically a built in function you can tell it anything and if it catches an error it will not stop the entire script outside the pcall.

So to use a pcall we would do

local success, err = pcall(function()
    -- Do stuff
end)

If the “Do stuff” didn’t get any errors the success is going to be true, meanwhile the error being nil, and if finds an error success will be nil and error will be true

so we can do:

local success, err = pcall(function()
    dataStore:SetAsync(player.UserId, tableToSave)
end)

And if the data got saved we can check if success then:

if success then -- If the data got saved
    print("Data has been saved")
else -- Else if the data didn't get saved
    print("Data has not been saved!")
end

So at the end it all looks like:

local function saveData(player) -- The functions that saves data
	
	local tableToSave = {
	    player.leaderstats.Money.Value, -- First value from the table
	    player.leaderstats.Coins.Value -- Second value from the table
	}
	
	local success, err = pcall(function()
		dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
	end)
	
	if success then
		print("Data has been saved!")
	else
		print("Data hasn't been saved!")
		warn(err)		
	end
end

-----------------------------------------------------------------------------------------------------------------------------------------
END OF PART 1
-----------------------------------------------------------------------------------------------------------------------------------------

Part2 - Saving the Data
game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves the game
    saveData(player) -- Save the data
end)

game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do
        saveData(player) -- Save the data
    end)
end)

Now that we have the function, we need to call it. So when the player leaves the server, we call the function.

And when we connect the function to player Removing we have to add a variable called player here

game.Players.PlayerRemoving:Connect(function(player) -- << this one

and then in the playerRemoving we call the function saveData() and inside the brackets we give it the variable player we got from the line above so it looks like this saveData(player)…

game.Players.PlayerRemoving:Connect(function(player) -- When a player leaves
	saveData(player) -- Save the data
end)

Now for the Bind Close, i will need to explain something.

Whenever a player leaves a server (the last player) the data will save then a few seconds later the server shutdowns.

However in roblox studio the server shuts down as soon as the player presses the stop button giving it no time to save the data. (BindToClose is also useful for unexpected shutdowns by roblox!)

So what we do to save data in roblox studio is use game:BindToClose()

in our case, when the game is closing we wanna loop through all the players and save their data like this

game:BindToClose(function()
	for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
		-- Do stuff
	end
end)

this line might be confusing but trust me it’s not that hard to understand for _, player in pairs(game.Players:GetPlayers())

The game.Players:GetPlayers() will return a table of all players in the game, and for _, player in pairs(playerTable) thats really what it is in short

ATTENTION: YOU CAN RENAME THE _, player TO ANYTHING YOU WANT!
Most people use i, v. But i like to look clean so i use _, player

So then we wanna do the same thing as for when the player leave, we save their data by calling the function and giving it the parameter (player).

game:BindToClose(function() -- When the server shuts down
	for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
	    saveData(player) -- Save the data
        end
end)

-----------------------------------------------------------------------------------------------------------------------------------------
END OF PART 2
-----------------------------------------------------------------------------------------------------------------------------------------

Part3 - Getting the Data
game.Players.PlayerAdded:Connect(function(player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local Money = Instance.new("IntValue")
	Money.Name = "Money"
	Money.Parent = leaderstats
	
	local Coins = Instance.new("IntValue")
	Coins.Name = "Coins"
	Coins.Parent = leaderstats
	
	local data
	local success, err = pcall(function()
		
		data = dataStore:GetAsync(player.UserId)
		
	end)
	
	if success then
		
		Money.Value = data[1]
		Coins.Value = data[2]
		
	else
		print("The player has no data!") -- The default will be set to 0	
	end
	
end)

This is the playerAdded part of the script, this is the place where we manage the leaderstats, aswell as the data, so the first few lines are just making the Coins and Money…

However the 2nd part of the script is receiving the data from the table.

So to receive data from the player we can just use

local data

data = dataStore:GetAsync(player.UserId)

But there’s 1 issue, if the player is new it never had any data saved, so we will use the pcall()

local data -- We are assigning this here so we can use it later on
local success, err = pcall(function()
		
	data = dataStore:GetAsync(player.UserId)
		
end)

The GetAsync is a built in function just as SetAsync, and we are calling the dataStore and telling it to give us the data with the player.UserId being the unique key.

If you’re confused with keys, every datastore has a key so you can access it, the easiest way to make a key is getting the players UserId as no one can change it, so the data doesn’t get lost…

So when we combine all of this it should look like this:

local success, err = pcall(function()
		
	data = dataStore:GetAsync(player.UserId)
		
end)

if success then	
	Money.Value = data[1]
	Coins.Value = data[2]
	
else
	print("The player has no data!") -- The default will be set to 0	
end

Ok, if you’re probably confused by the

Money.Value = data[1]

Lemme explain: Whenever we have a table and value inside of it, we can access each and every single value by doing:

local table = {
    7, -- This is the first value (1)
    4, -- This is the second value (2)
    5 -- This is the third value (3)
}

local firstValue = table[1]
print(firstValue)

-- OUTPUT

7

So that line of code is getting the first value from the table because 1 is represented as the first one, the 4 in out example would be 2 and for 5 the number would be 3.

So at the end the Money.Value is going to be the data[1] and Coins.Value is going to be data[2] as Money is the 1st value in the table and Coins it’s the 2nd value in the table.

After all of this we will type else (because if the pcall catches an error we wouldn’t get any data and the success will be nil) and then we print that the player has no data

-----------------------------------------------------------------------------------------------------------------------------------------
END OF PART 3
-----------------------------------------------------------------------------------------------------------------------------------------

Fun Bonus Info

After you’ve done everything, you might come to a scenario where you wanna change players saves (for example you can use data stores to ban people and sometimes you wanna unban them by changing their data)

To do this simply, you should get this plugin, It’s safe and it really helps out, I personally use it whenever i wanna change something manually.

When you get the plugin and run it you will be shown a gui with alot of text boxes

It will ask for a Name, Place id should be set automatically and for the Name set it to whatever you’ve called the datastore you wanna manually change data on

After you’ve done that, press Connect.

Then you will be asked to write a key, Now remember the key i was talking about, and how you set the unique key to be the players userId, Just write in the players userId you wanna change data on,

For example if i wanna change data for myself i would type my User Id and then press Enter.

After that it will show everything you’ve saved with that User Id on that datastore and you can manually change it!

Here’s a little challenge now! If you read through all 4 parts, I want you to look at the entire script at the top of the topic and see how easy everything is! Good Luck! :smiley: (Oh also don’t forget to save and publish your work!)

This topic took me a while to write so please if it helped you, i would support if you dropped a like… (Sorry i don’t wanna sound greedy or anything)

I hope I helped you guys understand this way better, for any questions just ask me!

Note:
You might see comments about some stuff, that i probably deleted/corrected, so no need to worry about that

PS. If you have any way of making this topic better for new readers just leave it in the comments!

213 Likes

Hi there,

I really like the tutorial. I have learnt things I have been doing wrong in my datastore code such not checking if user already had data, using BindToClose() for whenever the server shuts down and data needs to be saved and storing data in arrays.

I have got a few tips for you that you may want to consider using for your next datastore code.

I usually prefer wrapping the while loop in a coroutine so I can continue on writing other player added event code after it without having to worry about data saving. That’s just optional, though I always prefer adding it to prevent using too many scripts in my game and causing confusion or using up memory.
Using SetAsync() replaces the old value to a new value. I have read a post mentioning using UpdateAsync() puts less strain on the Lua code and is more efficient on data saving for your game.
We got a wonderful post made by ForeverHD explaining on why to not use SetAsync()


Although using SetAsync() is still proper, it would propertiese better practice on using UpdateAsync() for saving player data.

Anyways, keep up with the great tutorial. I’m sure you can help out many users as I will also suggest this thread to people that come to me asking for datastore scripts.

10 Likes

Thank you for the response!
As for now I’m trying to use simple code to help new people and introducing them to this type of stuff, but i will look into UpdateAsync() as soon as i have time,

Thanks!

7 Likes

Overall, it looks very clean. There are a few suggestions I have.

  1. Have more comments that explain the code
  2. For the saving, where it says this:

change it to:

while wait(120) do
    saveData(player)
end


That way, the script can do other things while waiting for the 2 minutes to be up.

Edit: This just changes the way the code looks, reducing a line of text, and also makes it a little bit more visually appealing

Other than that, nice work!

5 Likes

Thanks, i will definitely change that. And I will add more comments aswell.

3 Likes

That is a big no.
wait() is not a conditional statement as it can be used for while loops but is not suggested.


Read that document above then you will understand why.

colbert would be hating you if he saw your post

8 Likes

That isn’t true. Putting wait(120) in the conditional won’t start a new thread.

8 Likes

Great tutorial! I have learned a lot from it. Hopefully now I can have reliable datastores.

On a separate note, how would I assign a name and a key so that it can be modified with say DataStore Editor V2?

Oh that’s also my bad for not mentioning, in the entire script at the beginning of the topic, theres

local dataStore = DataStoreService:GetDataStore("YourDataStoreName") -- You can change the name to anything you want to

And then when you open Datastore editor

Enter you game id, and datastore name and then connect…
After that it will ask for your Key, and as i said our key in the tutorial is the players userid, so just type in the userid you want to check

And then press querry

Then you can change, modify whatever you want to!

4 Likes

Tysm! Will you update the tutorial at all to be UpdateAsync() or is that something I will have to figure out on my own?

1 Like

No need to use UpdateAsync() for now, i know some really really rare scenarios u will need the UpdateAsync() but you can stick with SetAsync() as it works perfectly fine, But yeah i will update the topic, In fact im updating it right now, in like 10 - 20 mins it will be updated!

Have a good day!

1 Like

Thank you so much! I will definitely come to you if I have any questions! :heart_decoration:

1 Like

In that document, one of the points it made was that using while wait() is normally used and makes the code messier and less readable:

Screen Shot 2020-08-20 at 10.00.15 PM

There, in that instance, I would agree, don’t use while wait. But in this case, I don’t see how the readability is any worse.

Also, another one of the points was that you didn’t get a return value of wait(). In this case, it wouldn’t matter.

Screen Shot 2020-08-20 at 10.04.35 PM Screen Shot 2020-08-20 at 10.04.51 PM

As it said, from a functionality standpoint, it’s not a problem in this situation. In the case of this datastore, there is no need for us to know how long it has actually been yielded for.

The only thing we need is for the autosave to run every two minutes, and we don’t need to know how long it actually took, we just need it to autosave about every 2 mins.

3 Likes

This is a duplicate of a tutorial from the DevHub.

Not only that the but content isn’t in conjunction with the title as it’s is clickbait

Issues found so far:

  • Not using Pcalls for SaveAsync
  • Not retrying failed Pcalls
  • Not using Coroutines in BindToClose
  • Not using UpdateAsync
  • Not checking for available Budget before DataStore calls
  • Not adding any cool-downs to DataStore calls
  • Not using GetPlayers instead of GetChildren
  • The loop inside PlayerAdded will continue to run after the player leaves

I recommend that you do not follow this tutorial as it causes data problems

13 Likes

GetPlayers is much more efficient than :GetChildren since the first one will return the actual players while the second one will return anything inside the Player’s service.

Oops, my bad, must’ve been a bit tired and havent seen that, my bad.

1 Like

I’m sorry to have those issues, but using this works perfectly fine to me, if they wanna know how to use coroutines and updateAsync, that’s on them. I understand that I could’ve mentioned it. But after all this is a tutorial for begginers, I’ve removed the auto-saving as i just saw that i it makes issues, and i fixed the GetChildren, I must have been tired and didn’t see it. Anyways thanks for the reply, have a great day

Quick point: You should update your DataStore Editor plugin to get V3!

6 Likes

I already got this one for 150 robux and i like this datastore plugin, but sure i can change it, can you link it to me i can’t find it ;3

It’s the same one! Go to Studio > Plugins tab, and click Manage Plugins. Then update from there.