Randomly Resetting Inventory

Hey everybody-

I’ve been coding an inventory system for my game. It uses a table-to string- to table method, as I found this method to be the easiest for me.

Unfortunately, I often experience random inventory resets. However, no error or warning is printed to the console.

Side note: Sometimes, the “EquippedItem” saves fine, but the inventory is erased. I do not know what the issue is here, and I’m hoping one of you can point it out for me.

local ds = game:GetService("DataStoreService")

local invSave = ds:GetDataStore("InveOkOkOkOkOkntoryDataNewOkYayStore22234")

local equippeditemsave = ds:GetDataStore("FirstOfManySave219")

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

	--player = plr

	local statsFolder = Instance.new("Folder")

	statsFolder.Name = "Inventory"
	statsFolder.Parent = plr

	local inventory = Instance.new("StringValue")

	inventory.Parent = statsFolder
	inventory.Name = "ItemsStringValue"
	
	local equippedItem = Instance.new("StringValue")
	
	equippedItem.Parent = statsFolder
	equippedItem.Name = "EquippedItem"
	
	
	
	equippedItem.Changed:Connect(function()
		local yay, nay = pcall(function()
			warn("The Equipped item has been changed.")
			plr.StarterGear:ClearAllChildren()
			plr.Backpack:ClearAllChildren()
			for i,v in pairs(plr.Backpack:GetChildren()) do
				v:Destroy()
			end
			for i,v in pairs(plr.Character:GetChildren()) do
				if v:IsA("Tool") then
					v:Destroy()
				end

			end
			
				local item1 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
				local item2 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
				item1.Parent = plr.Backpack
				item2.Parent = plr.StarterGear
		


			warn("Equipped item given to player.")
		end)
		if nay then
			while task.wait(0.1) do
				warn(nay)
			end
		end
	end)
	
	local blockAutoSave = false
	
	task.wait(5)
	
	local yay, nay = pcall(function()
		local saveData = invSave:GetAsync(plr.UserId)
		local saveData2 = equippeditemsave:GetAsync(plr.UserId)
		if saveData ~= nil then
			inventory.Value = saveData
		else
			inventory.Value = ""
		end
		if saveData2 ~= nil and saveData2 ~= "Nothing" then
			equippedItem.Value = saveData2
		else
			warn("data not found")
			equippedItem.Value = "Nothing"
		end



		plr.StarterGear:ClearAllChildren()
		plr.Backpack:ClearAllChildren()
		

		if equippedItem.Value ~= "Nothing" and game.ReplicatedStorage.Items:FindFirstChild(equippedItem.Value) then
			local item1 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
			local item2 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
			item1.Parent = plr.Backpack
			item2.Parent = plr.StarterGear
			
			
		end
		equippedItem.Value = equippedItem.Value
	end)

	

	if nay then
		warn("Error while loading data.".. nay)
		--inventory.Value = ""
		--equippedItem.Value = "Nothing"
		blockAutoSave = true
		plr:Kick("Something went wrong while trying to load your data. Please rejoin.")
		return
	end
	
	if yay then
		warn("Data loaded.")
	end
	
	while task.wait(120) do
		local yay, nay = pcall(function()
			if blockAutoSave then
				plr:Kick("Critical data failure occured. Please rejoin.")
			else
				invSave:SetAsync(plr.UserId, inventory.Value)
			end
			

		end)
		if nay then
			warn("Data save failed.")
			--plr:Kick("Data saving failed. Please rejoin or try again later.")
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local yay, nay = pcall(function()
		invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value)
		equippeditemsave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value)
	end)
	if nay then
		warn("Data save failed.".. nay)
		if nay then
			warn("Attempt 2.")
			invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value)
			equippeditemsave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value)
		end
	end
	if yay then
		warn("Data saved.")
	end
end)

Unfortunately, this issue is extremely hard to replicate, as it happens at random, I’d say it’s about a 1 in 20 chance this happens on join.

Thank you all!

1 Like

Try this code:

local ds = game:GetService("DataStoreService")

local invSave = ds:GetDataStore("InveOkOkOkOkOkntoryDataNewOkYayStore22234")
local equippeditemsave = ds:GetDataStore("FirstOfManySave219")
local sessionLock = ds:GetDataStore("SessionLock")

game.Players.PlayerAdded:Connect(function(plr)
	local sessionKey = "lock_" .. plr.UserId
	local alreadyPlaying = sessionLock:GetAsync(sessionKey)
	if alreadyPlaying then
		plr:Kick("You are already in the game from another device.")
		return
	end
	sessionLock:SetAsync(sessionKey, true)

	local statsFolder = Instance.new("Folder")
	statsFolder.Name = "Inventory"
	statsFolder.Parent = plr

	local inventory = Instance.new("StringValue")
	inventory.Name = "ItemsStringValue"
	inventory.Parent = statsFolder

	local equippedItem = Instance.new("StringValue")
	equippedItem.Name = "EquippedItem"
	equippedItem.Parent = statsFolder

	equippedItem.Changed:Connect(function()
		local yay, nay = pcall(function()
			warn("The Equipped item has been changed.")
			plr.StarterGear:ClearAllChildren()
			plr.Backpack:ClearAllChildren()

			for _, v in pairs(plr.Character:GetChildren()) do
				if v:IsA("Tool") then
					v:Destroy()
				end
			end

			if equippedItem.Value ~= "Nothing" and equippedItem.Value ~= "" then
				local item1 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
				local item2 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
				item1.Parent = plr.Backpack
				item2.Parent = plr.StarterGear
			end
		end)
		if not yay then
			while task.wait(0.1) do
				warn(nay)
			end
		end
	end)

	local blockAutoSave = false
	task.wait(5)

	local yay, nay = pcall(function()
		local saveData = invSave:GetAsync(plr.UserId)
		local saveData2 = equippeditemsave:GetAsync(plr.UserId)

		if type(saveData) == "string" then
			inventory.Value = saveData
		else
			inventory.Value = ""
		end

		if type(saveData2) == "string" and saveData2 ~= "Nothing" then
			equippedItem.Value = saveData2
		else
			warn("equipped item data not found")
			equippedItem.Value = "Nothing"
		end

		plr.StarterGear:ClearAllChildren()
		plr.Backpack:ClearAllChildren()

		if equippedItem.Value ~= "Nothing" and game.ReplicatedStorage.Items:FindFirstChild(equippedItem.Value) then
			local item1 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
			local item2 = game.ReplicatedStorage.Items:WaitForChild(equippedItem.Value):Clone()
			item1.Parent = plr.Backpack
			item2.Parent = plr.StarterGear
		end
	end)

	if not yay then
		warn("Error while loading data: " .. nay)
		blockAutoSave = true
		plr:Kick("Something went wrong while trying to load your data. Please rejoin.")
		sessionLock:RemoveAsync(sessionKey)
		return
	else
		warn("Data loaded.")
	end

	-- Auto-save loop
	while task.wait(120) do
		local yay, nay = pcall(function()
			if blockAutoSave then
				plr:Kick("Critical data failure occurred. Please rejoin.")
			else
				invSave:SetAsync(plr.UserId, inventory.Value or "")
				equippeditemsave:SetAsync(plr.UserId, equippedItem.Value or "Nothing")
			end
		end)
		if not yay then
			warn("Auto-save failed: " .. nay)
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local sessionKey = "lock_" .. plr.UserId
	local yay, nay = pcall(function()
		invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value or "")
		equippeditemsave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value or "Nothing")
	end)

	if not yay then
		warn("PlayerRemoving save failed: " .. nay)
		pcall(function()
			invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value or "")
			equippeditemsave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value or "Nothing")
		end)
	else
		warn("Data saved on PlayerRemoving.")
	end

	-- Release session lock
	pcall(function()
		sessionLock:RemoveAsync(sessionKey)
	end)
end)

