Saving Tools With ProfileService

Hello, Devs!

I am attempting to save tools with ProfileService so that if a player leaves and joins back their tools that were in their backpack are saved. Help with this would be appreciated! My ProfileService handler code it down below.

local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Modules = ReplicatedStorage.Modules
local Libs = Modules.Libs
local DataSaving = Libs.DataSaving
local Communication = Libs.Communication
local Base = Modules.Base


local Template = require(DataSaving.Template)
local ProfileService = require(DataSaving.ProfileService)
local Inventory = require(Base.InventoryServer)
local Signal = require(Communication.Signal)
local leaderStats = require(DataSaving["LeaderStats/Data"])



local DataHandler = {}

local ProfileStore = ProfileService.GetProfileStore("Data2024", Template)


DataHandler.Profiles = {}
local Profiles = DataHandler.Profiles


function DataHandler.PlayerAdded(player: Player)
	local profile = ProfileStore:LoadProfileAsync("Player_" .. player.UserId)
	
	
	if profile then
		profile:AddUserId(player.UserId)
		profile:Reconcile()
		
		
		profile:ListenToRelease(function()
			Profiles[player] = nil
			player:Kick("Failed to load session data. Please try rejoining after 5-10 minutes.")
		end)
		
		if not player:IsDescendantOf(Players) then
			profile:Release()
		else
			Profiles[player] = profile 
			leaderStats:Create(player, profile)
		end
	else
		player:Kick("Failed to load session data. Please try rejoining after 5-10 minutes.")
	end
end

function DataHandler:Init()
	for _, player in Players:GetPlayers() do
		task.spawn(DataHandler.PlayerAdded, player)
	end
	
	Players.PlayerAdded:Connect(DataHandler.PlayerAdded)
	
	Players.PlayerRemoving:Connect(function(player)
		if Profiles[player] then
			Profiles[player]:Release()
		end
	end)
end


function DataHandler:GetProfile(player)
	assert(Profiles[player], string.format("Profile does not exist for %s", player.UserId))
	
	return Profiles[player]
end

function DataHandler:Get(player, key)
	local profile = DataHandler:GetProfile(player)
	assert(profile.Data[key], string.format("Data does not exist for key: %s"), key)
	
	return profile.Data[key]
end

function DataHandler:Set(player, key, value)
	local profile = DataHandler:GetProfile(player)
	assert(profile.Data[key], string.format("Data does not exist for key: %s"), key)
	
	assert(type(profile.Data[key]) == type(value))
	
	
	profile.Data[key] = value
end

function DataHandler:Update(player, key, callBack)
	local profile = DataHandler:GetProfile(player)
	
	local oldData = self:Get(player, key)
	local newData = callBack(oldData)
	
	
	self:Set(player, key, newData)
end




return DataHandler

If you have a folder for the tools then you could just save the names and when the player joins read the names and get the tool from the folder

save code
-- save

local backpack = player.Backpack -- use waitforchild or findfirstchild please

local tools = {}

