Table of Contents

Class ItemStackable

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Represents the stackable component of an item, managing stack size, addition, removal, and unstacking logic.

public class ItemStackable : IStackable<Item>
Inheritance
ItemStackable
Implements
Inherited Members

Remarks

STACK MATHS CONTRACT — read before editing.

Every quantity here is a uint. There is no such thing as a negative stack, so every subtraction must be provably non-negative before it runs; an underflow does not produce a small number, it produces ~4.29 billion items and mints currency out of nothing.

This class previously used UIntExtensions.AbsoluteSubtract to compute "how much is left over after the merge". That helper returns |a - b|, NOT a saturating subtract, so it is only correct in the overflow branch (incoming > capacity). In the ordinary fits-entirely branch it returned the unused capacity instead of zero, which inflated the donor stack and underflowed the destination: MaxStackSize 10, destination 1, source 2 produced a destination of 4,294,967,292. The call sites are fixed here rather than changing AbsoluteSubtract — the helper lives in the shared utility library, its name promises absolute difference, and other callers rely on that meaning.

Constructors

ItemStackable(Item, uint)

Constructs a stackable component for an item with the given amount.

public ItemStackable(Item item, uint amount)

Parameters

item Item

The item instance.

amount uint

The initial stack amount.

Fields

Amount

The current amount in the stack.

public uint Amount

Field Value

uint

Properties

IsStackFull

Returns true if the stack is full (reached or exceeded max stack size).

public bool IsStackFull { get; }

Property Value

bool

Remarks

Compares with >= rather than == on purpose. A stack that was corrupted by an earlier overflow (or by a template whose MaxStackSize was lowered after the item was saved) sits above the cap; with an equality test such a stack reported "not full" forever and kept absorbing more items. The inequality lets an already-corrupted stack re-cap instead of growing.

RemainingCapacity

Returns the free room left in this stack, saturating at zero. A stack sitting at or above MaxStackSize reports no remaining capacity rather than underflowing into a ~4 billion result.

public uint RemainingCapacity { get; }

Property Value

uint

Methods

AddToStack(Item)

Adds the other item to this stack and sets the other stack's size to the remainder, if any. Returns false on failure.

public bool AddToStack(Item other)

Parameters

other Item

The item to add to the stack.

Returns

bool

True if the item was added, false otherwise.

Remarks

Conserves quantity exactly: transferred is clamped to whichever of the incoming amount and the remaining capacity is smaller, so this.Amount + other.Amount is identical before and after the call. Neither side can underflow.

CanAddToStack(Item)

Returns true only if the entire other item can be added to this stack. Checks for matching template, seed, and stack capacity.

public bool CanAddToStack(Item other)

Parameters

other Item

The item to add to the stack.

Returns

bool

True if the item can be fully added, false otherwise.

Remove(uint)

Removes the specified amount from the stack. Destroys the item if the stack reaches zero.

public void Remove(uint amount)

Parameters

amount uint

The amount to remove.

Remarks

Saturating. Removing more than the stack holds empties the stack and logs, rather than wrapping the uint around into a ~4 billion stack that also sails past the Amount == 0 destroy check.

TryUnstack(uint, out Item)

Attempts to split amount off this stack into a new item instance.

public bool TryUnstack(uint amount, out Item instance)

Parameters

amount uint

The amount to unstack.

instance Item

The new item instance, the original item, or null on failure.

Returns

bool

True if unstacking was successful, false otherwise.

Remarks

Requesting the whole stack (or more) is not a split — the original instance is handed back untouched so the caller can move it wholesale, and this stack is left alone.

A genuine split allocates a real new Item carrying the same template and the same generation seed, so the two halves remain mutually stackable. The new instance has ID = 0 and Slot = -1: it is not yet a database row and not yet in a container. The caller owns placing it and persisting it. The previous implementation decremented this stack, set instance = null, and still returned true — silently destroying the split-off quantity for any caller that trusted the result.