Function not reaching the end of itself

local function SaveInventory(Player)
	print("1")
	local PlayerInventory = InventoryTables[Player.UserId]
	print("2")
	local InventoryInfo = HttpService:JSONEncode(PlayerInventory)
	print("3")
	InventoryStore:SetAsync(Player.UserId,InventoryInfo)
	print("4")
	print(PlayerInventory, " This is what the inventory looks like when being saved")
end

This function is called when the player leaves the game, but in the output, it only prints 1,2,3. What about InventoryStore:SetAsync(Player.UserId,InventoryInfo) is causing the function to halt?

1 Like

The game could’ve closed before the thread finished running.

1 Like

How would I prevent that from happening?

Why would you need to prevent that from happening? The :SetAsync would have still ran, it’s just that the game would’ve stopped after. You can also save on game restart by using game:BindToClose.

I wanted to prevent that from happening so that it would reach the print statement to confirm that the inventory saved correctly. I’m trying to debug it right now because the inventory is not loading after leaving the game and joining back.

Is your loading system correct?

I think so?

Here’s the code for it if you want to review it

local function SaveInventory(Player)
	local PlayerInventory = InventoryTables[Player.UserId]
	local InventoryInfo = HttpService:JSONEncode(PlayerInventory)
	print(InventoryInfo, " This is what the inventory looks like when being saved")
	InventoryStore:SetAsync(Player.UserId,InventoryInfo)
end
local function LoadInventory(Player)
	local Data = InventoryStore:GetAsync(Player.UserId)
	local PlayerInventory

	if Data then
		local InventoryData = HttpService:JSONDecode(Data)
		print(InventoryData,"This is what's being pulled when JSONDecoding")
		PlayerInventory = Inventory.new(Player.UserId)
	PlayerInventory:LoadFromData(InventoryData)
	print(PlayerInventory, " This is what the inventory looks like when being loaded")
	else
		PlayerInventory = Inventory.new(Player.UserId)
	end

	InventoryTables[Player.UserId] = PlayerInventory
end

PlayerInventory:LoadFromData

function Inventory:LoadFromData(data)
	if type(data) == "table" then
		self = data
		warn(self.Data)
	else
		warn("Invalid data format for loading inventory.")
	end
end

What I mean by that is are you sure it’s not saving? Or is it because your loading system is wrong? Could you try printing what data was retrieved and see if it was saved or not?

I can’t tell if it is or not. This is what prints from SaveInventory() when I leave the game. It has the two items I picked up in it.


This is what prints from LoadInventory() when I boot the game back up. The table is blank.

I believe it is this line that is causing your data to not be correct under function LoadInventory:

It may work if you change it to be this:

PlayerInventory = PlayerInventory:LoadFromData(InventoryData)

However I cannot tell entirely due to not being able to see the ModuleScript that handles inventory loading; it could possibly be that you aren’t returning the new created self from the function also.

You’re showing the end function but not what is calling it. Also you don’t have much time to call things on leaving the game. Or I should say many calls. You may want to preload some of this. Can PlayerInventory be updated when they add/remove items to save them cycles from the end function?

Also a pcall will boost speed for a moment when called while checking for any potential errors during the SetAsync

Maybe something like this …

local function SaveInventory(Player)
    local PlayerInventory = InventoryTables[Player.UserId]
    local InventoryInfo = HttpService:JSONEncode(PlayerInventory)

    local success, error_message = pcall(function()
        InventoryStore:SetAsync(Player.UserId, InventoryInfo)
    end)

    if not success then
        warn("Error saving inventory:", error_message)
    else
        print(PlayerInventory, " This is what the inventory looks like when being saved")
    end
end

Here’s all the code

local DataStoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local InventoryStore = DataStoreService:GetDataStore("InventoryStore")

local StackLimits = require(workspace.StackLimits)
local Inventory = require(script.Inventory)
local InventoryTables = {}

local function CreateNewInventory(Player)
	local ID = Player.UserId
	local inventory = Inventory.new(ID)
	InventoryTables[ID] = inventory
end

