First time making a complex datastore

Greetings! Created my first data store that is fit to store alot of data, This works fine and i think its good but i may not be doing important stuff that could prevent data loss.

Please let me know how i could improve this code to make it save data better

Note: Data is saved every 5 minutes, when a player spends robux on dev product, and when the player leaves. Data is loaded every time the player joins

local DefaultEncoded = {0, true, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, "Right", 50, 50, false, false, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, "THIS IS A EMPTY SLOT FOR THE TIME THE PLAYER JOINED"}

local function Encode(Plr)
	local Pets = {}
	for i,v in pairs(Plr.OwnedPets:GetChildren()) do
		table.insert(Pets, v.Value)
	end
	
	local EquipedPets = {}
	for i,v in pairs(Plr.Equiped:GetChildren()) do
		if v.Value then
			table.insert(EquipedPets, tonumber(v.Value.Name:match("%d+")))
		else
			table.insert(EquipedPets, 0)
		end
	end
	
	local Table = {Plr.leaderstats.Gold.Value, Plr.InvisStats.FirstTimer.Value, Plr.InvisStats.Xp.Value, Plr.InvisStats.PlayTime.Value, Plr.InvisStats.BestWorld.Value, Plr.InvisStats.CurrentWorld.Value, Plr.InvisStats.BestFrisbee.Value, Plr.InvisStats.EquipedFrisbee.Value, Plr.InvisStats.Rebirths.Value, Plr.InvisStats.StatStrengthLevel.Value, Plr.InvisStats.StatXpLevel.Value, Plr.InvisStats.StatCoinLevel.Value, Plr.InvisStats.RobuxSpent.Value, Plr.InvisStats.DailyRewardDay.Value, Plr.InvisStats.DailyRewardClaimedDay.Value, Plr.InvisStats.PlaytimeClaimedReward.Value, Plr.Settings.Handedness.Value, Plr.Settings.MusicVolume.Value, Plr.Settings.SfxVolume.Value, Plr.Settings.WalkSpeed.Value, Plr.Settings.GoldenFrisbee.Value, Pets, EquipedPets, Plr.InvisStats.JoinDate.Value}
	
	local TheSame = true
	for i,v in pairs(DefaultEncoded) do
		if i ~= #Table then
			if type(v) ~= "table" then
				if Table[i] ~= v then
					TheSame = false
				end
			else
				for I,V in pairs(v) do
					if Table[i][I] ~= V then
						TheSame = false
					end
				end
			end
		end
	end
	if TheSame then
		return false
	end
	return Table
end

local function Decode(Plr, Data)	
	local Pets = {}
	for i,v in pairs(Plr.OwnedPets:GetChildren()) do
		table.insert(Pets, v)
	end
	
	local PetsNames = {}

	for i,v in pairs(game.ReplicatedStorage.Pets:GetChildren()) do
		table.insert(PetsNames, v.Name)
	end

	table.sort(PetsNames, function(a, b)
		return a < b
	end)

	local EquipedPets = {}
	for i,v in pairs(Plr.Equiped:GetChildren()) do
		table.insert(EquipedPets, v)
	end
	
	local Table = {Plr.leaderstats.Gold, Plr.InvisStats.FirstTimer, Plr.InvisStats.Xp, Plr.InvisStats.PlayTime, Plr.InvisStats.BestWorld, Plr.InvisStats.CurrentWorld, Plr.InvisStats.BestFrisbee, Plr.InvisStats.EquipedFrisbee, Plr.InvisStats.Rebirths, Plr.InvisStats.StatStrengthLevel, Plr.InvisStats.StatXpLevel, Plr.InvisStats.StatCoinLevel, Plr.InvisStats.RobuxSpent, Plr.InvisStats.DailyRewardDay, Plr.InvisStats.DailyRewardClaimedDay, Plr.InvisStats.PlaytimeClaimedReward, Plr.Settings.Handedness, Plr.Settings.MusicVolume, Plr.Settings.SfxVolume, Plr.Settings.WalkSpeed, Plr.Settings.GoldenFrisbee, Pets, EquipedPets, Plr.InvisStats.JoinDate}
	
	for i,v in pairs(Table) do
		if i ~= #Data then
			if type(v) ~= "table" then
				if Data[i] ~= v.Value then
					v.Value = Data[i]
				end
			else
				if #v == 3 then
					for I,V in pairs(v) do
						if Data[i][I] ~= 0 then
							for o, p in pairs(Pets) do
								if tonumber(p.Name:match("%d+")) == Data[i][I] then
									V.Value = p
									require(game.ReplicatedStorage.Modules.AddPets).AddPet(Plr, PetsNames[p.Value])
								end
							end
						end
					end
				elseif #v == 50 then
					for I,V in pairs(v) do
						if Data[i][I] ~= V.Value then
							V.Value = Data[i][I]
						end
					end
				end
			end
		end
	end
end

local DS = game:GetService("DataStoreService")
local Store = DS:GetDataStore("DataStore")

local DataStore = {}

function DataStore.SaveData(Plr)
	if Plr.InvisStats.SaveData.Value then
		local success, Error = pcall(function()
			local EncodedData = Encode(Plr)
			if EncodedData then
				Store:SetAsync(Plr.UserId, EncodedData)
			end
		end)
		if not success then
			warn("Failed to save: ", Error)
		end
	end
end

function DataStore.LoadData(Plr)
	local success, Error = pcall(function()
		local EncodedData = Store:GetAsync(Plr.UserId)
		if EncodedData then
			Decode(Plr, EncodedData)
		end
	end)
	if not success then
		warn("Failed to load: ", Error)
		Plr.InvisStats.SaveData.Value = false
		Plr:Kick("We failed to save your data! Try again later.")
	end
end

return DataStore

Thanks for any help!

1 Like

Hey @ElipsGames,

This is purely optional to be honest but I enjoy using Profile Service in my games as it includes profile session-locking, cross server gifting and profile data organizing.

For your pets, you could implement gifting across servers for example.

I’m aware there is also Profile Store which is newer but we maintain our legacy code using profile service hence why we are yet to switch, but let me know if you need any help with it.

3 Likes

Sounds better! i dont wana completely recode my data store but ill look into it for future games.

2 Likes

Outside of the fantastic recommendation of using Profile Service or Profile Store, I’d highly recommend saving a dictionary rather than an array. It’s way more readable so you don’t have to guess what index 27 of the table is.

local data = {
    Gold = Plr.leaderstats.Gold.Value,
    InvisStats = {
        FirstTimer = Plr.InvisStats.FirstTimer.Value,
        Xp = Plr.InvisStats.Xp.Value,
        PlayTime = Plr.InvisStats.PlayTime.Value,
        ... -- continued for all stats
    },
    Settings = {
        Handedness = Plr.Settings.Handedness.Value,
        ...
    },
    Pets = Pets,
    EquipedPets = EquipedPets -- type Equipped btw.
}

Store:SetAsync(Plr.UserId, data)

Later it helps when getting the data

local data = Store:GetAsync(Plr.UserId)

Plr.leaderstats.Gold.Value = data.Gold
for _, value in Plr.InvisStats:GetChildren() do
    value.Value = data.InvisStats[value.Name]
end
for _, value in Plr.Settings:GetChildren() do
    value.Value = data.Settings[value.Name]
end
for _, pet in data.Pets do
    -- custom logic for pets
end
1 Like