Data Store Efficiency

Hey guys, I am working on a game that requires a fairly large amount of data that needs to be saved. The data store script I wrote works fine, but I am afraid of the future unseen things that may happen. Is this script subject to data loss or inefficiency, and if so how do I make a better data store system? Thank you!

-- Set up table to return to any script that requires this module
local PlayerStatManager = {}


local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData3")
-- Table to hold player information for the current session
--local sessionData = {}

local AUTOSAVE_INTERVAL = 60



local Leveldata = {
	["Rank"] = 1,
	["XP"] = 0,
	["NeededXP"] = 120,
	["SkillPoints"] = 0,

}

local skills = {
	["HealthSkill_1"] = false,
	["HealthSkill_2"] = false,
	["HealthSkill_3"] = false,

	["EnduranceSkill_1"] = false,
	["EnduranceSkill_2"] = false,
	["EnduranceSkill_3"] = false,

	["AmmoSkill_1"] = false,
	["AmmoSkill_2"] = false,
	["AmmoSKill_3"] = false
}

local survivor = {
	["Survivor"] = "DefaultMale"

}

local survivorsUnlocked = {
	["DefaultMale"] = true,
	["DefaultFemale"] = true,
	["PlagueDoctor"] = true,
	["Horseman"] = true,
	["Channy97"] = true

}


local dailyTasks = {
	["LastRenew"] = 0,
	["LastLogin"] = 0,
	["ZombieKills"] = 0,
	
	
}

-- Function to add player to the "sessionData" table
local function setupPlayerData(player)

	print("Hi")
	local DataFromStore = nil

	local playerUserId = "Player_" .. player.UserId

	local leaderstats = Instance.new("Folder",player)
	leaderstats.Name = "leaderstats"

	local dataFolder = player:WaitForChild("leaderstats")

	local Cash = Instance.new("IntValue",dataFolder)
	Cash.Name = "Cash"
	Cash.Value = 0

	local zombieKills = Instance.new("IntValue",dataFolder)
	zombieKills.Name = "ZombieKills"
	zombieKills.Value = 0

	local headshots = Instance.new("IntValue", dataFolder)
	headshots.Name = "Headshots"
	headshots.Value = 0

	for name, value in pairs(Leveldata) do
		local new = Instance.new("NumberValue")
		new.Name = name
		new.Value = value
		new.Parent = leaderstats
	end

	local Skills = Instance.new("Folder",leaderstats)
	Skills.Name = "Skills"

	for name, value in pairs(skills) do
		local new = Instance.new("BoolValue")
		new.Name = name
		new.Value = value
		new.Parent = Skills

	end

	for name, value in pairs(survivor) do
		local new = Instance.new("StringValue")
		new.Name = name
		new.Value = value
		new.Parent = leaderstats

	end

	local SurvivorsUnlocked = Instance.new("Folder", leaderstats)
	SurvivorsUnlocked.Name = "SurvivorsUnlocked"

	for name, value in pairs(survivorsUnlocked) do
		local new = Instance.new("BoolValue")
		new.Name = name
		new.Value = value
		new.Parent = SurvivorsUnlocked

	end
	
	local DailyTasks = Instance.new("Folder",leaderstats)
	DailyTasks.Name = "Daily_Tasks"
	
	for name, value in pairs(dailyTasks) do
		local new = Instance.new("NumberValue")
		new.Name = name
		new.Value = value
		new.Parent = DailyTasks
	end

	local XP = leaderstats:WaitForChild("XP")
	local NeededXP = leaderstats:WaitForChild("NeededXP")
	local Rank = leaderstats:WaitForChild("Rank")
	local SkillPoints = leaderstats:WaitForChild("SkillPoints")

	-- LEVEL UP --
	XP.Changed:Connect(function()
		local leaderstats = Instance.new("Folder",player)
		leaderstats.Name = "leaderstats"
		if dataFolder:WaitForChild("XP").Value >= dataFolder:WaitForChild("NeededXP").Value then
			Rank.Value = Rank.Value + 1
			if Rank.Value % 5 == 0 then
				SkillPoints.Value = SkillPoints.Value + 1
			end
			XP.Value = XP.Value - NeededXP.Value
			NeededXP.Value = math.floor(NeededXP.Value * 1.33)
		end
	end)

	local s, e = pcall(function()
		DataFromStore = playerData:GetAsync("Player_" .. player.UserId)
	end)

	if s then
		print("Getting Data For: " .. player.Name)
		if DataFromStore then
			for name, value in pairs(DataFromStore) do
				if player.leaderstats:FindFirstChild(name) then
					print(player.leaderstats[name])
					player.leaderstats[name].Value = value

				elseif player.leaderstats.Skills:FindFirstChild(name) then
					print(player.leaderstats.Skills[name])
					player.leaderstats.Skills[name].Value = value

				elseif player.leaderstats.SurvivorsUnlocked:FindFirstChild(name) then
					print(player.leaderstats.SurvivorsUnlocked[name])
					player.leaderstats.SurvivorsUnlocked[name].Value = value

				else
					print("Not Found")
				end

			end
			print("Data Loaded For: " .. player.Name)
		else
			print("Created New Data For: " .. player.Name)
		end
	else 
		warn("Error Getting Data For: " .. player.Name)
	end