local function SaveInventory(Player)
	local PlayerInventory = InventoryTables[Player.UserId]
	local InventoryInfo = HttpService:JSONEncode(PlayerInventory)
	print(InventoryInfo, " This is what the inventory looks like when being saved")
	InventoryStore:SetAsync(Player.UserId,InventoryInfo)
end
local function LoadInventory(Player)
	local Data = InventoryStore:GetAsync(Player.UserId)
	local PlayerInventory

	if Data then
		local InventoryData = HttpService:JSONDecode(Data)
		print(InventoryData,"This is what's being pulled when JSONDecoding")
		PlayerInventory = Inventory.new(Player.UserId)
	PlayerInventory:LoadFromData(InventoryData)
	print(PlayerInventory, " This is what the inventory looks like when being loaded")
	else
		PlayerInventory = Inventory.new(Player.UserId)
	end

	InventoryTables[Player.UserId] = PlayerInventory
end

local function GetInventory(Player)
	local PlayerInventory = InventoryTables[Player.UserId]
	print(PlayerInventory, "This is the inventory when mapping")
	game.ReplicatedStorage.Inventory.MapInventory:FireClient(Player, PlayerInventory)
end

local function SubtractItem(Player,args)
	print(Player,args)
	local Inventory = InventoryTables[Player.UserId]
	local i = 1
	while true do
		if args["Item"..i] then
			local Subtract = Inventory:SubtractItem(args,i)
			i+=1
		else
			break
		end
	end
	
end


game.ReplicatedStorage.Inventory.AddItemBindable.Event:Connect(function(player,args)
	print(args)
	local PlayerInventory = InventoryTables[args.PlayerID]
	local i = 1
		while true do
		if args["Item"..i] then
			local AddItem = PlayerInventory:AddItem(args,i)
			if AddItem then
				PlayerInventory:StackFixer(args,i)
			end
			local FullFixer = PlayerInventory:FullFixer(args,i)
			if FullFixer then
				args["Item"..i] = nil
			end
			i+=1
		else
			args.Location.Finished:Fire(args)
				i = 1
				break
			end
		end
	warn(Inventory)
end)


--game.Players.PlayerAdded:Connect(CreateNewInventory)
game.Players.PlayerAdded:Connect(LoadInventory)
game.Players.PlayerRemoving:Connect(SaveInventory)

game.ReplicatedStorage.Inventory.MapInventory.OnServerEvent:Connect(GetInventory)
game.ReplicatedStorage.Inventory.SubtractItem.OnServerEvent:Connect(SubtractItem)
local StackLimits = require(workspace.StackLimits)
local Inventory = {}

local Item
local Amount
local CompletedAdd = false
local MAX_SLOTS = 28
local CurrentSlot
Inventory.__index = Inventory

function Inventory.new(player)
	local i = 1
	local self = {}

	self.Player = player
	self.Data = {}
	for i = 1, MAX_SLOTS do
		self.Data[i] = {}
	end
	
	self.Data["Extra"] = {}
	i = 1
	return setmetatable(self, Inventory)
end

function Inventory:NewSlot(Data,NewItem,NewAmount,TotalAmount)
	for Key,Slot in pairs(Data) do
		if next(Slot) == nil then
			if Key == "Extra" then
				Data["Extra"][NewItem] = TotalAmount
				return false
			else
				CurrentSlot = Key
				Data[Key][NewItem] = NewAmount
				return true
			end
		end
		if not (Key == "Extra") then
			Key += 1
		else
			break
		end
	end
end

function Inventory:AddItem(args,i)
	local Location = args.Location
	CompletedAdd = false
	local Passed = false
	local Extra
	
	if args["Item"..i] then
		Item = args["Item"..i]
		Amount = args["Num"..i]
		
		for Key,Slot in self.Data do
			print(self.Data,Slot,Slot[Item])
		if Slot[Item] and Slot[Item] < StackLimits[Item] then
			Passed = true
			Slot[Item] += Amount
			CurrentSlot = Key
			if Slot[Item] > StackLimits[Item] then
				warn("Slot went over the stack limit whilst adding onto an existing slot")
				Extra = Slot[Item] - StackLimits[Item]
				CurrentSlot = Key
				end
				return true
		elseif not Slot[Item] then
			Passed = false
		end
		end
		
		print(Passed)
		if Passed == false then
			print("New slot is being created for",Item)
			local NewSlot = Inventory:NewSlot(self.Data,Item,Amount)
			print(self.Data)
			if NewSlot then
				return true
			else
				return false
			end
			
		end
		i+=1
	end
