Issue with saving datastores

  1. What do you want to achieve? Keep it simple and clear!
    I want to create a recursive datastore saving system so that it will save any values inside of values, etc.
  2. What is the issue? Include screenshots / videos if possible!
    The saving system itself works, but for some reason it won’t load any values that are supposed to be in other values.
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve tried printing for starts, but I am completely confused as to why it prints nothing in the console when I try to print a table.

It saves and when you join the game it successfully gets the save:

You check the StatsFolder and none of the values that were meant to be inside of other values are there.
What it’s supposed to look like when loaded:
image
What it looks like:
image

Here is my script: (the issue is in the parse() function from lines 65 - 129.

-- CentiDev :)

local dataStoreService = game:GetService("DataStoreService")
local dataStoreVersion = 1.4
local dataStore = dataStoreService:GetDataStore("DataStoreV"..dataStoreVersion)
local httpService = game:GetService("HttpService")


local defaultData = {
	Money = 0,
	Exp = 0,
	Donated = 0
}

local function len(t)
	local n = 0

	for _ in pairs(t) do
		n += 1
	end
	return n
end

local function removeEntry(t, e)
	local n = 0
	for i, v in pairs(t) do
		n += 1
		if i == e then
			table.remove(t, n)
		end
	end
end

local function find(t, e)
	for i, v in pairs(t) do
		if i == e then
			return true
		else
			return false
		end
	end
end

local function checkRealValue(valueName)
	for i, v in pairs(defaultData) do
		if i == valueName then
			return typeof(v)
		end
	end	
end

local function returnIndexValue(index, tbl)
	-- haha i look liek hackr man with all these functions
	local n = 0
	for i, v in pairs(tbl) do
		v += 1
		if n == index then
			return i, v
		end
	end
end

local serverCloseReady = Instance.new("BindableEvent")

local function parse(i, v, folder)
	-- yay it works
	if typeof(v) == "number" then
		local inst = Instance.new("NumberValue")
		inst.Name = i
		inst.Value = v
		inst.Parent = folder
	elseif typeof(v) == "table" then
		local Type = checkRealValue(i)
		if Type == "number" then
			local inst = Instance.new("NumberValue")
			inst.Name = i
			inst.Value = v.Value
			inst.Parent = folder
			
			print(unpack(v))
			for x, y in pairs(v) do
				if x ~= "Value" then
					parse(x, y, inst)
				end
			end
		elseif Type == "boolean" then
			local inst = Instance.new("BoolValue")
			inst.Name = i
			inst.Value = v.Value
			inst.Parent = folder

			for x, y in pairs(v) do
				if x ~= "Value" then
					parse(x, y, inst)
				end
			end
		elseif Type == "string" then
			local inst = Instance.new("StringValue")
			inst.Name = i
			inst.Value = v.Value
			inst.Parent = folder

			for x, y in pairs(v) do
				if x ~= "Value" then
					parse(x, y, inst)
				end
			end
		elseif Type == "table" then
			local inst = Instance.new("NumberValue")
			inst.Name = i
			inst.Value = v.Value
			inst.Parent = folder
			
			for x, y in pairs(v) do
				parse(x, y, inst)
			end
		end
	elseif typeof(v) == "boolean" then
		local inst = Instance.new("BoolValue")
		inst.Name = i
		inst.Value = v
		inst.Parent = folder
	elseif typeof(v) == "string" then
		local inst = Instance.new("StringValue")
		inst.Name = i
		inst.Value = v
		inst.Parent = folder	
	end
end

local function setupPlayer(plr, data)
	if data then
		
		print(len(data), len(defaultData))
		
		print(httpService:JSONEncode(data))
		if len(data) == len(defaultData) then
			for i, v in pairs(data) do
				parse(i, v, plr.StatsFolder)
			end
			-- ur mum
		elseif len(data) < len(defaultData) then
			print("More datavalues added!")
			for y, x in pairs(defaultData) do
				if not find(data, defaultData) then
					parse(y, x, plr.StatsFolder)
				end
			end
		elseif len(data) > len(defaultData) then
			print("Datavalues removed!")
			for i, v in pairs(data) do
				if not find(defaultData, i) then
					print("Removing indigent value: "..i)
					removeEntry(data, i)
				else
					parse(i, v, plr.StatsFolder)
				end
			end
		end
	else
		setupPlayer(plr, defaultData)
		print("Data is nil, using default data.")
	end
	print("Done setting up data!")
end

