1. I'm attempting to write an extension method which removes a certain amount of a particular item from a player's inventory. Here's what I have:

    Code:
    /// <summary>
    /// Removes a number of a particular item from the player inventory.
    /// </summary>
    /// <param name="inventory">The inventory to operate upon.</param>
    /// <param name="itemId">The item ID to remove.</param>
    /// <param name="amount">The number of items to remove (e.g. 100 wood)</param>
    /// <param name="mustHaveEnough">Only remove items if the player has enough. If set to false, everything will be taken if the amount exceeds what the player has.</param>
    /// <returns>The number of items removed.</returns>
    public static int RemoveItems(this PlayerInventory inventory, int itemId, int amount, bool mustHaveEnough)
    {
        // find all stacks of the particular item
        var stacks = inventory.FindItemIDs(itemId);    // got no stacks? return false
        if (stacks.Count == 0)
            return 0;    // get the total number of the particular item in the player's inventory
        int totalAmount = stacks.Sum(item => item.amount);    // if we must have enough, and the player has insufficient, return 0
        if (mustHaveEnough && (totalAmount < amount))
            return 0;    // loop through all stacks to remove the items.
        int remaining = amount;
        foreach (var stack in stacks)
        {
            if (stack.amount <= remaining)
            {
                // stack is smaller than we need, or exactly the right size, so remove the whole stack
                remaining -= stack.amount;
                stack.Remove(0f);
            }
            else
            {
                // stack has more than we need, so just remove some
                stack.amount -= remaining;
                stack.MarkDirty();
                remaining = 0;
            }        // if we've taken all we need, drop out
            if (remaining == 0)
                break;
        }    // return the amount that we were able to take
        // (for mustHaveEnough = true, remaining should always be 0)
        return amount - remaining;
    }
    This appears to function as expected, but I'm not well-versed in Rust's API. Are there any gotchas I should be aware of? I'm primarily concerned about the MarkDirty call (and lack thereof on the Remove branch) and race conditions (stack amounts are summed, then later removed, allowing for changes between the two operations) or other potential exploits.
     
  2. I have actually not used a lot of inventory related methods but I do not think this would cause any issues as you describe. Perhaps, if you haven't already, you could have a look at ItemContainer.Remove(Item item), Item.Remove(float fTime) or ItemContainer.Take(List<Item> collect, int itemid, int iAmount) which is all used by the server to remove items from an ItemContainer.

    Code:
    internal bool Remove(Item item)
    {
        if (!this.itemList.Contains(item))
        {
            return false;
        }
        this.itemList.Remove(item);
        item.parent = null;
        this.MarkDirty();
        if (this.onItemAddedRemoved != null)
        {
            this.onItemAddedRemoved();
        }
        return true;
    }public void Remove(float fTime)
    {
        if (this.removeTime <= 0f)
        {
            foreach (ItemMod mod in this.info.GetComponents<ItemMod>())
            {
                mod.OnRemove(this);
            }
            this.removeTime = Time.time + fTime;
            this.OnDirty = null;
            this.position = -1;
            if (this.isServer)
            {
                SingletonComponent<ItemManager>.Instance.RemoveItem(this, fTime);
            }
        }
    }public int Take(List<Item> collect, int itemid, int iAmount)
    {
        int num = 0;
        List<Item> list = new List<Item>();
        foreach (Item item in this.itemList)
        {
            if ((item.info.itemid == itemid) && !item.isBlueprint)
            {
                int num2 = iAmount - num;
                if (item.amount > num2)
                {
                    item.MarkDirty();
                    item.amount -= num2;
                    num += num2;
                    Item item2 = ItemManager.CreateByItemID(itemid, 1, false);
                    item2.amount = num2;
                    if (collect != null)
                    {
                        collect.Add(item2);
                    }
                    break;
                }
                if (item.amount <= num2)
                {
                    num += item.amount;
                    list.Add(item);
                    if (collect != null)
                    {
                        collect.Add(item);
                    }
                }
                if (num == iAmount)
                {
                    break;
                }
            }
        }
        foreach (Item item3 in list)
        {
            item3.RemoveFromContainer();
        }
        return num;
    }
    
    An implementation of Item.Remove in Item.UseOnItem
    Code:
    public void UseOneItem()
    {
        if (this.amount <= 1)
        {
            this.Remove(0f);
        }
        else
        {
            this.amount--;
            this.MarkDirty();
        }
    }
    
    An implementation of ItemContainer.Take in ItemCrafter.CollectIngredients:
    Code:
    private void CollectIngredient(int item, int amount, List<Item> collect)
    {
        foreach (ItemContainer container in this.containers)
        {
            amount -= container.Take(collect, item, amount);
        }
    }
    
     
  3. Thanks for the info! Very useful. The Rust framework is remarkably intuitive once you find where things are.
     
  4. What does this.MarkDirty(); do?
     
  5. I guess I`ve found a bug in ItemContainer.Take method, but could be mistaken since I am just a beginner in programming.
    The thing is that if player has, for example, 25 wood in main container and 100 in belt, using player.inventory.Take(null, itemid, 30); results in taking 25 wood from main container and 30 from belt.
    Probably the error is in PlayerInventory class
    Code:
    public int Take(List<Item> collect, int itemid, int amount)
        {
            int num = 0;
            num = num + this.containerMain.Take(collect, itemid, amount);
            if (amount == num)
            {
                return num;
            }
            num = num + this.containerBelt.Take(collect, itemid, amount);
            if (amount == num)
            {
                return num;
            }
            num = num + this.containerWear.Take(collect, itemid, amount);
            if (amount == num)
            {
                return num;
            }
            return num;
        }