How to make a Script that saves multiple Folders

Hey Developers,
In my game I use multiple Datastores. I also have multiple folders. I use different Datastores for these Folders, because (for example) I have a Donation Datastore which I cannot change because people already donated to my game. Now I ran into a Problem: I was planning on releasing an Update to my Obby game: Halos, Free Skips and Points. For each of these things I created multiple Folders because the Point Instance should be Visible, so I added these in the leaderstats Section. For the Things like Free Skips I had to add a Timer which goes down and saves when the Player leaves and should not be visible. So for each of these Values I am using different Datastores. And now I got this warning in output: DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests.Key = User-1051459521 - Studio That means I created too many Datastores. As I said I cannot change every Value to 1 Datastore and I cannot create 1 Folder for all the 3 Values because some of these should be not visible for the player. So my question is if you can create a Script, that Saves multiple Folders (in this case the leaderstats, Free Skips folder and the Halo Folder) with their stuff in it (I ONLY use values for every Folder but I use accessories which are located in the Halos Folder). So I want to make a Script that Saves the different Folders with their Stuff in it. (for the halo save script I created this one):

game.Players.PlayerRemoving:Connect(function(player)
	pcall(function()
		local HaloSave = {}
		for i, Halo in pairs(player.OwnedHalos:GetChildren()) do
			if Halo then
				table.insert(HaloSave,Halo.Name)
			end
		end
		DataStore:SetAsync("User-"..player.UserId,HaloSave)
	end)
end)
game:BindToClose(function()
	for i,player in pairs(game.Players:GetPlayers()) do
		local HaloSave = {}
		for i, Halo in pairs(player:WaitForChild("OwnedHalos"):GetChildren()) do
			if Halo then
				table.insert(HaloSave,Halo.Name)
			end
		end
		DataStore:SetAsync("User-"..player.UserId,HaloSave)
	end
end)

Any help would be appreciated!!!

1 Like

My Idea: Create 1 Table for all the Folders with their Childs in it. But I have no idea how to create 1 HUGE Table for DIFFERENT folders tho :confused:

local table = {}
for i,v in pairs(folder:GetChildren()) do
table.insert(table, v)
end

Is there a way to make the Same Table saving multiple Folders with their Children, not only 1 folder?

local table = {}
for i,v in pairs(folders:GetChildren()) do
   table[v.Name] = {}
   for i,g in pairs(v:GetChildren()) do
      table.insert(v.Name, g)
   end
end
local table = {}
for i,v in pairs(folders:GetChildren()) do
   table[v.Name] = {}
   for i,g in pairs(v:GetChildren()) do

for i,s in pairs(folders:GetChildren()) do
   table[v.Name] = {}

      table.insert(v.Name, g)
   end
end

so like this if i want to add more folders?

The code he gave you was assuming that all of your folders were under one larger folder. If you want to just use the code he gave you, just replace the ā€œfoldersā€ variable with a list of all the different folders you would like to save. It will look something like this:

--First, you want to add all of your folders to the list:
local folders = {}
folders.insert( plr.leaderstats ) --using leaderstats as an example, do this for all of your folders

--Next, we will create a larger folder containing all of your other folders

local data = {}
for i,v in pairs(folders:GetChildren()) do
   --for each folder, we are creating a subfolder in our data
   data[v.Name] = {}
   for i,g in pairs(v:GetChildren()) do
      --this will put each item in that folder into the subfolder
      data.insert(v.Name, g)
   end
end

Let me know if something doesn’t work, I’m writing this on my phone right now which is a pain

1 Like

Ok I tried to transfer that for my game. Here is the Code:

local DataStore = game:GetService("DataStoreService"):GetDataStore("MainData")

game.Players.PlayerRemoving:Connect(function(plr)
local folders = {}
	folders.insert(plr.FreeSkips) 
	folders.insert(plr.OwnedHalos) 
	
local data = {}
for i,v in pairs(folders:GetChildren()) do
	
	data[v.Name] = {}
	for i,g in pairs(v:GetChildren()) do
		data.insert(v.Name, g)
		end
	end
	DataStore:SetAsync("User-"..plr.UserId,data)
end)

