How to save items In datastores

So there have been numerous posts around the forums of how to make a datastore that can save items. Making such a datastore would have many uses like creating an inventory so I decided to make a tutorial about it.

In order to understand this tutorial you need basic knowledge of the following:

This tutorial will focus on the format of saving items and not on how to retrieve attributes of items as that can be done by making your own functions.


Disclaimer
This tutorial is obviously not using the best practises (for example you would want to use module scripts to recover/update items, retry when data acquisition fails e.t.c) and is only to teach beginners a way to save items in order for them to be able to create their own frameworks for inventories/shops or whatever other system they think of.


When you are saving items it is very likely that you want to save some attributes/properties with them,
First thing to understand is that when you want to save an item you don’t save the item itself but a referance to that item and save it’s attributes/properties along with the referance.

First of all you would want all the obtainable items to be in a folder( When you get more advanced you can put them on multiple folders based on the item’s class for the shake of organization)
aa4418b4ac66ba39b916962e297ac382

Let’s suppose you want to save a sword and that sword has 2 attributes. Them being damage, cooldown. One way you could go about this is creating a table that would contain these attributes. So let’s say I want to save an item called “Sword” with these 2 attributes, I would use the following table.

["Inventory"] = {
   ["Sword"] = {
       ["damage"] = 5,
       ["cooldown"] = 1
   }
}

This table allows you to pass the name of the item along with it’s properties. Reason you want to pass the name is that when you recover the data (player joins) you want to know what items he owns and clone them into his inventory(StarterGear) so you would do something like this.

local DSS = game:GetService("DataStoreService")
local DS = DSS:GetDataStore("Inventory")
local RS = game:GetService("ReplicatedStorage")
local data = {}
game.Players.PlayerAdded:Connect(function(plr)
     data[plr] = {}
     local RecData = {}
     local succ,err = pcall(function()
           RecData= DS:GetAsync("UserID_"..(plr.userId))
     end)
     if not RecData then RecData["Inventory"] = {} end
     for i,v in pairs(RecData["Inventory"] ) do
          data[plr][i] = v
          RS.Items[i]:Clone().Parent = plr.StarterGear
          RS.Items[i]:Clone().Parent = plr.Backpack
     end
end)

Although saving items like this would present problems because you wouldn’t be able to store more than one items (With different stats) with the same name.You can also store items in quantity if you don’t care about items having different stats and being unique but that would limit you a lot and make updating your game a lot harder when it comes to inventory features therefore I don’t recommend it. A way to go arround this is using GUID when saving items. You may ask why use guid and not increment the table? Using guid provides the item with a unique identifier and can provide easier integration of future systems like trading and helps you keep better track of the items. Although this would require 1 extra attribute which would be the item’s name:

["Inventory"] = {
   ["94b717b2-d54f-4340-a504-bd809ef5bf5c"] = {
       ["Item"] = "Sword",
       ["damage"] = 5,
       ["cooldown"] = 1
   }
}
local DSS = game:GetService("DataStoreService")
local DS = DSS:GetDataStore("Inventory")
local RS = game:GetService("ReplicatedStorage")
local data = {}
game.Players.PlayerAdded:Connect(function(plr)
     data[plr] = {}
     local RecData = {}
     local succ,err = pcall(function()
           RecData= DS:GetAsync("UserID_"..(plr.userId))
     end)
     if not RecData then RecData["Inventory"] = {} end
     for i,v in pairs(RecData["Inventory"]) do
          data[plr][i] = v
          RS.Items[v["Item"]]:Clone().Parent = plr.StarterGear
          RS.Items[v["Item"]]:Clone().Parent = plr.Backpack
     end
end)

Now if you want to Add data you would have a function like the following:

local HttpService = game:GetService("HttpService")
function AddItem(plr,item,damage,cooldown)
     data[plr][HttpService:GenerateGUID(false)] = {
          ["Item"] = item, --Item Name (string)
          ["damage"] = damage, --integer or float
          ["cooldown"] = cooldown --integer or float
     }
end

