Saving a folder of values and loading them back

Hi, I’m trying to save a folder full of values with a Datastore. An example of what I’m trying to do is like an inventory. There’s about 10 items and I want to save the amount of that item the player has for all 10 of those items.

The issue is, I don’t know how to do that.

I’ve tried using a module script to make a function to update the amount the player has but I’m not sure how I’d get the amount for every one of those items and be able to save the amount to those designated items amount values.

Any help would be appreciated!

2 Likes

Just save a dictionary to the DataStore.

local ds = game:GetService("DataStoreService")
local store = ds:GetDataStore("DataStoreNameHere")
local function save(player)
    local folder = --folder path here
    local items = {}
    --this is assuming all the items within the folder are BoolValues, and the value would be true if it is owned.
    for _, item in pairs(folder) do
        if item.Value == true then
            table.insert(items, item.Name)
        end
    end
    local playerData = {
        ["Inventory"] = items
    }
    local success, err = pcall(function()
        store:SetAsync(dataKey, playerData) --where 'dataKey' is the key you use to access the DataStoreService
    end)
end

game.Players.PlayerRemoving:Connect(save)

I appreciate it but, this isn’t quite what I’m trying to do. I’m trying to save the amount of that item that a player has.

So inside the Folder instance within the player, are they all IntValues?

1 Like

The first value is a StringValue, containing the item name as the value. There are 2 IntValues inside of the StringValue that are named “Rarity” and “Amount”. I’m trying to save and load the Amount.Value for all of those StringValues, but I’m not sure how.

Just want to make sure I got it right before I write any code, so you don’t want to save anything else other than the Amount for each string value? Do you want to save the StringValue’s item name as well?

I don’t have to because I’ve made a folder in ReplicatedStorage of the folder with all of the StringValues and their children. When the player joins the game, a script will clone that folder and parent it to the player. So I am only assigning the “Amount” value as of now.

Ok, but you might want to save the name of the item as well, so that you know what value is for what item when you load the data.

local ds = game:GetService("DataStoreService")
local store = ds:GetDataStore("DataStoreNameHere")

local folder = nil --path to folder within the player
local items = {}
for _, item in pairs(folder) do
	local assignItem = {
		[item.Name] = {
			["AmountOwned"] = item.Amount.Value
		}
	}
    table.insert(items, assignItem)
end
local key = "DataSavingKeyHere"
local success, err = pcall(function()
	store:SetAsync(key, items)
end)

Hope this helps!

2 Likes

This is exactly what I needed, I appreciate your help!

1 Like

Quick question, if I were to have an autosave feature, would I need to do table.clear(items) before re-inserting new values? (Since there would already be items in the table)

Well, presuming that script I sent above would be used on the PlayerRemoving event, and you are using SetAsync() and not UpdateAsync(), you wouldn’t need to. Just use that same code again.

--autosaves every 5 minutes
while task.wait(300) do
    local folder = nil --path to folder within the player
    local items = {}
    for _, item in pairs(folder) do
	    local assignItem = {
		    [item.Name] = {
			    ["AmountOwned"] = item.Amount.Value
		    }
	    }
        table.insert(items, assignItem)
    end
    local key = "DataSavingKeyHere"
    local success, err = pcall(function()
	    store:SetAsync(key, items)
    end)
end

The only trouble would be with UpdateAsync(), because that reads before it writes. If you use SetAsync(), you should be fine.

2 Likes

I have one more question… I’m trying to load the data I saved, but I’m having trouble.

This is my code to load the amount:

if data.items then
	for i, v in pairs(data.items) do
		local currentItem = items:FindFirstChild(v)
		--
		currentItem.Amount.Value = v.amount
	end
end

In the above code, data is a table containing all player data, items is the folder that gets parented to the player, and data.items is the table I have the Name saved to. The Name contains the Amount .

Every time I run this code, I get the same error, it says: attempt to index nil with "Amount"

EDIT: When I print (v) I get a table that contains: {[“Common”] = { amount = 0 }}

I’m guessing local currentItem = items:FindFirstChild(v) is nil. But I’m not sure how I would get the name inside of a table that is inside of another table.

Try items:WaitForChild(v). Let me know if you get an Infinite Yield warning.

Yeah, it says: Infinite yield possible on 'Players.Devastationial.Items:WaitForChild("{}")

Seems like it might be an issue with the saving, not the table itself. Make sure:

  1. You are accessing the same data store with the same key for saving and loading.
  2. You are using a BindToClose() function to ensure the game has enough time to save the data before it shuts down.

I tried this, I still am getting the infinite yield, if I print(v) it returns the exact value I want, I just don’t know how to access it.

Output result of print(v):

{ ["Common"] = {["amount"] = 0 }}

The infinite yield might not be preventing the code from running - the game might just be taking a while to load. As for accessing the “amount” key within that table:

local randomTable = {
    ["Common"] = {
        ["amount"] = 0
    }
}

print(randomTable["Common"]["amount"])

or in a loop:

for i, v in pairs(randomTable) do
    if v["amount"] then
        print(v["amount"])
    end
end

Do you think this could work if I’m trying to cycle through all the items and set the amount for those items?

for i, v in pairs(data.items) do
    local currentItem = items:WaitForChild(v)
    --
    currentItem.Amount.Value = v["amount"]
end

My only concern is that I won’t be able to access currentItem.Amount.Value because it’s in an infinite yield.

Can you send the line (and surrounding lines) that the Infinite Yield warning is coming from?

Yeah, here you go:

-- Sync Items --
if data.items then
	for i, v in pairs(data.items) do
        print(v) --successfully prints the item in the table
		local currentItem = items:WaitForChild(v) --Infinite Yield
		--
		currentItem.Amount.Value = v["amount"]
	end
end