end
function Inventory:SubtractItem(args,i)
	if args["Item"..i] then
		Item = args["Item"..i]
		Amount = args["Num"..i]
		for Key,Slot in pairs(self.Data) do
			warn(Item)
			if Slot[Item] then
				warn(Slot[Item])
				Slot[Item] -= Amount
				if Slot[Item] <= 0 then
					self.Data[Key]={}
					print(self.Data)
				end
			end
			
		end
	end
end
function Inventory:StackFixer(args,i)
	if CompletedAdd == false then
		if self.Data[CurrentSlot][Item] > StackLimits[Item] then
			warn("Our current item is set to",self.Data[CurrentSlot][Item])
			local FullStacks = math.floor(self.Data[CurrentSlot][Item]/StackLimits[Item])
			local Remaning = self.Data[CurrentSlot][Item] % StackLimits[Item]
			local TotalAmount = (FullStacks*StackLimits[Item])+Remaning
			warn(FullStacks,Remaning)
			self.Data[CurrentSlot][Item] = StackLimits[Item]
			
			while FullStacks > 1 do
				Item = args["Item"..i]
				Amount = StackLimits[Item]
				local NewSlot = Inventory:NewSlot(self.Data,Item,Amount,TotalAmount)
				if NewSlot then
					FullStacks -= 1
					TotalAmount -= Amount
					print(TotalAmount)
				else
					FullStacks = 0
				end
			end

			if Remaning > 0 then
				Item = args["Item"..i]
				Amount = Remaning
				Inventory:NewSlot(self.Data,Item,Amount,TotalAmount)
			end

		end
	else
		return
	end
	
end

function Inventory:FullFixer(args,i)
	if CompletedAdd == false then
		if next(self.Data["Extra"]) == nil then
			return true
		else
			local Location = args.Location
			local ItemSurplus = self.Data["Extra"][Item]
			warn("GetBack Firing")
			Location:FindFirstChild("GetBack"):Fire(ItemSurplus,i)
			self.Data["Extra"] = {}
		end
	else
		return
	end

end
function Inventory:LoadFromData(data)
	warn(data)
		self = data
		warn(self)
	return self
end
return Inventory

Another thing that might be important is that the variable InventoryInfo prints
" {“Data”:[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],“Player”:1572007720}"
instead of one of the {…}

Well this isn’t good …
Also maybe try setting local PlayerInventory = InventoryTables[Player.UserId]
when you add and sub items … that is just a table, So you can have that precached so you don’t need to add that to the small amount of time you have when ending.

1 Like

Thats because it becomes a JSON string.

Anyway, you could just change the line in the function LoadInventory to what I put and it’d be working, but that’s up to you.

1 Like

Oh I see … Never used that myself. (Yet)

1 Like

I changed it and nothing happened.

Edit: I tried putting it into 2122Jay’s code and the Inventory functions broke

You can force the server to stay alive for a maximum of 30 more seconds after shutdown using BindToClose:

--Paste this in a script under ServerScriptService
game:BindToClose(function()
	--keep the server alive for 20 more seconds
	task.wait(20) --the maximum value here is 30
end)

I noticed that an infinite loop I used for testing outside of that script was able to fire the following error just before the actual shutdown occurred: Not running script because past shutdown deadline. I assume you can take advantage of this by pcalling the API call you want to make and checking for that error so you know if it finished on time or not.

But that is on a full close … as in that was the last player in the game and they just left.
As I understand it. That is why I asked what is calling that save function.

1 Like

“broke” as in showed: warn(“Error saving inventory:”, error_message)?
pcalls don’t break the code …

1 Like

Broke as in when I tried calling AddItem() it told me that method didn’t exist

1 Like