(Sorry I am not experienced with Scripting) Does this Script Save the Children of the specific Folders?
Also ( I mentioned this in my first post), I save the Halo Folder in a weird way:

game.Players.PlayerAdded:Connect(function(plr)
	pcall(function()
		local Halo = DataStore:GetAsync("User-"..plr.UserId)
		if Halo then
			for i,v in pairs(Halo) do
				local HaloFound = game.ReplicatedStorage.HaloEvents.Halos:FindFirstChild(v)
				if HaloFound then
					HaloFound:Clone().Parent = plr:WaitForChild("OwnedHalos")
				end
			end
		end
	end)
end)

This script basically checks for a Folder in Replicated Storage. If any Halo of the Halo Folder is the Same as there it gets cloned into the HaloFolder in the Player. Because you don’t the SetAsync here it won’t be a Problem (sorry if that’s a stupid question), but will every accessory get cloned to the HaloFolder? Because you are creating a Main Folder for all the Folders u want to save… Maybe you have to add a Script that puts them back if you join the Game?

Oh and (I forgot), since there are Values in the FreeSkips Folder, and when the Player joins the first Time so he has no Data yet, I have to setup a Starter Value. Here is my current Code for setting up the several Folders and that is also the reason why the Datastore overfills with requests:

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("FreeSkipsDataSaves")
local ResetTime = 1800

function loadData(player)
	local Key = "Player_"..player.UserId

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

	local FreeSkips = Instance.new("IntValue", FreeSkipsFolder)
	FreeSkips.Name = "FreeSkips"
	
	local Time = Instance.new("IntValue", FreeSkipsFolder)
	Time.Name = "Time"
	
	local succes, err = pcall(function()
		local data = DataStore:GetAsync(Key)

		if data then
			FreeSkips.Value = data[1] 
			Time.Value = data[2] 
		else
			FreeSkips.Value = 0
			Time.Value = ResetTime
		end
	end)

	if err then
		print("Could not load data!")
		warn(err)
	end
end

function saveData(player)
	local Key = "Player_"..player.UserId
	local FreeSkipsFolder = player:WaitForChild("FreeSkips")

	local succes, err = pcall(function()

		local stufftoSave = {
			FreeSkipsFolder.FreeSkips.Value,
			FreeSkipsFolder.Time.Value,
		}

		if FreeSkipsFolder then
			DataStore:SetAsync(Key, stufftoSave)
		end

	end)

	if err then
		print("Could not save data!")
		warn(err)
	end
end

function onServerShutdown()
	if game:GetService("RunService"):IsStudio() then
		wait(2)
	else
		for _, player in pairs(game.Players:GetPlayers()) do
			local finished = Instance.new("BindableEvent")
			local FreeSkipsFolder = player:WaitForChild("FreeSkips")
			local Key = "Player_"..player.UserId

			local stufftoSave = {
				FreeSkipsFolder.FreeSkips.Value,
				FreeSkipsFolder.Time.Value,
			}

			if FreeSkipsFolder then
				DataStore:SetAsync(Key, stufftoSave)
			end
			finished.Event:Wait()
		end		
	end
end

game:BindToClose(onServerShutdown)
game.Players.PlayerAdded:Connect(loadData)
game.Players.PlayerRemoving:Connect(saveData)

I made a mistake in my original script: Datastores cannot save raw objects. This includes integer values, folders, etc.

To fix this issue (and a little time in the future), I would recommend the following steps:

  • Put everything into one script, in one space, so that the code isn’t too cluttered

  • When you’re saving the data, save the values of them
    ** This will require you to assign numerical or string values to the halos, I recommend integer values as a ā€œHaloIDā€ in the halo

  • Create a function meant to ā€œformatā€ your data so you can easily look back and know what to do with it, here’s an example:

function formatData(player)
	
	local data = {}
	
	local FreeSkipsFolder = player:WaitForChild("FreeSkips")
	local skips = {}
	
	for i,piece in pairs(FreeSkipsFolder:GetChildren()) do
		table.insert(skips, piece.Value)
	end
	
	table.insert(data, skips)
	
	local HalosFolder = player:WaitForChild("OwnedHalos")
	local halos = {}
	
	for i,halo in pairs(HalosFolder:GetChildren()) do
		table.insert(halos, halo:WaitForChild("HaloID").Value)
	end

	table.insert(data, halos)
	
	return data
