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!

--!strict

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()
			local function populateData(dataTemplate, data)
				for key, value in dataTemplate do
					if type(value) == "table" then
						if not data[key] then
							data[key] = {}
						end
						populateData(value, data[key])
					else
						if not data[key] then
							data[key] = value
						end
					end
				end
			end

			populateData(dataTemplate, profileMethods.Data)
		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
		
		function profileMethods:Release()
			table.clear(profileMethods)
		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.

5 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