Can't save player's custom inventory data

The problem is: the table which contains all the user data won’t save anything most of the time and it won’t warn any error.

I’m kind of new to Roblox programming so i’m very open minded for every advise possible. I’m going to try to be as clear as possible to explain the context of the code, i started by coding a “custom service” (basically a folder that stores events) through a ModuleScript that is located in ReplicatedFirst, that ModuleScript also creates all the intvalues that will be used to store data like money, level, exp etc. The inventory system is also structured around intvalues, each of those corresponds to a slot in the inventory, so if there’s an item inside a inventory slot, the intvalue that corresponds to that slot will have a number inside. This part will be explained a bit better soon.

Then i made all the basic stuff for data storage like bindable events to save and load data, and made remote events to store and withdraw items server sidedly to the inventory.

For everyone reading the code, the “syncd” variable may be confusing but it is basically what i said before, “syncd” is the intvalue that corresponds to the slot interacted.

How the inventory system works

  • To store an item
    The code will check if inside the tool there’s an intvalue named “ID”, if there is, the tool is deleted from your hand, the ID’s parent (tool) name appears on the slot you clicked and the value of ‘syncd’ is mirrored to the ID of the tool you had in hands.

  • To withdraw an item
    There’s a folder in ReplicatedStorage named Items, there are located all the items that can be used in the game. The script will look through every descendant named “ID” of the items folder and it will compare if the value of the ID is the same as syncd. Remember, syncd is the intvalue instance that corresponds to the slot interacted with.

Video showing it doesn’t always save the data:

I have tried doing a more dynamic inventory like, only creating values on the player’s inventory data when an item is stored, but i couldn’t make that work either.

Any questions please comment

Module Script code:

local DataStoreService = game:GetService("DataStoreService")
local database = DataStoreService:GetDataStore("database")

local sessionData = {}

local service = Instance.new("Folder") -- creates the custom service
service.Name = "DataStorage"
service.Parent = game.ReplicatedStorage

local DataLoad = Instance.new("BindableEvent") -- creates DataLoad bindEvent
DataLoad.Name = "DataLoad"                       
DataLoad.Parent = service

local DataSave = Instance.new("BindableEvent") -- creates DataSave bindEvent
DataSave.Name = "DataSave"
DataSave.Parent = service

local itemStore = Instance.new("RemoteEvent") -- creates itemStore remoteEvent
itemStore.Name = "itemStored"
itemStore.Parent = service

local itemWithdraw = Instance.new("RemoteEvent") -- creates itemWithdraw remoteEvent
itemWithdraw.Name = "itemWithdraw"
itemWithdraw.Parent = service

