Inventory Save Redone

Hello, So I made a shop that saves with DataStore, but sometimes some of the swords do not save and it is really bad when you lose information. I have been using DataStore2 on my currency system and it has been working really well, so I was wondering whether there is a way to either fix the script for my DataStore, or change to DataStore2

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyDataStore")

game.Players.PlayerAdded:Connect(function(plr)
	
	
	
	
	local data
	
	local success, errorMessage = pcall(function()
		data = DataStore:GetAsync(plr.UserId)
	end)
	if data ~= nil then
		for _, toolName in pairs(data) do
			
			local tool = game.ReplicatedStorage.Tools:FindFirstChild(toolName)
			
			if tool then
				local newTool = tool:Clone()
				newTool.Parent = plr.Backpack
				
				local newTool = tool:Clone()
				newTool.Parent = plr.StarterGear
				
			end
		end
	end	
end)
game.Players.PlayerRemoving:Connect(function(plr)
	
	local toolsTable = {}
	
	for _, tool in pairs(plr.Backpack:GetChildren()) do
		if game.ReplicatedStorage.Tools:FindFirstChild(tool.Name) then
			table.insert(toolsTable,tool.Name)
		end
	end
	
	
	local success, errorMessage = pcall(function()
		DataStore:SetAsync(plr.UserId,toolsTable)
	end)
	
end)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
			local toolsTable = {}
			
			for _, tool in pairs(plr.Backpack:GetChildren()) do
				if game.ReplicatedStorage.Tools:FindFirstChild(tool.Name) then
					table.insert(toolsTable,tool.Name)
				end
			end
			
			
			local success, errorMessage = pcall(function()
				DataStore:SetAsync(plr.UserId,toolsTable)
			end)	
		end
end)

Maybe this will work

local newTool = tool:Clone()
newTool.Parent = plr.Backpack

local newTool2 = tool:Clone()
newTool2.Parent = plr.StarterGear

Can you please see if the data-saving works in an actual server? (not studio test mode)

If it doesn’t, please test out your game in studio and post a photo of the studio output window.

Your current code prevents data store errors from stopping the code from running. However, it doesn’t retry when something goes wrong in the first try. After an attempt to save or get data from datastore, you should check if it succeeded (you already have a variable for this), and if it didn’t, then retry a few times. There’s some example code for the retries in this developer hub article. The retry code is at the end of the article.
https://developer.roblox.com/en-us/articles/Saving-Player-Data

Yes, it does work. In Studio to. It’s just that sometimes players will lose information.

I am not sure because my shop has more than 2 tools, and I think if you change the name of newTool, it is supposed to put the Tool in StarterGear and Backpack

So, I would add this to my script?

 if sessionData[playerUserId] then
		local tries = 0	
		local success
		repeat
			tries = tries + 1
			success = pcall(function()
				playerData:SetAsync(playerUserId, sessionData[playerUserId])
			end)
			if not success then wait(1) end
		until tries == 3 or success
		if not success then
			warn("Cannot save data for player!")
		end
	end
end

Yes, that will make saving more reliable. Also add similar retry code for getting data, because it can fail too.

How would I replace the variables though in those codes to fix it?

How would I fix the end part?

local DataStore = game:GetService("DataStoreService"):GetDataStore("MyDataStore")

game.Players.PlayerAdded:Connect(function(plr)
	
	
	
	
	local data
	
	local success, errorMessage = pcall(function()
		data = DataStore:GetAsync(plr.UserId)
	end)
	if data ~= nil then
		for _, toolName in pairs(data) do
			
			local tool = game.ReplicatedStorage.Tools:FindFirstChild(toolName)
			
			if tool then
				local newTool = tool:Clone()
				newTool.Parent = plr.Backpack
				
				local newTool = tool:Clone()
				newTool.Parent = plr.StarterGear
				
			end
		end
	end	
end)
game.Players.PlayerRemoving:Connect(function(plr)
	
	local toolsTable = {}
	
	for _, tool in pairs(plr.Backpack:GetChildren()) do
		if game.ReplicatedStorage.Tools:FindFirstChild(tool.Name) then
			table.insert(toolsTable,tool.Name)
		end
	end
	
	
	local success, errorMessage = pcall(function()
		DataStore:SetAsync(plr.UserId,toolsTable)
	end)
	
end)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
			local toolsTable = {}
			
			for _, tool in pairs(plr.Backpack:GetChildren()) do
				if game.ReplicatedStorage.Tools:FindFirstChild(tool.Name) then
					table.insert(toolsTable,tool.Name)
				end
			end
			
			
			local success, errorMessage = pcall(function()
				DataStore:SetAsync(plr.UserId,toolsTable)
			end)	
		end
end)

if sessionData[playerUserId] then
		local tries = 0	
		local success
		repeat
			tries = tries + 1
			success = pcall(function()
				playerData:SetAsync(playerUserId, sessionData[playerUserId])
			end)
			if not success then wait(1) end
		until tries == 3 or success
		if not success then
			warn("Cannot save data for player!")
		end
	end
end

Here’s a function I made. Having this as a function will reduce code repetition and improve readability. I haven’t tested it and I have never used datastores so I’m not sure if it works. I don’t recommend publishing the game before you are sure that it works. You should replace your current code’s pcalls with calling this function.

local function getOrSetWithRetries(dataStore, method, key, val, maxTries)
    maxTries = maxTries or 3
    local tries, success, result = 0, false, nil
    repeat
        tries += 1
        success, result = pcall(method, dataStore, key, val)
        if not success then
            wait(1)
        end
    until success or tries == maxTries
    if not success then
        warn("Couldn't save or load data for player")
    end
    return success, result
end

When setting data, do something like this

local saveSuccess = getOrSetWithRetries(DataStore, DataStore.SetAsync, plr.UserId, toolsTable, --[[optional max tries, if not given, it'll be 3]])

You can use saveSuccess to see if the saving finally succeeded, if you want to do something if it doesn’t succeed even after some retries (for example try more times and maybe wait a little before that).

Getting the data happens this way.

local loadSuccess, data = getOrSetWithRetries(DataStore, DataStore.GetAsync, plr.UserId, nil --[[you can remove this nil if you don't give a max tries value]], --[[optional max tries]])

I recommend checking loadSuccess before you save and not saving if it’s false. If the data wasn’t loaded and you replace it with new data, the player will lose their earlier data.

There are probably more ways to improve this, but as I already said, I’m not experienced with datastores.