end





local function savePlayerData(player,playerUserId)

	local DataForStore = {}

	for name, value in pairs(player.leaderstats:GetChildren()) do
		if value.Name == "Skills" then
			for name1, value1 in pairs(player.leaderstats.Skills:GetChildren()) do
				DataForStore[value1.Name] = value1.Value
			end
		elseif value.Name == "SurvivorsUnlocked" then
			for name2, value2 in pairs(player.leaderstats.SurvivorsUnlocked:GetChildren()) do
				DataForStore[value2.Name] = value2.Value
			end
		elseif value.Name == "Daily_Tasks" then
			for name3, value3 in pairs(player.leaderstats.Daily_Tasks:GetChildren()) do
				DataForStore[value3.Name] = value3.Value
			end
		else
			DataForStore[value.Name] = value.Value
		end
	end

	local success = pcall(playerData.SetAsync, playerData, "Player_" .. player.UserId, DataForStore)

	if success then
		print("Successfully Saved Data For: " .. player.Name)
	end

	--playerData:SetAsync("Player_" .. player.UserId, DataForStore)

end




-- Function to save player data on exit
--local function saveOnExit(player)
	--local playerUserId = "Player_" .. player.UserId
	--savePlayerData(player,playerUserId)
--end

for _,player in ipairs(game.Players:GetPlayers()) do
	wait(60)
	coroutine.wrap(savePlayerData(player))
end


-- Connect "setupPlayerData()" function to "PlayerAdded" event
game.Players.PlayerAdded:Connect(setupPlayerData)

-- Connect "saveOnExit()" function to "PlayerRemoving" event

game.Players.PlayerRemoving:Connect(function(player)
	local DataForStore = {}

	for name, value in pairs(player.leaderstats:GetChildren()) do
		if value.Name == "Skills" then
			for name1, value1 in pairs(player.leaderstats.Skills:GetChildren()) do
				DataForStore[value1.Name] = value1.Value
			end
		elseif value.Name == "SurvivorsUnlocked" then
			for name2, value2 in pairs(player.leaderstats.SurvivorsUnlocked:GetChildren()) do
				DataForStore[value2.Name] = value2.Value
			end
		elseif value.Name == "Daily_Tasks" then
			for name3, value3 in pairs(player.leaderstats.Daily_Tasks:GetChildren()) do
				DataForStore[value3.Name] = value3.Value
			end
		else
			DataForStore[value.Name] = value.Value
		end
	end

	local success = pcall(playerData.SetAsync, playerData, "Player_" .. player.UserId, DataForStore)

	if success then
		print("Successfully Saved Data For: " .. player.Name)
	end

	--playerData:SetAsync("Player_" .. player.UserId, DataForStore)
end)
1 Like
  1. First convert the table to a string using HttpService and :JSONEncode().
  2. After, compress the string using an open-source Compression Module, or make one yourself.

When you want to fetch the data, you must de-compress what is retrieved then use HttpService to :JSONDecode() to decode the string back into your original table.

The DataStoreService automatically encodes and decodes data to and from a JSON format when that data is saved and loaded respectively.

But without encoding manually, one cannot compress their data using a string compressor. When retrieving data, they must decompress, then decode, otherwise it will be stuck as a string table.

You can compress/decompress table values in the same way you would a string value, i.e; expressing number values in hexadecimal.

I mean sure, go ahead, I was just giving them a heads up on what they could do.