Data System For Beginner Scripters

The data module provides a straightforward system which allows players to save and access their data, which works similar to ProfileService. The system provides users with a lightweight solution which they can easily use while creating their games.

local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local SSS = game:GetService("ServerScriptService")
local DSS = game:GetService("DataStoreService")
local DSSKey = "_Molo"
local DS = DSS:GetDataStore("DaMoloTest")
local DataTemplate = require(script.DataTemplate)
local RSS = game:GetService("RunService")
local DaDataModule = {}
local DaDataTable = {}
local DaLoadedPlayer = {}
local DaSessionId = game.JobId
local DaForceLoadTime = 10

local function CopyDaTemplateToDaPlayer(Profile)
	local ProfileA = {}
	for Data, DaThing in pairs(Profile) do
		if type(DaThing) == "table" then
			ProfileA[Data] = CopyDaTemplateToDaPlayer(DaThing)
		else
			ProfileA[Data] = DaThing
		end
	end
	return ProfileA
end

local function ReconcileDaData(Data, Template)
	for Key, Value in pairs(Template) do
		if Data[Key] == nil then
			if type(Value) == "table" then
				Data[Key] = CopyDaTemplateToDaPlayer(Value)
			else
				Data[Key] = Value
			end
		elseif type(Data[Key]) == "table" and type(Value) == "table" then
			ReconcileDaData(Data[Key], Value)
		end
	end
end

local function DaOwnsSession(Data)
	return Data and Data.DaSessionId == DaSessionId
end

function DaDataModule.LoadDaPlayerData(Player)
	local DaReturn
	local DaStartTime = os.clock()
	while true do
		local Success, Result = pcall(function()
			return DS:UpdateAsync(Player.UserId .. DSSKey, function(OldData)
				if OldData and OldData.DaSessionId and OldData.DaSessionId ~= DaSessionId then
					if os.clock() - (OldData.DaLastUpdate or 0) < DaForceLoadTime then
						return OldData
					end
				end
				local NewData = OldData or CopyDaTemplateToDaPlayer(DataTemplate)
				NewData.DaSessionId = DaSessionId
				NewData.DaLastUpdate = os.clock()
				return NewData
			end)
		end)
		if Success and Result and Result.DaSessionId == DaSessionId then
			DaReturn = Result
			break
		end
		if os.clock() - DaStartTime >= DaForceLoadTime then
			Player:Kick("Molo Attack")
			return
		end
		task.wait(1)
	end
	ReconcileDaData(DaReturn, DataTemplate)
	DaDataTable[Player.UserId] = DaReturn
	DaLoadedPlayer[Player.UserId] = true
	return DaDataTable[Player.UserId]
end

function DaDataModule.SaveDaPlayerData(Player)
	if not RSS:InStudio() and DaLoadedPlayer[Player.UserId] then
		for I = 1, 3 do
			local Success = pcall(function()
				DS:UpdateAsync(Player.UserId .. DSSKey, function(OldData)
					if not DaOwnsSession(OldData) then
						return OldData
					end
					local Data = DaDataTable[Player.UserId]
					if Data then
						Data.DaLastUpdate = os.clock()
					end
					return Data
				end)
			end)
			if Success then
				break
			end
			task.wait(1)
		end
	end
	DaDataTable[Player.UserId] = nil
	DaLoadedPlayer[Player.UserId] = nil
end

function DaDataModule.GetDaData(Player)
	return DaDataTable[Player.UserId]
end

Players.PlayerAdded:Connect(function(Player)
	DaDataModule.LoadDaPlayerData(Player)
end)

Players.PlayerRemoving:Connect(function(Player)
	DaDataModule.SaveDaPlayerData(Player)
end)

game:BindToClose(function()
	for _, DaPlayer in pairs(Players:GetPlayers()) do
		task.spawn(function()
			DaDataModule.SaveDaPlayerData(DaPlayer)
		end)
	end
	task.wait(3)
end)

return DaDataModule
2 Likes

For any opinions that can improve this system please reply and let me know!

1 Like