Saving a player's inventory

The way I’m looking to go about this is by saving a table which lists all the items in a player’s backpack. When the player rejoins it’ll loop through a folder which contains all in game tools and clone them into their backpack.

Issue is I’m not sure how to or if you can save a table in a datastore.

1 Like

you can do it exactly how you do it with values, like you mentioned you loop through the player’s backpack and character, insert them to {} (or tables) then

:SetAsync(key, table) 

then you get the arrays of the data and that’s basically it (i hope I’m not mistaken)

You can save it like any other value.

If you’re asking how to save a dictionary, for some reason in my experience I can’t save dictionaries in datastores, for example, I can’t save something like this in a datastore:

Datastore:SaveAsync(key, {item = value, item2 = value2, etc…})

It doesn’t work. However, if you put that dictionary in a normal table and save that, like this:
Datastore:SaveAsync(key, {{item = value, item2 = value2, etc…}})
Somehow, it works. Not sure why, but that’s how it is. That might fix it.

All of these ideas’ suggested by others are good but here is one that is simple and efficient.


local ToolFolder = game:GetService("ServerStorage"):FindFirstChild("SaveAbleTools")
local ToolSave = DSS:GetDataStore("ToolData")

game.Players.PlayerAdded:Connect(function(Player)

	local ToolData = ToolSave:GetAsync(Player.UserId)

	local Backpack = Player:WaitForChild("Backpack")
	local StarterGear = Player:WaitForChild("StarterGear")

	if ToolData ~= nil then
		print("Looking For Tool Data")
		for i, v in pairs(ToolData) do
			print("Found Data")
			if ToolFolder:FindFirstChild(v) and Backpack:FindFirstChild(v) == nil and StarterGear:FindFirstChild(v) == nil then
				print(Player.Name.."'s ToolData Has been Found")
				ToolFolder[v]:Clone().Parent = Backpack
				ToolFolder[v]:Clone().Parent = StarterGear
				warn(Player.Name.."'s Tools have been added back")
			else
				warn(Player.Name.."'s tools had an error whilst loading!")
			end
		end
		Player.CharacterRemoving:Connect(function(Character)
			Character:WaitForChild("Humanoid"):UnequipTools()
		end)
	end

end)

game.Players.PlayerRemoving:Connect(function(Player)

	print("Tools Saving")
	local ToolTable = {}

	for i,v in pairs(Player.Backpack:GetChildren()) do
		table.insert(ToolTable, v.Name)
		if ToolTable ~= nil then
			ToolSave:SetAsync(Player.UserId, ToolTable)
			warn(Player.Name.."'s Tools have been saved")
		end
	end
end)


This script goes into ServerScriptService.

You also need a folder with a duplicate of your tools inside that you want to save.

This script is from GamerM8 btw

2 Likes

So, I’m still confused on how to do this…

Right now I’m doing this to save a backpack (which isn’t working but it was my best guess)

