Making an Inventory system with Profile Service and Replica Service

  1. What do you want to achieve? Keep it simple and clear!
    I aim to create a system where the Inventory UI on the client-side accurately reflects the current state of the player’s inventory on the server-side. When an item is added to or removed from the player’s inventory, the UI should update accordingly to show the current inventory items.

  2. What is the issue? Include screenshots / videos if possible!
    The primary issue is that the Inventory UI does not update or refresh when items are added or removed from the inventory. When an item is sold, it remains visible in the UI, and when new items are obtained, they do not appear in the UI. This discrepancy between the server-side data and client-side UI could potentially confuse or frustrate players.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I have tried to use the ReplicaController to listen for changes in the inventory array and trigger a function to update the UI accordingly. Additionally, I tried creating a function to handle the addition and removal of UI elements based on the changes in the inventory.

function Inventory:LoadItems()
	ReplicaController.ReplicaOfClassCreated("PlayerProfile", function(replica)
		if not replica then
			return
		end

		local inventory = replica.Data.ItemInventory.Inventory

		local bagSize = replica.Data.ItemInventory.MaxBagSize


		local itemCount = 0
		for _ in pairs(inventory) do
			itemCount = itemCount + 1
			
		end

		BagSize.Text = `{tostring(itemCount)}/{tostring(bagSize)}`
		while #inventory > bagSize do
			BagSize.Text = `{tostring(itemCount)}/{tostring(bagSize)}`
			task.wait(5)
		end

		local IDCache = {}
		for _, itemInfo in pairs(inventory) do
			print("Processing item", itemInfo.Name)
			if InventScroll then
				local newItemButton = InventTemplate:Clone()
				newItemButton.Parent = InventScroll
				newItemButton.Name = itemInfo.Name

				print("Updating item button text for", itemInfo.Name)
				newItemButton.ItemName.Text = itemInfo.Name
				newItemButton.Rarity.Text = itemInfo.Rarity
				newItemButton.Value.Text = tostring(itemInfo.Value)

				print("Updating viewport image for", itemInfo.Name)
				self:UpdateViewportImage(newItemButton, itemInfo.Name)
				self:MapSellButton(newItemButton, itemInfo.Name)
			else
				print("InventScroll not found")
			end
			table.insert(IDCache, itemInfo.Name)
		end

		print("Cleaning up old item buttons...")
		for _, Frame in pairs(InventScroll:GetChildren()) do
			if Frame:IsA("ImageButton") and not table.find(IDCache, Frame.Name) then
				Frame:Destroy()
			end
		end
	end)
end

function Inventory:MapSellButton(itemButton, itemName)
	local sellButton = itemButton:FindFirstChild("SellButton")
	if sellButton then
		sellButton.MouseButton1Click:Connect(function()
			SellDebounce = true

			-- Ensure your server-side function and events are set up to handle this request
			Network:InvokeServer("SellItem", Player, itemName)
			playSound("CashRegister", 1, workspace.Sounds)
			SellDebounce = false
		end)
	else
		warn("SellButton not found in itemButton for item:", itemName)
	end
end

function Inventory:UpdateInventoryUI(addedItems, removedItems)
    -- Handle added items
    for _, item in pairs(addedItems) do
        local newItemButton = InventTemplate:Clone()
        newItemButton.Parent = InventScroll
        newItemButton.Name = item.Name
        
        newItemButton.ItemName.Text = item
        newItemButton.Rarity.Text = item.Rarity
        newItemButton.Value.Text = tostring(item.Value)
        
        self:UpdateViewportImage(newItemButton, item.Name)
        self:MapSellButton(newItemButton, item.Name)
    end
    
    -- Handle removed items
    for _, item in pairs(removedItems) do
        for _, frame in pairs(InventScroll:GetChildren()) do
            if frame:IsA("ImageButton") and frame.Name == item then
                frame:Destroy()
            end
        end
    end
end

