Need help using GetAsync and SetAsync to make an efficient inventory saving (or other methods of saving)

Hey dev forums, I need help with creating an inventory save system hopefully using GetAsync and SetAsync. I do know how to use the two functions but the player inventory is an array, making saving it using SetAsync as it is impossible. What can I do to save the inventory? I am also open to any easier methods of saving the player inventory if any exist.

An easy way is to rewrite your datastore using ProfileStore By loleris I have used this module and it is perfect for all your data store needs. Many other people use it as well. There is many tutorials out there on youtube and it can only take 20 minutes max to fully setup your game and make handling data easier.

Although if you wish to keep using Roblox’s Data Store Service and the limitations of it, let me know and we can figure out a solution.

2 Likes

Even though I do see this module as something I could definitely use, I have already used GetAsync and SetAsync for all of my data saving and I seem to understand it more. I would like to just have a way to save the inventory as something other than an array that works if possible. Although I will still consider using it if using GetAsync and SetAsync causes too much trouble.

In that case you should be able to do this

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Testing") -- Your DS name.

local function savePlayerData(player)
	local inventory = {}

	for _, item in pairs(player.Backpack:GetChildren()) do
		if item:IsA("Tool") then
			table.insert(inventory, item.Name)
		end
	end

	local playerData = {
		Money = 100, --replace with actual money tracking and other stuff
		Inventory = inventory,
	}

	local success, err = pcall(function()
		mainDataStore:SetAsync("Player_"..player.UserId, playerData)
	end)

	if not success then
		warn("Error saving data for", player.Name, ":", err)
	end
end

Then if you need to load it you can try and do this

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStore = game:GetService("DataStoreService"):GetDataStore("Testing")

local function loadPlayerData(player)
	local success, data = pcall(function()
		return mainDataStore:GetAsync("Player_"..player.UserId)
	end)

	if success and data then
		-- Load data references with data.[NAME]
		for _, itemName in ipairs(data.Inventory or {}) do
			local itemTemplate = ReplicatedStorage.Items:FindFirstChild(itemName)
			if itemTemplate then
				itemTemplate:Clone().Parent = player.Backpack
			end
		end

		-- Use data.Money or whatever you decide to save
		print("Loaded money:", data.Money)

	else
		warn("Failed to load data for", player.Name, ":", data)
	end
end

I am unable to test this in roblox studio right now but i’m somewhat confident it will work

For a more in depth explanation of what it going on…

Saving

  • Make a temporary table variable to store the items we are going to save
  • Do a for loop of each tool in the player’s backpack (You may also want to force unequip everything)
  • Inserts it into the table
  • Create the playerData we are saving you can store anything here.
  • Do a pcall so we can handle errors and if its a success we save our playerData

Loading

  • pcall function to get the player’s data
  • if its a success and the data table exists we load the data
  • Check the player’s data.Inventory and parents any tool they had from ReplicatedStorage to the player
  • Also prints data.Money as an example to show how you can save more data inside one data store.

If you have any more questions let me know!

2 Likes

I’m not home to test this either lol, but I will have a look at it once I get the chance tonight. Thank you. (also this post is staying open in case others have ideas)

1 Like

Well, first things first, you should use UpdateAsync, not SetAsync.

Then, if you’re normal, you can save inventory data into a table. Code sample below.

