DataSave - Session Locking on DataStore v2 - (Archived)

This code was only used as a way of learning the new API of DataStore v2, and as a proof of concept, usage is discouraged, consider using ProfileService instead. ProfileService will properly work with DS V2 and will allow you to rollback versions, just like this, that’s why it’s marked as “Archived”.

You can use it to learn some of the aspects of the new API, in this case, see some of that code in action, that’s the only reason why I’m publishing it at the moment.

If you do use it, I would like to also add that you might have to actively check for updates and update your code, maybe changing some event names, things like that, these are the things I’m mostly messing with right now as that’s what I ain’t satisfied with on the inital release.

Also do note that some of the code is kind of ugly and messy, that’s what I’m gonna be fixing as soon as I can.

Documentation might be improved soon, there’s a lot in there which is not done, beware.

With all that said however, I will still be maintaining it, as I’m using it on one of my only public projects, and it works pretty decently well there, and is exactly what I want there.

This module was made with function names and others very similar to ProfileService, a lot of them being the same, as it was something most people would be more familiar with, and most importantly something that I would have been familiar with. These things might change in a future update.

A thing to add is that I don’t plan to have as much backwards compatibility in my head as other libraries I have, might be that in the future, if you wanna update to a new version, you might have to re-write your code to suit some new behaviour.


DataSave

Download
Documentation
Source Code


DataSave is a datastore library that uses session-locking and was made with DataStore v2 in mind, and uses some of their many features.

It works very similarly to ProfileService, main difference is that you don’t need to have your own profile handler for it. It simply works out of the box.

It caches profiles for you, allows you to wait for them to load, try to search them, etc.

It has features to properly connect UserIds to a profile, allowing for proper self GPDR by Roblox, along with possibly better GPDR messages describing what keys are linked to a certain player.

Usage Example:

local Players = game:GetService('Players');

local DataSave = require(game:GetService('ReplicatedStorage'):WaitForChild('DataSave'));
local ProfileStore = DataSave.GetProfileStore('PlayerData', {
    --\\ data that loads when no data is found. (template)
    
    Inventory = {
        Owned = {};
        Equipped = {};
    };

    Money = 0;
})

local function OnPlayerAdded(player)
    local profile = ProfileStore:LoadProfileAsync(player.UserId, function()
        if player.Parent ~= Players then
            return 'Cancel'; --\\ Stop trying to load their data if the player has left.
        else
            return 'ForceLoad';
        end
    end, player.UserId) --\\ Connected UserId(s);

    if profile then
        profile:Reconcile(); --\\ adds missing keys that are in the template but not in the data. if you add new stuff to your data-saving this is needed.

        if player.Parent ~= Players then
            --\\ if the player has left, release their data so another server can load it.
            player:Kick()
            profile:Release()
            return
        end

        profile:Released(function()
            --\\ fires whenever a profile has to be released because of some server-collision, or datastores being down.
            player:Kick('Data collided with another server! Join later!')
        end)

        local leaderstats = Instance.new('Folder')
        leaderstats.Name = 'leaderstats'
        leaderstats.Parent = player

        local Money = Instance.new('IntValue')
        Money.Name = 'Money'
        Money.Parent = leaderstats

        Money.Value = profile.Data.Money

        Money:GetPropertyChangedSignal('Value'):Connect(function()
            profile.Data.Money = Money.Value
        end)

        profile:Cache()
        --\\ :Cache() makes :WaitForProfile() and :FindFirstProfile() notice the loaded profile.
        --\\ :Cache() helps in data migration!;
    else
        player:Kick('\n \n There has been a problem loading your data. \n Please re-join. \n')
    end
end

for _, player in ipairs(Players:GetPlayers()) do
    task.defer(OnPlayerAdded, player)
    --\\ Handle players that joined before the script loaded.
end

Players.PlayerAdded:Connect(OnPlayerAdded);

Players.PlayerRemoving:Connect(function(plr)
    local profile = ProfileStore:FindFirstProfile(plr.UserId)
    --\\ Try to find an already loaded profile!

    if profile then
        profile:Release()
    end
end)
3 Likes

I also have a testing place you can try abusing the module or something.

1 Like

Release

https://github.com/LucasMZReal/DataSave/releases

  • Changed saving queue to use a linked list instead.
  • Using task.spawn on my DataStoreScheduler instead of coroutine.resume, fixes a silent error issue.