game.Players.PlayerAdded:Connect(function(plr)
	local statsFolder = Instance.new("Folder")
	statsFolder.Name = "StatsFolder"
	statsFolder.Parent = plr	
	
	local data
	
	local succ, amogus = pcall(function()
		local json = dataStore:GetAsync(plr.UserId)
		data = httpService:JSONDecode(json)
	end)
	
	-- haha lol amogus funny totally not overrated sussy 😳
	
	print(succ, amogus)
	
	if succ then
		setupPlayer(plr, data)
		print("Success getting data!\nSetting up data...")
	else
		for i = 1,3,1 do
			print("Retrying data...")
			wait(1)
			local succ, amogus = pcall(function()
				local json = dataStore:GetAsync(plr.UserId)
				if json then
					data = httpService:JSONDecode(json)
				end
			end)
			
			if succ then
				setupPlayer(plr, data)
				break
			end
		end
		setupPlayer(plr, defaultData)
	end
end)

local function recursiveSave(i, v, data)	
	if #v:GetChildren() == 0 then
		if v:IsA("NumberValue") then
			local tbl = {}
			tbl["Value"] = v.Value
			data[v.Name] = tbl
		elseif v:IsA("BoolValue") then
			local tbl = {}
			tbl["Value"] = v.Value
			data[v.Name] = tbl
		elseif v:IsA("StringValue") then
			local tbl = {}
			tbl["Value"] = v.Value
			data[v.Name] = tbl
		elseif v:IsA("Folder") then
			local tbl = {}
			data[v.Name] = tbl
		end
	else
		if v:IsA("NumberValue") then
			local tbl = {}
			
			for y, x in pairs(v:GetChildren()) do
				tbl = recursiveSave(y, x, tbl)
				tbl["Value"] = v.Value
			end
			data[v.Name] = tbl
			
		elseif v:IsA("BoolValue") then
			local tbl = {}
			
			for y, x in pairs(v:GetChildren()) do
				tbl = recursiveSave(y, x, tbl)
				tbl["Value"] = v.Value
			end
			data[v.Name] = tbl
			
		elseif v:IsA("StringValue") then
			local tbl = {}
						
			for y, x in pairs(v:GetChildren()) do
				tbl = recursiveSave(y, x, tbl)
				tbl["Value"] = v.Value
			end
			data[v.Name] = tbl
			
		elseif v:IsA("Folder") then
			local tbl = {}
			
			for y, x in pairs(v:GetChildren()) do
				tbl = recursiveSave(y, x, tbl)
			end
			data[v.Name] = tbl
		end
	end
	
	return data
end

game.Players.PlayerRemoving:Connect(function(plr)
	if plr.StatsFolder:GetChildren() ~= 0 then
		local data = {}
		for i, v in pairs(plr.StatsFolder:GetChildren()) do
			data = recursiveSave(i, v, data)
		end
		local encoded = httpService:JSONEncode(data)

		local succ, amogus = pcall(function()
			dataStore:SetAsync(plr.UserId, encoded)
		end)

		print(succ, amogus)

		if succ then
			print(plr.Name.."\n"..encoded)
		else
			for i = 1,3,1 do 
				wait(1)
				local succ, amogus = pcall(function()
					dataStore:SetAsync(plr.UserId, encoded)
				end)

				print(plr.Name.."\n"..encoded.."\nDone with "..i.." tries left!")
			end
		end
	else
		dataStore:SetAsync(plr.UserId, nil)
	end
	serverCloseReady:Fire()
end)

game:BindToClose(function()
	serverCloseReady.Event:Wait()
	-- el manana is a good song fite me
end)

