Having trouble saving players tools when joining

I have been having problems with saving tools for countless years now. I think that I have finally found the solution. (this script) -

local DatastoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ToolDataStore = DatastoreService:GetDataStore("tools")

local ToolRefrence = ReplicatedStorage.CurcWeapons

game.Players.PlayerAdded:Connect(function(player: Player)	
   local savedTools = nil
   local success, err = pcall(function()
   	savedTools = ToolDataStore:GetAsync(player.UserId)
   end)

   if success then
   	print("Tools Loaded!")
   	print(savedTools)
   elseif err then
   	warn(err)
   end

   local starterGear = player.StarterGear
   -- Give's tools based on the saved tool names 
   if savedTools then
   	for toolName, _ in pairs(savedTools) do
   		local toolTemplate = ToolRefrence:FindFirstChild(toolName)
   		if toolTemplate then
   			local newStarterGear = toolTemplate:Clone()
   			newStarterGear.Parent = starterGear
   		end
   	end
   end

   -- When the player dies make sure that the unsaved weapons will still be in the backpack
   player.CharacterRemoving:Connect(function()
   	for _,tool in pairs(player.Backpack:GetChildren()) do
   		if starterGear:FindFirstChild(tool.Name) == nil then
   			local clone = tool:Clone()
   			clone.Parent = starterGear
   		end
   	end
   end)

   if not player:HasAppearanceLoaded() then
   	player.CharacterAppearanceLoaded:Wait()
   end

   -- Add tools if not given by starter pack
   for _,tool in pairs(player.StarterGear:GetChildren()) do
   	if player.Backpack:FindFirstChild(tool.Name) == nil then
   		local clone = tool:Clone()
   		clone.Parent = player.Backpack
   	end
   end
end)


game.Players.PlayerRemoving:Connect(function(player)
   local toolSave = {}

   for _, tool in pairs(player.StarterGear:GetChildren()) do
   	if ToolRefrence:FindFirstChild(tool.Name) then -- Makes sure that the tool is able to be loaded back in
   		toolSave[tool.Name] = true
   	end
   end

   local success, err = pcall(function()
   	ToolDataStore:SetAsync(player.UserId, toolSave)
   end)

   if success then
   	print("Tools Saved!")
   	print(toolSave)
   elseif err then
   	warn(err)
   end

end)

game:BindToClose(function()
   if RunService:IsStudio() then
   	task.wait(3)
   else
   	task.wait(10)
   end
end)

As you can see in the screenshot the weapons do load in and are being recognized by the script but I think I might need to add delay somewhere so they have time to parent to the backpack? As of the moment I have to reset my player to load in them in correctly. I had some friends join with me and for them even after resetting some tools were missing. Can someone please finally help me fix this issue after years of trying? :melting_face:

You shouldn’t put the tools in starter gear, you should put it in the player.Backpack or player.Character. This is because the replication of starter gear only takes place when the character spawns. In your case here, the character already exists, so the replication won’t take place immediately

I do agree that putting them in starter gear isn’t the best practice and darth should probably change that. Unfortunately, it isn’t the root cause, since according to this quote even after respawning some tools aren’t there. Also, he does have a function specifically for if they weren’t given by the starter pack.

As for this function, I think it’s a little bit of strange practice to store the saved tools as a dictionary full of strings that all equate to true, if you’re worried at performance I think you should just save all the tools as their names as an array and change the pair loops accordingly.

As for the actual root problem, first like mystery implied you should stop prioritizing the starter pack since we’re handling the player as soon as they join, and if you haven’t found a solution supposedly after years of trying then you can try this:

Remove all code that tries to immediately insert tools into the character once data is loaded, since it doesn’t seem to be working.

Create a folder (will be parented to the player afterwards) when they join and put all their tools in it, this will only be used when they first exit the lobby or loading screen

Confirm that all tools are correctly put into starterPack on your initial code and if not, there is something wrong with your datastores and it should be an easy fix. If so, put a copy of all the tools into the newly created folder. (I understand that this seems to be the problem, but as far as I can see there’s nothing wrong with how you handle the data besides the aforementioned loop)

Create any sort of loading screen or lobby when they first join (then set their spawn to normal afterwards in case they reset)

Only parent the folder to the player after all the data loading is done.

Now, for the player to exit the loading screen or lobby, have the server check if the player has the folder parented to them (this makes it so they’re unable to join the actual game without any missing data)

Once the player decides to go into game, get all the folder children and put them into the character, then delete the folder assuming starter pack would handle everything from there on.

Or

You can do it all automatically, and pause loading for the character, code the folder logistics stated above, and when player:WaitForChild(“FolderName”) passes, load the character, and you should be all set.

Manual (lobby / loading screen)

  • Easy to code
  • If code is slow, player could reset in lobby and go into game before data loads
  • Disabling resets could be worked around by any exploiters

Automatic

  • Harder to code
  • Could really mess things up if data isn’t properly loaded
  • Smoother join experience