Understanding datastores and creating a datastore effectively. -- Community tutorial

Tutorial basic requirements

You’ll need to know the fundamentals of scripting, so like functions, printing, if statements, events, tables, and what not. Please nail down these fundamentals before proceeding onto the tutorial. You’ll also need to know how to create basic leaderstats without saving. Datastores are pretty tricky to understand but I’ll try to keep it as simple as possible.

You will also need to save/publish your place on studio, and enable Studio access to API Services so that this will work. This can be done simply in the following order: Game Settings > Security > Enable Studio Access to API Services

Why use datastoreservice?
I’m pretty sure you know why you want to use it but, the reason you should use datastoreservice is to save things like items, values and what not. Anything to do with saving something of the player after leaving a session requires the use of datastoreservice. So let’s begin.

How to access a datastore

First you need to get the service. How? Simple.

local Datastoreservice = game:GetService("DataStoreService")

Now that we have the service we can access the datastore, using the method :GetDataStore()

local DataStore = Datastoreservice:GetDataStore("Data", "Gold")

The first parameter required is the name (string) of your datastore. Now, the name of your datastore is very important. If you randomly decide to change the name of your datastore it’ll create a new datastore and everyone’s data will be reset. So bare that in mind if you decide to change the name, the second parameter is an optional parameter that you can write in like I did (I named mine “Gold”) but by default is called “global”. The second parameter is called a scope (a unique string). A scope allows you to fetch keys and values which I will explain later. A scope also allows you to keep your code clean, scopes will automatically attach themselves to the keys that you provide using :GetAsync() and I will explain that method later on aswell.

Scopes

Here’s an example of what I mean by scopes attaching themselves to the keys you provide:

Key: gold/PlayerUserId
Scope: gold

Loading Data

Okay, hopefully that made sense. Next, we need to use :GetAsync() to fetch the value from the key.

game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"

local gold = Instance.new("IntValue", leaderstats)
gold.Name = "Gold"
data = DataStore:GetAsync("Player_"..player.UserId) -- Concatening the string with the player's user 
id
end)

Let me explain this now. :GetAsync() only requires one parameter (well technically two but no one uses the second really, also we’re making a basic datastore). This parameter is what we call a key (string). So, :GetAsync() also returns a value (which is a Tuple, a tuple is a list of values) from the provided key, I am storing that value in the variable called data as you can see in the code above. Let me explain why I did this in the next section. We’re loading the data in a PlayerAdded event because we need to be loading the player’s data when they join.

Error handling

However, :GetAsync() can sometimes fail or error. Having errors in a datastore is a huge issue for a game. THerefore, it’s best to wrap this method in a pcall

game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
	
local gold = Instance.new("IntValue", leaderstats)
gold.Name = "Gold"

local success, errormessage = pcall(function()
data = DataStore:GetAsync("Player_"..player.UserId)
end)
if success and data then
print("Success")
player.leaderstats.Gold.Value = data
else
warn(errormessage)
player:Kick()
end

Let me explain what this above code is doing, the first variable success is checking if the function has succeeded, if it has it will print a message in the output "Success". then I am setting Gold’s value to data, this is because when we use :SetAsync() later we will be setting a value to the key, we then need to set the leaderstats’ gold value to the value saved to the key . If :GetAsync() fails however, It will show a warning in the output stating why the function failed and kick the player. Using a pcall is important because if :GetAsync() does error then the rest of the script would not break, and the player can for example simply just rejoin and the data would successfully load the next time they join. We also need to use a pcall for :SetAsync(), I will explain what this method does soon.

Setting Data

Now, I’ll need to explain the concept of keys and datastores further.

Keys and datastores

A datastore is sort of like a lua dictionairy, the key that you provide will store a value. Here’s an illustration.

 Key:        Value:
"Gold"       = 1230                        
"4040312"    = "Amritss"
"cheese"     = true

To save/set our data that we have loaded, we must use :SetAsync(). This method allows you to set a value to the key, this will create essentially a dictionary like we have above with a key holding a value.

game.Players.PlayerRemoving:Connect(function(player)
local success, errormessage = pcall(function()
	DataStore:SetAsync("Player_"..player.UserId, player.leaderstats.Gold.Value)
	end)
	if success then
		print("Success")
	else
		warn(errormessage)
	end
end)

So we’re setting the player’s gold value to the key so that when the player leaves which I hope you guys sort of know why we do that, we set the value to the key so that when the player joins back
and we use :GetAsync() we can set the player’s gold value to the value we set to the key right now with :SetAsync().

Testing the Datastore

Now I am going to join on studio and see if my data saves, I’ve seen that sometimes data will be saved/loaded if you’re joining and leaving on studio and sometimes there will be errors shown even if your datastore is working. Therefore, try publishing your game and testing it there for it to work 100%.

Best practices

Send fewer datastore requests: It’s recommended to not use these Async methods too often since they have limits on how many times they can be sent since they have a throttle system. Which basically means the amount of “requests” you can send are limited (When you are using a method containing the word Async you are essentially sending requests).

Whenever you need to modify data, you should just change the value of it rather than getting the value stored from the datastore using getasync. (e.g. change gold value by doing leaderstats.Player.Gold.Value += 100 instead of getting the saved value returned by :GetAsync)

Only datastore async methods (e.g. GetAsync/SetAsync) have limits on how many times they can be sent. Therefore, this doesn’t need to be applied with any other async methods. You can call those however many times you please, but all async methods may fail so bare that in mind. But datastore async methods are given special care due to their limit system.

Create fewer datastores: Data stores behave similarly to tables in databases. Minimize the number of data stores in an experience and put related data in each data store. This approach allows you to configure each data store individually (versioning and indexing/querying) to improve the service’s efficiency to operate the data. As more features become available, this approach also allows you to more easily manage data stores.

  • Good tutorial?

0 voters

12 Likes