game.Players.PlayerRemoving:Connect(function(plr)
     local succ,err = pcall(function()
         DS:SetAsync("UserID_"..(plr.userId),data[plr]})
     end)
     data[plr] = nil
end)

Reason we are using a data table is so that you can easily recover damage/durability and other attributes through the use of functions without the need to use GetAsync everytime you want to retrieve something. Also storing inventory data in folders/stringvalues e.t.c is bad practise.


A plugin that I believe will help you a lot if you can afford it is DataStore Editor by @sleitnick as it will help you visualize your datastores. Here are some examples of some of my old datastores using this plugin:
98c09fd2c3bc9668651e491d659b2df5
As you get more and more advanced you will be able to organize them into categories for example:

["Progression"] = {
     ["Level"] = 1,
     ["EXP"] = 50
},
["Inventory"] = {
     ["94b717b2-d54f-4340-a504-bd809ef5bf5c"] = {
       ["Item"] = "Sword",
       ["damage"] = 5,
       ["cooldown"] = 1
     },
     ...
}


So let’s say you have added mutliple swords using the AddItem function your data table would look something like this

["Inventory"] = {
   ["94b717b2-d54f-4340-a504-bd809ef5bf5c"] = {
       ["Item"] = "Sword",
       ["damage"] = 5,
       ["cooldown"] = 1
   },
   ["ea106a8f-c46a-41db-ab78-30d6f30e332f"] = {
       ["Item"] = "Sword",
       ["damage"] = 6,
       ["cooldown"] = 2
   },
   ["d12f0063-b881-4d53-a485-aae07b0b19e9"] = {
       ["Item"] = "Sword3",
       ["damage"] = 2,
       ["cooldown"] = 8
   }
}

When player exits the script would take the whole table above and replace the already existing data with it. If you understand how to save basic items with their attributes you can then save multiple things like Armors, Shields, Part placements using CFrame and other properties.


Thank you for reading. The code above was not tested in studio as it is clearly for demonstration so if you spot any spelling mistakes e.t.c please message me so I can fix them. Leave any feedback bellow.

16 Likes

Nice tutorial. I was thinking about making something like this, then you made it. Well done!

Also you spelled “Progression” wrong.

2 Likes

You should probably not kick players if you can’t get their data since you are losing players this way.

yeah i know normally you would retry e.t.c but as i said above this is for demonstration

1 Like

Yet many people will follow this and end up kicking players.

1 Like

I understand your consern so I removed the kick part to improve this tutorial.

1 Like

Pretty sure this is going to error out as you’re assuming the player always has a backpack.

You should do

for i,v in pairs(RecData["Inventory"] or {}) do

It is assumed that if the player doesnt have data they will get initialized. I will add that in as some people may get confused

Alright well regardless, this tutorial is pretty nice. Would recommend this to beginners looking to save items.

A more efficient way would be using quantity concept.

["Inventory"] = {
	["Sword Of Chaos"] = {
		["Slot"] = {1, "Z"}, -- custom inventory & tool_bar ui
		["Quantity"] = 3,
	}
}


for Name, Item in pairs(Data["Inventory"]) do
	local Tool = ItemsFolder:FindFirstChild(Name)
	if not Tool then continue end
	for i = 1, Item["Quantity"] do
		Tool:Clone().Parent = Player.Backpack  -- etc
	end
end

Not really because you wont be able to have items with different stats and no item is unique. It would obviously work and take less space/time but is a bad way of doing things unless you dont care about items having different values. It is also the way i did my first inventory but quickly figured out i couldnt do the updates i wanted because of it. Saving items by quantity is very restrictive and I wouldn’t recommend it.

1 Like

yeah, that was the intention. If you gonna have some kind of upgrade system for items where you need to save damage then yeah quantity is stupid here.

but there’s also another alternative for that while still using quantity

local Inventory = {
    ["Sword Of Chaos"] = {
         ["Damage"] = {6, 20, 20, 12, 6}, -- each slot represents the damage for each item
         ["Rarity"] = {1, 5, 5, 2, 1} -- each slot represents the rarity for each item
         ["Quantity"] = 5,
    }
}