-- dataLoad event set up
DataLoad.Event:Connect(function(player, userid)
	
	-- default data
	local dataFolder = Instance.new("Folder")
	dataFolder.Name = "PlayerData"
	
	local Money = Instance.new("IntValue")
	Money.Name = "Money"
	Money.Parent = dataFolder
	
	local Level = Instance.new("IntValue")
	Level.Name = "Level"
	Level.Parent = dataFolder
	
	local EXP = Instance.new("IntValue")
	EXP.Name = "EXP"
	EXP.Parent = dataFolder
	
	local StatPoints = Instance.new("IntValue")
	StatPoints.Name = "StatPoints"
	StatPoints.Parent = dataFolder
	
	local Sword = Instance.new("IntValue")
	Sword.Name = "Sword"
	Sword.Parent = dataFolder
	
	local Fruit = Instance.new("IntValue")
	Fruit.Name = "Fruit"
	Fruit.Parent = dataFolder
	
	local Inventory = Instance.new("Folder")
	Inventory.Name = "Inventory"
	Inventory.Parent = dataFolder
	
	local counter = 0
	repeat
		counter += 1
		local slot = Instance.new("IntValue") -- creates 25 intvalues which are for every slot of the inventory
		slot.Name = "Slot"..counter
		slot.Parent = Inventory
		
	until counter == 25
	
	-------------------------------------------------------
	local success = nil
	local playerData = nil
	local attempt = 1
	
	repeat
		success, playerData = pcall(function()
			return database:GetAsync(userid)
		end)
		
		attempt += 1
		if not success then
			warn(playerData)
			task.wait(3)
		end
	until success or attempt == 5
	
	if success then
		print(player, "was connected to database")
		if not playerData then
			print("Assigning default data")
			playerData = {
				["Money"] = 0,
				["Level"] = 1,
				["EXP"] = 0,
				["StatPoints"] = 0,
				["Sword"] = 0,
				["Fruit"] = 0,
				["Inventory"] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
			}
			
		end
		sessionData[userid] = playerData
	else
		warn("Unable to get data for player", player)
		player:Kick("Unable to load your data. Try again later.")
	end
	---------------------------------------------------------

	Money.Value = sessionData[userid].Money
	Money.Changed:Connect(function()
		sessionData[userid].Money = Money.Value
	end)
	
	Level.Value = sessionData[userid].Level
	Level.Changed:Connect(function()
		sessionData[userid].Level = Level.Value
	end)
	
	EXP.Value = sessionData[userid].EXP
	EXP.Changed:Connect(function()
		sessionData[userid].EXP = EXP.Value
	end)
	
	StatPoints.Value = sessionData[userid].StatPoints
	StatPoints.Changed:Connect(function()
		sessionData[userid].StatPoints = StatPoints.Value
	end)
	
	Sword.Value = sessionData[userid].Sword
	Sword.Changed:Connect(function()
		sessionData[userid].Sword = Sword.Value
	end)
	
	Fruit.Value = sessionData[userid].Fruit
	Fruit.Changed:Connect(function()
		sessionData[userid].Fruit = Fruit.Value
	end)
	
	for i, v in pairs(Inventory:GetChildren()) do -- Inventory:Getchildren() = 25 intvalues, which are the custom inventory slots
		print(sessionData[userid].Inventory[i])
		v.Value = sessionData[userid].Inventory[i]
		v.Changed:Connect(function()
			sessionData[userid].Inventory[i] = v.Value
			print(sessionData[userid].Inventory)
		end)
	end
	dataFolder.Parent = player
end)

-- dataSave event setup
DataSave.Event:Connect(function(player, userid)
	if sessionData[userid] then
		local success = nil
		local errorMsg = nil
		local attempt = 1
		
		repeat
			success, errorMsg = pcall(function()
				database:SetAsync(userid, sessionData[userid])
			end)
			
			attempt += 1
			if not success then
				warn(errorMsg)
				task.wait(3)
			end
		until success or attempt == 5
		
		if success then
			print("Data saved for", player)
		else
			warn("Failed to save data for", player)
		end
	end
end)

itemStore.OnServerEvent:Connect(function(player, syncd) -- itemStore event setup
	local tool = player.Character:FindFirstChildWhichIsA("Tool")
	syncd.Value = tool.ID.Value
	tool:Destroy()
end)

local ToolsStorage = game.ReplicatedStorage:WaitForChild("Items")
local toolDescen = ToolsStorage:GetDescendants()

itemWithdraw.OnServerEvent:Connect(function(player, syncd) -- itemWithdraw event setup
	for i, v in pairs(toolDescen) do
		if v.Name == "ID" and v.Value == syncd.Value then
			local clone = v.Parent:Clone()
			clone.Parent = player.Backpack
			
		end
	end
	syncd.Value = 0
end)

-- ignore below
local module = {}

return module

For saving things such as player data, it’s critical to use :UpdateAsync() instead of :SetAsync(). You may still encounter the issue you’re experiencing with this function, so I believe the problem is due to caching in data stores.

In a nutshell, data stores cache for ~6 seconds, so you’ll want to yield for a short time before attempting that call again.

Try binding a function to game:BindToClose() that yields until your data is for sure saved (give it a few seconds of wiggle room too). This will cause studio to freeze for that amount of time though, so I like to disable it for studio work since data is not critical there.

3 Likes