key = "User"..player.userId
-- other stuff
items = {item1, item2}
for i,v in pairs(player.Backpack:GetChildren() do
table.insert(items, v)
end
datastore:SetAsync(key, items)

Which returns "104: Cannot store Array in data store. Data stores can only accept valid UTF-8 characters. "

Honestly no idea what any of that means.

See I save a table but in the table are bool, string, etc. values. I’m looking to store the player’s backpack. I hope this makes sense (sorry if I’m just being stupid :sweat_smile:

try emptying the table of the somewhat random item1 and item2 stuff and insert them manually like when looping the other obj

Ok. So I somehow got it to work by messing around.

Basically I do this.

if storeditems[10] ~= nil then
			for i,v in pairs(storeditems) do
				for b,d in pairs(game.ReplicatedStorage.StoredTools:GetDescendants()) do
					if d.Name == v then
						local tool = d:Clone()
						tool.Parent = player.Backpack
					end
				end
			end
		end

I also changed the insert table part from v to v.Name like this:

for i,v in pairs(player.Backpack:GetChildren()) do
			table.insert(items, v.Name)
		end

Thank you for helping me out along the way though. If this is really undefective or something please tell me! I don’t want to be doing something the worst way possible.

i have an idea, you separate it into 3 tables (tools, strings and bools) for 3 separate datas as that would not error due to having valid variety in terms of being an array or a dictionary

To do this would I:

datastore:SetAsync(key, tools, strings, bools)

or

datastore1:SetAsync(key, tools)
datastore2:SetAsync(key, strings)
datastore3:SetAsync(key, bools)

also how does this affect datastore saving limit?
I’m worried that if a player has too many items in their inventory it’ll reach max save limit and you could lose things

you do the latter and also differentiate the key naming as well

actually, you don’t have to switch datas, they can be the same one

Alr, so to recap.

local bools = {all ur bool values}
local strings = {all ur string values}
local tools = {players inventory}
--making sure they don't have a tool equipped and gets lost
for i,v in pairs(character:GetChildren()) do
	if v:IsA("Tool") then
		v.Parent = player.Backpack
	end
end
--Inserting plr's tools into tools table
for i,v in pairs(player.Backpack:GetChildren()) do
	table.insert(items, v.Name)
end
BoolDataStores:SetAsync(key, bools)
StringDataStores:SetAsync(key, strings)
ToolDataStores:SetAsync(key, tools)

and when a player joins you want to load them back in by doing

storedtools = ToolDataStores:GetAsync(key)

if storedtools ~= nil then
for i,v in pairs(storedtools) do
		for b,d in pairs(game.ReplicatedStorage.StoredTools:GetDescendants()) do
			if d.Name == v and not d:FindFirstChild("Weapon") then
--Weapons is a folder I have in weapon tools because I have a seperate loading system for those
				local tool = d:Clone()
				tool.Parent = player.Backpack
			end
		end
	end
end

--rest of datastore loading stuff for bools and strings etc.

This looks all good?

Lol here is some code that can do exactly that. Storage is your folder that holds your tools. It has to be in a server script.

local HttpService = game:GetService("HttpService")

local Storage = game.ReplicatedStorage.Storage.Tools

local BackpackStore = DataStoreService:GetDataStore("BackpackStore")

game.Players.PlayerAdded:Connect(function(player)
	local tools = HttpService:JSONDecode(BackpackStore:GetAsync(player.UserId) or "")
	for i,v in pairs(tools) do
		local tool = Storage:FindFirstChild(v)
		if tool then
			tool:Clone().Parent = player.Backpack
		end
	end
	
	player.CharacterRemoving:Connect(function(character)
		local toolList = {character:FindFirstChildOfClass("Tool") and character:FindFirstChildOfClass("Tool").Name}
		for i,v in pairs(player.Backpack:GetChildren()) do
			if v:IsA("Tool") then
				table.insert(toolList,v.Name)
			end
		end
		BackpackStore:SetAsync(player.UserId,HttpService:JSONEncode(toolList))
	end)
end)

No idea how this works and also get an error “Can’t parse JSON” from line 8

I’ll use strings and a decoder.

The DataStore can be anything you want, as long as it has a unique name within your game.

I’m gonna save and load some Vector3s as strings for my example DataStore.

local GPS = game:GetService("DataStoreService"):GetDataStore("GPS")

This DataStore is structured like so, keys are the player’s IDs, and a string is the Vector3. (exampleplayer.UserId,"-1, -1, 0")

local key = tostring(player.UserId)
-- vec3.Value = (-1, -1, 0)
local strig = tostring(vec3.Value) -- Vector3
if key and strig then
	GPS:SetAsync(ID,strig) -- Vector3 saved, we don't need :UpdateAsync()
end

Now when I want to load it, that’s the hard part, there’s strategies to do this but I believe it’s down to the ClassNames.

local str = GPS:GetAsync(tostring(player.UserId)) or nil -- getting the player's unique key first

We know that when you tostring(Vector3) x, y, and z are separated by two commas and two spaces.

(-1, -1, 0) Can be broken apart by finding the characters that separate the data!

You can name the strings anything, just have either a method to separate them using special characters or make each name a specific length.

My method of decoding the string for a Vector3 was this function.

local function vec3(str) --- Vec3 decoder, input is string "-1, -1, 0"
	local vec = Vector3.new(0,0,0)
	local last = string.find(str,",") last-=1
	local i = string.sub(str, 1, last) 
	vec += Vector3.new(tonumber(i),0,0)
	str = string.sub(str,last+2,string.len(str))
	last = string.find(str,",") last-=1
	i = string.sub(str, 1, last) 
	vec += Vector3.new(0,tonumber(i),0)
	str = string.sub(str,last+2,string.len(str))
	last = string.len(str)
	i = string.sub(str, 1, last) 
	vec += Vector3.new(0,0,tonumber(i))
	return vec -- We get (-1, -1, 0)
end

Make it however you want, go wild. You can do some insane stuff with the sheer amounts of strings a single key can hold. Make sure to use :UpdateAsync() if you’re saving more than one single thing that overwrites!

Hello, make sure you have to have the “Enable Studio Access to API Services” checked in the game settings in security.

You can try saving the inventory into a table then loading that back up using a loop