Saving a folders with values in the easiest way

I want to save some folders with some values, but the folders are in the player and the script must doesn’t know what kind of folder they are because when I add something new to the folders I don’t need to edit the script, this will save my time, i like that:).
Save when player left.
Load when player joined.

I’ve already seen tutorials on this topic, but didn’t find anything that could help me.
image

1 Like

So you basically want to traverse the player folders and find all values and then serialize/deserialize them?
Just use GetDescendants() and cherry-pick only these instances whose class is ValueBase.
After you cherry-picked all value based instances, check their type if it’s NumberValue, StringValue or whatever else, take the value from it and save to datastore, or load the datastore and set the value.

2 Likes

What’s the difference with GetChildren()) and GetDescendants()?

I use the GetChildren()) to get all parts.

GetChildren())

for _,part in ipairs(workspace:GetChildren) do
	if part.ClassName == "Part" or part.ClassName == "Model" or part.ClassName == "Folder" then
		part:Destroy()
	end
end

GetDescendants()

for _,part in ipairs(workspace:GetDescendants()) do
	if part:IsA("Part") or part:IsA("Model") or part:IsA("Folder") then
		part:Destroy()
	end
end

GetChildren() only checks for instances under specified one (one level check)
GetDescendants() checks all instances and their instances under them and so on (recursive traverse)

Your GetChildren implementation wont work since you’re missing parentheses in ipairs, you’re not using it as function.

1 Like

Sorry for making a error with ipairs lol.

Oh just learning the difference of them.ill train the GetDescendants() command to use it succesfully.

learning it new.i was using the GetChildren method everytime :confused:

Okay, but to be honest, I don’t understand saving data and I think it would be difficult for me. I can make the most common scripts that act on the world (game.Workspace/ReplicationStorage) and local scripts that act on the player (game.Players) and that’s all I can do, so I don’t understand how to implement what was written above.

What’s difficult to understand for you? I’ll try to explain it. I think I’ll not show some examples, but I can try to explain the theoretical part of it.

I read this today: Saving Data | Documentation - Roblox Creator Hub
But when it came to the example “Gold Rush”(Place), the script was not quite the same as in the documentation. Maybe i miss something?

Do not use the example game but try to utilize the example code provided you in the article.
This article basically shows you basic use of datastore, which is the part where I described that it is easy. It gets difficult when there are too much data in the game, but that’s not your current situation I’d say.

SetAsync saves data to datastore
GetAsync loads data from datastore

1 Like

I will try write script, but what the easiest:
Save folders(folder will save with values?),
Save Values in folders,
Save folders and values?

It’s working but i need “Saving folders with values in the easiest way”

local DataStoreService = game:GetService("DataStoreService")
local goldStore = DataStoreService:GetDataStore("PlayerGold")


game.Players.PlayerAdded:Connect(function(player)
	
	local playerUserID = player.UserId
	local playerGold = 250

	-- Read data store key
	local getSuccess, currentGold = pcall(function()
		return goldStore:GetAsync(playerUserID)
	end)
	if getSuccess then
		print(currentGold)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)

	local playerUserID = player.UserId
	local playerGold = player.leaderstats.Money.Value

	-- Set data store key
	local setSuccess, errorMessage = pcall(function()
		goldStore:SetAsync(playerUserID, playerGold)
	end)
	if not setSuccess then
		warn(errorMessage)
	end
end)

You cant save folders… You can save only primitive values or structures(tables) with primitive values.

Here’s my attempt of creating an easy dynamic way to convert a folder/value structure to a dictionary and vice versa:

local function isIntVal(val: number)
	--IntValues in the range of x < -2^51 < 2^51 < x
	--Will be converted to NumberValues on next decode
	local safeMaxInt = 2^51
	if val < -safeMaxInt or val > safeMaxInt then return false end
	return val == math.floor(val)
end

local function toReal(num: number): number
	local offset = 10^(-15)
	repeat
		num += offset*5
		offset *= 10
	until not isIntVal(num)
	return num
end

local function toDictionary(root: Instance): {} 
	local data = {}
	for _, child in pairs(root:GetChildren()) do
		if child:IsA("NumberValue") and isIntVal(child.Value) then
			data[child.Name] = toReal(child.Value)
		elseif child:IsA("ValueBase") then
			data[child.Name] = child.Value
		elseif child:IsA("Folder") then
			data[child.Name] = toDictionary(child)
		end
	end
	return data 
end

local function toFolders(dict: {}, folder: Folder?): Folder 
	local folder = folder or Instance.new("Folder") 
	local easyTypes = {"BrickColor", "CFrame", "Color3", "Ray", "Vector3"}
	for name, value in pairs(dict) do
		local valType = typeof(value)
		local val
		if valType == "number" and isIntVal(value) then
			val = Instance.new("IntValue") 
		elseif valType == "number" then
			val = Instance.new("NumberValue") 
		elseif valType == "boolean" then
			val = Instance.new("BoolValue")
		elseif valType == "string" then
			val = Instance.new("StringValue") 
		elseif valType == "Instance" or valType == "nil" then
			val = Instance.new("ObjectValue") 
		elseif table.find(easyTypes, valType) then
			val = Instance.new(valType.."Value")
		elseif valType == "table" then
			val = Instance.new("Folder") 
			toFolders(value, val) 
		else
			warn("Failed to load key", name, "and value", value, "ignoring...")
			continue 
		end
		val.Name = name 
		if valType ~= "table" then val.Value = value end
		val.Parent = folder 
	end
	return folder 
end

Example usage:

--According to game logic specified on topic description