--[[ I am very handome aren't I?

........,,,,,,,,,,,,,,.                                                         
,,,,,,,,,,,,,,,,,,,,,,,,..                                                      
,,,,,,,,,,,,,,*********,,,..                                                    
,,,,,*,******************,,,..                                                  
,**************************,,,..                                                
*****************************,,,...                                             
******************************,,,,...                                           
********************************,,,....                                         
******************//**/**********,,,,,...                                       
**************///////////////******,,,,,...                                     
**********//////////////////////******,,,,...                                   
******/////////////////////////////*****,,,,....                                
*****////////////////////////////(((*,/(*/**,,*,,.                              
*********//////////////////((((##((((((/((/(//**,,,.,,                          
**********//////////////(###%%%%%%%######(((((((/,,,, ,*,                       
*************/////////#%#%%%%#%%%%%%%##%#####(((((**,.. .*,.                    
***************/////(%(#%#%%%%%%%%%%%%%%########(((/*,...  *,                   
****************///###%%%#%%%%%%%%%%%%%%%%%%#####((((/,.*.  .,.                 
****************//%%#%%%###(#%%###%%##%%%%%%%######(///*,/.,.***,.              
****************/%%%#%#%#(/*/%%##(#%#####%%%%%%##((##(/////.,,//*.,,            
***************/%%&&#####(****/#%###%%###((##%%%%####((,,.*,*/*//...*           
**************(##%%%%%#%#/***((##%%%###(((((########(((/, ,((((////.,*.         
*************###%%%#%%&%(***/((###%%%%#(//((##%%#%%%##((/,.(#((((((/,/*...      
,***********%#%%%%%#&&&%(***//(/##%#%#((/////(######(/*,..,(##(#((((((/,,,..    
,********/(%#%%%%%%%&&&&(*,,,**///((///***//,,*///////**,,,#%%##(#####(**,,.,***
/////*//#&%%%%%%%%%&&&&&&********/////****///*,/((((/////**%%%%%######(***//**,,
((#%(%&%%%%%%&&&&&&%&&&&&(*******//(((/**//(((/,(##(((/////%%%%%%%%%%%#/*/((***,
#%%%%%%%&&&&&&&%&&&&&&&&&&/*****///(((((%#(##%%####(((((//#&&&&&&&&%%%*(/#((/***
%%%&&&&&&&&&&&@@@@@&@&&&&&#/***///////////(##((((###((((((&&&&&&&%&%%%%#(/*/(***
&&&&%&%&&&&&@@@&&@&&&&&&&%%////////////*/((((#(*(####((((#&%&&&&&&&&&&&&&%%%#(*.
%&&&&&&&&&@@@@&&@&%%%%%%%%%#///////(##%%%%%%%%%%&&%###((#&&&%&&&&&&&&&&&&&&&&%%%
&&&&&&&&&@@@@&@&&%%%%%%%%%%%%/////((((((##%%%%%%########&&&&%&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&%#%%%%%%%%%%&&((//((((((################%&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&@&&&&&&@&&&&&&&&&@@@@&&%/((((/////((######((##%#(#&&&&&@&&&&&&&&&&&&&&&&&&
&&&&&%&&&&&&&&&&&&&&@@&&&&&&(///(((((//(((########%%##(/&&&@@@&&&&&&&#&&&&&&&&&&
%&%&%&@@@&&&&&@&@@@@@@@&&&&#////(((((######%%%%%%%%%##(/&&&&&&&&&&&&&&&&&&&&&&&&
#&&&&@@&@@@@@@&&@@@@@@@@@&&(/////(((((######%%%%%%%###(&&&&@@&&&&&&&&&&&&&&&&&&&
&&&&@@@@@@@@@@&@@@@@@@@@&&%(////////((((###########(((%&&&&&@&&&&&@@&&&%&&&&%%&&
&&&&@@@@@@@@@&@@@@@@@@@@&&&(/////////(((((((((((///**#&&&&&&@@&&&&&@@@&&&%&&%%&&
&&&&&&@@@@@&@@@@@@@@@@@@@&&&(/////////////////////**#&&&@&&&@@@&&&&&&@@@&&&&&&(&
&&&&&&@@@@@&@@@@@@@@@@@@@@&&&&#(//////////////***/#&&&&@@&&&&@@@&&&&&&@@@&&&&&&&
&&&&&&&@@@&@@@@@@@@@@@@@@@&&&&&&&&&&&&(////////&&&&&&&&@&&&&&@@@@&&&&&&&&&&&&&&&
&&&&&&&&@&@@@@@@@@@@@@@@@@@&&@&&&&&&@@@&&&&&&@@@@@&&&&@@&&&&&&&@@@&&&&&&&&&&&&&&

]]--
2 Likes

Looks like you’re encoding with JSON before saving, Roblox already encodes the data with JSON and may cause errors encoding the data again, you must use a table that contains all values.

Also, you’re sending the SetAsync call 3 times?

			for i = 1,3,1 do 
				wait(1)
				local succ, amogus = pcall(function()
					dataStore:SetAsync(plr.UserId, encoded)
				end)

				print(plr.Name.."\n"..encoded.."\nDone with "..i.." tries left!")
			end

That’s why you must read the DataStore limitations before creating a script, the SetAsync function only accepts 6 seconds between write requests

1 Like

That is not answering my question. The script saves the data just fine (and saving the data 3 times was not intentional thats only supposed to retry if it errors and stop when it works ill fix it when i can)

The problem is that when converting the data to instances, only the first values are converted, the rest are ignored for some reason hence me asking this question.