My data store script doesn't seem to be working properly

I’m trying to save a folder of boolean values. And then load all those values when the player joins the game.
My current script: – it doesn’t seem to work for some items, wont load the data properly. Any idea what I’m doing wrong? Basically I have bool values, “Item1”, “Item2”, “Powerup1”, “Powerup2” etc…And they are stored in a folder. I just want to load those values when the player joins.

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

print("Ran data store")

local folderChildren = {
	"Stars",
	"Item1",
	"Item2",
	"Item3",
	"Item4",
	"Item5",
	"Item6",
	"Item7",
	"Item8",
	"Item9",
	"Pet1",
	"Pet2",
	"Powerup1",
	"Powerup2"
}

game.Players.PlayerAdded:Connect(function(player)
	local folder = player:WaitForChild("PlayerData")
	local savedValues = DataStore:GetAsync("FolderValues")
	if savedValues then
		for _, child in ipairs(folder:GetChildren()) do
			for i, childName in ipairs(folderChildren) do
				if child.Name == childName then
					child.Value = savedValues[i]
					break
				end
			end
		end
	else
		warn("Failed to load data for player " .. player.Name)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local folder = player:WaitForChild("PlayerData")
	local values = {}
	for _, child in ipairs(folder:GetChildren()) do
		for i, childName in ipairs(folderChildren) do
			if child.Name == childName then
				values[i] = child.Value
				break
			end
		end
	end
	DataStore:SetAsync("FolderValues", values)
end)

I am a bit confused on how you’re managing things. Mainly these 2 parts:

  1. You go through a folder’s children.
  2. You loop through the list with values they should have
  3. You break the function after setting/getting a value if the child’s name is equal to childName.

Well so an issue I have noticed is that no where in your script you actually create a folder called PlayerData, if you’re doing this in another script I advice not to do that, because you will first wait for the folder to be added and then get all the children even when they’ve not all been created.

Also I suggest instead to do something more like this for the saving:

for i, childName in ipairs(folderChildren) do
	local child = folder:FindFirstChild(childName)
	if child then
		values[i] = child.Value
	end
end

For the loading it’s quite alike, but we need to create the values.

for i, childName in ipairs(folderChildren) do
	local child = Instance.new(...) -- Which type of value
	child.Value = savedValues[i]
	child.Name = childName
	child.Parent = folder
end

Also to define the types of values I’d instead make the array an dictionary or an array inside an array:

-- Dictionary
{["Stars"] = "Number", ...}
-- Array in an array
{[1] = {"Stars", "Number"}, ...} 
{{"Stars", "Number"}, ...}

Then for the dictionary save the keys on the childName, which now is the index. Then when creating a value use

childName, valueType

Instance.new(valueType .. "Value")

For the Array it’d look like:

index, childInfo
-- childInfo:
[1] = childName, [2] = valueType

Instance.new(childInfo[2] .. "Value")

I create the folder and values in another script on a playeradded event, works fine. Don’t have any issues of not existing.

Yeah so as I said the issue then at hand is:

  1. On player added
  2. PlayerData gets created & the other script stops Waiting for it
  3. Other values get created & the other script already gets the children while not all have them have been added
  4. Not all values get loaded.

If you’d just add a wait() before getting the children you will probably face another issue and that is the player leaving before you added all their info to the values which will cause their save to get overwritten and none of their data to be saved.

1 Like

Also if that is all archive this post by marking a reply as solution.

If I create the value instead of having it pre-created with another script. How do I also store the value type as well as the name of the value.

Like I’ve got everything labeled: Item1 → bool, Item2 → bool, coins → int

etc

What I meant is using 1 script for both actions. Basically immediately after creating said value you want to load in all the information before parenting it to the PlayerData, if you use 2 scripts for this you either need to also use folder.ChildAdded or wait, but then the issue at hand is that the values are already parented to the PlayerData, which will cause their data to be set to 0 or whatever by default, so if they leave before script 2 loads in their data into said values you’ll have a big problem.

So basically:

  1. Create value
  2. Set value to either loaded value or default value
  3. Parent the value

These 3 actions have to happen in this exact sequence, to reduce any chance of not saving data.

[Edit: is it possible the values in the folder are being erased but the folder exists] ? When the player is leaving?

So I inside the player joined event, in the same script I copy and parent the folder of values to the player.
The save function doesn’t seem to always run though, like only half the time. If it runs, I see all the values printed and their names. But if it doesn’t work, nothing is printed and the data isn’t saved. Any idea? I’m waiting for the folder to exist before accessing it’s data.

