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 ="Folder") -- creates the custom service
service.Name = "DataStorage"
service.Parent = game.ReplicatedStorage

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

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

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

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

-- dataLoad event set up
DataLoad.Event:Connect(function(player, userid)
	-- default data
	local dataFolder ="Folder")
	dataFolder.Name = "PlayerData"
	local Money ="IntValue")
	Money.Name = "Money"
	Money.Parent = dataFolder
	local Level ="IntValue")
	Level.Name = "Level"
	Level.Parent = dataFolder
	local EXP ="IntValue")
	EXP.Name = "EXP"
	EXP.Parent = dataFolder
	local StatPoints ="IntValue")
	StatPoints.Name = "StatPoints"
	StatPoints.Parent = dataFolder
	local Sword ="IntValue")
	Sword.Name = "Sword"
	Sword.Parent = dataFolder
	local Fruit ="IntValue")
	Fruit.Name = "Fruit"
	Fruit.Parent = dataFolder
	local Inventory ="Folder")
	Inventory.Name = "Inventory"
	Inventory.Parent = dataFolder
	local counter = 0
		counter += 1
		local slot ="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
		success, playerData = pcall(function()
			return database:GetAsync(userid)
		attempt += 1
		if not success then
	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}
		sessionData[userid] = playerData
		warn("Unable to get data for player", player)
		player:Kick("Unable to load your data. Try again later.")

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

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

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

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
	syncd.Value = 0

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