Make values match from GetAsync to Object values?

I’m currently trying to figure out how to set values from GetAsync to match with the Object’s values when loadData is running. I have been trying to do this for the past three hours, so that’s why I’m asking for help here.

The structure (Object Values) can look like this: image

--// This is the code so far and it isn't working for some reason.

local function loadData(player)
	local PlayerKey = "data:"..player.UserId
	local Data = PlayerData:GetAsync(PlayerKey)
	if not Data then --// In this scenario I have data and this doesn't matter.
		return warn("Server | ".. player.Name .. " has no data!")
	end
	if Data then
		warn("Server | ".. player.Name .. " has data!")
		for _, Index in pairs(player.PlayerData:GetDescendants()) do
			if Index:IsA("Folder") then
				Data[Index.Name] = {}
			elseif Index:IsA("ValueBase")  then
				Data[Index.Name] = Index.Value
			end
		end
	end
end

I’m not sure if this is intentional or not, but when you go to load the data in your for loop (I assume that’s what you’re wanting to do?) you’re actually overwriting/setting keys in the returned data table.

So, you get the player’s data:

local Data = PlayerData:GetAsync(PlayerKey)

But then you write to it:

Data[Index.Name] = {}

I would think you’d want to read from it instead, and set it to the player’s values accordingly.

With all that said, I think you’re going about this the right way. You’d just have to read from Data instead of writing to it.

1 Like

That is the problem. I do not know how to read the Data. It is not possible to loop GetChildren nor GetDescendants from the Data above, or am I wrong?

I’ll write you some pseudocode in hopes of you learning from that, as I don’t really know how to assist you with your current code.

function loadData(player) --//Handles the loading of player data
local data = someDataStore:GetAsync(player.UserId)
--//Do all of your folder/instance drawing here. Then, we'll run a loop on the player's data to load all of their stuff.
for I, folder_data in pairs(data) do --Iterate over the player's saved data
for value_name, value in pairs(folder_data) do
player[folder_data][value_name].Value = value --Find the value in the player and set it to their saved value.
end
end
end

function saveData(player) --//Handles the saving of player data
local tab = {} --Define a table to save to DataStore, we'll add player data to this
for I, folder in pairs(player:GetChildren()) do --Iterate over the folders of player
tab[folder.Name] = {} --Create data for this folder.
for i2, value in pairs(folder:GetChildren()) do --Iterate over each folder and store their values.
tab[folder.Name][value.Name] = value --Write a new field for this folder's data.
end
end
someDataStore:SetAsync(player.UserId, tab) --Save data. I use SetAsync, not the best practice.
end

This is very barebones and untested, but I think the logic is pretty close to what you’d be looking for. If you need further clarification let me know!

1 Like

I think player[folder_data][value_name].Value = value does not work because there can be tables inside the tables instead of values only. I am just really bad at iterating & etc.

Example of how it can look like:
image

Datastore example:
image
Currencies:
image
Pets:
image

Error:

ServerScriptService.Assets.Modules.DataService.Datastore:46: bad argument #2 to '?' (string expected, got table)

I wouldn’t really recommend adding that many nested values; as that could get messy to work with.

It might, in that case, be easier to not even use value instances, but to store the data directly in a table per-player server-sided.

It’s probably easier to just not nest so many values though.

1 Like

First I’d suggest naming your values depending on it’s job, for instance the money value name would be Money, then you’ll just have a function to get the value’s value and return them as a table :

function getDataFromFolder(player) -- assuming that PlayerData is under the player object
    local dataFolder = player:FindFirstChild("PlayerData")
    if not dataFolder then warn("Couldn't find data folder!") return end
    local data = { -- set up the data table that you'll save in the datastore
        currency = {}, -- currency
        inventory = {} -- inventory
    }
    local currencyFolder = dataFolder.Currency -- the currency folder should exist if dataFolder exists
    local inventoryFolder = dataFolder.Inventory -- same thing
    for _,val in ipairs(currencyFolder:GetChildren()) do -- loop through the folder's children and get the values value + name
        data.currency[val.Name] = val.Value -- yes
    end
    for _,val in ipairs(inventoryFolder:GetChildren()) do -- loop through the folder's children and get the values value + name
        data.inventory[val.Name] = val.Value -- yes
    end
    return data -- here you got yourself the data table which has the inventory and money
end
 -- Now to save the data all you have to do is just SetAsync :shrug: and to load it you do : 

function loadDataToFolder(data,player) -- again assuming that PlayerData is under the player object and data is the table from GetAsync
    local dataFolder = player:FindFirstChild("PlayerData") -- also assuming you already created PlayerData if not then uh create it!
    if not dataFolder then warn("no data folder?") return end
    local currencyFolder = dataFolder.Currency
    local inventoryFolder = dataFolder.Inventory

    -- now all you have to do is loop through the data.Currency and data.Inventory table and set the values : 
    for name,val in pairs(data.currency) do
        local currentValue = currencyFolder:FindFirstChild(name)
        if not currentValue then continue end -- Didn't find the value so we continue to the next index 
        currentValue.Value = val
    end
    for name,val in pairs(data.inventory) do
        local currentValue = inventoryFolder:FindFirstChild(name)
        if not currentValue then continue end -- Didn't find the value so we continue to the next index 
        currentValue.Value = val
    end
end
1 Like