local DataStoreService = game:GetService("DataStoreService")


local DATASTORE_NAME = "PlayerData"
local KEY_TEMPLATE = "Player_%s"


local function saveData(player)
	-- Create datastore key for player
	local key = KEY_TEMPLATE:format(player.UserId)

	-- Get folder holding player data
	local folder = player:WaitForChild("PlayerData")
	if not folder or not folder:IsA("Folder") then
		return
	end

	-- Save values to datastore
	for _, value in ipairs(folder:GetChildren()) do
		if value:IsA("ValueBase") then
			DataStoreService:GetDataStore(DATASTORE_NAME):SetAsync(key .. value.Name, value.Value)
			print(value.Name, value.Value)
		end
	end
end


local function loadData(player)

	
	script:WaitForChild("PlayerData"):Clone().Parent = player
	
	
	local key = KEY_TEMPLATE:format(player.UserId)


	local folder = player:WaitForChild("PlayerData")
	if not folder or not folder:IsA("Folder") then
		return
	end


	for _, value in ipairs(folder:GetChildren()) do
		if value:IsA("ValueBase") then
			local success, savedValue = pcall(DataStoreService:GetDataStore(DATASTORE_NAME).GetAsync, DataStoreService:GetDataStore(DATASTORE_NAME), key .. value.Name)
			if success then
				value.Value = savedValue
				print(value.Name, value.Value)
			end
		end
	end
end


game.Players.PlayerRemoving:Connect(saveData)


game.Players.PlayerAdded:Connect(loadData)

It could be, but like I said the real issue is that the value creating script doesnt create the values BEFORE the loading script gets the values (the children of the folder), this causes only the select few values to be loaded.

Then the other issue at hand is that the values get already parented BEFORE the data gets loaded in, so what happens is if you leave just before said data gets loaded some of the data you previously had will not be saved and stored.

Trying to fix the first issue can cause the 2nd issue to become a bigger problem.
An example of this is using a wait() function before loading all the data into their respective values, this will fix issue 1, but now issue 2 has a bigger window where the player can leave and mess up their data.

So as I suggested before making both scripts into 1 script will address both issues at once, because only the paranted values will be saved and also you immediately put in their respective values.

I’ve been testing, it seems like the function is having a thread crash or something.
When it doesn’t work, it prints this:
– it’s as if the script stops running part way through?

  04:34:54.986  Next  -  Server - DataStore:20
  04:34:54.986  20  -  Server - DataStore:21
  04:34:54.986  Loop  -  Server - DataStore:23