end

This will stores things into the datastore in a way that would look something like this (please excuse my poor illustration skills):

EDIT: Datastore should say ā€˜Data’, and represents the data you are either uploading or retrieving.

1 Like

woah u didnt need to write that all :sweat_smile: thank you firstly!!! I will try to use the Script later. Only 1 thing, the Halo save script works fine tho, it just uses a separate :SetAsync() function. Oh, and is a Script neccessary to unfolder all the saved folders?

this is the halo script (character limit)

When implementing this, use the function to easily save data, as well as edit how you save it. It should clean up the code a bit, so that your save data looks something like this:

function saveData(player)
	local Key = "Player_"..player.UserId
	local stuffToSave = formatData(player)
	
	local succes, err = pcall(function()

		if stuffToSave then
			DataStore:SetAsync(Key, stuffToSave)
		end

	end)

	if err then
		warn('WARNING: COULD NOT SAVE PLAYER DATA: '..err)
	end
end

Your loading may be a little more messy, but you can also make a function to match HaloID’s of the halos in ReplicatedStorage

function matchHaloData(player, haloData)
	for i,halo in pairs(game.ReplicatedStorage.HaloEvents.Halos:GetChildren()) do
		--table.find will return a value if an ID exists in the table, so if it does, we will clone that halo to the player 'inventory'
		if table.find(haloData, halo.HaloID.Value) ~= nil then 
			halo:Clone().Parent = player:WaitForChild("OwnedHalos")
		end
		
	end
end

All of this leading to the Load function:

function loadData(player)
	--CREATING FOLDERS
	local Key = "Player_"..player.UserId

	local SkipsFolder = Instance.new("Folder", player)
	SkipsFolder.Name = "FreeSkips"
	
	local Skips = Instance.new("IntValue", SkipsFolder)
	local Time = Instance.new("IntValue", SkipsFolder)
	
	Skips.Name = "FreeSkips" --May want to change this so it doesn't have the same name as the folder
	Time.Name = "Time"
	
	local HalosFolder = Instance.new("Folder", player)
	HalosFolder.Name = "OwnedHalos"
	
	--GETTING PLAYER DATA
	local sucess, err = pcall(function()
		local data = DataStore:GetAsync(Key)
		
		if data then
			--Setting up the Halos using the function we made earlier:
			matchHaloData(player, data[2])
			
			--Using the Data to set up skips
			Skips.Value = data[1][1] -- The first section of the folder, first value: FreeSkips
			Time.Value = data[1][2]
		else
			Skips.Value = 0
			Time.Value = ResetTime
		end
		
	end)
	
	if err then
		warn('WARNING: COULD NOT LOAD PLAYER DATA: '..err)
	end
	
end

Here’s what these functions would look like all in one place:

--Vars and Services
local DataStore = game:GetService('DataStoreService')

local ResetTime = 0 --Don't forget to set this later

--Functions
function formatData(player)

	local data = {}

	local FreeSkipsFolder = player:WaitForChild("FreeSkips")
	local skips = {}

	for i,piece in pairs(FreeSkipsFolder:GetChildren()) do
		table.insert(skips, piece.Value)
	end

	table.insert(data, skips)

	local HalosFolder = player:WaitForChild("OwnedHalos")
	local halos = {}

	for i,halo in pairs(HalosFolder:GetChildren()) do
		table.insert(halos, halo:WaitForChild("HaloID").Value)
	end

	table.insert(data, halos)

	return data
end

function saveData(player)
	local Key = "Player_"..player.UserId
	local stuffToSave = formatData(player)

	local succes, err = pcall(function()

		if stuffToSave then
			DataStore:SetAsync(Key, stuffToSave)
		end

	end)

	if err then
		warn('WARNING: COULD NOT SAVE PLAYER DATA: '..err)
	end
end

function matchHaloData(player, haloData)
	for i,halo in pairs(game.ReplicatedStorage.HaloEvents.Halos:GetChildren()) do
		--table.find will return a value if an ID exists in the table, so if it does, we will clone that halo to the player 'inventory'
		if table.find(haloData, halo.HaloID.Value) ~= nil then 
			halo:Clone().Parent = player:WaitForChild("OwnedHalos")
		end

	end