for i, tool in pairs(backpack:GetChildren()) do
    if (tool:IsA("Tool")) then
        tools[#tools + 1] = tool.Name
    end
end

DataHandler:Set(player, "tools", tools)

serialize tool as table, for example Name:Pickaxe, Damage:100 ect.

Hey! I tried your method but found it hard to work with. Down below is the code I’ve decided to go with. The first codeblock is the DataHandler, and the second is from a second script that handles the Player data inside a PlayerAdded function. Everything is working besides Argument 1 missing or nil - Server - InventoryServer:306 (Line 306 is the following code:

if not toolsFolder:FindFirstChild(ToolsWithStacks.ToolName) then print(“:C”) continue end

)
Being printed on the inventory sometimes and “Nil!” always being printed in the DataHandler. Also, “Done” never gets printed.


function DataHandler:Init()
	for _, player in Players:GetPlayers() do
		task.spawn(DataHandler.PlayerAdded, player)
	end
	
	Players.PlayerAdded:Connect(DataHandler.PlayerAdded)
	
	Players.PlayerRemoving:Connect(function(player)
		if Profiles[player] then
			local Tools = {}
			for _, Tool in pairs (player.Backpack:GetChildren()) do
				if Tools[Tool.Name] == nil then
					print("Nil!")
					Tools[Tool.Name] = Tool.Name
				elseif Tools[Tool.Name] ~= nil then
					print("Inserting....")
					table.insert(Tools, Tool.Name)
				end
			end
			table.clear(Tools)
			Profiles[player].Data.Items = Tools
			Profiles[player]:Release()
		end
	end)
end

Second:

local Profile = DataHandler:Get(player, true)
	for _, ToolsWithStacks in pairs(Profile.Data.Items) do
		if not toolsFolder:FindFirstChild(ToolsWithStacks.ToolName) then print(":C") continue end
		for i = 1, #ToolsWithStacks do
			print("Done")
			local Tool = toolsFolder[ToolsWithStacks.ToolName]:Clone()
			Tool.Parent = player:WaitForChild("Backpack")	
		end
	end

That’s because Tools is an empty table:

for _, Tool in pairs (player.Backpack:GetChildren()) do
	if Tools[Tool.Name] == nil then -- table needs to be (not exactly): {["name_of_tool"] = nil} or {}
		print("Nil!")
		Tools[Tool.Name] = Tool.Name -- table: {["name_of_tool"] = "name_of_tool"}
	elseif Tools[Tool.Name] ~= nil then -- table needs to be (not exactly): {["name_of_tool"] = any}
		print("Inserting....")
		table.insert(Tools, Tool.Name) -- table: {["name_of_tool"] = "name_of_tool", "name_of_tool"}
	end
end

also remove the table.clear since it just sets the table to {}.

Try looking at the lua table docs:
lua table docs
roblox table docs

Hello, thank you for your response; although I am still a bit confused as to what you mean.

I wrote out the following code below:

	Players.PlayerRemoving:Connect(function(player)
		if Profiles[player] then
			local Tools = {}
			for _, Tool in pairs(player.Backpack:GetChildren()) do
				if Tools{["NameOfTool"] = nil} or {} then -- table needs to be (not exactly): {["name_of_tool"] = nil} or {}
					print("Nil!")
					Tools{["NameOfTool"] = Tool.Name}
					print(Tools)
					
				elseif Tools{["NameOfTool"] = true}  then -- table needs to be (not exactly): {["name_of_tool"] = any}
					print("Inserting....")
					Tools{["NameOfTool"] = "NameOfTool", Tool.Name}
				end
			end	
			Profiles[player].Data.Items = Tools
			Profiles[player]:Release()
			
		end
	end)
end

I have attempted to debug and read docs but they are not of much help.

Well, not what I meant but you should use arrays:

Players.PlayerRemoving:Connect(function(player)
	if Profiles[player] then
		local Tools = {}
		for _, Tool in pairs(player.Backpack:GetChildren()) do
			if not table.find(Tools, Tool.Name, 1) then -- You can keep this away if the player can have the sime tools more than one time
				print("Inserting into save")
				table.insert(Tools, Tool.Name)
			else -- You can keep this away if the player can have the sime tools more than one time
				print("Tool is already in the save!")
			end
		end	
		Profiles[player].Data.Items = Tools
		Profiles[player]:Release()
	end
end)

You should think of tables like arrays and dictionaries combined into one data structure

Hey, I updated my codebase to suit your code and changed some things that I may or may not regret changing :sweat_smile:. Below is my codebase following your changes. For some reason, the "Done" in the server code NEVER gets printed.

[1] The DataSaving Script
[2] The tool Adding Script

[1]:

if Profiles[player] then
	local Tools = {ToolsWithStacks = {}}
	for _, Tool in pairs(player.Backpack:GetChildren()) do
		table.insert(Tools.ToolsWithStacks, {ToolName = Tool.Name})
	end	
	Profiles[player].Data.Items = Tools
	Profiles[player]:Release()	
end

[2]:

for _, Tools in Profile.Data.Items.ToolsWithStacks do
	if not toolsFolder:FindFirstChild(Tools.ToolName) then print(":C") continue end
	for i = 1, #Tools do

		print("Done")
		local Tool = toolsFolder[Tools.ToolName]:Clone()
		Tool.Parent = player.Backpack
	end
end
end)

why are you doing this? you don’t need that for loop

1 Like

Sorry, that was old code that I thought had a use but it didn’t. Wrote it while debugging and testing. Otherwise, thank you!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.