local function saveData(player)
	-- Create datastore key for player
	local key = KEY_TEMPLATE:format(player.UserId)

	-- Get folder holding player data
	local folder = player:WaitForChild("PlayerData")
	if not folder or not folder:IsA("Folder") then
		print("Returning sad :(")
		return
	end

	-- Save values to datastore
	print("Next")
	print(#folder:GetChildren())
	for _, value in ipairs(folder:GetChildren()) do
		print("Loop")
		if value:IsA("ValueBase") then
			DataStoreService:GetDataStore(DATASTORE_NAME):SetAsync(key .. value.Name, value.Value)
			print(value.Name, value.Value)
			else print(value.Name)
		end
	end
end

Does “WaitForChild” not wait for child values to load? I was under the impression that it did.\

Btw, I put everything into 1 script, it copies the folder with the value and parents it to the player in the playeradded function. No 2 scripts anymore. Is that what you meant?

Yes, you wait for the PlayerData to be added, but the values get only parented after the PlayerData has been parented, so you do not wait for the other values to be added, so when you get the children at that point only the first few created values will be added.

I do not literally mean to have 2 playerAdded functions running at the same time, they should run directly after eachother.

So basically after you create a value you immediately want to load the info regaring that value, then after having done that you only then want to parent it to the PlayerData folder.

Ok I’ll create the values manually in the script, btw. I added a wait(1) in the save script, and now that function doesn’t run at all. Can playerleave events not have any wait?

So if you’re going to add a wait() function it has to be after the :WaitForChild() for the PlayerData inside the load function.

Doesn’t seem to work, I have the wait after the waitforchild function.

local DataStoreService = game:GetService("DataStoreService")


local DATASTORE_NAME = "PlayerData"
local KEY_TEMPLATE = "Player_%s"


local function saveData(player)
	-- Create datastore key for player
	local key = KEY_TEMPLATE:format(player.UserId)

	-- Get folder holding player data
	local folder = player:WaitForChild("PlayerData")
	if not folder or not folder:IsA("Folder") then
		print("Returning sad :(")
		return
	end
	task.wait(1)
	-- Save values to datastore
	print("Next")
	print(#folder:GetChildren())
	for _, value in ipairs(folder:GetChildren()) do
		print("Loop")
		if value:IsA("ValueBase") then
			DataStoreService:GetDataStore(DATASTORE_NAME):SetAsync(key .. value.Name, value.Value)
			print(value.Name, value.Value)
			else print(value.Name)
		end
	end
end


local function loadData(player)

	
	
	
	local key = KEY_TEMPLATE:format(player.UserId)


	local folder = Instance.new("Folder")
	folder.Name = "PlayerData"
	
	local stars = Instance.new("IntValue", folder)
	local item1 = Instance.new("BoolValue", folder)
	local item2 = Instance.new("BoolValue", folder)
	local item3 = Instance.new("BoolValue", folder)
	local item4 = Instance.new("BoolValue", folder)
	local item5 = Instance.new("BoolValue", folder)
	local item6 = Instance.new("BoolValue", folder)
	local item7 = Instance.new("BoolValue", folder)
	local item8 = Instance.new("BoolValue", folder)
	local item9 = Instance.new("BoolValue", folder)
	local powerup1 = Instance.new("BoolValue", folder)
	local powerup2 = Instance.new("BoolValue", folder)
	local powerup3 = Instance.new("BoolValue", folder)
	local powerup4 = Instance.new("BoolValue", folder)
	local powerup5 = Instance.new("BoolValue", folder)
	local powerup6 = Instance.new("BoolValue", folder)
	local powerup7 = Instance.new("BoolValue", folder)
	local powerup8 = Instance.new("BoolValue", folder)
	
	stars.Name = "Stars"
	item1.Name = "Item1"
	item2.Name = "Item2"
	item3.Name = "Item3"
	item4.Name = "Item4"
	item5.Name = "Item5"
	item6.Name = "Item6"
	item7.Name = "Item7"
	item8.Name = "Item8"
	item9.Name = "Item9"
	powerup1.Name = "Powerup1"
	powerup2.Name = "Powerup2"
	powerup3.Name = "Powerup3"
	powerup4.Name = "Powerup4"
	powerup5.Name = "Powerup5"
	powerup6.Name = "Powerup6"
	powerup7.Name = "Powerup7"
	powerup8.Name = "Powerup8"
	
	folder.Parent = player


	for _, value in ipairs(folder:GetChildren()) do
		if value:IsA("ValueBase") then
			local success, savedValue = pcall(DataStoreService:GetDataStore(DATASTORE_NAME).GetAsync, DataStoreService:GetDataStore(DATASTORE_NAME), key .. value.Name)
			if success then
				value.Value = savedValue
				print(value.Name, value.Value)
			end
		end
	end
end


game.Players.PlayerRemoving:Connect(saveData)


game.Players.PlayerAdded:Connect(loadData)

No, read what I said.

DO NOT put it inside the SAVE function, if you’re going to use a wait() put it inside the LOAD function.

Ah, okay. So lets say I remove that. I’m not having issues loading. I’m having issues saving.

It will do this:

 	print("Next")
	print(#folder:GetChildren())
        print("Loop")

But it wont do this print → print(value.Name, value.Value)

And it also wont do the else statement print → else print(value.Name)

It’s as if it’s having a thread crash.

So basically, I’ll write a comment where it appears to be crashing

local function saveData(player)
	-- Create datastore key for player
	local key = KEY_TEMPLATE:format(player.UserId)

	-- Get folder holding player data
	local folder = player:WaitForChild("PlayerData")
	if not folder or not folder:IsA("Folder") then
		print("Returning sad :(")
		return
	end
	-- Save values to datastore
	print("Next")
	print(#folder:GetChildren())
	for _, value in ipairs(folder:GetChildren()) do
		print("Loop")
-- does not seem to run anything below
		if value:IsA("ValueBase") then
			DataStoreService:GetDataStore(DATASTORE_NAME):SetAsync(key .. value.Name, value.Value)
			print(value.Name, value.Value)
			else print(value.Name)
		end
	end
end

I am not quite understanding what you’re getting at, but if you put that inside the load function you’ll notice that folder:GetChildren() does not contain the same amount of children at that time of printing as it should have.

Also, do not put this in the SAVE function, but in the LOAD function. Like I said before that is the issue.

I’m now creating all the values manually via script, so I don’t think I’ll need a wait anymore?

Like I’m not having issues with the load anymore, I’m having issues with the save. The values are loading everytime, the save is not working half the time though.