Issues with saving a grid inventory/item class system

I’m working on a grid inventory, where items can be multiple sizes such as 2x2 or 3x3, etc. I’m using an OOP class called “Item” to create the items that can be equipped/put into the inventory. Each item will look similar to this:

local testBullet = {}
testBullet.GeneralData = {}
testBullet.GeneralData.ItemId = "TestBullet"
testBullet.GeneralData.Type = "Round"
testBullet.GeneralData.Weight = 0.01
testBullet.GeneralData.GridSize = Vector2.new(1, 1)
testBullet.GeneralData.GridIcon = "rbxassetid://0"
testBullet.GeneralData.StackSize = 999
return TestBullet

A major issue is that obviously data stores can’t use Instances, and when an “item” is created, it is associated with an Instance such as a tool.

function Item.new(itemInstance: Instance, overwrite)
	local self = setmetatable(overwrite or require(ItemConfigs:FindFirstChild(itemInstance.Name)), Item)
	
	warn("[Item Class] Create Item")
	
	self.Model = itemInstance
	
	itemCache[itemInstance] = self
	
	return self
end

( Overwrite is used when the player rejoins, and the data manager creates an item from the player’s inventory, replacing the default item data with the saved one )

All player data is in another table, which contains the Inventory Class". This table saves DIRECTLY to the datastore, so all properties of inventory and their attributes are saved. The main issue is that the items are associated using an Instance “self.Model = Instance”. If I remove self.Model then the game does not know which instance the “item” belongs to. How can I reformat my code to fit this?

Here’s a larger section of code:

Inventory Manager

local function onPlayerAdded(player : Player)
	repeat wait() until PlayerDataManager:GetDataFromPlayer(player)
	local playerData = PlayerDataManager:GetDataFromPlayer(player)
	local function onCharacterAdded(character)
		local inventory
		if playerData.Inventory then
			for i, item in pairs(playerData.Inventory.items) do
				local newItemModel = ServerStorage.Server.Assets.Items:FindFirstChild(item.GeneralData.ItemId):Clone()
				ItemClass.new(newItemModel, item)	
				newItemModel.Parent = player:FindFirstChild("Backpack")
			end
			inventory = InventoryClass.new(player, playerData.Inventory.Containers, playerData.Inventory.Equipped, playerData.Inventory.Items)
		else
			inventory = InventoryClass.new(player)
		end
		playerData.Inventory = {}
		playerData.Inventory.containers = inventory.containers
		playerData.Inventory.equipped = inventory.equipped
		playerData.Inventory.items = inventory.items
		
		--[[
		local a = ServerStorage.Server.Assets.Items:FindFirstChild("TestBullet"):Clone()
		ItemClass.new(a)	
		a.Parent = player:FindFirstChild("Backpack")
		--]]
		
		local humanoid = character:WaitForChild("Humanoid")
		humanoid.Died:Connect(function()
			local oldInventory = InventoryClass:GetInventory(player)
			oldInventory:DropAllItems()
			oldInventory:Destroy()
			playerData.Inventory = nil
		end)
	end
	if player.Character then onCharacterAdded(player.Character) end
	player.CharacterAdded:Connect(onCharacterAdded)
end

Inventory Class

function Inventory.new(player, containers, equipped, items)
	local self = setmetatable({}, Inventory)
	
	warn("[InventoryClass] Create Inventory")
	
	repeat wait() until player.Backpack
	
	self.player = player
	self.containers = containers or {}
	self.equipped = equipped or {}
	self.items = items or {}
	
	self.containers[1] = ContainerClass.new(self, Vector2.new(8, 3), "Pockets")
	
	self.player.Backpack.ChildAdded:Connect(function(...) self:_BackpackChildAdded(...) end)
	self.player.Backpack.ChildRemoved:Connect(function(...) self:_BackpackChildRemoved(...) end)
	
	inventoryCache[player] = self
	
	for i, v in pairs(self.player.Backpack:GetChildren()) do
		self:_BackpackChildAdded(v)
	end
	
	return self
end

You’d have to convert the instance into data that can be saved.
So for example, you just have a big folder of all items and all you need is the name of the item to re-add it. Then store the name instead of the instance, and when the player joins, go through their inventory and replace the names with newly given instances.
If you store anything else within the instance however, you’ll have to add that to the data too. and then re-add it to a new instance when you load the data.

The main issue is that when I create an item such as a tool, the item class is created. So when the data is saved, the instance is still inside the table. I don’t need to save the instance to the datastore, only the name, but I need to keep track of the instance when it’s created so it can still be equipped and tracked by the item class.

The players data is a table that contains the inventory, and all of its children such as .items and .containers, the .items table contains the item class, which contains the instance in a value valled self.Model

PlayerData = {
	Inventory = {
		Items = {
			[Instance3219038812] = {
				GridSize = {1,1},
				ItemId = "TestItem",
				Model = Instance3219038812,
			}
		}
	}
}

I could change the way that the data is stored, such as using GUIDs to keep track, however the .Model variable is still necessary to keep track of the item.

You need to change the formatting of the data when you save it, instead of directly saving it like you said you currently do in the original post.
Since you only need the name of the item, you can discard the model value when you go to save. And swap out the index for a GUID.
Then when the time comes to load the players data, you go through and generate new instances for each of their items and re-format them to the current format, bringing back the model value.

I’ll try your idea about not saving the PlayerData directly and instead formatting it before saving, however i’m using ProfileService to save data easier, and there’s no way to determine when it saves since it auto saves. I think if I just create a temporary table for server-wide player data to store data, and then make a global table that actually gets saved by the datastore, I can filter what goes through it. Thank you!