Improve my datastore?

The question is simply what I could improve with the system? (I have not implemented BindToClose and UpdateAsync because I do not know how to use them correctly.)

Setup:

image

image

Module

--// 𝙎𝙀𝙍𝙑𝙀𝙍
local DataService = {}

-- VARIABLES
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Marketplace = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local PlayerData = DataStoreService:GetDataStore("PlayerData")

local GameVersion = script:WaitForChild("Version")

local AutoSaveTime = 120
local PlayerSessionData = {} -- Storage for every players' data.

-- DATA STRUCTURE
local DataStructure = {
	Version = GameVersion.Value,
	Currencies = {
		Money = 0,
	},
}

-- DATASTORE
-- Change version value to one higher when an update is made.
function DataService:GetVersion(data, version) -- Gets current version of players' data.
	if data.Version ~= version.Value then -- Checks if data version is the same as dataservice' version.
		return true
	else
		return false
	end
end

function DataService:CopyTable(t)
	local tCopy = {}
	for k,v in pairs(t) do
		if (type(v) == "table") then
			tCopy[k] = self:CopyTable(v)
		else
			tCopy[k] = v
		end
	end
	return tCopy
end

function DataService:Sync(tbl, templateTbl)
	for k,v in pairs(tbl) do
		local vTemplate = templateTbl[k]
		if (vTemplate == nil) then
			tbl[k] = nil
		elseif (type(v) ~= type(vTemplate)) then
			if (type(vTemplate) == "table") then
				tbl[k] = self:CopyTable(vTemplate)
			else
				tbl[k] = vTemplate
			end
		elseif (type(v) == "table") then
			self:Sync(v, vTemplate)
		end
	end
	for k,vTemplate in pairs(templateTbl) do
		local v = tbl[k]
		if (v == nil) then
			if (type(vTemplate) == "table") then
				tbl[k] = self:CopyTable(vTemplate)
			else
				tbl[k] = vTemplate
			end
		end
	end
end

function Sync2(orig)
	local orig_type = type(orig)
	local copy
	if orig_type == 'table' then
		copy = {}
		for orig_key, orig_value in next, orig, nil do
			copy[Sync2(orig_key)] = Sync2(orig_value)
		end
		setmetatable(copy, Sync2(getmetatable(orig)))
	else
		copy = orig
	end
	return copy
end

function DataService:UpToDate(player) -- Check if any new data is added.
	local Data = PlayerData:GetAsync("data:"..player.UserId)
	local Updated = self:GetVersion(Data, GameVersion)
	if Updated then
		print(player.Name.."'s data is not up to date!")
		self:Sync(PlayerSessionData[player], DataStructure)
		PlayerSessionData[player].Version = GameVersion.Value
		print(player.Name.."'s data should now be up to date!")
	else
		print(player.Name.."'s data is already up to date!")
	end
end

function DataService:SaveData(player) -- Saves players' data.
	local tries = 0
	local success
	repeat
		tries = tries + 1
		success = pcall(function()
			PlayerData:SetAsync("data:"..player.UserId, PlayerSessionData[player])
		end)
		if not success then
			wait(1)
		end
	until tries == 5 or success
	if not success then
		warn("Cannot save data for "..player.Name.."!")
	else
		warn("Data was saved for "..player.Name.."!")
	end
end

function DataService:LoadData(player) -- Loads players' data.
	local success, currentData = pcall(function()
		return PlayerData:GetAsync("data:"..player.UserId)
	end)
	if success then
		local Data = PlayerData:GetAsync("data:"..player.UserId)
		PlayerSessionData[player] = Sync2(Data)
	else
		player:Kick("Data could not load successfully!")
	end
end

function DataService:SetupLeaderstats(player)
	local Folder = Instance.new("Folder", player)
	Folder.Name = "leaderstats"
	local Money = Instance.new("IntValue", Folder)
	Dribbles.Name = "Money"
	Dribbles.Value = PlayerSessionData[player].Currencies.Money
end

function DataService:SyncLeaderstats(player)
	local leaderstats = player:WaitForChild("leaderstats")
	leaderstats.Money.Value = PlayerSessionData[player].Currencies.Money
end

function DataService:SetupData(player) -- The setup for players' data.
	local Data = PlayerData:GetAsync("data:"..player.UserId)
	if not Data then -- Creates new data if the player doesn't have it.
		PlayerSessionData[player] = Sync2(DataStructure)
		self:SetupLeaderstats(player) -- Add physically visible leaderstats.
		return print(player.Name .. " has no data!")
	end
	if Data then
		print(player.Name.." has data!")
		self:LoadData(player) -- Load data.
		wait(0.5) -- Wait extra to wait for data to prevent UpToDate error.
		self:UpToDate(player) -- Check for any new additions & etc.
		wait(0.5)
		self:SetupLeaderstats(player) -- Add physically visible leaderstats.
	end
end

return DataService

Script

--// 𝙎𝙀𝙍𝙑𝙀𝙍
-- VARIABLES
local DataService = require(script.Parent.Parent.DataService)
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local PlayerData = DataStoreService:GetDataStore("PlayerData")
local RunService = game:GetService("RunService")

-- FUNCTIONS
game.Players.PlayerAdded:Connect(function(player)
	DataService:SetupData(player)
end)

game.Players.PlayerRemoving:Connect(function(player)
	DataService:SaveData(player)
end)
3 Likes

Unsure why you’d need to kick the player out for unloadable progress when you can use the retry repeat loop from saving data. I hope you keep in mind by default GetAsync returns nil meaning players who join for the first time thus have no data so you should put default data.

Past that, I only see micro-optimizations like localizing your Sync2 function.

2 Likes