function LoadData(player)
    local data
    local success, err = pcall(function)
        data = dataStore:GetAsync(player.UserId)
    end)
    if not success then --// Kick player if their data failed to load
    player:Kick("Error loading data") warn(err)
    
    elseif success and not data then --// Player is new, no data detected.
        print("No data found!"
    
    end
    
    print(data)
    --// Just write how YOU want to load data, this is just an example.
end

function SaveData(player)
    local data = {
        Coins = 500,
        XP = 0,
        
        Inventory = {
            ["Classic Sword"] = false, --// Unequipped
            ["Rocket Launcher"] = true, --// Equipped
        },
    }
    
    dataStore:UpdateAsync(player.UserId, function()
        return data
    end)
end

(Yes, you can also use a single data store to store EVERYTHING. You don’t need one for cash, another for XP and a third for inventory)

To load data, you can use a variety of things, most often people use folders and instances.
You decide how to save items, if they need certain data, then you save that.

If for example, you just want to save what tools a player has, when they leave, loop through their backpack and save every tool name to a table, and then check the player for a tool and save that tools name too, if it exists.

Here’s a code sample:

function getTools(player)
    local playerTools = {}
    for _, tool in player.Backpack:GetChildren() do
        table.insert(playerTools, tool.Name)
    end
    if player.Character:FindFirstChildOfClass("Tool") then
        table.insert(playerTools, player.Character:FindFirstChildOfClass("Tool").Name)
    end
    
    return playerTools
end

--// sample to get data
local saveData = {
    Coins = player.Coins.Value,
    Inventory = getTools(player)
}

Then to load the tools, all you’d have to do is save them somewhere (replicated storage, server storage etc) and copy-paste them into the backpack when the player joins.

function giveTools(player, inventory)
    for _, tool in inventory do
        local inst = game.ReplicatedStorage.Tools[tool]:Clone()
        inst.Parent = player:WaitForChild("Backpack")
    end
end

Butttt how you load items does entirely depend on what it is you’re saving.
If what you’re saving requires to have certain data attached (for example, attachments or particle effects), you can save it as a table or a long string within the inventory folder.


Buutttt if you’re a maniac such as myself, you can use a fully attribute driven system and save everything into a single string.
There’s so much to explain here I won’t bother with much, but heres a sample to show you how to “read” the string. Then after reading the string, just apply the same logic as above to load items.

local text = "Classic Sword|Rocket Launcher"

function readData(dataToRead)
    for _, data in string.split(dataToRead, "|") do
        print(data) --// outputs "Classic Sword" and then "Rocket Launcher"
    end
end
readData(text)

So yeah, data saving is pretty simple and it only gets complicated if you want to make it complicated.


Anyway, hopefully this information will be helpful to you!

LOL, AI? I guess thanks for the compliment? If you are suggesting I write good, then thank you? If you cant confirm something then don’t accuse someone. Just for future reference :smiley:

Actually I see the string method you use working fairly well, since all guns are just as they are with no customizability. And just in case it helps, I am saving all children (just a bunch of string value instances with different names, probably not efficient but oh well) parented to a folder called inventory.

and yall please dont cause an AI argument here, Ill decide what works best for my game whether its AI or not.

Apologies, I don’t use the AI check websites as they’re pretty often inaccurate.

The way I base my accusations is off formatting, positioning of text and in code, how many comments there are.

Your post followed what I’d call the “generic” AI format.

  1. Comment either relating to what the user asked, talking about code below or both.
  2. Code block 1 for half the function (repeat this step and above step for however many code blocks are used)
  3. End the post with an explanation of everything being done, using large, bold text and drop points.

I don’t unless if I’m pretty confident, again the AI checkers are usually just wrong (especially when code starts getting mentioned) so I don’t trust them.

You did a good job breaking down saving, though, better than I did.


(for op)

And adding onto this part in my code, if you need to save data as a string, probably a terrible method of doing it but its what I do.

function saveToString()
    local invString = ""
    for _, v in player.Backpack:GetChildren() do
        invString = invString .. v.Name .. "|"
    end
    return invString
end
1 Like

lol I agree with the fact that its probably a terrible method but I understand it so much better than other methods. Being self taught kind of makes the methods I use inefficient but it works.

I will try this out when I get home. I’m hoping the method you use will work since it seems simpler and quick (which is what I need because I had to extend the Easter Event since I wasn’t ready for inventory saving yet)

Thanks!

1 Like

While i appreciate you owning up to your accusations. It’s just the way i type, I work at as an IT Support Specialist, so i do a TON of helpdesks tickets a day. It’s just the formatting we use here.

  • Conclusion to solution
  • Break Down Solution and how it worked and how I came up with it.
  • If any more problems / questions come up don’t be afraid to ask.

No worries about the AI Checkers, i know they are very inaccurate some of my code has been rejected from my professors (Freshman CS) because i guess they just assume I can’t code on my own time lol, I’ve been doing roblox and Java, c#, for the longest but then i had to prove myself in class for them to stop questioning it.

But your response is way more detailed and lengthy and shows multiple ways to organize the code. Have an amazing weekend!

2 Likes

What I did was create a structure that mimics the folder of your inventory system.
Then create functions that load and save using this handy structure

local PlayerFolderStructure = { -- This basically makes it much easier to save/load data from ServerStorage.PlayerValues
	Inventory = { 
		Hotbar = {},
		Inventory = {},
		Armor = {
			["Helmet"] = "",
			["Chestplate"] = "",
			["Pants"] = "",
			["Boots"] = ""
		},
		Gold = 0
	},
	Human = {
		Health = 100,
		maxHealth = 100,
		XP = 0,
		Mana = 100,
		Level = 0,
		Stamina = 100,
		maxMana = 100,
		maxXP = 0
	}
}

Which mimicked the folder structure of my PlayerValues

I then created two functions that save/load data from and to my datastores.

function saveData(player, folder)
	local PlayerFolder = PlayerFolderStructure -- Clone the table
	
	for _, v in folder.Inventory.Hotbar:GetChildren() do -- Loop for each item in hotbar. v is the StringValue
		PlayerFolder.Inventory.Hotbar[v.Name] = v.Value -- Set the table values for Hotbar
	end
	for _, v in folder.Inventory.Inventory:GetChildren() do -- Loop for each item in inventory. v is the StringValue
		PlayerFolder.Inventory.Inventory[v.Name] = v.Value -- Set the table values for Inventory
	end
	for _, v in folder.Human:GetChildren() do -- Loop for each item in Human. v is the StringValue
		PlayerFolder.Human[v.Name] = v.Value -- Set the table values for Inventory
	end
	
	for _, v in folder.Inventory.Armor:GetChildren() do -- Loop for each item in Armor. v is the StringValue
		PlayerFolder.Inventory.Armor[v.Name] = v.Value -- Set the table values for Armor
	end
	
	PlayerFolder.Inventory.Gold = folder.Inventory.Gold.Value -- Set the Gold Value in the table
	
	local success, err = pcall(function() -- pcall for datastore
		playerDataStore:SetAsync(player.UserId, PlayerFolder) -- Save Data to the DataStore
	end)
	
	if success then -- Successful Save
		print("Data for ".. player.Name .. " saved successfully.")
	else -- Unsuccessful Save
		warn("Failed to save data for ".. player.Name .. ". Error: "..err)
	end
end
function loadData(player, folder) 
	local success, data = pcall(function() -- pcall for datastore
		return playerDataStore:GetAsync(player.UserId)
	end)
	
	if success and data then -- checks whether the data loading was successful and data was given
		for i, v in pairs(data.Inventory.Hotbar) do -- Loop between every value inside hotbar stored in the Datastore
			local formattedName = string.format("%02d", tostring(i)) -- Format the name to be 01, 02, 03 etc. 
			local HotbarItem = folder.Inventory.Hotbar:FindFirstChild(formattedName) -- Item in the Hotbar Folder in PlayerValues
			
			if not HotbarItem then -- If the item doesn't exist, create it
				HotbarItem = Instance.new("StringValue", folder.Inventory.Hotbar)
				HotbarItem.Name = formattedName
			end
			
			HotbarItem.Value = v -- Set the value to the value stored in the Datastore.
		end
		
		for i, v in pairs(data.Inventory.Inventory) do -- Loop between every value inside inventory stored in the Datastore
			local formattedName = string.format("%02d", tostring(i)) -- Format the name to be 01, 02, 03 etc. 
			local InventoryItem = folder.Inventory.Inventory:FindFirstChild(formattedName) -- Item in the Inventory Folder in PlayerValues
			
			if not InventoryItem then -- If the item doesn't exist, create it
				InventoryItem = Instance.new("StringValue", folder.Inventory.Inventory)
				InventoryItem.Name = formattedName
			end
			
			InventoryItem.Value = v	-- Set the value to the value stored in the Datastore.
		end
		
		for i, v in pairs(data.Inventory.Armor) do -- Loop between every value inside armor stored in the Datastore
			local ArmorItem = folder.Inventory.Armor:FindFirstChild(i) -- Item in the Armor Folder in PlayerValues
			
			if not ArmorItem then -- If the item doesn't exist, create it
				ArmorItem = Instance.new("StringValue", folder.Inventory.Armor)
				ArmorItem.Name = i
			end
			
			ArmorItem.Value = v -- Set the value to the value stored in the Datastore.
		end
		
		for i, v in pairs(data.Human) do -- Loop between every value inside human stored in the Datastore
			local item = folder.Human:FindFirstChild(i) -- Item stored in the Human Folder in PlayerValues
			if item then -- Checks whether the Item exists
				item.Value = v -- then sets the value to the value stored in the Datastore.
			end
		end
		
		folder.Inventory.Gold.Value = data.Inventory.Gold -- Set the value to the value stored in the Datastore.
		
	else
		warn("Failed to load data for ".. player.Name) -- Error in Loading
	end
end

(yes, I used a lot of comments because I was documenting the creation of this, since it was for a gamejam project that i didn’t continue)

Then, you can link events to these functions

game.Players.PlayerAdded:Connect(function(player) -- When Player Joins
	local PlayerFolder = PlayerValues:Clone() -- Clone the Player Values folder
	PlayerFolder.Parent = player -- Give it to the Player
	
	loadData(player, PlayerFolder) -- Load the Data
	
	if player:FindFirstChild("leaderstats") then
		player:FindFirstChild("leaderstats"):Destroy()
	end
	
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	
	local Level = Instance.new("IntValue", leaderstats)
	Level.Name = "Level"
	Level.Value = player.PlayerValues.Human.Level.Value
	
	local Gold = Instance.new("IntValue", leaderstats)
	Gold.Name = "Gold"
	Gold.Value = player.PlayerValues.Inventory.Gold.Value
	
end)

game.Players.PlayerRemoving:Connect(function(player) -- When Player Leaves
	saveData(player, player:FindFirstChild("PlayerValues")) -- Save the Data
end)

game:BindToClose(function() -- When Server Shuts Down
	for _, player in game.Players:GetPlayers() do -- Loop between all the Players
		saveData(player, player:FindFirstChild("PlayerValues")) -- Save the Data
		player:Kick("Server has shut down. Your data has been saved.") -- Kick Player
	end
end)

Really, you don’t need ProfileService. If you like the session-lock feature, you can easily create that using DataStoreService.

Hope this helps!

2 Likes

this worked after some tweaking. Thanks!

1 Like