How to save your players' inventory items

In this tutorial, I will show you how to save player’s inventory items.

Setup

The way I do it with storing tools, is having a folder in ServerStorage named Tools, and all my tools would go there.

image

A second folder would be a folder containing sub-folders, which would contain each players’ inventory.
Example of hierarchy:

image
The Inventories folder would start out empty, but a folder with the name of the player who joins will be created, and the tools they have will be put in there aswell.

Now onto the actual coding!

Loading

Before talking about loading, I will quickly talk about how we will even save in the first place.

We will be saving the name of the tools into a table in this tutorial, but you can always use something else to identify each tool.

Alright back to loading.

When the player joins, just give them their tools:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local ServerStorage = game:GetService("ServerStorage")

local player_data = DataStoreService:GetDataStore("player_data")

local tools = ServerStorage.Tools
local inventories = ServerStorage.Inventories

Players.PlayerAdded:Connect(function(client)
	local key = "client_" .. client.UserId
	local inventory = player_data:GetAsync(key) -- Not worrying about pcalls, do that yourself
		
	local inventory_folder = Instance.new("Folder")
	inventory_folder.Name = client.Name
	inventory_folder.Parent = inventories

	for _, name in ipairs(inventory or { }) do
		local tool = tool[name]
		tool:Clone().Parent = client.Backpack -- For the player to use
		tool:Clone().Parent = inventory_folder -- For saving and loading
	end
end)

Not too complicated. We create a new folder of their name. We clone any tools they have into their folder and backpack.
If no data is present, you can either just use an empty table or a starter table of free items.

Saving

When saving, just go through their Inventories folder. Save all the names of the tools into a table, and save that to the data store. Finally, delete the folder as it is no longer of use.

Players.PlayerRemoving:Connect(function(client)
	local key = "client_" .. client.UserId
	local tools = { }
	local inventory_folder = inventories[client.Name]
	
	for _, item in ipairs(inventory_folder:GetChildren()) do
		table.insert(tools, item.Name)
	end

	player_data:UpdateAsync(key, function(prev)
		return tools
	end)

	inventory_folder:Destroy()
end)

And there we go. Bam. You have a fully functional inventory-saving system. but this tutorial ain’t over yet bro

You might ask how to add items to their inventory in the first place. Generally, games have a shop UI where you can buy items.
So with the power of remotes, we can implement one:

Not the best but just to demonstrate it. When the button is clicked, send a request to the server to buy a tool:

local ui = script.Parent
local frame = ui:WaitForChild("Frame")
local remote = game:GetService("ReplicatedStorage"):WaitForChild("OnRequest")

for _, button in ipairs(frame:GetChildren()) do
	button.Activated:Connect(function()
		remote:FireServer(button.Name)
	end)
end

The way I do it is, I name the buttons the name of the item it corresponds to. So I can just easily run a for loop like that.

The server code simply gives the player the requested item, and adds it to their inventory folder.

local remote = game:GetService("ReplicatedStorage").OnRequest

local ServerStorage = game:GetService("ServerStorage")
local tools = ServerStorage.Tools
local inventories = ServerStorage.Inventories

remote.OnServerEvent:Connect(function(client, request)
	local inventory_folder = inventories[client.Name]
	local tool = tools[request]
	tool:Clone().Parent = client.Backpack
	tool:Clone().Parent = inventory_folder	
end)

I didn’t code an entire currency system, or add any sanity checks; that is for you to implement.


But there is the tutorial for you. I hope you liked this tutorial, and if you did, please leave a like. It really helps out a lot.
Feedback would be appreciated if I accidentally made a mistake somewhere.

237 Likes

This really helped alot Thanks!

9 Likes

I would recommend adding a pcall, because if there is any error you can figure it out:

local success, err = pcall(function()
    player_data:UpdateAsync(key, function(prev)
		return tools
	end)
end

if not success then
    warn(err)
end
15 Likes

Hey thank you, I didn’t pcall on purpose as this isn’t meant to be a full tutorial on data stores, it just serves to get the point across. You would have to do that yourself.

18 Likes

Where would we put these scripts?

1 Like
game.ServerStorage

^^^^^^^^^^^^^^^^^^^^^^^
(This is an educated guess, not 100% sure)

4 Likes

Don’t mean to bump this thread but @octavodad 1,2,4 scripts server, 3rd local.

1 Like

“Unknown global tool” what is happening?

11 Likes

Hey @sjr04 - this is an awesome tutorial, but I did run into an issue:

In your tutorial you showed:

Players.PlayerAdded:Connect(function(client)
	local key = "client_" .. client.UserId
	local inventory = player_data:GetAsync(key) -- Not worrying about pcalls, do that yourself
		
	local inventory_folder = Instance.new("Folder")
	inventory_folder.Name = client.Name
	inventory_folder.Parent = inventories

	for _, name in ipairs(inventory or { }) do
		local tool = tool[name]
		tool:Clone().Parent = client.Backpack -- For the player to use
		tool:Clone().Parent = inventory_folder -- For saving and loading
	end
end)

I’m not sure if you meant to display tool = tools[name] instead of tool[name] - but it was failing to fetch the tools. Which made sense on why I was confused for why tool wasn’t defined globally.

I then did:

local tool = tools[name] - however it then was trying to fetch the baseballbat within the baseballbat, so I made the following adjustments:

Players.PlayerAdded:Connect(function(client)
	local key = "client_" .. client.UserId
	local inventory = player_data:GetAsync(key)
	
	local inventory_folder = Instance.new("Folder")
	inventory_folder.Name = client.Name
	inventory_folder.Parent = inventories
	print("defined all the stuff")
	
wait(10)
	
	for _, name in ipairs(inventory or { }) do
		print("started for loop")
		wait(1)
		print(tools[name])
		tools[name]:Clone().Parent = client.Backpack
		tools[name]:Clone().Parent = inventory_folder
	end
end)

I added a wait(10) seconds function because the script was processing everything faster then the server was loading in the serverstorage. Perhaps this is because I’m using an empty baseplate to use this tutorial in order to make a custom inventory system?

I hope this helps anybody that may have had similar issues with this tutorial. Again, awesome tutorial - it helped me a lot with understanding the mechanics of for loops with datastore’s, much appreciated! <3

10 Likes

You should also check if the tools even exists before cloning it. Idk if this changes anything or if it is already working.

This is awesome! I was wondering how I could use this and implement it into my system? I have a module script with a dictionary storing tables of info on each weapon skin (these are actual models), then also a folder storing every weapon skin in the game, both in rep storage. When a player buys a skin I want it so it fires a remote event, clones the table to an inventory table, then also clones the model into an inventory folder. Then I want to set it up so when a new weapon skin is added to the inventory or a player loads into the game it’ll create a new slot template, read the info table stored in the inventory table, then fill out all the info. Then I want it to save the folder and inventory table when a player leaves. However I don’t know how to execute this. I also have 2 problems. How do I make it so it recognizes what model is for the slot the player has equipped? (For example the inventory loaded all the data, made the templates, changed them based off the individual info tables, and then gives the model when a player clicks equip). That’s problem #1. Secondly I want to somehow hook all of the system to this too How to Make Server-Synced Daily Shops. My game has two shops, a permanent shop and a weekly shop. I’m gonna use this system linked. However what I want to know is how I can make it so when a skin is selected off the randomized table for the weekly system it’ll see the name and then go to the info table for that skin and then change the slot in the shop to display it’s info (name, price, imageid, etc).