Save your player data with ProfileService! (DataStore Module)

[07/28/2020] Most recent bug fix to ProfileService improves :BindToClose() functionality - please update to the latest version via github or Roblox library and see that your module contains this change:

19 Likes

ProfileService’s benefit of preventing duplicate data should be considered highly superior to DataStore2 because it’d mean such a detrimental vulnerability in data being fixed in your module.

What causes duplicate data in data store to be possible anyways in detail?

6 Likes

I go over exactly what happens in the first 5 minutes of this video.

5 Likes

I understood now but I was confused on how loleris solved the issue using session locking, how would this exactly work in Roblox code, how is session locking done within a Roblox Lua module? (not asking for the script but an explanation of how it’d work inside code)

2 Likes

A ProfileService profile is saved on a single DataStore key. that same key (Within the table saved on that key) will be tagged with a server-unique JobId (game.JobId) to become session-locked. When a server releases the profile, the JobId tag is removed. When a server steals a profile, the JobId tag is replaced and the previous server loses writing privileges to that profile.

14 Likes

Thanks for that explanation, it was really easy to understand. I’m going to implement a method like this thanks to you! Never even understood how big of a problem item duping could be and how important a session locking system is

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

Is there much of a reason as to why non defined template values would need to be written in manually? I just find it slightly tedious to do so. Could the module not do this automatically or is there some sort of bad practice to this?

Great contribution, I’m definitely switching over to this module before my games release.

2 Likes

That is a pretty crude example. The reality is that ProfileService is pretty open to any sort of writing / reading style you prefer - all you’re really working with is a Lua table profile.Data which will, pretty much, stay whatever you set it to between your game sessions… With a few exceptions.

ProfileService is basically designed to only solve your loading / caching / session-locking problems. It can easily be extended with another module (or your personal code) for data reading / writing, replication to clients, etc.

5 Likes

[07/30/2020] New troubleshooting documentation is available!
image

5 Likes

Is there any built in functionality which allows you to have a callback function before the profile is actually saved?

One use case could be packing leaderstats data into the profile before it’s saved instead of periodically updating the profile with the leaderstats values (unless I’m thinking of this wrong)

Or lets say you have a game like Booga Booga where you want to remove combat tags when the game is shutdown (to prevent items being lost).

1 Like

The goal of ProfileService is to free you from thinking about when to save - ProfileService WILL save ALL changes to the Profile.Data table until the profile is released.

It would also be a good idea to make the Profile.Data as a “single source of truth” while leaderstats would just be a visual representation of data within that table.

Dynamic data like combat tags that you speak of is probably better off as data that is not stored in a player profile to begin with. Even if it is, you should rather reset it when the player joins a new server, NOT when the existing server is shut down. Editing player data mostly at the end of a session is making yourself vulnerable for data loss risk when Roblox servers crash - don’t do that - look at your player data as something fluid and independent from when a player joins or leaves the game. Data transforms when a player does something.

In layman’s terms - ProfileService is a great combination of speed, easy implementation and reliability. If you want more bells and wisthles… You won’t really even get anything better than this at this moment :sunglasses:

11 Likes

Honestly this is pretty epic!
Being able to have a single table to read-write to and have it save itself is just amazing!
Session lock? Extremly underrated!

Overall 10/10, would use if my datastore saving script wasn’t a cryptic mess.
Great job @loleris!

12 Likes

I am confused as to why session locking is even necessary. If you are saving the “metadata” to the same key and you retrieve data from that key when you load the profile, why can’t you just retrieve the saved data? I understand what session locking does, but wouldn’t it prove useless if you are storing the status of the session lock under the same key that stores the profile data?

You might not be familiar with how GlobalDataStore:UpdateAsync() works - Roblox tries to ensure that all UpdateAsync calls among multiple Roblox servers are handled in the order they were called in global time. UpdateAsync is both a :GetAsync() and a :SetAsync() and won’t let anything change in the DataStore key between the time it takes to perform twose two actions.

We can’t store the session-lock tag on a separate DataStore key because after we would check a separate key of a session-lock and know “it’s okay to write” to a profile, there will appear a small time period where the session-lock tag could be changed by an external server BEFORE the current server makes changes to the player data key. You would also be making twice as many DataStore calls to maintain two DataStore keys for completely no benefit.

A session lock is a time-critical state and servers must know what this state is EVERY TIME they write to a profile. There must be a guarante that a remote server can’t steal the session lock before the current server finishes writing to the profile, otherwise the remote server might grab an older version of profile data - only UpdateAsync can give that guarantee.

6 Likes

Apologies for not understanding, but what do you mean by this statement? I understand how UpdateAsync can be viewed as a GetAsync and SetAsync packed into one.

As in calling :GetAsync() and :SetAsync() quickly one after another won’t guarantee there can’t be another server getting or setting same data in between those two calls.

4 Likes

Just wondering, is the session lock enabled by default?

Yes, it should be enabled by default.

1 Like

There is no way to load a profile without session locking it while using ProfileService :stuck_out_tongue:

ProfileService also stops saving Profile.Data when the server no longer owns the session lock for that profile.

4 Likes

From my experience, it doesn’t seem like the session lock creates many problems if you’re using it for traditional player data saving and loading if you implement it correctly and release the profiles as soon as you’re done with them. The fix loleris committed to the github repo the other week fixed the shutdown process releasing the profiles properly.

1 Like