Save your player data with ProfileService! (DataStore Module)

In my opinion, this module should be used as your primary data management solution with bereza’s method of saving data as backups, if you really want them. This module seems to handle lots of nasty edge cases you ordinarily would just have to encounter yourself before you figure out how painstakingly difficult they are to fix.

4 Likes

I don’t know if I’m using this wrong or what, but whenever I change the data it still uses the default.
This is how I have mine set up right now:

-- ProfileTemplate table is what empty profiles will default to.
-- Updating the template will not include missing template values
--   in existing player profiles!
local ProfileTemplate = {
   	text = "None"
}

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

local ProfileService = require(script.ProfileService)

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

local Players = game:GetService("Players")

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

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

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




local function PlayerAdded(player)
    local profile = GameProfileStore:LoadProfileAsync(
        "Player_" .. player.UserId,
        "ForceLoad"
    )
    if profile ~= nil then
        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
			print(profile.text.Stand)
			profile.Data.text= "haskfhaf"
            -- A profile has been successfully loaded:
        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
    coroutine.wrap(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)

Taken practically straight from your example.
The first time running, it should print None, then set the text to haskfhaf
The next time running, it should print haskfhaf, but it doesn’t save, and instead prints None

3 Likes

You are printing the table member Stand, though you’re declaring text in the template.

12 Likes

Forgot to change that portion, but even with it fixed the issue still exists.

EDIT:
Looks like I found the issue:

Problems in Roblox studio testing

ProfileService data saves will not persist between your Roblox studio testing sessions. In testing mode ProfileService will store and load all your profiles to and from a mock-up “DataStore” table which will disappear after you finish your testing session. The only way to know if your data saving works is through playing your game online on the Roblox servers.

I was testing in studio, my bad.

10 Likes

Would you mind explaining a bit how you implemented the session locking functionality?

9 Likes

First off, ProfileService stores your saved data Profile.Data AND additional meta data (data about itself) on the same DataStore key.

When a Roblox server wants to load a certain profile, it will first read the DataStore key for its meta data where a session-lock tag resides - at any given time a profile can be marked as session-locked (tag is marked with {game_id, server_job_id}) or released (tag is nil). If a session-lock for that profile is nil, a ProfileService load call will immediately session-lock it with a tag unique to that Roblox server (game.JobId) and will continue to auto-save to this profile until it is released by the same server or some remote server makes a release request OR a remote server steals the session-lock by changing the session-lock tag.

If a Roblox server attempts to load a profile which is session-locked by another server (And you’ve told ProfileService to “ForceLoad” the profile), it will request the remote server to release the profile by setting a special “release request” tag which the remote server will notice during its next auto-save. A “ForceLoad” request will wait until the remote server releases the profile or until a little over a minute passes after which the profile will be session-stolen - the session-lock tag will be overwritten with a new value regardless of whether the remote server noticed the “release request” tag.

23 Likes

I don’t really see any sense in using this. (do not mind it. i’ve changed my mind)

5 Likes

The issues that it fixes are issues that you really have to experience before you know you have them, and they are a massive pain to fix on a game that’s already launched because you’re dealing with live data.

15 Likes

Could you make an in depth youtube video using this module, I’m more of a visual learner

12 Likes

This module is actually very useful for handling data loss issues and item duplication bugs it also contains features like global updates which again can be very useful in a lot of cases. I have recently switched over to this module and it has been working like a charm the creator is also very helpful with any problems you might run into whilst using this module I totally recommend it.

6 Likes

I’ll consider provinding further learning material if this module gains more traction - maybe at that point someone else will make a tutorial video XD

As a super experienced developer I might not see the module the same way you see it and I might not be the best person to explain it.

19 Likes

Mind if you make a tutorial on this. Thanks

7 Likes

Hey loleris, do you know why it takes so long to get the player data with the module? It takes from 20 seconds up to like 40 (normal datastore takes almost no time at all). My current script is almost exact same as the example script just different profile_template

8 Likes

Send me your source in DM’s and we’ll figure it out

9 Likes

This is a really promising module. Datastores have always been a weak point for me and any errors take me days to fix. I’ve read up the API and it seems really useful, however as other people here also said, It doesn’t really have good beginner examples or a tutorial. I think making an open source project (small project) will actually help a lot for people who don’t know how this would be used in a production game or project

15 Likes

This looks really great and really easy for someone who has a lot of trouble with DataStores (me) to comprehend and work with.

What name and scope does this API use so we can connect to our DataStores using @Crazyman32’s DataStore Editor?

7 Likes

It does not append anything on your datastore name and datastore key strings, while the scope for the datastore object is not set (Which means it defaults to “global”).

image

Be cautious of what you edit - setting ProfileService related data to the wrong value types (outside of .Data / .MetaTags you’ve saved) will cause ProfileService to overwrite the profile with a blank one and register a profile corruption. I did make ProfileService save a human readable format, so it shouldn’t be too hard to edit.

You should expect the following structure saved:

	-- {
		Data = {},
		MetaData = {
			ProfileCreateTime = 0,
			SessionLoadCount = 0,
			ActiveSession = {place_id, game_job_id} / nil,
			ForceLoadSession = {place_id, game_job_id} / nil,
			MetaTags = {},
		},
		GlobalUpdates = {
			update_index,
			{
				{update_id, version_id, update_locked, update_data},
				...
			}
		},
	-- }
	
	OR
	
	-- {
		GlobalUpdates = {
			update_index,
			{
				{update_id, version_id, update_locked, update_data},
				...
			}
		},
	-- }
15 Likes

Thanks @okeanskiy for creating a video guide for ProfileService!
:arrow_right: YouTube playlist

30 Likes

And thank you as well for the support :+1:

If anyone has any questions about my videos, my DMs are open as well.

22 Likes

How is this better then the already existing DataStore2 which handles dataloss just fine?

8 Likes