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.
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.
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