function Inventory:Init()
	self:LoadItems()
	ReplicaController.ReplicaOfClassCreated("PlayerProfile", function(replica)
		if not replica then
			return
		end
		print("replica found")
		replica:ListenToArrayInsert({ "ItemInventory", "Inventory" }, function(newValue)
			print(newValue.Name .. " has been added.")
			Inventory:UpdateInventoryUI({ newValue }, {})
			local bagSize = replica.Data.ItemInventory.MaxBagSize
			BagSize.Text = `{tostring(newValue)}/{tostring(bagSize)}`
		end)
		replica:ListenToArrayRemove({ "ItemInventory", "Inventory" }, function(newValue)
			print(newValue.Name .. " has been removed.")
			Inventory:UpdateInventoryUI({}, { newValue })
			local bagSize = replica.Data.ItemInventory.MaxBagSize
			BagSize.Text = `{tostring(newValue)}/{tostring(bagSize)}`
		end)
	end)
end

More Info
Once the player complete the action, it will add 3 random items to the player’s inventory.

If you can help me that would be greatly appreciated!

i found a few errors in your code and i tried to correct them: Heres another code:

''local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local Network = ReplicatedStorage:WaitForChild(“Network”)
local ReplicaController = require(script:WaitForChild(“ReplicaController”))

local InventScroll = script.Parent.InventScroll
local BagSize = script.Parent.BagSize
local InventTemplate = script.Parent.InventTemplate

local Inventory = {}

function Inventory:MapSellButton(itemButton, itemName)
local sellButton = itemButton:FindFirstChild(“SellButton”)
if sellButton then
sellButton.MouseButton1Click:Connect(function()
Network:InvokeServer(“SellItem”, Players.LocalPlayer, itemName)
end)
else
warn(“SellButton not found in itemButton for item:”, itemName)
end
end

function Inventory:UpdateInventoryUI(addedItems, removedItems)
for _, item in pairs(addedItems) do
local newItemButton = InventTemplate:Clone()
newItemButton.Parent = InventScroll
newItemButton.Name = item.Name

    newItemButton.ItemName.Text = item.Name
    newItemButton.Rarity.Text = item.Rarity
    newItemButton.Value.Text = tostring(item.Value)

    self:MapSellButton(newItemButton, item.Name)
end

for _, itemName in pairs(removedItems) do
    local itemButton = InventScroll:FindFirstChild(itemName)
    if itemButton then
        itemButton:Destroy()
    end
end

end

function Inventory:LoadItems()
ReplicaController.ReplicaOfClassCreated(“PlayerProfile”, function(replica)
if not replica then
return
end

    local inventory = replica.Data.ItemInventory.Inventory
    local bagSize = replica.Data.ItemInventory.MaxBagSize

    local itemCount = #inventory
    BagSize.Text = tostring(itemCount) .. "/" .. tostring(bagSize)

    for _, itemInfo in pairs(inventory) do
        self:UpdateInventoryUI({ itemInfo }, {})
    end
end)

end

function Inventory:Init()
self:LoadItems()

ReplicaController.ReplicaOfClassCreated("PlayerProfile", function(replica)
    if not replica then
        return
    end

    replica:ListenToArrayInsert({ "ItemInventory", "Inventory" }, function(_, newValue)
        self:UpdateInventoryUI({ newValue }, {})
        BagSize.Text = tostring(#newValue) .. "/" .. tostring(replica.Data.ItemInventory.MaxBagSize)
    end)

    replica:ListenToArrayRemove({ "ItemInventory", "Inventory" }, function(_, removedValue)
        self:UpdateInventoryUI({}, { removedValue })
        BagSize.Text = tostring(#replica.Data.ItemInventory.Inventory) .. "/" .. tostring(replica.Data.ItemInventory.MaxBagSize)
    end)
end)

end

Inventory:Init()
`’

2 Likes

Thank you for responding, I will try these changes out.

1 Like

I appreciate the help, your solution worked as intended. Thank you!

1 Like

No problem! I wish you good luck on your future things :smiley:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.