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.
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!
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)
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
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.
- Comment either relating to what the user asked, talking about code below or both.
- Code block 1 for half the function (repeat this step and above step for however many code blocks are used)
- 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
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!
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!
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!
this worked after some tweaking. Thanks!