--For saving:
local dict = toDictionary(player) 

--For loading:
local folder = toFolders(dict)
for _, child in pairs(folder:GetChildren()) do
	child.Parent = player 
end

Keep in mind that datastores don’t allow the saving of instances. So for that you have to add an extra step on top of this to convert them into another data type, for example serializing them as strings or only storing the necessary pieces of information to recreate them.

Unless you’re using deprecated Data Persistence :fearful:.

Sorry, but I don’t understand a lot. Functions? How did I use them? PlayerAdded/PlayerRemoving :smiling_face_with_tear:

Here’s another example:

game.Players.PlayerAdded:Connect(function(player)
	local dict = --the dictionary you fetched from DS
	local folder = toFolders(dict)
	for _, child in pairs(folder:GetChildren()) do
		child.Parent = player
		print(child)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local dict = toDictionary(player)
	print(dict)
	--save dictionary here
end)
  1. What dictionary is? (I read: Dictionaries are tables that associate names or keys with a value instead of an index. Is that true?)
  2. Script need to scan everything in folders, put everything in dictionary(table), save it when player leave and load when player join?

I understood it this way.

Sir,if you don’t know scripting too much like Datastore,variables,i wouldn’t recommended opening this post.but you should understand what they say.

Just a example​:woman_bald:t6::woman_bald:t6::woman_bald:t6:

local DataStore = game:GetService("DataStoreService")
local D = DataStore:GetDataStore("PlayerData")

game:GetService("Players").PlayerAdded:Connect(function(Plr)
	local success,data = pcall(function()
		return D:GetAsync(Plr.UserId)
	end)
	
	if success then
		if data ~= nil then
			for i,v in data do
				print("bro got scared and got smarter and it is the "..v.." Number⚠️❌🐷🔎😱🥵")
			end
		end
	end
end)

game:GetService("Players").PlayerRemoving:Connect(function(Plr)
	D:SetAsync(Plr.UserId,{math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000),math.random(0,1000)})
end)

Try checking this out, just made it

local DataStoreService = game:GetService("DataStoreService")
local STORE = DataStoreService:GetDataStore("STR")

game.Players.PlayerAdded:Connect(function(Player)
	local Key = tostring(Player.UserId)
	local DataPlayer = STORE:GetAsync(Key)
	if DataPlayer ~= nil then
		--ASGSA
		for NameFolder,CheckFolder in pairs(DataPlayer) do
			local NewFolder = Instance.new("Folder")
			NewFolder.Name = NameFolder
			NewFolder.Parent = Player
			for _,CheckValue in pairs(CheckFolder) do
				local ThisVal = nil
				if CheckValue["TypeValue"] == "String" then
					local NewValue = Instance.new("StringValue")
					ThisVal = NewValue
				elseif CheckValue["TypeValue"] == "BoolValue" then
					local NewValue = Instance.new("BoolValue")
					ThisVal = NewValue
				elseif CheckValue["TypeValue"] == "IntValue" then
					local NewValue = Instance.new("IntValue")
					ThisVal = NewValue
				elseif CheckValue["TypeValue"] == "NumberValue" then
					local NewValue = Instance.new("NumberValue")
					ThisVal = NewValue
				end
				if ThisVal then
					ThisVal.Value = CheckValue["Value"]
					ThisVal.Name = CheckValue["NameValue"]
					ThisVal.Parent = NewFolder
				end
			end
		end
	end
end)
local BlackListSave = {
	
}
game.Players.PlayerRemoving:Connect(function(Player)
	local TableToSave = {}
	for _,CheckingFolder in pairs(Player:GetChildren()) do
		if CheckingFolder:IsA("Folder") then
			for _,CheckValue in pairs(CheckingFolder:GetChildren()) do
				if CheckValue:IsA("ValueBase") then
					if table.find(BlackListSave,CheckValue.Name) == nil then
						if table.find(TableToSave,CheckingFolder.Name) == nil then
							local TableToInst = {}
							table.insert(TableToSave,TableToInst)
							TableToSave[CheckingFolder.Name] = TableToSave[1]
							TableToSave[1] = nil
						end
						local ThisTable = TableToSave[CheckingFolder.Name]
						if ThisTable then
							if CheckValue:IsA("StringValue") then
								local ThisValue = {
									["TypeValue"] = "String",
									["NameValue"] = CheckValue.Name,
									["Value"] = CheckValue.Value
								}
								table.insert(ThisTable,ThisValue)
							elseif CheckValue:IsA("BoolValue") then
								local ThisValue = {
									["TypeValue"] = "BoolValue",
									["NameValue"] = CheckValue.Name,
									["Value"] = CheckValue.Value
								}
								table.insert(ThisTable,ThisValue)
							elseif CheckValue:IsA("IntValue") then
								local ThisValue = {
									["TypeValue"] = "IntValue",
									["NameValue"] = CheckValue.Name,
									["Value"] = CheckValue.Value
								}
								table.insert(ThisTable,ThisValue)
							elseif CheckValue:IsA("NumberValue") then
								local ThisValue = {
									["TypeValue"] = "NumberValue",
									["NameValue"] = CheckValue.Name,
									["Value"] = CheckValue.Value
								}
								table.insert(ThisTable,ThisValue)
								-- Else Value
							end
						end
					end
				end
			end
		end
	end
	local Key = tostring(Player.UserId)
	STORE:SetAsync(Key,TableToSave)
end)

It doesn’t work :smiling_face_with_tear:
Game published, API Services enabled, what wrong?
Maybe, the problem is another scripts creating folder with values?
This script looks like it actually works, but it’s don’t.