Advanced DataStore system with list

In this tutorial we will see how to make the best DataStoreService system I know.
For this tutorial we will use two services which are the DataStoreService and the HttpService

To do this, we will therefore call these two services using two variables.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")

One of the problems with roblox databases are the limits given to players.

They can be seen here:
Server Limits

For example, instead of saving the coins value each time it is modified, we will save it when the player disconnects, so that many requests are not made unnecessarily.

We will also retrieve the player’s information when he connects.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")

game.Players.PlayerAdded:Connect(function(player)

end)

game.Players.PlayerRemoving:Connect(function(player)

end)

The best way to save player data is to use lists. These allow you to store a lot of information in just a single request. The problem of not using a list and that each data represents a request

We will first create a variable to retrieve the table containing all the player data and we will also retrieve the data of a player.

To retrieve the data of a player we will use are UserId followed by a text representing the data that we retrieve, this avoids using the same Key.

! As you probably know it is impossible to save a list with the DataStoreService these are why we are going to use the HttpService to convert the list into text or convert a text into a list.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayerTable = DataStore:GetDataStore("PlayerData")

game.Players.PlayerAdded:Connect(function(player)
    local GetData = PlayerTable:GetAsync(player.UserId.."Data")
end)

game.Players.PlayerRemoving:Connect(function(player)

end)

Now that we have retrieved the player data we will check if it exists.

If it does not exist:
We will create a list representing the default data of the players.

If this data exists:
We will convert the text to a list.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayerTable = DataStore:GetDataStore("PlayerData")

game.Players.PlayerAdded:Connect(function(player)
    local GetData = PlayerTable:GetAsync(player.UserId.."Data")
    if GetData == nil then
        GetData = {} -- We transform the variable into a list
        GetData.Coins = 0 -- We establish the default values ​​of the list
        GetData.Level = 1
        GetData.Rank = "Noob"
        GetData.XP = 0
    else
        GetData = HttpService:JSONDecode(GetData) -- We turn the text into a list
    end
end)

game.Players.PlayerRemoving:Connect(function(player)

end)

After retrieving the player’s information, we will have to store it somewhere to be able to modify it or retrieve it.

Of course, since the code is on the Server side, the players will not be able to modify these values.
To save them we will create a folder inside the player named PlayerInfo. Inside this folder there will be a list of player information.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayerTable = DataStore:GetDataStore("PlayerData")

game.Players.PlayerAdded:Connect(function(player)
    local GetData = PlayerTable:GetAsync(player.UserId.."Data")
    if GetData == nil then
        GetData = {} -- We transform the variable into a list
        GetData.Coins = 0 -- We establish the default values ​​of the list
        GetData.Level = 1
        GetData.Rank = "Noob"
        GetData.XP = 0
    else
        GetData = HttpService:JSONDecode(GetData) -- We turn the text into a list
    end

    local Fold = Instance.new("Folder")
    Fold.Parent = player
    Fold.Name = "PlayerInfo"

    for name, value in pairs(GetData) do
        local NewInstance = nil
        if tonumber(value) ~= nil then
            NewInstance = Instance.new("NumberValue")
        else
            NewInstance = Instance.new("StringValue")
        end
        NewInstance.Parent = Fold
        NewInstance.Value = value
        NewInstance.Name = name
    end
end)

game.Players.PlayerRemoving:Connect(function(player)

end)

After making a system to automatically create the data we need, we ended up retrieving player information. Now we only have to save the player data.

local DataStore = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local PlayerTable = DataStore:GetDataStore("PlayerData")

game.Players.PlayerRemoving:Connect(function(player)
    local ListData = player.PlayerInfo:GetChildren()
    local DataList = {}
    for i = 1, #ListData do
        local Info = ListInfo[i]
        DataList[Info.Name] = Info.Value
    end

    PlayerTable:SetAsync(player.UserId.."Data", HttpService:JSONEncode(DataList))
end)

Of course, this tutorial is an example to show you how useful lists can be. It is possible to integrate lists into lists for advanced developers.

Thanks to the use of lists in the DataStoreService I was able to create a save system for a level editor.

Thank you for reading this tutorial right to the end! I would modify it according to your opinions or your requests.

2 Likes

There is a bit of misinformation, which I would like to point out here.

Who told you this?

Its Entirely Possible to Save a Table into a DataStore without needing to convert it to JSON before Saving it.

When usings DataStores, The Data should already be Encoded when Saved, and Decoded when using Retireving this Data, So I do not see the purpose of using HttpService when using DataStoreService, If anything, Its completely useless to do.

The only Practical use for :JSONEncode() on DataStores is to check how much space a DataStore will take up. With DataStoreService you only have up to 4 Megabytes of Data, which is roughly 4 million Bytes, Each Character (at least with a unicode below 128) will take up only 1 byte of information, With Regular Tables in Lua, you cant get all the Data that will be saved into a DataStore by checking the number of items it has, with :JSONEncode() it can help you determine how much of the data you are saving in all, here is an example:

max = 4e6 -- this is just the simplified version of saying 4 million (4000000)
luaTable = { -- Regular Table in Lua
    Level = 1;
    Cash = 0;
    EXP = 0;
}

spaceTaken = #HttpService:JSONEncode(luaTable) -- lua table to JSON
-- # is to get the number of characters in the string returned

print(`{spaceTaken}/{max}`) --> 28/4000000
-- In Total, this table should take up only 28 bytes out of the estimnated 4 million
-- Meaning you can save a lot of information in a DataStore

In All, There is no reason to use HttpService when using DataStoreService, Its just unnessacary code.


Now here is something small, but can very much improve your DataStore when used properly

When Saving Data into a DataStore, You have to understand that It can fail, and when it fails, your code will likely not function at all because there any precautions for this at all.

pcall() is used to Handle Errors, and a Common Misconception is that It completely Prevents it, which isnt true at all, When there is an error that you cannot control, you use this function to take care of what happened and what to do when it happens.
A pcall() will contain two pieces of data when finished, its status, and its error message, when the pcall() has completed its task, its status will return true, indicating that it was a success, since there is no error message, it will return as nil, this second piece of Data can also be used to get Data, which in the case of DataStoreService, can be used to get the Data from a Key.
When the pcall() fails, its status will return false, which as you guessed, means it failed, error message will appear.

local success, errorMessage = pcall(function) -- lest say you fire a function

if success then -- if the pcall was successful
    print("pcall() was successful!")
else -- if the pcall failed
    print(errorMessage) --> outputs an Error Message (which can vary)
end
6 Likes