I have a problem that I think should not occur, and that is that the data is duplicated when you exit the game. I know I made a post about the same thing, but this one is different as here I am trying to save the data that I put into the tool (tool stacks and tool name). From what I have noticed, the tools are duplicated when exiting the game (This happens when you have a tool and exit and it saves, then when entering, they will have duplicated)
The Data handled by the ProfileService
local Players = game:GetService("Players")
local ProfileService = require(script.ProfileService)
local saveStructure = {
Tools = {ToolsWithStacks = {}, ToolsWithoutStacks = {}};
Money = 0;
LogInTimes = 0;
LogInGiift = 0;
Shadows = "Medio";
Water = "Medio";
Tree = "Enabled";
FPS = "UnEnabled";
Ping = "UnEnabledPing";
BrilloVol = 0.7;
PlayerChose = "ForLocalPlayer";
Language = "English";
SlotsInventory = 90;
Crouch = "Keep";
Sprint = "Keep";
}
local PlayerProfileStore = ProfileService.GetProfileStore("test32", saveStructure)
local cachedProfiles = {}
local function PlayerAdded(player)
local profile = PlayerProfileStore:LoadProfileAsync("Player_".. player.UserId, "ForceLoad")
if profile ~= nil then
profile:Reconcile()
profile:ListenToRelease(function()
cachedProfiles[player] = nil
player:Kick("Tus datos no han sido cargados. Por favor Ăşnase nuevamente")
end)
if player:IsDescendantOf(Players) then
cachedProfiles[player] = profile
DoSomethingWithALoadedProfile(player, profile)
else
profile:Release()
end
else
player:Kick("No se pueden cargar tus datos. Por favor Ăşnase nuevamente")
end
end
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(PlayerAdded)(player)
end
local function PlayerRemoving(player)
local profile = cachedProfiles[player]
for _,tool in pairs (player.Backpack:GetChildren()) do
if (table.find(profile.Data.Tools,tool.Name)) then
continue
end
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
table.insert(profile.Data.Tools.ToolsWithStacks, {ToolName = tool.Name, Stacks = tool.Stack.Value})
else
table.insert(profile.Data.Tools.ToolsWithoutStacks, {ToolName = tool.Name})
end
end
if profile ~= nil then
profile:Release()
end
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
function cachedProfiles:Get(player, yield)
local profile = cachedProfiles[player]
if yield and not profile then
repeat task.wait(0.1)
profile = cachedProfiles[player]
until profile or (not player.Parent)
end
if profile then
return profile
end
end
return cachedProfiles
When loading the tools:
Players.PlayerAdded:Connect(function(player)
local Profile = DataManager:Get(player, true)
if not Profile then
repeat task.wait(0.1)
Profile = DataManager:Get(player, true)
until Profile or (not player.Parent)
end
if Profile then
--Obtener las herramientas
for _, ToolsWithStacks in pairs(Profile.Data.Tools.ToolsWithStacks) do
if not toolsFolder:FindFirstChild(ToolsWithStacks.ToolName) then continue end
local Tool = toolsFolder[ToolsWithStacks.ToolName]:Clone()
Tool:WaitForChild("Stack").Value = ToolsWithStacks.Stacks
Tool.Parent = player:WaitForChild("Backpack")
local ToolGear = toolsFolder[ToolsWithStacks.ToolName]:Clone()
ToolGear:WaitForChild("Stack").Value = ToolsWithStacks.Stacks
ToolGear.Parent = player:WaitForChild("StarterGear")
end
for _, ToolsWithoutStacks in pairs(Profile.Data.Tools.ToolsWithoutStacks) do
if not toolsFolder:FindFirstChild(ToolsWithoutStacks.ToolName) then continue end
local Tool = toolsFolder[ToolsWithoutStacks.ToolName]:Clone()
Tool.Parent = player:WaitForChild("Backpack")
local ToolGear = toolsFolder[ToolsWithoutStacks.ToolName]:Clone()
ToolGear.Parent = player:WaitForChild("StarterGear")
end
end
end)
Analyzing your code, seems like you are trying to search a tool inside the Tools table. The issue is that table.find() won’t search for values inside ToolsWithStacks or ToolsWithoutStacks, and will always return nil because of that.
Instead, you should try searching inside each table:
if (table.find(profile.Data.Tools.ToolsWithStacks,tool.Name) or table.find(profile.Data.Tools.ToolsWithoutStacks,tool.Name)) then
continue
end
Did some testing at studio and I found the error. It was pretty similar, the tools are saved inside tables and once again table.find() wasn’t looking inside each tool table. I added a for loop to iterate over all the tools and it solved the issue for me.
Try this:
local toolFound = false
for _, toolTypeTable in pairs(profile.Data.Tools) do -- iterate over ToolsWithStacks and ToolsWithoutStacks
for _, toolInfoTable in pairs(toolTypeTable) do -- iterate over each tool table
if type(toolInfoTable) == "table" then -- just so it doesn't error if you accidentaly add something that's not a table here
if toolInfoTable.ToolName == tool.Name then -- compare tool names
toolFound = true
end
end
end
end
if toolFound == true then
continue
end
The whole function should look like this:
local function PlayerRemoving(player)
local profile = cachedProfiles[player]
for _,tool in pairs (player.Backpack:GetChildren()) do
local toolFound = false
for _, toolTypeTable in pairs(profile.Data.Tools) do
for _, toolInfoTable in pairs(toolTypeTable) do
if type(toolInfoTable) == "table" then
if toolInfoTable.ToolName == tool.Name then
toolFound = true
end
end
end
end
if toolFound == true then
continue
end
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
table.insert(profile.Data.Tools.ToolsWithStacks, {ToolName = tool.Name, Stacks = tool.Stack.Value})
else
table.insert(profile.Data.Tools.ToolsWithoutStacks, {ToolName = tool.Name})
end
end
if profile ~= nil then
profile:Release()
end
end
Sorry, I didn’t understand what exactly you tried to say.
Did you mean that removed tools won’t be removed from the player’s data when leaving? If that’s the case, your script doesn’t handle removing tools, only adding them. You would have to compare the saved tools with the player’s backpack and remove the ones that are not found inside it.
mmmmmm, I’ll explain: ProfileService saves the data, but duplicates the tools. What I want to save is the added tools, and if a tool was removed, I want that tool to cease to exist in the player’s ProfileService / Backpack player.
It is difficult for me to explain, but, I want the data of the tools or the tools to be saved if there are changes (a tool was added, a tool was eliminated (I don’t want the profileService to save tools that cease to exist), the stak of a tool).
Pepito has 3 tools, he leaves his house (he leaves the game and save the tools) and when he enters again he grabs his tools (he enters the game and loads 3 tools). Later, Pepito loses a tool, leaves his house (he leaves the game and the tools are saved) and when he enters again, he grabs his tools (he enters the game and loads his two tools).
Camila has a toy pistol with 25 bullets, she drops her pistol (I left the game and saves the data following the example of Pepito, but with the difference that I want the bullets to be saved), and grabs her pistol (loads the tool with the tool data (bullets according to this example)). camila spends 5 bullets, drops her gun (saves the data), and grabs it (loads the data, which is now the gun with 20 bullets).
sorry this is my best way to explain
oh right, camila spends all her bullets and then magically explodes the weapon (following this example (bullets are stacks), if the stack reaches 0, the ProfileService tool is deleted)
This script is a normal DataStore (but with the things I want the ProfileService to do)
local dss = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local toolsDS = dss:GetDataStore("ToolDataPlayer")
local ServerStorage = game:GetService("ServerStorage")
local toolsFolder = ServerStorage.ToolsFolder
Players.PlayerAdded:Connect(function(plr)
local OwnedTools = toolsDS:GetAsync(plr.UserId)
for _, toolData in pairs(OwnedTools) do
for _2, ToolValue in pairs(toolData.ValueTools) do
if not toolsFolder:FindFirstChild(ToolValue.ToolName) then continue end
local Tool = toolsFolder[ToolValue.ToolName]:Clone()
Tool:WaitForChild("Stack").Value = ToolValue.Stacks
Tool.Parent = plr.Backpack
local ToolGear = toolsFolder[ToolValue.ToolName]:Clone()
ToolGear:WaitForChild("Stack").Value = ToolValue.Stacks
ToolGear.Parent = plr.StarterGear
end
for _3, NoValueTools in pairs(toolData.NotValueTools) do
if not toolsFolder:FindFirstChild(NoValueTools.ToolName) then continue end
local Tool = toolsFolder[NoValueTools.ToolName]:Clone()
Tool.Parent = plr.Backpack
local ToolGear = toolsFolder[NoValueTools.ToolName]:Clone()
ToolGear.Parent = plr.StarterGear
end
end
plr.CharacterRemoving:Connect(function(char)
char.Humanoid:UnequipTools()
end)
end)
Players.PlayerRemoving:Connect(function(plr)
local OwnedTools = { }
for i, tool in pairs(plr.Backpack:GetChildren()) do
local Tools_Without_Value = {}
local Tools_With_Value = {}
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
table.insert(Tools_With_Value, {ToolName = tool.Name, Stacks = tool.Stack.Value})
else
table.insert(Tools_Without_Value, {ToolName = tool.Name})
end
table.insert(OwnedTools, {NotValueTools = Tools_Without_Value, ValueTools = Tools_With_Value})
end
local success, errormsg = pcall(function()
toolsDS:SetAsync(plr.UserId, OwnedTools)
end)
if errormsg then
warn(errormsg)
end
end)
As you can see, it is quite simple, and it does the functions that I want it to do: It saves the tools without stacks, it saves the tools with stacks with their respective stacks; After loading the data (it does not delete it, I think it is updated) when exiting and there is no tool that you had before, that tool will no longer be in the DataStore (and that is what I want the ProfileService to do)
Oh now I understand. Your old code didn’t handle removing tools that cease to exist since you never used a table.remove() or something similar to detect whether the player lost a tool from his inventory. I rewrote the PlayerRemoving() function so it should work just fine now.
local function PlayerRemoving(player)
local profile = cachedProfiles[player]
local OwnedTools = {ToolsWithStacks = {}, ToolsWithoutStacks = {}} -- creates an empty table to add tools inside it (if the player lost all his tools, he won't have any tool upon rejoining)
for _,tool in pairs (player.Backpack:GetChildren()) do
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
if tool.Stack > 0 then -- if the stack is equal or lower than 0, don't save the tool
OwnedTools.ToolsWithStacks[tool.Name] = {ToolName = tool.Name, Stacks = tool.Stack} -- tools with the same name will be saved under the same table, so it won't duplicate
end
else
OwnedTools.ToolsWithoutStacks[tool.Name] = {ToolName = tool.Name} -- tools with the same name will be saved under the same table, so it won't duplicate
end
end
profile.Data.Tools = OwnedTools -- overwrite player tools with the table we just created
if profile ~= nil then
profile:Release()
end
end
I tested dropping a tool and leaving the game and it worked for me, I lost it. I also tested getting 2 of the same gear, only one was saved.
By the way, if you want to manually check if the player has a gear, you can do it easier using this, example:
print(profile.Data.Tools.ToolsWithoutStacks["OmegaRainbowSword"] ~= nil) -- will print true if the player has the tool
Thanks! That’s what I meant … Although now there is a little problem, like this, little one (sorry for bothering). As you can see in the title, I wanted the items not to be duplicated (since the items were duplicated when saving, thanks to you, not anymore) but now, I don’t know, they save the items that are the same as others, I mean, if there are others tools that are the same, they are not saved (not duplicated, but tools that were added for something are not saved, for example: buy several tools and only one of that tool is saved, I want the tools that you obtained to be saved by yourself, not duplicates)
I get it. Did some new modifications to address your issue. This time I had to change load tools script too. Also no problems! Feel free to ask how many times you need to.
PlayerRemoving function:
local function PlayerRemoving(player)
local profile = cachedProfiles[player]
local OwnedTools = {ToolsWithStacks = {}, ToolsWithoutStacks = {}}
for _,tool in pairs (player.Backpack:GetChildren()) do
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
if tool.Stack.Value > 0 then
if OwnedTools.ToolsWithStacks[tool.Name] == nil then
OwnedTools.ToolsWithStacks[tool.Name] = {{ToolName = tool.Name, Stacks = tool.Stack.Value}}
elseif OwnedTools.ToolsWithStacks[tool.Name] ~= nil then
table.insert(OwnedTools.ToolsWithStacks[tool.Name], {ToolName = tool.Name, Stacks = tool.Stack.Value})
end
end
else
if OwnedTools.ToolsWithoutStacks[tool.Name] == nil then
OwnedTools.ToolsWithoutStacks[tool.Name] = {ToolName = tool.Name, Amount = 1}
elseif OwnedTools.ToolsWithoutStacks[tool.Name] ~= nil then
OwnedTools.ToolsWithoutStacks[tool.Name].Amount += 1
end
end
end
profile.Data.Tools = OwnedTools
if profile ~= nil then
profile:Release()
end
end
LoadTools script:
local Players = game:GetService("Players")
local DataManager = require(script.Parent:WaitForChild("ProfileCacher"))
local ServerStorage = game:GetService("ServerStorage")
local toolsFolder = ServerStorage:WaitForChild("toolsFolder")
Players.PlayerAdded:Connect(function(player)
local Profile = DataManager:Get(player, true)
if not Profile then
repeat task.wait(0.1)
Profile = DataManager:Get(player, true)
until Profile or (not player.Parent)
end
if Profile then
--Obtener las herramientas
for _, ToolsWithStacks in pairs(Profile.Data.Tools.ToolsWithStacks) do
if not toolsFolder:FindFirstChild(ToolsWithStacks[1].ToolName) then continue end
for i = 1, #ToolsWithStacks do
local Tool = toolsFolder[ToolsWithStacks[1].ToolName]:Clone()
Tool:WaitForChild("Stack").Value = ToolsWithStacks[i].Stacks
Tool.Parent = player:WaitForChild("Backpack")
local ToolGear = toolsFolder[ToolsWithStacks[1].ToolName]:Clone()
ToolGear:WaitForChild("Stack").Value = ToolsWithStacks[i].Stacks
ToolGear.Parent = player:WaitForChild("StarterGear")
end
end
for _, ToolsWithoutStacks in pairs(Profile.Data.Tools.ToolsWithoutStacks) do
if not toolsFolder:FindFirstChild(ToolsWithoutStacks.ToolName) then continue end
for i = 1, ToolsWithoutStacks.Amount do
local Tool = toolsFolder[ToolsWithoutStacks.ToolName]:Clone()
Tool.Parent = player:WaitForChild("Backpack")
local ToolGear = toolsFolder[ToolsWithoutStacks.ToolName]:Clone()
ToolGear.Parent = player:WaitForChild("StarterGear")
end
end
end
end)
I tested it both with and without stacks and worked fine with me. See if it solves the issue!
Now if the tools are put away! Well, just keep the tools without stacks are saved.
As you can see, the print does not show any tools in the stacks part (there should be tools with their respective stacks but it does not show them because it does not save them)
I don't know if I hurt something
local Players = game:GetService("Players")
local ProfileService = require(script.ProfileService)
local saveStructure = {
Tools = {ToolsWithStacks = {}, ToolsWithoutStacks = {}};
Money = 0;
LogInTimes = 0;
LogInGiift = 0;
Shadows = "Medio";
Water = "Medio";
Tree = "Enabled";
FPS = "UnEnabled";
Ping = "UnEnabledPing";
BrilloVol = 0.7;
PlayerChose = "ForLocalPlayer";
Language = "English";
SlotsInventory = 90;
Crouch = "Keep";
Sprint = "Keep";
}
local PlayerProfileStore = ProfileService.GetProfileStore("test37", saveStructure)
local cachedProfiles = {}
local function DoSomethingWithALoadedProfile(player, profile)
local GiftMoney = math.random(100, 270)
profile.Data.LogInTimes = profile.Data.LogInTimes + 1
profile.Data.LogInGiift = profile.Data.LogInGiift + 1
print(player.Name, " has logged in " .. tostring(profile.Data.LogInTimes).. " time" .. ((profile.Data.LogInTimes > 1) and "s" or ""))
if profile.Data.LogInGiift >= 120 then
profile.Data.Money = profile.Data.Money + GiftMoney
print(player.Name, "has been given a gift of $".. GiftMoney.. ".", "Actualmente tiene $".. profile.Data.Money.. ".")
profile.Data.LogInGiift = 0
else
print(player.Name .. " owns " .. tostring(profile.Data.Money) .. " now!")
end
end
local function PlayerAdded(player)
local profile = PlayerProfileStore:LoadProfileAsync("Player_".. player.UserId, "ForceLoad")
if profile ~= nil then
profile:Reconcile()
profile:ListenToRelease(function()
cachedProfiles[player] = nil
player:Kick("Tus datos no han sido cargados. Por favor Ăşnase nuevamente")
end)
if player:IsDescendantOf(Players) then
cachedProfiles[player] = profile
DoSomethingWithALoadedProfile(player, profile)
else
profile:Release()
end
else
player:Kick("No se pueden cargar tus datos. Por favor Ăşnase nuevamente")
end
end
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(PlayerAdded)(player)
end
local function PlayerRemoving(player)
local profile = cachedProfiles[player]
local OwnedTools = {ToolsWithStacks = {}, ToolsWithoutStacks = {}}
for _,tool in pairs (player.Backpack:GetChildren()) do
if not tool:IsA("Tool") then continue end
if tool:FindFirstChild("Stack") then
if tool.Stack.Value > 0 then
if OwnedTools.ToolsWithStacks[tool.Name] == nil then
OwnedTools.ToolsWithStacks[tool.Name] = {{ToolName = tool.Name, Stacks = tool.Stack.Value}}
elseif OwnedTools.ToolsWithStacks[tool.Name] ~= nil then
table.insert(OwnedTools.ToolsWithStacks[tool.Name], {ToolName = tool.Name, Stacks = tool.Stack.Value})
end
end
else
if OwnedTools.ToolsWithoutStacks[tool.Name] == nil then
OwnedTools.ToolsWithoutStacks[tool.Name] = {ToolName = tool.Name, Amount = 1}
elseif OwnedTools.ToolsWithoutStacks[tool.Name] ~= nil then
OwnedTools.ToolsWithoutStacks[tool.Name].Amount += 1
end
end
end
profile.Data.Tools = OwnedTools
if profile ~= nil then
profile:Release()
end
print(profile)
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
function cachedProfiles:Get(player, yield)
local profile = cachedProfiles[player]
if yield and not profile then
repeat task.wait(0.1)
profile = cachedProfiles[player]
until profile or (not player.Parent)
end
if profile then
return profile
end
end
return cachedProfiles
Tools with stacks saved just fine with me. Did you change LoadTools script to the one I sent? Is “Stack” a NumberValue?
If both these answers are yes, I don’t know what could be causing the issue. One alternative would be saving only one tool with stack and, instead of loading separate tools of that type, load a single one with both stacks added together.