Save your player data with ProfileService! (DataStore Module)

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

1 Like

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

3 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.

3 Likes

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

4 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.

12 Likes

I don’t really see any sense in using this.

1 Like

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.

5 Likes

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

5 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.

2 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.

9 Likes

Mind if you make a tutorial on this. Thanks

3 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

3 Likes

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

2 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

7 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?

3 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},
				...
			}
		},
	-- }
6 Likes

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

15 Likes

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

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

8 Likes

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

5 Likes

Short answer:
YouTube: Roblox - Dungeon Quest How To Duplicate Items (2020) (PATCHED)
The duplication exploit was patched by the developer himself, since DataStore2 does not offer session locking - he wouldn’t have to bother with such bugs with a module like ProfileService and now Dungeon Quest’s economy is under a permanent impact.

Data loss is not the enemy here, item duplication is:
Ninja Legends item duplication
Bubblegum simulator item duplication
Murder Mystery 2 item duplication
…And basically every game that had trading ever.
Except my game The Mad Murderer 2 which never had reports of item duplication because I am using session locking.

Long answer:
ProfileService is both simple to use and a professional-grade DataStore implementation - it means this module is just as useful as DataStore2 for the little developer, but when your game grows large and you go for huge features like trading, gifting, hopping between universe places, you’ll be pulling your hair out trying to manage the stability of such complex systems and preventing players from destroying your economy.

A few ProfileService vs DataStore2 points (Most relevant, descending):

  • DataStore2 does not session-lock internally. Your first trading system made with DataStore2 is 99% guaranteed to have duplication loopholes unless you understand session locking (and are willing to script this nightmare yourself).
  • DataStore2 is (massively) overrated and kind of built around false claims (claims that are not relevant in 2020) of DataStoreService being completely unreliable. First off, for what my name is worth, just know that I personally do not support such claims at all and thus see manual versioning as a waste of your game’s processing resources. ProfileService will, theoretically, only lose data when literally everyone else in the Roblox community will be losing data alongside with you. When DataStore2 developers talk about data loss, they refer to this problem specifically when DataStoreService returned nil instead of your saved data - Roblox would not afford making mistakes like that in the future and I don’t believe this will be a reoccurring problem which DataStore2 swears to defend you from. DataStore2 will not efficiently protect you from any other type of data loss such as caused by developer error (and neither will ProfileService).
  • To an experienced developer, ProfileService is SO MUCH MORE easier to use, because it does not overwhelm you with tons of getter and setter methods.
  • ProfileService has analytics endpoints and DataStore2 does not - this is crucial for big commercial projects!!!
  • If you’re familiar with DataStore2 API, DataStore2.Combine() was not something that should’ve existed at all - it just adds a technical obscurity layer to the primary function of DataStore2.
  • DataStore2 API nomenclature is a bit of a mess - almost everyone keeps getting confused between DataStore2 API objects and Roblox API objects since the creator picked the same names.
  • DataStore2 is bound to the Player instance. There’s no way to load offline player data or create savable data profiles that belong to a non-player entity while using the DataStore2 API.
  • DataStore2 source code might be harder to read and understand than ProfileService for a beginner.

If you’re part of the herd then at least try to learn a little about what exactly we’re all praising :joy:

31 Likes