ReduxWrapper - Easy/Light-weight alternative to ProfileService (Datastore Module)

ReduxWrapper :happy1:

This module is an alternative to ProfileService made to exactly replicate it but without all of the session lock stuff which some people might not need, manipulating data on ProfileService is more complicated than it should be sometimes; session-locking is not as useful sometimes either.


So, I’d ask of you all to not come and yell at me for “oh my god profileservice is better!!1!” because this module was made just to lighten it and make it easier to use.

This code isn’t complex at all, and this requires a bit intermediate scripting knowledge in order to setup!

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local ContentProvider = game:GetService("ContentProvider")
local HttpService = game:GetService("HttpService")
local DataStoreService = game:GetService("DataStoreService")

local ReduxWrapper = {};

ReduxWrapper.waitForRequestBudget = function(requestType)
    local currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType);

    while true do
        if currentBudget < 1 then
            currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)

            task.wait(2.5)
        else
            break
        end
    end
end

ReduxWrapper.new = function(dataName, dataTemplate)
    local dataObj = DataStoreService:GetDataStore(dataName);
    local method = {};

    function method:LoadProfileAsync(key)
        local profileMethods = {};
        local success, err;

        repeat
            ReduxWrapper.waitForRequestBudget(Enum.DataStoreRequestType.GetAsync);

            success, err = pcall(dataObj.GetAsync, dataObj, key)
        until success

        local profileData = err;

        if profileData == nil then
            profileData = {
                MetaData = {
                    ProfileCreateTime = os.time();
                    LastUpdated = os.time();
                };

                Data = {}
            };
        end

        profileMethods.Data = profileData.Data;

        function profileMethods:AddUserId(userId)
            profileData.MetaData.UserId = userId;
        end

        function profileMethods:Reconcile()
            for dataTemplateKey, dataTemplateValue in dataTemplate do
                if not profileMethods.Data[dataTemplateKey] then
                    profileMethods.Data[dataTemplateKey] = dataTemplateValue;
                end
            end
        end
        
        function profileMethods:GetMetaTags()
            return profileData.MetaData;
        end

        function profileMethods:Save(noWait)
            local success, err;

            repeat
                if not noWait then
                    ReduxWrapper.waitForRequestBudget(Enum.DataStoreRequestType.UpdateAsync);
                end

                success, err = pcall(dataObj.UpdateAsync, dataObj, key, function()
                    return {
                        MetaData = profileData.MetaData;
                        Data = profileMethods.Data;
                    };
                end)
            until success
        end

        return profileMethods
    end

    return method
end

return ReduxWrapper

API Usage:

local profileStore = ReduxWrapper.new(datastoreName: string, datastoreTemplate: table) → Constructs a new datastore with the following “datastoreName” and “datastoreTemplate” to store

local profile = profileStore:LoadProfileAsync(keyName: string) → Returns a profile object from the datastore with all the exposed methods and profile.Data value

profile:AddUserId(userId: number) → Sets the UserId in the Meta Tags section which makes it easier to handle GDPR requests sent by Roblox

profile:Reconcile → Reconciles all of the missing values from the DataTemplate that weren’t in the current profile.Data value

profile:GetMetaTags() → This returns a table with the Meta Tag data which is the following:

  • ProfileCreateTime: number
  • LastUpdated: number
  • UserId: number (optional)

profile:Save() attempts to save the current profile.Data value to the profileStore

Saving and Modifying Data:

It’s simple to save and modify data using ReduxWrapper, matter of fact it’s the exact same way ProfileService does it, all you have to do is :LoadProfileAsync and then change the .Data value to whatever you desire.

Example code:

local ProfileStore = ReduxWrapper.new("PlayerData", {
     Coins = 0;
     Level = 3;
})

local profile = ProfileStore:LoadProfileAsync("Player_1")
profile:Reconcile()
profile.Data.Coins = 55;
profile:Save()

Obviously this is very bare bones, but this explains to you what you can do in order to save data.

Thank you for reading, This probably won’t be updated; also global updates are not a thing.

4 Likes

Another resource dropped by The only and one Redux!, Awesome, i will use it, i am using your redux hitbox module too as always!

1 Like