Questions about ProfileService code

All scipt examples are taken from Basic Usage - ProfileService

-- ProfileTemplate table is what empty profiles will default to.
-- Updating the template will not include missing template values
--   in existing player profiles!
local ProfileTemplate = {
    Cash = 0,
    Items = {},
    LogInTimes = 0,
}

----- Loaded Modules -----

local ProfileService = require(game.ServerScriptService.ProfileService)

----- Private Variables -----

local Players = game:GetService("Players")

local ProfileStore = ProfileService.GetProfileStore(
    "PlayerData",
    ProfileTemplate
)

local Profiles = {} -- [player] = profile

----- Private Functions -----

local function GiveCash(profile, amount)
    -- If "Cash" was not defined in the ProfileTemplate at game launch,
    --   you will have to perform the following:
    if profile.Data.Cash == nil then
        profile.Data.Cash = 0
    end
    -- Increment the "Cash" value:
    profile.Data.Cash = profile.Data.Cash + amount
end

local function DoSomethingWithALoadedProfile(player, profile)
    profile.Data.LogInTimes = profile.Data.LogInTimes + 1
    print(player.Name .. " has logged in " .. tostring(profile.Data.LogInTimes)
        .. " time" .. ((profile.Data.LogInTimes > 1) and "s" or ""))
    GiveCash(profile, 100)
    print(player.Name .. " owns " .. tostring(profile.Data.Cash) .. " now!")
end

local function PlayerAdded(player)
    local profile = ProfileStore:LoadProfileAsync("Player_" .. player.UserId)
    if profile ~= nil then
        profile:AddUserId(player.UserId) -- GDPR compliance
        profile:Reconcile() -- Fill in missing variables from ProfileTemplate (optional)
        profile:ListenToRelease(function()
            Profiles[player] = nil
            -- The profile could've been loaded on another Roblox server:
            player:Kick()
        end)
        if player:IsDescendantOf(Players) == true then
            Profiles[player] = profile
            -- A profile has been successfully loaded:
            DoSomethingWithALoadedProfile(player, profile)
        else
            -- Player left before the profile loaded:
            profile:Release()
        end
    else
        -- The profile couldn't be loaded possibly due to other
        --   Roblox servers trying to load this profile at the same time:
        player:Kick() 
    end
end

----- Initialize -----

-- In case Players have joined the server earlier than this script ran:
for _, player in ipairs(Players:GetPlayers()) do
    task.spawn(PlayerAdded, player)
end

----- Connections -----

Players.PlayerAdded:Connect(PlayerAdded)

Players.PlayerRemoving:Connect(function(player)
    local profile = Profiles[player]
    if profile ~= nil then
        profile:Release()
    end
end)

I have several questions regarding the code because I don’t understand it or need some clarifications. I would appreciate if someone could explain it to me or point me to any sources that can do that.

1) In the part of the code that deals with players who left before their profile was loaded, the script uses Release(). As I understand it, Release() would remove session locking and save the player’s data for the last time. Then, you call Release(), and it triggers the ListenToRelease() function that would kick the player and set their profile to nil in the profile table. However, if the profile was not even loaded, what is there to save with the Release() function, and do you necessarily have to kick the player and set their profile table to nil in this case? Why don’t you just kick the player rather than using Release()?

if player:IsDescendantOf(Players) == true then
            Profiles[player] = profile
            -- A profile has been successfully loaded:
            DoSomethingWithALoadedProfile(player, profile)
        else
            -- Player left before the profile loaded:
            profile:Release()

2) I don’t understand what this would do or what the two parameters in task.spawn do.

for _, player in ipairs(Players:GetPlayers()) do
    task.spawn(PlayerAdded, player)
end

3) What is “profile” defined as here, and what is “Data” that you index? Where does “Data” even come from?

local function GiveCash(profile, amount)
    -- If "Cash" was not defined in the ProfileTemplate at game launch,
    --   you will have to perform the following:
    if profile.Data.Cash == nil then
        profile.Data.Cash = 0
    end
    -- Increment the "Cash" value:
    profile.Data.Cash = profile.Data.Cash + amount
end

ok anyone correct me if I’m wrong but I’ll answer your questions.

  1. From what I believe the reason release is used is to allow their data to be loaded into another server. If you kicked them without doing release, upon rejoining or joining a new server in your game they would get kicked because profile service thinks their data is being used in the server they were kicked from. Release just removes the session lock
    this can be bypassed by passing some parameters as shown here: API - ProfileService

  2. task.spawn in that code is putting the PlayerAdded function onto its own thread and passing the player as a parameter. I assume it does this because each time PlayerAdded is called its making API calls which can take some time individually if Roblox’s servers are lagging, or any other reason.

  3. the profile in that case is the players profile that you pass, so you would set up a server-side use of the function. As for what the data is, each profile contains a bunch of hidden values used by ProfileService to make everything work. All of the players data, which is outlined in the ProfileTemplate, is stored in its own table inside of the profile called Data.

1 Like

So, everything in the ProfileTemplate is stored in some table called Data that you can index and get the values from. How does the code understand what exactly is the “Template” table so that it would it in the Data table? Also, could you please send me the code in the ProfileService itself with the Data table, I can’t find it myself.

You need a profile template to store default values.