(sorry for late reply)
(extra late reply, DevForum went read-only and deleted most of my progress)
Good question! The logic may seem a little daunting at first, but try your best to gain an intuition for it. It’s really easy once you know what’s going on.
Disclaimer: This isn’t a script you can copy+paste in your game and expect it to work. This is a basic expression; a framework, of a true inventory system. The design of your game is inherently yours, and it should stay that way.
In this example, the inventory is an array of 27 items. We can use a STACKS dictionary to define stack sizes for different items.
local STACKS = {
stone = 64,
ender_pearl = 16,
diamond_sword = 1
}
local Inventory = {}
Inventory.Items = {}
for i = 1, 27 do
table.insert(Inventory.Items, { Count = 0 })
end
Now, I’ll define a helper function to merge items:
-- merges source onto dest, swap arguments for the opposite effect
function Inventory.Merge(source, dest)
if not dest.Item or (source.Item == dest.Item) then
-- either no item in dest, or both types are the same
local result = source.Count + dest.Count
local max_stack = STACKS[dest.Item]
dest.Count = math.min(result, max_stack)
source.Count = result - max_stack
if source.Count < 1 then
-- source depleted; destroy the item
-- handle this your own way, i'll just unassign its type
source.Item = nil
end
end
end
If we test this function, you can see items get properly stacked just like they do in untitled block game.
-- for example, source could be the item held at the cursor
local source = {
Item = "stone",
Count = 48
}
local dest = {
Item = "stone",
Count = 32
}
Inventory.Merge(source, dest)
print(source.Count) -- 16
print(dest.Count) -- 64

Now, items don’t get swapped yet; they always go to the inventory. But, you can implement that with a few extra checks. I’ll make a separate function for that, as swapping logic may be unintentional at times, such as when picking items off the ground.
function Inventory:Swap(slot, item)
local current = self.Items[slot] -- get the item for later
self.Items[slot] = item -- replace the item
return current -- return the old item. this goes to the cursor
end
Finally, we can step back a bit, and simulate a cursor-to-inventory interaction, such as left-clicking on items.
local function Inventory:LMB(slot, cursor)
-- cursor being the item held at the cursor, if any
local inv = self.Items[slot]
if not (inv.Item and cursor.Item) or inv.Item ~= cursor.Item then
-- if there's one or more items missing, just swap
-- if items aren't the same, swap
return self:Swap(slot, cursor)
else
-- merge from cursor
self.Merge(cursor, inv)
end
end
And that’s it! Whether your stack size is 100, 64 or 1, the same logic will still apply.
The actual networking required to make this work is for you to do. This is a formulaic expression, and will hopefully give you the intuition to create a real, and awesome, inventory system. If it’s wrong by chance, I apologize. I wrote this on a touchscreen.
I also apologize if this isn’t exactly what you needed right from the gate. I don’t feel like writing an entire functioning system, networking and all, on my phone, while mentally debugging as best as I can. Inventory systems require at least some programming knowledge as a prerequisite, so I’m assuming you have that.