end

function loadData(player)
	--CREATING FOLDERS
	local Key = "Player_"..player.UserId

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

	local Skips = Instance.new("IntValue", SkipsFolder)
	local Time = Instance.new("IntValue", SkipsFolder)

	Skips.Name = "FreeSkips" --May want to change this so it doesn't have the same name as the folder
	Time.Name = "Time"

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

	--GETTING PLAYER DATA
	local sucess, err = pcall(function()
		local data = DataStore:GetAsync(Key)

		if data then
			--Setting up the Halos using the function we made earlier:
			matchHaloData(player, data[2])

			--Using the Data to set up skips
			Skips.Value = data[1][1] -- The first section of the folder, first value: FreeSkips
			Time.Value = data[1][2]
		else
			Skips.Value = 0
			Time.Value = ResetTime
		end

	end)

	if err then
		warn('WARNING: COULD NOT LOAD PLAYER DATA: '..err)
	end

end

--Forgot Server Shutdown function on the last code: here it is

function onServerShutdown()
	if game:GetService("RunService"):IsStudio() then
		wait(2)
	else
		for _, player in pairs(game.Players:GetPlayers()) do
			local finished = Instance.new("BindableEvent")
			
			saveData(player) --It's easier to use the previous saveData function on each player than it is to write new code
			
			finished.Event:Wait()
		end		
	end
end




--Binding Events
game:BindToClose(onServerShutdown)
game.Players.PlayerAdded:Connect(loadData)
game.Players.PlayerRemoving:Connect(saveData)

Hope this helps :smiley:

Also it’s no prob, I don’t have the motivation to make my own stuff, but I love helping people with theirs

EDIT: I tried to comment where I could, but let me know if it either doesn’t work or if you need more info about a certain part, I’d be happy to DM you my discord tag and we can figure this out

2 Likes

u deserve a solution for that, I will test this out tomorrow! Thank you so much!!!

1 Question for the Halo ID: so i have to add a Value inside of the Halos and just give every Halo another ID?

Each halo will have an IntValue inside of it, with the value being a unique id for the Halo. It would be the same as calling them ā€œHalo 1ā€ or ā€œHalo 200398ā€

Idea for this: Since all the Halos have different Names I just make a Script that when the Player Joins, changes the IntValues Value to the Name of the Halo. Would this work too?

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("leaderstatDataSaves")

function loadData(player)
	local Key = "Player_"..player.UserId

	local leaderstats = player:WaitForChild("leaderstats")


	local Points = Instance.new("IntValue", leaderstats)
	Points.Name = "Points"

	local Wins = Instance.new("IntValue", leaderstats)
	Wins.Name = "Wins"

	local succes, err = pcall(function()
		local data = DataStore:GetAsync(Key)

		if data then
			Points.Value = data[1] 
			Wins.Value = data[2] 
		else
			Points.Value = 0
			Wins.Value = 0
		end
	end)

	if err then
		print("Could not load data!")
		warn(err)
	end
end

function saveData(player)
	local Key = "Player_"..player.UserId
	local leaderstats = player:WaitForChild("leaderstats")

	local succes, err = pcall(function()

		local stufftoSave = {
			leaderstats.Points.Value,
			leaderstats.Wins.Value,
		}

		if leaderstats then
			DataStore:SetAsync(Key, stufftoSave)
		end

	end)

	if err then
		print("Could not save data!")
		warn(err)
	end
end

function onServerShutdown()
	if game:GetService("RunService"):IsStudio() then
		wait(2)
	else
		for _, player in pairs(game.Players:GetPlayers()) do
			local finished = Instance.new("BindableEvent")
			local leaderstats = player:WaitForChild("leaderstats")
			local Key = "Player_"..player.UserId

			local stufftoSave = {
				leaderstats.Points.Value,
				leaderstats.Wins.Value,
			}

			if leaderstats then
				DataStore:SetAsync(Key, stufftoSave)
			end
			finished.Event:Wait()
		end		
	end
end

game:BindToClose(onServerShutdown)
game.Players.PlayerAdded:Connect(loadData)
game.Players.PlayerRemoving:Connect(saveData)