game:BindToClose(function()
	for _, plr in ipairs(game.Players:GetPlayers()) do
		pcall(function()
			invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value or "")
			equippeditemsave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value or "Nothing")
			sessionLock:RemoveAsync("lock_" .. plr.UserId)
		end)
	end
end)

The random inventory resets are likely caused by the ‘Changed’ event firing before data loads, missing auto saves for the equipped item and overwriting valid data with empty values. I’d use a flag to suppress early ‘Changed’ events, save both inventory and equipped item in the loop, and validate data before saving. Also I’d remove unnecessary delays before loading data and add debug prints to trace when things go wrong until you know your system is solid.

I’ve played around with your code a bit to help with the above recommendations

local ds = game:GetService("DataStoreService")
local invSave = ds:GetDataStore("InventoryData")
local equippedSave = ds:GetDataStore("EquippedItemData")

game.Players.PlayerAdded:Connect(function(plr)
	local stats = Instance.new("Folder")
	stats.Name = "Inventory"
	stats.Parent = plr

	local inventory = Instance.new("StringValue")
	inventory.Name = "ItemsStringValue"
	inventory.Parent = stats

	local equipped = Instance.new("StringValue")
	equipped.Name = "EquippedItem"
	equipped.Parent = stats

	local suppressChanged = true
	local blockAutoSave = false

	equipped.Changed:Connect(function()
		if suppressChanged then return end
		local success, err = pcall(function()
			plr.StarterGear:ClearAllChildren()
			plr.Backpack:ClearAllChildren()
			for _, tool in pairs(plr.Character:GetChildren()) do
				if tool:IsA("Tool") then tool:Destroy() end
			end
			if equipped.Value ~= "" and equipped.Value ~= "Nothing" then
				local item1 = game.ReplicatedStorage.Items:FindFirstChild(equipped.Value)
				if item1 then
					item1:Clone().Parent = plr.Backpack
					item1:Clone().Parent = plr.StarterGear
				end
			end
		end)
		if not success then warn(err) end
	end)

	local success, err = pcall(function()
		local invData = invSave:GetAsync(plr.UserId)
		local equipData = equippedSave:GetAsync(plr.UserId)

		inventory.Value = invData or ""
		equipped.Value = (equipData and equipData ~= "Nothing") and equipData or "Nothing"

		print("Loaded inventory:", inventory.Value)
		print("Loaded equipped:", equipped.Value)
	end)

	if not success then
		warn("Data load failed:", err)
		blockAutoSave = true
		plr:Kick("Data failed to load. Please rejoin.")
		return
	end

	suppressChanged = false

	while task.wait(120) do
		if blockAutoSave then
			plr:Kick("Critical data failure. Please rejoin.")
			return
		end
		pcall(function()
			if inventory.Value ~= "" then
				invSave:SetAsync(plr.UserId, inventory.Value)
			end
			if equipped.Value ~= "" and equipped.Value ~= "Nothing" then
				equippedSave:SetAsync(plr.UserId, equipped.Value)
			end
		end)
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	pcall(function()
		invSave:SetAsync(plr.UserId, plr.Inventory.ItemsStringValue.Value)
		equippedSave:SetAsync(plr.UserId, plr.Inventory.EquippedItem.Value)
	end)
end)

Thank you for the code, however I am seeking the issue, not the copy-and-paste solution. If you could explain what went wrong and why this one works, I would appreciate it.

I’ll try a few of these solutions out, and report back to you whether I experience this more often or not. Thank you for the solutions!

1 Like

Only save the inventory when it changes, keep the real data in a table instead of a StringValue, and make sure not to save if the data is empty or looks wrong.

If you use table-to-string serialization, make sure that logic is elsewhere in your system (e.g., when updating inventory.Value) and not causing nil or corrupted data. Also, keep your DataStore names consistent and try to test with real players using Publish to Roblox to ensure DataStore access

No pcall for equippeditemsave:SetAsync in the autosave loop.
If the inventory string is set to the same value, .Changed won’t fire.